Compare commits

..
Author SHA1 Message Date
Rob Bygrave 33d7e10ad8 [maven-release-plugin] prepare release avaje-ebeanorm-4.0.2 2014-05-18 23:18:42 +12:00
Rob Bygrave 2201f9220e Change version in pom for 4.0.2 release 2014-05-18 23:17:17 +12:00
Rob Bygrave b8743a5ef2 Fix for #85 - Invalid query when ORDER BY with DISTINCT 2014-05-18 23:00:28 +12:00
Rob Bygrave b587aa8172 No effective change - touch up test assert 2014-05-18 22:04:37 +12:00
Rob Bygrave c37fa83675 No effective change - update test with better assert 2014-05-18 21:51:14 +12:00
Rob Bygrave 08602afcff Fix for #37 - disjunction expression should not produce inner join - left outer join instead 2014-05-18 21:17:19 +12:00
Rob Bygrave 5b98d82f58 No effective change - reformat 2014-05-17 23:53:23 +12:00
Rob Bygrave 22cd5b0e82 Fix for #128 storing UUID as binary 2014-05-17 21:52:51 +12:00
Rob Bygrave 93855478dd Fix for #128 storing UUID as binary 2014-05-17 21:52:18 +12:00
Rob Bygrave 59b86d5b19 Fix for #128 - UUID stored as binary(16) rather than varchar(40) 2014-05-17 21:51:18 +12:00
Rob Bygrave 27afb9ee2e Merge pull request #41 from corux/master
Unit test for BUG 402 / #37
2014-05-17 01:31:21 +12:00
Rob Bygrave a49cd42a0c Fix for #119 - Add support for @PrePersist, @PostPersist, @PostLoad 2014-05-17 01:24:32 +12:00
Rob Bygrave 8c8442ec58 Fix for #112 - ENH (411) : RawSql to support discriminator column mapping. 2014-05-17 00:11:44 +12:00
Rob Bygrave 41f3173adf Add PagedList was part of #96 2014-05-16 01:41:56 +12:00
Rob Bygrave 1a4192b40f Add another embedded bean l2 cache test 2014-05-15 23:57:20 +12:00
Rob Bygrave fffda7c789 Test and additional fix for #56 - Using findIterate with MySQL streams
the entire result set at once
2014-05-15 22:08:23 +12:00
Rob Bygrave b7f6596bcb Test case for #109 - Ebean magically adding +1 hour for a datetime that is inside the hour of a daylight saving change. This issue does not reproduce. 2014-05-15 00:18:03 +12:00
Rob Bygrave 1ea49dd206 Test for #110 - BeanSet of sub type with inheritance has null listen mode 2014-05-15 00:07:25 +12:00
Rob Bygrave 32e4f114b4 No effective change - cleanup of imports on test code 2014-05-14 23:47:46 +12:00
Rob Bygrave fcbedda597 Fix for #102 - When using @Transient along with @Formula I'm not seeing any values in the field. 2014-05-14 23:35:15 +12:00
Rob Bygrave 2055349d76 Fix for #103 DDL creation doesn't handle nullable attribute of @JoinColumn 2014-05-14 23:21:31 +12:00
Rob Bygrave 44ee64cde5 Fix for #57 - Embedded Entities with Autofetch Exception, plus some additional tests 2014-05-14 23:13:14 +12:00
Rob Bygrave 9f916998c9 Test and Fix for #127 Exception using findIds() with inheritance
SINGLE_TABLE
2014-05-14 21:10:52 +12:00
Rob Bygrave 2ddef37632 Merge pull request #58 from mbell697/autofetch-embedded
Failing test case for issue #57
2014-05-01 00:21:59 +12:00
Rob Bygrave 538ebdc899 Fix for #122 Allow the global row limit for queries to be configurable ( CQuery.GLOBAL_ROW_LIMIT) 2014-05-01 00:12:48 +12:00
Rob Bygrave d6e0d661fb no effective change - javadoc update 2014-05-01 00:11:59 +12:00
Rob Bygrave 289deb397a Embedded beans support in l2 cache - initial work 2014-04-30 01:49:20 +12:00
Rob Bygrave 68b4aab837 Add test for old (427) : Binding enum on find throws No ScalarType registered 2014-04-30 00:08:23 +12:00
Rob Bygrave 563293f7a9 Test case and fix for #113 (395) : On save @OneToOne does not cascade parent IDs to child node 2014-04-30 00:01:54 +12:00
Rob Bygrave 5049694fd3 Add test for old 408 bug - does not reproduce, not sure when fixed at this point 2014-04-29 23:19:03 +12:00
Rob Bygrave a9c8d1960c Add test - self referencing relationships: ResourceFile 2014-04-29 22:25:14 +12:00
Rob Bygrave 066b409a65 No effective change -Add test: Truck, Car, Wheel plus test code cleanup 2014-04-29 22:00:07 +12:00
Rob Bygrave 54ad2e0ad0 remove the Object[] transient from the test 2014-04-29 21:21:44 +12:00
Rob Bygrave 77f7bc2a7e Fix for #111 - Ebean doesn't honor @Transient for non-primitive array properties 2014-04-29 21:20:42 +12:00
Rob Bygrave f773d74597 Test for #105 - did not reproduce - Ebean is trying to delete new
objects added to the list of an entity and then removed without being
persisted before
2014-04-29 20:42:51 +12:00
Rob Bygrave 90b23c4985 Bump version of agent and mavenenhancer 2014-04-29 20:36:00 +12:00
Rob Bygrave 60b4ad410b [maven-release-plugin] prepare for next development iteration 2014-04-27 01:59:10 +12:00
Mark Bell d8391ba90c Test embedded fetch also as this throws the same exception 2013-11-11 13:23:53 -05:00
Mark Bell c4d41ecb00 Failing test case for auto fetch + embedded exception 2013-11-11 13:13:46 -05:00
Tobias Wallura 39f5284a76 unit test for issue #37 2013-07-10 10:24:03 +02:00
135 changed files with 4884 additions and 1577 deletions
+3 -3
View File
@@ -9,7 +9,7 @@
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm</artifactId>
<version>4.0.1-RC1</version>
<version>4.0.2</version>
<packaging>jar</packaging>
<name>avaje-ebeanorm</name>
@@ -94,7 +94,7 @@
<dependency>
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm-agent</artifactId>
<version>4.1.0</version>
<version>4.1.2</version>
<scope>test</scope>
</dependency>
@@ -177,7 +177,7 @@
<plugin>
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm-mavenenhancer</artifactId>
<version>4.1.0</version>
<version>4.1.2</version>
<executions>
<execution>
<id>main</id>
+24 -1
View File
@@ -547,10 +547,32 @@ public interface EbeanServer {
/**
* Find using a PagingList with explicit transaction and pageSize.
* @deprecated
* Deprecated in favour of findPagedList().
* @deprecated
*/
public <T> PagingList<T> findPagingList(Query<T> query, Transaction transaction, int pageSize);
/**
* Return a PagedList for this query.
* <p>
* The benefit of using this over just using the normal {@link Query#setFirstRow(int)} and
* {@link Query#setMaxRows(int)} is that it additionally wraps an optional call to
* {@link Query#findFutureRowCount()} to determine total row count, total page count etc.
* </p>
* <p>
* Internally this works using {@link Query#setFirstRow(int)} and {@link Query#setMaxRows(int)} on
* the query. This translates into SQL that uses limit offset, rownum or row_number
* function to limit the result set.
* </p>
*
* @param pageIndex
* The zero based index of the page.
* @param pageSize
* The number of beans to return per page.
* @return The PagedList
*/
public <T> PagedList<T> findPagedList(Query<T> query, Transaction transaction, int pageIndex, int pageSize);
/**
* Execute the query returning a set of entity beans.
* <p>
@@ -1079,4 +1101,5 @@ public interface EbeanServer {
*/
public JsonContext createJsonContext();
}
@@ -200,10 +200,34 @@ public interface ExpressionList<T> extends Serializable {
*
* @param pageSize
* the number of beans fetched per Page
*
* @deprecated
*/
public PagingList<T> findPagingList(int pageSize);
/**
* Return a PagedList for this query.
* <p>
* The benefit of using this over just using the normal {@link Query#setFirstRow(int)} and
* {@link Query#setMaxRows(int)} is that it additionally wraps an optional call to
* {@link Query#findFutureRowCount()} to determine total row count, total page count etc.
* </p>
* <p>
* Internally this works using {@link Query#setFirstRow(int)} and {@link Query#setMaxRows(int)} on
* the query. This translates into SQL that uses limit offset, rownum or row_number
* function to limit the result set.
* </p>
*
* @param pageIndex
* The zero based index of the page.
* @param pageSize
* The number of beans to return per page.
* @return The PagedList
*/
public PagedList<T> findPagedList(int pageIndex, int pageSize);
/**
* Add some filter predicate expressions to the many property.
*/
public ExpressionList<T> filterMany(String prop);
/**
@@ -0,0 +1,80 @@
package com.avaje.ebean;
import java.util.List;
import java.util.concurrent.Future;
/**
* Represents a page of results.
* <p>
* The benefit of using PagedList over just using the normal Query with
* {@link Query#setFirstRow(int)} and {@link Query#setMaxRows(int)} is that it additionally wraps
* functionality that can call {@link Query#findFutureRowCount()} to determine total row count,
* total page count etc.
* </p>
* <p>
* Internally this works using {@link Query#setFirstRow(int)} and {@link Query#setMaxRows(int)} on
* the query. This translates into SQL that uses limit offset, rownum or row_number function to
* limit the result set.
* </p>
*
* @param <T>
* the entity bean type
*
* @see Query#findPagedList(int, int)
*/
public interface PagedList<T> {
/**
* Initiate the loading of the total row count in the background.
*/
public void loadRowCount();
/**
* Return the Future row count. You might get this if you wish to cancel the total row count query
* or specify a timeout for that query.
*/
public Future<Integer> getFutureRowCount();
/**
* Return the list of entities for this page.
*/
public List<T> getList();
/**
* Return the total row count for all pages.
*/
public int getTotalRowCount();
/**
* Return the total number of pages based on the page size and total row count.
*/
public int getTotalPageCount();
/**
* Return the index position of this page. Zero based.
*/
public int getPageIndex();
/**
* Return true if there is a next page.
*/
public boolean hasNext();
/**
* Return true if there is a previous page.
*/
public boolean hasPrev();
/**
* Helper method to return a "X to Y of Z" string for this page where X is the first row, Y the
* last row and Z the total row count.
*
* @param to
* String to put between the first and last row
* @param of
* String to put between the last row and the total row count
*
* @return String of the format XtoYofZ.
*/
public String getDisplayXtoYofZ(String to, String of);
}
+23 -17
View File
@@ -589,9 +589,7 @@ public interface Query<T> extends Serializable {
/**
* Execute find list query in a background thread.
* <p>
* This returns a Future object which can be used to cancel, check the
* execution status (isDone etc) and get the value (with or without a
* timeout).
* Deprecated with a view to simplifying internals.
* </p>
*
* @return a Future object for the list result of the query
@@ -600,25 +598,33 @@ public interface Query<T> extends Serializable {
public FutureList<T> findFutureList();
/**
* Return a PagingList for this query.
* <p>
* This can be used to break up a query into multiple queries to fetch the
* data a page at a time.
* </p>
* <p>
* This typically works by using a query per page and setting
* {@link Query#setFirstRow(int)} and and {@link Query#setMaxRows(int)} on the
* query. This usually would translate into SQL that uses limit offset, rownum
* or row_number function to limit the result set.
* </p>
*
* @param pageSize
* the number of beans fetched per Page
* This is being deprecated in favour of the simplier {@link Query#findPagedList(int, int)}.
*
* @deprecated
*/
public PagingList<T> findPagingList(int pageSize);
/**
* Return a PagedList for this query.
* <p>
* The benefit of using this over just using the normal {@link Query#setFirstRow(int)} and
* {@link Query#setMaxRows(int)} is that it additionally wraps an optional call to
* {@link Query#findFutureRowCount()} to determine total row count, total page count etc.
* </p>
* <p>
* Internally this works using {@link Query#setFirstRow(int)} and {@link Query#setMaxRows(int)} on
* the query. This translates into SQL that uses limit offset, rownum or row_number function to
* limit the result set.
* </p>
*
* @param pageIndex
* The zero based index of the page.
* @param pageSize
* The number of beans to return per page.
* @return The PagedList
*/
public PagedList<T> findPagedList(int pageIndex, int pageSize);
/**
* Set a named bind parameter. Named parameters have a colon to prefix the
* name.
@@ -182,6 +182,13 @@ public final class EntityBeanIntercept implements Serializable {
public Object getEmbeddedOwner() {
return embeddedOwner;
}
/**
* Return the property index (for the parent) of this embedded bean.
*/
public int getEmbeddedOwnerIndex() {
return embeddedOwnerIndex;
}
/**
* Special case for a OneToOne, Set the parent bean (by relationship). This is
@@ -444,6 +451,9 @@ public final class EntityBeanIntercept implements Serializable {
return -1;
}
/**
* Return the property name for the given property.
*/
public String getProperty(int propertyIndex) {
if (propertyIndex == -1) {
return null;
@@ -451,18 +461,38 @@ public final class EntityBeanIntercept implements Serializable {
return owner._ebean_getPropertyName(propertyIndex);
}
/**
* Return the number of properties.s
*/
public int getPropertyLength() {
return owner._ebean_getPropertyNames().length;
}
/**
* Set the property to be treated as unloaded. Used for properties initialised in default
* constructor.
*/
public void setPropertyUnloaded(int propertyIndex) {
loadedProps[propertyIndex] = false;
}
/**
* Set the property to be loaded.
*/
public void setLoadedProperty(int propertyIndex) {
loadedProps[propertyIndex] = true;
}
/**
* Return true if the property is loaded.
*/
public boolean isLoadedProperty(int propertyIndex) {
return loadedProps[propertyIndex];
}
/**
* Return true if the property is considered changed.
*/
public boolean isChangedProperty(int propertyIndex) {
return (changedProps != null && changedProps[propertyIndex]);
}
@@ -204,6 +204,12 @@ public class ServerConfig {
* Default behaviour for updates when cascade save on a O2M or M2M to delete any missing children.
*/
private boolean updatesDeleteMissingChildren = true;
/**
* Setting to indicate if UUID should be stored as binary(16) or varchar(40).
*/
private boolean uuidStoreAsBinary;
private List<BeanPersistController> persistControllers = new ArrayList<BeanPersistController>();
private List<BeanPersistListener<?>> persistListeners = new ArrayList<BeanPersistListener<?>>();
@@ -796,6 +802,21 @@ public class ServerConfig {
this.dbEncrypt = dbEncrypt;
}
/**
* Return true if UUID should be stored as binary(16) (as opposed to varchar(40)).
*/
public boolean isUuidStoreAsBinary() {
return uuidStoreAsBinary;
}
/**
* Set to true if UUID should be stored as binary(16) (as opposed to varchar(40)).
*/
public void setUuidStoreAsBinary(boolean uuidStoreAsBinary) {
this.uuidStoreAsBinary = uuidStoreAsBinary;
}
/**
* Set to true to run the DDL generation on startup.
*/
@@ -1282,7 +1303,7 @@ public class ServerConfig {
collectQueryStatsByNode = p.getBoolean("collectQueryStatsByNode", true);
collectQueryOrigins = p.getBoolean("collectQueryOrigins", true);
updateChangesOnly = p.getBoolean("updateChangesOnly", true);
boolean defaultDeleteMissingChildren = p.getBoolean("defaultDeleteMissingChildren", true);
@@ -1299,6 +1320,7 @@ public class ServerConfig {
databaseBooleanTrue = p.get("databaseBooleanTrue", null);
databaseBooleanFalse = p.get("databaseBooleanFalse", null);
databasePlatformName = p.get("databasePlatformName", null);
uuidStoreAsBinary = p.getBoolean("uuidStoreAsBinary", false);
lazyLoadBatchSize = p.getInt("lazyLoadBatchSize", 1);
queryBatchSize = p.getInt("queryBatchSize", DEFAULT_QUERY_BATCH_SIZE);
@@ -5,7 +5,7 @@ import java.util.Set;
/**
* Used to enhance or override the default bean persistence mechanism.
* <p>
* Note that if want to totally change the finding, you need to use a BeanFinder
* Note that if want to totally change the finding, you need to use a BeanQueryAdapter
* rather than using postLoad().
* </p>
* <p>
@@ -2,8 +2,8 @@ package com.avaje.ebeaninternal.api;
import java.util.List;
import com.avaje.ebean.Transaction;
import com.avaje.ebean.bean.EntityBeanIntercept;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
/**
* Request for loading ManyToOne and OneToOne relationships.
@@ -18,10 +18,12 @@ public class LoadBeanRequest extends LoadRequest {
private final boolean loadCache;
public LoadBeanRequest(LoadBeanBuffer LoadBuffer, Transaction transaction, boolean lazy, String lazyLoadProperty,
boolean loadCache) {
super(transaction, lazy);
public LoadBeanRequest(LoadBeanBuffer LoadBuffer, boolean lazy, String lazyLoadProperty, boolean loadCache) {
this(LoadBuffer, null, lazy, lazyLoadProperty, loadCache);
}
public LoadBeanRequest(LoadBeanBuffer LoadBuffer, OrmQueryRequest<?> parentRequest, boolean lazy, String lazyLoadProperty, boolean loadCache) {
super(parentRequest, lazy);
this.LoadBuffer = LoadBuffer;
this.batch = LoadBuffer.getBatch();
this.lazyLoadProperty = lazyLoadProperty;
@@ -2,69 +2,71 @@ package com.avaje.ebeaninternal.api;
import java.util.List;
import com.avaje.ebean.Transaction;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
/**
* Request for loading Associated One Beans.
* Request for loading Associated Many Beans.
*/
public class LoadManyRequest extends LoadRequest {
private final List<BeanCollection<?>> batch;
private final List<BeanCollection<?>> batch;
private final LoadManyBuffer loadContext;
private final LoadManyBuffer loadContext;
private final boolean onlyIds;
private final boolean onlyIds;
private final boolean loadCache;
public LoadManyRequest(LoadManyBuffer loadContext, Transaction transaction, int batchSize, boolean lazy,
private final boolean loadCache;
public LoadManyRequest(LoadManyBuffer loadContext, int batchSize, boolean lazy,boolean onlyIds, boolean loadCache) {
this(loadContext, null, batchSize, lazy, onlyIds, loadCache);
}
public LoadManyRequest(LoadManyBuffer loadContext, OrmQueryRequest<?> parentRequest, int batchSize, boolean lazy,
boolean onlyIds, boolean loadCache) {
super(transaction, lazy);
this.loadContext = loadContext;
this.batch = loadContext.getBatch();
this.onlyIds = onlyIds;
this.loadCache = loadCache;
}
super(parentRequest, lazy);
this.loadContext = loadContext;
this.batch = loadContext.getBatch();
this.onlyIds = onlyIds;
this.loadCache = loadCache;
}
public String getDescription() {
return "path:" + loadContext.getFullPath() + " size:"+ batch.size();
}
public String getDescription() {
return "path:" + loadContext.getFullPath() + " size:" + batch.size();
}
/**
* Return the batch of collections to actually load.
*/
public List<BeanCollection<?>> getBatch() {
return batch;
}
/**
* Return the batch of collections to actually load.
*/
public List<BeanCollection<?>> getBatch() {
return batch;
}
/**
* Return the load context.
*/
public LoadManyBuffer getLoadContext() {
return loadContext;
}
/**
* Return the load context.
*/
public LoadManyBuffer getLoadContext() {
return loadContext;
}
/**
* Return true if lazy loading should only load the id values.
* <p>
* This for use when lazy loading is invoked on methods such
* as clear() and removeAll() where it generally makes sense to
* only fetch the Id values as the other property information is
* not used.
* </p>
*/
public boolean isOnlyIds() {
return onlyIds;
}
/**
* Return true if lazy loading should only load the id values.
* <p>
* This for use when lazy loading is invoked on methods such as clear() and removeAll() where it
* generally makes sense to only fetch the Id values as the other property information is not
* used.
* </p>
*/
public boolean isOnlyIds() {
return onlyIds;
}
/**
* Return true if we should load the Collection ids into the cache.
*/
public boolean isLoadCache() {
return loadCache;
}
/**
* Return true if we should load the Collection ids into the cache.
*/
public boolean isLoadCache() {
return loadCache;
}
}
@@ -1,22 +1,36 @@
package com.avaje.ebeaninternal.api;
import com.avaje.ebean.Transaction;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
/**
* Request for loading Associated One Beans.
*/
public abstract class LoadRequest {
protected final boolean lazy;
protected final OrmQueryRequest<?> parentRequest;
protected final Transaction transaction;
protected final Transaction transaction;
public LoadRequest(Transaction transaction, boolean lazy) {
protected final boolean lazy;
this.transaction = transaction;
public LoadRequest(OrmQueryRequest<?> parentRequest, boolean lazy) {
this.parentRequest = parentRequest;
this.transaction = parentRequest == null ? null : parentRequest.getTransaction();
this.lazy = lazy;
}
/**
* Log the just executed secondary query with the 'root' query if 'logSecondaryQuery' is set to
* true. This is for testing purposes to confirm the secondary query executes etc.
*/
public void logSecondaryQuery(SpiQuery<?> query) {
if (parentRequest != null && parentRequest.isLogSecondaryQuery()) {
parentRequest.getQuery().logSecondaryQuery(query);
}
}
/**
* Return true if this is a lazy load and false if it is a secondary query.
*/
@@ -1,13 +1,15 @@
package com.avaje.ebeaninternal.api;
import java.io.Serializable;
import java.util.Set;
import java.util.Collection;
import java.util.TreeMap;
import java.util.TreeSet;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.el.ElPropertyDeploy;
import com.avaje.ebeaninternal.server.query.SplitName;
import com.avaje.ebeaninternal.server.query.SqlJoinType;
/**
* Holds the joins needs to support the many where predicates.
@@ -17,12 +19,32 @@ public class ManyWhereJoins implements Serializable {
private static final long serialVersionUID = -6490181101871795417L;
private final TreeSet<String> joins = new TreeSet<String>();
private final TreeMap<String,PropertyJoin> joins = new TreeMap<String,PropertyJoin>();
private StringBuilder formulaProperties = new StringBuilder();
private boolean formulaWithJoin;
/**
* 'Mode' indicating that joins added while this is true are required to be outer joins.
*/
private boolean requireOuterJoins;
/**
* Return the current 'mode' indicating if outer joins are currently required or not.
*/
public boolean isRequireOuterJoins() {
return requireOuterJoins;
}
/**
* Set the 'mode' to be that joins added are required to be outer joins.
* This is set during the evaluation of disjunction predicates.
*/
public void setRequireOuterJoins(boolean requireOuterJoins) {
this.requireOuterJoins = requireOuterJoins;
}
/**
* Add a many where join.
*/
@@ -34,10 +56,10 @@ public class ManyWhereJoins implements Serializable {
join = addManyToJoin(join, p.getName());
}
if (join != null){
joins.add(join);
addJoin(join);
String secondaryTableJoinPrefix = p.getSecondaryTableJoinPrefix();
if (secondaryTableJoinPrefix != null) {
joins.add(join+"."+secondaryTableJoinPrefix);
addJoin(join+"."+secondaryTableJoinPrefix);
}
addParentJoins(join);
}
@@ -58,11 +80,16 @@ public class ManyWhereJoins implements Serializable {
private void addParentJoins(String join) {
String[] split = SplitName.split(join);
if (split[0] != null){
joins.add(split[0]);
addJoin(split[0]);
addParentJoins(split[0]);
}
}
private void addJoin(String property) {
SqlJoinType joinType = (requireOuterJoins) ? SqlJoinType.OUTER: SqlJoinType.INNER;
joins.put(property, new PropertyJoin(property, joinType));
}
/**
* Return true if there are no extra many where joins.
*/
@@ -73,10 +100,22 @@ public class ManyWhereJoins implements Serializable {
/**
* Return the set of many where joins.
*/
public Set<String> getJoins() {
return joins;
public Collection<PropertyJoin> getPropertyJoins() {
return joins.values();
}
/**
* Return the set of property names for the many where joins.
*/
public TreeSet<String> getPropertyNames() {
TreeSet<String> propertyNames = new TreeSet<String>();
for (PropertyJoin join : joins.values()) {
propertyNames.add(join.getProperty());
}
return propertyNames;
}
/**
* In findRowCount query found a formula property with a join clause so building a select clause
* specifically for the findRowCount query.
@@ -0,0 +1,39 @@
package com.avaje.ebeaninternal.api;
import com.avaje.ebeaninternal.server.query.SqlJoinType;
/**
* Represents a join required for a given property and whether than needs to be an outer join.
*/
public class PropertyJoin {
/**
* The property name.
*/
private final String property;
/**
* Set to true if the property needs to be an outer join.
*/
private final SqlJoinType joinType;
public PropertyJoin(String property, SqlJoinType joinType) {
this.property = property;
this.joinType = joinType;
}
/**
* Return the property that should be joined.
*/
public String getProperty() {
return property;
}
/**
* Return true if this join is required to be an outer join.
*/
public SqlJoinType getSqlJoinType() {
return joinType;
}
}
@@ -10,13 +10,12 @@ import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
*/
public interface SpiExpression extends Expression {
/**
* Process "Many" properties populating ManyWhereJoins.
* <p>
* Predicates on Many properties require an extra independent
* join clause.
* </p>
*/
/**
* Process "Many" properties populating ManyWhereJoins.
* <p>
* Predicates on Many properties require an extra independent join clause.
* </p>
*/
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins whereManyJoins);
/**
@@ -260,10 +260,25 @@ public interface SpiQuery<T> extends Query<T> {
*/
public Boolean isAutofetch();
// /**
// * Return explicit forUpdate setting or null.
// */
// public boolean isForUpdate();
/**
* Set to true if you want to capture executed secondary queries.
*/
public void setLogSecondaryQuery(boolean logSecondaryQuery);
/**
* Return true if executed secondary queries should be captured.
*/
public boolean isLogSecondaryQuery();
/**
* Return the list of secondary queries that were executed.
*/
public List<SpiQuery<?>> getLoggedSecondaryQueries();
/**
* Log an executed secondary query.
*/
public void logSecondaryQuery(SpiQuery<?> query);
/**
* If return null then no autoFetch profiling for this query. If a
@@ -15,10 +15,20 @@ public class CachedBeanDataFromBean {
Object[] data = new Object[desc.getPropertyCount()];
boolean[] loaded = new boolean[desc.getPropertyCount()];
BeanProperty idProperty = desc.getIdProperty();
if (idProperty != null) {
int propertyIndex = idProperty.getPropertyIndex();
if (ebi.isLoadedProperty(propertyIndex)) {
// extract the id property value
data[propertyIndex] = idProperty.getCacheDataValue(bean);
loaded[propertyIndex] = true;
}
}
BeanProperty[] props = desc.propertiesNonMany();
Object naturalKey = null;
// extract all the non-many properties
for (int i = 0; i < props.length; i++) {
BeanProperty prop = props[i];
if (ebi.isLoadedProperty(prop.getPropertyIndex())) {
@@ -13,19 +13,17 @@ public class CachedBeanDataToBean {
EntityBeanIntercept ebi = bean._ebean_getIntercept();
BeanProperty idProperty = desc.getIdProperty();
if (idProperty != null) {
// load the id property
loadProperty(bean, cacheBeanData, ebi, idProperty);
}
// load the non-many properties
BeanProperty[] props = desc.propertiesNonMany();
for (int i = 0; i < props.length; i++) {
BeanProperty prop = props[i];
int propertyIndex = prop.getPropertyIndex();
if (cacheBeanData.isLoaded(propertyIndex)) {
if (ebi.isLoadedProperty(propertyIndex)) {
// already loaded (lazy load on partially loaded bean)
} else {
Object data = cacheBeanData.getData(propertyIndex);
prop.setCacheDataValue(bean, data);
}
}
loadProperty(bean, cacheBeanData, ebi, props[i]);
}
BeanPropertyAssocMany<?>[] manys = desc.propertiesMany();
@@ -38,4 +36,17 @@ public class CachedBeanDataToBean {
return true;
}
private static void loadProperty(EntityBean bean, CachedBeanData cacheBeanData, EntityBeanIntercept ebi, BeanProperty prop) {
int propertyIndex = prop.getPropertyIndex();
if (cacheBeanData.isLoaded(propertyIndex)) {
if (ebi.isLoadedProperty(propertyIndex)) {
// already loaded (lazy load on partially loaded bean)
} else {
Object data = cacheBeanData.getData(propertyIndex);
prop.setCacheDataValue(bean, data);
}
}
}
}
@@ -8,6 +8,7 @@ import java.sql.Types;
import java.util.Calendar;
import java.util.UUID;
import com.avaje.ebeaninternal.server.type.ScalarTypeUUIDBinary;
/**
* Default implementation of TypeConverter.
@@ -192,6 +193,9 @@ public final class BasicTypeConverter implements Serializable {
if (value instanceof String) {
return UUID.fromString((String) value);
}
if (value instanceof byte[]) {
return ScalarTypeUUIDBinary.convertFromBytes((byte[])value);
}
return (UUID) value;
}
@@ -23,6 +23,7 @@ import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.api.SpiQuery.Mode;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
/**
@@ -139,6 +140,9 @@ public class DefaultBeanLoader {
desc.cacheManyPropPut(many, bc, parentId);
}
}
// log the query (for testing secondary queries)
loadRequest.logSecondaryQuery(query);
}
public void loadMany(BeanCollection<?> bc, boolean onlyIds) {
@@ -321,22 +325,33 @@ public class DefaultBeanLoader {
// necessary but allow processing to continue until it is accessed by client code
ebis[i].checkLazyLoadFailure();
}
// log the query (for testing secondary queries)
loadRequest.logSecondaryQuery(query);
}
public void refresh(EntityBean bean) {
refreshBeanInternal(bean, SpiQuery.Mode.REFRESH_BEAN);
refreshBeanInternal(bean, SpiQuery.Mode.REFRESH_BEAN, -1);
}
public void loadBean(EntityBeanIntercept ebi) {
refreshBeanInternal(ebi.getOwner(), SpiQuery.Mode.LAZYLOAD_BEAN);
refreshBeanInternal(ebi.getOwner(), SpiQuery.Mode.LAZYLOAD_BEAN, -1);
}
private void refreshBeanInternal(EntityBean bean, SpiQuery.Mode mode) {
private void refreshBeanInternal(EntityBean bean, SpiQuery.Mode mode, int embeddedOwnerIndex) {
EntityBeanIntercept ebi = ((EntityBean) bean)._ebean_getIntercept();;
PersistenceContext pc = ebi.getPersistenceContext();
BeanDescriptor<?> desc = server.getBeanDescriptor(bean.getClass());
if (EntityType.EMBEDDED == desc.getEntityType()) {
// lazy loading on an embedded bean property
EntityBean embeddedOwner = (EntityBean)ebi.getEmbeddedOwner();
int ownerIndex = ebi.getEmbeddedOwnerIndex();
refreshBeanInternal(embeddedOwner, mode, ownerIndex);
}
Object id = desc.getId(bean);
if (pc == null) {
@@ -348,7 +363,7 @@ public class DefaultBeanLoader {
}
}
if (ebi != null) {
if (embeddedOwnerIndex == -1) {
if (SpiQuery.Mode.LAZYLOAD_BEAN.equals(mode) && desc.isBeanCaching()) {
// lazy loading and the bean cache is active
if (desc.cacheBeanLoad((EntityBean)bean, ebi, id)) {
@@ -365,6 +380,11 @@ public class DefaultBeanLoader {
query.setLazyLoadProperty(ebi.getLazyLoadProperty());
}
if (embeddedOwnerIndex > -1) {
String embeddedBeanPropertyName = ebi.getProperty(embeddedOwnerIndex);
query.select("id,"+embeddedBeanPropertyName);
}
// don't collect autoFetch usage profiling information
// as we just copy the data out of these fetched beans
// and put the data into the original bean
@@ -373,12 +393,13 @@ public class DefaultBeanLoader {
query.setMode(mode);
query.setId(id);
// make sure the query doesn't use the cache
if (mode.equals(SpiQuery.Mode.REFRESH_BEAN)) {
if (embeddedOwnerIndex > -1 || mode.equals(SpiQuery.Mode.REFRESH_BEAN)) {
// make sure the query doesn't use the cache
query.setUseCache(false);
}
if (ebi != null && ebi.isReadOnly()) {
if (ebi.isReadOnly()) {
query.setReadOnly(true);
}
@@ -30,6 +30,7 @@ import com.avaje.ebean.Filter;
import com.avaje.ebean.FutureIds;
import com.avaje.ebean.FutureList;
import com.avaje.ebean.FutureRowCount;
import com.avaje.ebean.PagedList;
import com.avaje.ebean.PagingList;
import com.avaje.ebean.Query;
import com.avaje.ebean.QueryIterator;
@@ -96,6 +97,7 @@ import com.avaje.ebeaninternal.server.query.CallableQueryIds;
import com.avaje.ebeaninternal.server.query.CallableQueryList;
import com.avaje.ebeaninternal.server.query.CallableQueryRowCount;
import com.avaje.ebeaninternal.server.query.CallableSqlQueryList;
import com.avaje.ebeaninternal.server.query.LimitOffsetPagedList;
import com.avaje.ebeaninternal.server.query.LimitOffsetPagingQuery;
import com.avaje.ebeaninternal.server.query.QueryFutureIds;
import com.avaje.ebeaninternal.server.query.QueryFutureList;
@@ -1419,6 +1421,12 @@ public final class DefaultServer implements SpiEbeanServer {
return new LimitOffsetPagingQuery<T>(this, spiQuery, pageSize);
}
@Override
public <T> PagedList<T> findPagedList(Query<T> query, Transaction transaction, int pageIndex, int pageSize) {
return new LimitOffsetPagedList<T>(this, (SpiQuery<T>)query, pageIndex, pageSize);
}
public <T> void findVisit(Query<T> query, QueryResultVisitor<T> visitor, Transaction t) {
SpiOrmQueryRequest<T> request = createQueryRequest(Type.LIST, query, t);
@@ -54,7 +54,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
private HashQuery cacheKey;
private HashQueryPlan queryPlanHash;
/**
* Create the InternalQueryRequest.
*/
@@ -363,4 +363,11 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
beanDescriptor.flushPersistenceContextOnIterate(persistenceContext);
}
/**
* Return true if the request wants to log the secondary queries (test purpose).
*/
public boolean isLogSecondaryQuery() {
return query.isLogSecondaryQuery();
}
}
@@ -210,6 +210,11 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
private final int versionPropertyIndex;
/**
* Properties that are initialised in the constructor need to be 'unloaded' to support partial object queries.
*/
private final int[] unloadProperties;
/**
* Properties local to this type (not from a super type).
*/
@@ -430,14 +435,46 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
if (Modifier.isAbstract(beanType.getModifiers())) {
this.idPropertyIndex = -1;
this.versionPropertyIndex = -1;
this.unloadProperties = new int[0];
} else {
EntityBeanIntercept ebi = prototypeEntityBean._ebean_getIntercept();
this.idPropertyIndex = (idProperty == null) ? -1 : ebi.findProperty(idProperty.getName());
this.versionPropertyIndex = (versionProperty == null) ? -1 : ebi.findProperty(versionProperty.getName());
this.unloadProperties = derivePropertiesToUnload(prototypeEntityBean);
}
}
/**
* Derive an array of property positions for properties that are initialised in the constructor.
* These properties need to be unloaded when populating beans for queries.
*/
private int[] derivePropertiesToUnload(EntityBean prototypeEntityBean) {
boolean[] loaded = prototypeEntityBean._ebean_getIntercept().getLoaded();
int[] props = new int[loaded.length];
int pos = 0;
// collect the positions of the properties initialised in the default constructor.
for (int i = 0; i < loaded.length; i++) {
if (loaded[i]) {
props[pos++] = i;
}
}
if (pos == 0) {
// nothing set in the constructor
return new int[0];
}
// populate a smaller/minimal array
int[] unload = new int[pos];
for (int i = 0; i < pos; i++) {
unload[i] = props[i];
}
return unload;
}
/**
* Create an entity bean that is used as a prototype/factory to create new instances.
*/
@@ -842,6 +879,20 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
cacheBeanPutData((EntityBean)bean);
}
/**
* Extract the raw cache data from the bean.
*/
public CachedBeanData cacheBeanExtractData(EntityBean bean) {
return cacheHelp.beanExtractData(bean);
}
/**
* Load the raw cache data into the bean.
*/
public void cacheBeanLoadData(EntityBean bean, CachedBeanData data) {
cacheHelp.beanLoadData(bean, data);
}
/**
* Put a bean into the bean cache.
*/
@@ -1128,7 +1179,17 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
*/
public EntityBean createEntityBean() {
try {
return (EntityBean)prototypeEntityBean._ebean_newInstance();
EntityBean bean = (EntityBean)prototypeEntityBean._ebean_newInstance();
if (unloadProperties.length > 0) {
// 'unload' any properties initialised in the default constructor
EntityBeanIntercept ebi = bean._ebean_getIntercept();
for (int i = 0; i < unloadProperties.length; i++) {
ebi.setPropertyUnloaded(unloadProperties[i]);
}
}
return bean;
} catch (Exception ex) {
throw new PersistenceException(ex);
}
@@ -360,12 +360,20 @@ public final class BeanDescriptorCacheHelp<T> {
}
}
public CachedBeanData beanExtractData(EntityBean bean) {
return CachedBeanDataFromBean.extract(desc, bean);
}
public void beanLoadData(EntityBean bean, CachedBeanData data) {
CachedBeanDataToBean.load(desc, bean, data);
}
/**
* Put a bean into the bean cache.
*/
public void beanCachePut(EntityBean bean) {
CachedBeanData beanData = CachedBeanDataFromBean.extract(desc, bean);
CachedBeanData beanData = beanExtractData(bean);
Object id = desc.getId(bean);
if (beanLog.isDebugEnabled()) {
@@ -402,15 +410,15 @@ public final class BeanDescriptorCacheHelp<T> {
@SuppressWarnings("unchecked")
private T beanCacheGetInternal(Object id, Boolean readOnly) {
CachedBeanData d = (CachedBeanData) getBeanCache().get(id);
if (d == null) {
CachedBeanData data = (CachedBeanData) getBeanCache().get(id);
if (data == null) {
if (beanLog.isTraceEnabled()) {
beanLog.trace(" GET {}({}) - cache miss", cacheName, id);
}
return null;
}
if (cacheSharableBeans && !Boolean.FALSE.equals(readOnly)) {
Object bean = d.getSharableBean();
Object bean = data.getSharableBean();
if (bean != null) {
if (beanLog.isTraceEnabled()) {
beanLog.trace(" GET {}({}) - hit shared bean", cacheName, id);
@@ -425,7 +433,7 @@ public final class BeanDescriptorCacheHelp<T> {
bean._ebean_getIntercept().setReadOnly(true);
}
CachedBeanDataToBean.load(desc, bean, d);
beanLoadData(bean, data);
if (beanLog.isTraceEnabled()) {
beanLog.trace(" GET {}({}) - hit", cacheName, id);
}
@@ -149,6 +149,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
private final XmlConfig xmlConfig;
private final BeanLifecycleAdapterFactory beanLifecycleAdapterFactory;
/**
* Create for a given database dbConfig.
*/
@@ -177,6 +179,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
this.updateChangesOnly = config.getServerConfig().isUpdateChangesOnly();
this.beanLifecycleAdapterFactory = new BeanLifecycleAdapterFactory();
this.persistControllerManager = new PersistControllerManager(bootupClasses);
this.persistListenerManager = new PersistListenerManager(bootupClasses);
this.beanQueryAdapterManager = new BeanQueryAdapterManager(bootupClasses);
@@ -1010,6 +1013,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
desc.setUpdateChangesOnly(updateChangesOnly);
beanLifecycleAdapterFactory.addLifecycleMethods(desc);
// set bean controller, finder and listener
setBeanControllerFinderListener(desc);
deplyInherit.process(desc);
@@ -0,0 +1,186 @@
package com.avaje.ebeaninternal.server.deploy;
import java.lang.reflect.Method;
import java.util.Set;
import javax.persistence.PostLoad;
import javax.persistence.PostPersist;
import javax.persistence.PostRemove;
import javax.persistence.PostUpdate;
import javax.persistence.PrePersist;
import javax.persistence.PreRemove;
import javax.persistence.PreUpdate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.event.BeanPersistAdapter;
import com.avaje.ebean.event.BeanPersistRequest;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
/**
* Helper that looks for methods annotated with lifecycle events and registers an adapter for them.
* <p>
* This includes PrePerist, PostPerist, PreUpdate, PostUpdate, PreRemove, PostRemove and PostLoad
* lifecycle events.
* </p>
*/
public class BeanLifecycleAdapterFactory {
private static final Logger logger = LoggerFactory.getLogger(BeanLifecycleAdapterFactory.class);
/**
* Register a BeanPersistController for methods annotated with lifecycle events.
*/
public void addLifecycleMethods(DeployBeanDescriptor<?> deployDesc) {
Method[] methods = deployDesc.getBeanType().getMethods();
MethodHolder methodHolder = new MethodHolder();
for (Method m : methods) {
methodHolder.checkMethod(m);
}
if (methodHolder.hasListener()) {
deployDesc.addPersistController(new Adapter(methodHolder));
}
}
/**
* Holds Methods for the lifecycle events.s
*/
private static class MethodHolder {
private boolean hasListener;
private Method preInsert;
private Method postInsert;
private Method preUpdate;
private Method postUpdate;
private Method preDelete;
private Method postDelete;
private Method postLoad;
private boolean hasListener() {
return hasListener;
}
private void checkMethod(Method method) {
if (method.isAnnotationPresent(PrePersist.class)) {
preInsert = method;
hasListener = true;
}
if (method.isAnnotationPresent(PostPersist.class)) {
postInsert = method;
hasListener = true;
}
if (method.isAnnotationPresent(PreUpdate.class)) {
preUpdate = method;
hasListener = true;
}
if (method.isAnnotationPresent(PostUpdate.class)) {
postUpdate = method;
hasListener = true;
}
if (method.isAnnotationPresent(PreRemove.class)) {
preDelete = method;
hasListener = true;
}
if (method.isAnnotationPresent(PostRemove.class)) {
postDelete = method;
hasListener = true;
}
if (method.isAnnotationPresent(PostLoad.class)) {
postLoad = method;
hasListener = true;
}
}
}
/**
* BeanPersistAdapter using reflection to invoke lifecycle methods.
*/
private static class Adapter extends BeanPersistAdapter {
private final MethodHolder methodHolder;
private Adapter(MethodHolder methodHolder) {
this.methodHolder = methodHolder;
}
@Override
public boolean isRegisterFor(Class<?> cls) {
// Not used
return false;
}
private void invoke(Method method, Object bean) {
try {
method.invoke(bean);
} catch (Exception e) {
logger.error("Error invoking lifecycle adapter", e);
}
}
private void invoke(Method method, BeanPersistRequest<?> request) {
invoke(method, request.getBean());
}
@Override
public boolean preDelete(BeanPersistRequest<?> request) {
if (methodHolder.preDelete != null) {
invoke(methodHolder.preDelete, request);
}
return true;
}
@Override
public boolean preInsert(BeanPersistRequest<?> request) {
if (methodHolder.preInsert != null) {
invoke(methodHolder.preInsert, request);
}
return true;
}
@Override
public boolean preUpdate(BeanPersistRequest<?> request) {
if (methodHolder.preUpdate != null) {
invoke(methodHolder.preUpdate, request);
}
return true;
}
@Override
public void postDelete(BeanPersistRequest<?> request) {
if (methodHolder.postDelete != null) {
invoke(methodHolder.postDelete, request);
}
}
@Override
public void postInsert(BeanPersistRequest<?> request) {
if (methodHolder.postInsert != null) {
invoke(methodHolder.postInsert, request);
}
}
@Override
public void postUpdate(BeanPersistRequest<?> request) {
if (methodHolder.postUpdate != null) {
invoke(methodHolder.postUpdate, request);
}
}
@Override
public void postLoad(Object bean, Set<String> includedProperties) {
if (methodHolder.postLoad != null) {
invoke(methodHolder.postLoad, bean);
}
}
}
}
@@ -27,6 +27,7 @@ import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
import com.avaje.ebeaninternal.server.query.SqlBeanLoad;
import com.avaje.ebeaninternal.server.query.SqlJoinType;
import com.avaje.ebeaninternal.server.reflect.BeanReflectGetter;
import com.avaje.ebeaninternal.server.reflect.BeanReflectSetter;
import com.avaje.ebeaninternal.server.text.json.ReadJsonContext;
@@ -73,6 +74,11 @@ public class BeanProperty implements ElPropertyValue {
*/
final boolean unidirectionalShadow;
/**
* Flag set if this maps to the inheritance discriminator column
*/
final boolean discriminator;
/**
* Flag to mark the property as embedded. This could be on
* BeanPropertyAssocOne rather than here. Put it here for checking Id type
@@ -267,6 +273,7 @@ public class BeanProperty implements ElPropertyValue {
this.propertyIndex = deploy.getPropertyIndex();
this.unidirectionalShadow = deploy.isUndirectionalShadow();
this.discriminator = deploy.isDiscriminator();
this.localEncrypted = deploy.isLocalEncrypted();
this.dbEncrypted = deploy.isDbEncrypted();
this.dbEncryptedType = deploy.getDbEncryptedType();
@@ -305,12 +312,6 @@ public class BeanProperty implements ElPropertyValue {
this.readMethod = deploy.getReadMethod();
this.writeMethod = deploy.getWriteMethod();
this.getter = deploy.getGetter();
if (descriptor != null && getter == null) {
if (!unidirectionalShadow) {
String m = "Null Getter for: " + getFullBeanName();
throw new RuntimeException(m);
}
}
this.setter = deploy.getSetter();
this.dbColumn = tableAliasIntern(descriptor, deploy.getDbColumn(), false, null);
@@ -368,6 +369,7 @@ public class BeanProperty implements ElPropertyValue {
this.fetchEager = source.fetchEager;
this.unidirectionalShadow = source.unidirectionalShadow;
this.discriminator = source.discriminator;
this.localEncrypted = source.isLocalEncrypted();
this.isTransient = source.isTransient();
this.secondaryTable = source.isSecondaryTable();
@@ -469,6 +471,13 @@ public class BeanProperty implements ElPropertyValue {
return formula;
}
/**
* Return true if this property maps to the inheritance discriminator column.
*/
public boolean isDiscriminator() {
return discriminator;
}
public void copyProperty(EntityBean sourceBean, EntityBean destBean) {
Object value = getValue(sourceBean);
setValue(destBean, value);
@@ -501,14 +510,14 @@ public class BeanProperty implements ElPropertyValue {
* Add any extra joins required to support this property. Generally a no
* operation except for a OneToOne exported.
*/
public void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) {
public void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
if (formula && sqlFormulaJoin != null) {
ctx.appendFormulaJoin(sqlFormulaJoin, forceOuterJoin);
ctx.appendFormulaJoin(sqlFormulaJoin, joinType);
} else if (secondaryTableJoin != null) {
String relativePrefix = ctx.getRelativePrefix(secondaryTableJoinPrefix);
secondaryTableJoin.addJoin(forceOuterJoin, relativePrefix, ctx);
secondaryTableJoin.addJoin(joinType, relativePrefix, ctx);
}
}
@@ -977,6 +986,13 @@ public class BeanProperty implements ElPropertyValue {
return isTransient;
}
/**
* Return true if this property is loadable from a resultSet.
*/
public boolean isLoadProperty() {
return !isTransient || formula;
}
/**
* Return true if this is a version column used for concurrency checking.
*/
@@ -16,6 +16,7 @@ import com.avaje.ebeaninternal.server.deploy.id.ImportedIdSimple;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc;
import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
import com.avaje.ebeaninternal.server.query.SqlJoinType;
/**
* Abstract base for properties mapped to an associated bean, list, set or map.
@@ -129,22 +130,15 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
/**
* Add table join with table alias based on prefix.
*/
public boolean addJoin(boolean forceOuterJoin, String prefix, DbSqlContext ctx) {
return tableJoin.addJoin(forceOuterJoin, prefix, ctx);
public SqlJoinType addJoin(SqlJoinType joinType, String prefix, DbSqlContext ctx) {
return tableJoin.addJoin(joinType, prefix, ctx);
}
/**
* Add table join with explicit table alias.
*/
public boolean addJoin(boolean forceOuterJoin, String a1, String a2, DbSqlContext ctx) {
return tableJoin.addJoin(forceOuterJoin, a1, a2, ctx);
}
/**
* Add table join with explicit table alias.
*/
public void addInnerJoin(String a1, String a2, DbSqlContext ctx) {
tableJoin.addInnerJoin(a1, a2, ctx);
public SqlJoinType addJoin(SqlJoinType joinType, String a1, String a2, DbSqlContext ctx) {
return tableJoin.addJoin(joinType, a1, a2, ctx);
}
/**
@@ -14,6 +14,7 @@ import com.avaje.ebean.Transaction;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.bean.EntityBeanIntercept;
import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebeaninternal.server.cache.CachedBeanData;
import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate;
import com.avaje.ebeaninternal.server.deploy.id.IdBinder;
import com.avaje.ebeaninternal.server.deploy.id.ImportedId;
@@ -22,6 +23,7 @@ import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
import com.avaje.ebeaninternal.server.query.SplitName;
import com.avaje.ebeaninternal.server.query.SqlBeanLoad;
import com.avaje.ebeaninternal.server.query.SqlJoinType;
import com.avaje.ebeaninternal.server.text.json.ReadJsonContext;
import com.avaje.ebeaninternal.server.text.json.WriteJsonContext;
@@ -323,15 +325,15 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
}
public Object getCacheDataValue(EntityBean bean){
if (embedded) {
throw new RuntimeException();
Object ap = getValue(bean);
if (ap == null){
return null;
}
if (embedded) {
return targetDescriptor.cacheBeanExtractData((EntityBean) ap);
} else {
Object ap = getValue(bean);
if (ap == null){
return null;
} else {
return targetDescriptor.getId((EntityBean)ap);
}
return targetDescriptor.getId((EntityBean)ap);
}
}
@@ -339,7 +341,10 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
public void setCacheDataValue(EntityBean bean, Object cacheData){
if (cacheData != null) {
if (embedded){
throw new RuntimeException();
EntityBean embeddedBean = targetDescriptor.createEntityBean();
targetDescriptor.cacheBeanLoadData(embeddedBean, (CachedBeanData) cacheData);
setValue(bean, embeddedBean);
} else {
T ref = targetDescriptor.createReference(Boolean.FALSE, cacheData);
setValue(bean, ref);
@@ -504,9 +509,9 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
}
@Override
public void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) {
public void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
if (!isTransient) {
localHelp.appendFrom(ctx, forceOuterJoin);
localHelp.appendFrom(ctx, joinType);
}
}
@@ -583,7 +588,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
abstract void appendSelect(DbSqlContext ctx, boolean subQuery);
abstract void appendFrom(DbSqlContext ctx, boolean forceOuterJoin);
abstract void appendFrom(DbSqlContext ctx, SqlJoinType joinType);
}
@@ -629,7 +634,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
}
@Override
void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) {
void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
}
@Override
@@ -725,11 +730,11 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
}
@Override
void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) {
void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
if (targetInheritInfo != null) {
// add join to support the discriminator column
String relativePrefix = ctx.getRelativePrefix(name);
tableJoin.addJoin(forceOuterJoin, relativePrefix, ctx);
tableJoin.addJoin(joinType, relativePrefix, ctx);
}
}
@@ -818,10 +823,10 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
}
@Override
void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) {
void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
String relativePrefix = ctx.getRelativePrefix(getName());
tableJoin.addJoin(forceOuterJoin, relativePrefix, ctx);
tableJoin.addJoin(joinType, relativePrefix, ctx);
}
}
@@ -862,4 +867,18 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
EntityBean eb = (EntityBean)detailBean;
return targetDescriptor.isReference(eb._ebean_getIntercept());
}
/**
* Set the parent bean to the child bean if it has not already been set.
*/
public void setParentBeanToChild(EntityBean parent, EntityBean child) {
if (mappedBy != null) {
BeanProperty beanProperty = targetDescriptor.getBeanProperty(mappedBy);
if (beanProperty != null && beanProperty.getValue(child) == null) {
// set the 'parent' bean to the 'child' bean
beanProperty.setValue(child, parent);
}
}
}
}
@@ -127,7 +127,7 @@ public class DRawSqlSelect {
sqlTree.setSummary(desc.getName());
LinkedHashSet<String> includedProps = new LinkedHashSet<String>();
SqlTreeProperties selectProps = new SqlTreeProperties(desc);
SqlTreeProperties selectProps = new SqlTreeProperties();
for (int i = 0; i < selectColumns.length; i++) {
@@ -1,5 +1,7 @@
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebeaninternal.server.query.SqlJoinType;
/**
* Used to provide context during sql construction.
*/
@@ -72,7 +74,7 @@ public interface DbSqlContext {
* Append a Sql Formula join. This converts the "${ta}" keyword to the current
* table alias.
*/
public void appendFormulaJoin(String sqlFormulaJoin, boolean forceOuterJoin);
public void appendFormulaJoin(String sqlFormulaJoin, SqlJoinType joinType);
/**
* Return the current content length.
@@ -13,338 +13,332 @@ import com.avaje.ebeaninternal.server.deploy.parse.DeployInheritInfo;
import com.avaje.ebeaninternal.server.query.SqlTreeProperties;
/**
* Represents a node in the Inheritance tree. Holds information regarding Super
* Subclass support.
* Represents a node in the Inheritance tree. Holds information regarding Super Subclass support.
*/
public class InheritInfo {
private final String discriminatorStringValue;
private final Object discriminatorValue;
private final String discriminatorStringValue;
private final Object discriminatorValue;
private final String discriminatorColumn;
private final String discriminatorColumn;
private final int discriminatorType;
private final int discriminatorType;
private final int discriminatorLength;
private final int discriminatorLength;
private final String where;
private final String where;
private final Class<?> type;
private final Class<?> type;
private final ArrayList<InheritInfo> children = new ArrayList<InheritInfo>();
private final ArrayList<InheritInfo> children = new ArrayList<InheritInfo>();
/**
* Map of discriminator values to InheritInfo.
*/
private final HashMap<String, InheritInfo> discMap;
/**
* Map of class types to InheritInfo (taking into account subclass proxy classes).
*/
private final HashMap<String, InheritInfo> typeMap;
/**
* Map of discriminator values to InheritInfo.
*/
private final HashMap<String, InheritInfo> discMap;
private final InheritInfo parent;
/**
* Map of class types to InheritInfo (taking into account subclass proxy classes).
*/
private final HashMap<String, InheritInfo> typeMap;
private final InheritInfo root;
private final InheritInfo parent;
private BeanDescriptor<?> descriptor;
private final InheritInfo root;
public InheritInfo(InheritInfo r, InheritInfo parent, DeployInheritInfo deploy) {
this.parent = parent;
this.type = deploy.getType();
this.discriminatorColumn = InternString.intern(deploy.getDiscriminatorColumn(parent));
this.discriminatorValue = deploy.getDiscriminatorObjectValue();
this.discriminatorStringValue = deploy.getDiscriminatorStringValue();
this.discriminatorType = deploy.getDiscriminatorType(parent);
this.discriminatorLength = deploy.getDiscriminatorLength(parent);
this.where = InternString.intern(deploy.getWhere());
if (r == null) {
// this is a root node
root = this;
discMap = new HashMap<String, InheritInfo>();
typeMap = new HashMap<String, InheritInfo>();
registerWithRoot(this);
private BeanDescriptor<?> descriptor;
} else {
this.root = r;
// register with the root node...
discMap = null;
typeMap = null;
root.registerWithRoot(this);
}
}
/**
* Visit all the children in the inheritance tree.
*/
public void visitChildren(InheritInfoVisitor visitor) {
for (int i = 0; i < children.size(); i++) {
InheritInfo child = children.get(i);
visitor.visit(child);
child.visitChildren(visitor);
}
}
public InheritInfo(InheritInfo r, InheritInfo parent, DeployInheritInfo deploy) {
/**
* return true if anything in the inheritance hierarchy has a relationship
* with a save cascade on it.
*/
public boolean isSaveRecurseSkippable() {
return root.isNodeSaveRecurseSkippable();
}
private boolean isNodeSaveRecurseSkippable() {
if (!descriptor.isSaveRecurseSkippable()){
return false;
}
for (int i = 0; i < children.size(); i++) {
InheritInfo child = children.get(i);
if (!child.isNodeSaveRecurseSkippable()){
return false;
}
}
return true;
}
/**
* return true if anything in the inheritance hierarchy has a relationship
* with a delete cascade on it.
*/
public boolean isDeleteRecurseSkippable() {
return root.isNodeDeleteRecurseSkippable();
this.parent = parent;
this.type = deploy.getType();
this.discriminatorColumn = InternString.intern(deploy.getDiscriminatorColumn(parent));
this.discriminatorValue = deploy.getDiscriminatorObjectValue();
this.discriminatorStringValue = deploy.getDiscriminatorStringValue();
this.discriminatorType = deploy.getDiscriminatorType(parent);
this.discriminatorLength = deploy.getDiscriminatorLength(parent);
this.where = InternString.intern(deploy.getWhere());
if (r == null) {
// this is a root node
root = this;
discMap = new HashMap<String, InheritInfo>();
typeMap = new HashMap<String, InheritInfo>();
registerWithRoot(this);
} else {
this.root = r;
// register with the root node...
discMap = null;
typeMap = null;
root.registerWithRoot(this);
}
}
/**
* Visit all the children in the inheritance tree.
*/
public void visitChildren(InheritInfoVisitor visitor) {
for (int i = 0; i < children.size(); i++) {
InheritInfo child = children.get(i);
visitor.visit(child);
child.visitChildren(visitor);
}
}
/**
* return true if anything in the inheritance hierarchy has a relationship with a save cascade on
* it.
*/
public boolean isSaveRecurseSkippable() {
return root.isNodeSaveRecurseSkippable();
}
private boolean isNodeSaveRecurseSkippable() {
if (!descriptor.isSaveRecurseSkippable()) {
return false;
}
for (int i = 0; i < children.size(); i++) {
InheritInfo child = children.get(i);
if (!child.isNodeSaveRecurseSkippable()) {
return false;
}
}
return true;
}
/**
* return true if anything in the inheritance hierarchy has a relationship with a delete cascade
* on it.
*/
public boolean isDeleteRecurseSkippable() {
return root.isNodeDeleteRecurseSkippable();
}
private boolean isNodeDeleteRecurseSkippable() {
if (!descriptor.isDeleteRecurseSkippable()) {
return false;
}
for (int i = 0; i < children.size(); i++) {
InheritInfo child = children.get(i);
if (!child.isNodeDeleteRecurseSkippable()) {
return false;
}
}
return true;
}
/**
* Set the descriptor for this node.
*/
public void setDescriptor(BeanDescriptor<?> descriptor) {
this.descriptor = descriptor;
}
/**
* Return the associated BeanDescriptor for this node.
*/
public BeanDescriptor<?> getBeanDescriptor() {
return descriptor;
}
/**
* Get the bean property additionally looking in the sub types.
*/
public BeanProperty findSubTypeProperty(String propertyName) {
BeanProperty prop = null;
for (int i = 0, x = children.size(); i < x; i++) {
InheritInfo childInfo = children.get(i);
// recursively search this child bean descriptor
prop = childInfo.getBeanDescriptor().findBeanProperty(propertyName);
if (prop != null) {
return prop;
}
}
private boolean isNodeDeleteRecurseSkippable() {
if (!descriptor.isDeleteRecurseSkippable()) {
return false;
}
for (int i = 0; i < children.size(); i++) {
InheritInfo child = children.get(i);
if (!child.isNodeDeleteRecurseSkippable()) {
return false;
}
}
return true;
return null;
}
/**
* Add the local properties for each sub class below this one.
*/
public void addChildrenProperties(SqlTreeProperties selectProps) {
for (int i = 0, x = children.size(); i < x; i++) {
InheritInfo childInfo = children.get(i);
selectProps.add(childInfo.descriptor.propertiesLocal());
childInfo.addChildrenProperties(selectProps);
}
/**
* Set the descriptor for this node.
*/
public void setDescriptor(BeanDescriptor<?> descriptor) {
this.descriptor = descriptor;
}
/**
* Return the associated BeanDescriptor for this node.
*/
public BeanDescriptor<?> getBeanDescriptor() {
return descriptor;
}
/**
* Get the bean property additionally looking in the sub types.
*/
public BeanProperty findSubTypeProperty(String propertyName) {
BeanProperty prop = null;
for (int i = 0, x=children.size(); i < x; i++) {
InheritInfo childInfo = children.get(i);
// recursively search this child bean descriptor
prop = childInfo.getBeanDescriptor().findBeanProperty(propertyName);
if (prop != null){
return prop;
}
}
return null;
}
/**
* Add the local properties for each sub class below this one.
*/
public void addChildrenProperties(SqlTreeProperties selectProps) {
for (int i = 0, x=children.size(); i < x; i++) {
InheritInfo childInfo = children.get(i);
selectProps.add(childInfo.descriptor.propertiesLocal());
childInfo.addChildrenProperties(selectProps);
}
}
}
/**
* Return the associated InheritInfo for this DB row read.
*/
public InheritInfo readType(DbReadContext ctx) throws SQLException {
/**
* Return the associated InheritInfo for this DB row read.
*/
public InheritInfo readType(DbReadContext ctx) throws SQLException {
String discValue = ctx.getDataReader().getString();
return readType(discValue);
}
/**
* Return the associated InheritInfo for this discriminator value.
*/
public InheritInfo readType(String discValue) {
String discValue = ctx.getDataReader().getString();
return readType(discValue);
}
if (discValue == null) {
return null;
}
/**
* Return the associated InheritInfo for this discriminator value.
*/
public InheritInfo readType(String discValue) {
InheritInfo typeInfo = root.getType(discValue);
if (typeInfo == null) {
String m = "Inheritance type for discriminator value [" + discValue + "] was not found?";
throw new PersistenceException(m);
}
return typeInfo;
if (discValue == null) {
return null;
}
/**
* Return the associated InheritInfo for this bean type.
*/
public InheritInfo readType(Class<?> beanType) {
InheritInfo typeInfo = root.getTypeByClass(beanType);
if (typeInfo == null) {
String m = "Inheritance type for bean type [" + beanType.getName() + "] was not found?";
throw new PersistenceException(m);
}
return typeInfo;
InheritInfo typeInfo = root.getType(discValue);
if (typeInfo == null) {
throw new PersistenceException("Inheritance type for discriminator value [" + discValue + "] was not found?");
}
return typeInfo;
}
/**
* Create an EntityBean for this type.
*/
public EntityBean createEntityBean() {
return descriptor.createEntityBean();
}
/**
* Return the IdBinder for this type.
*/
public IdBinder getIdBinder() {
return descriptor.getIdBinder();
}
/**
* return the type.
*/
public Class<?> getType() {
return type;
}
/**
* Return the associated InheritInfo for this bean type.
*/
public InheritInfo readType(Class<?> beanType) {
/**
* Return the root node of the tree.
* <p>
* The root has a map of discriminator values to types.
* </p>
*/
public InheritInfo getRoot() {
return root;
}
/**
* Return the parent node.
*/
public InheritInfo getParent() {
return parent;
InheritInfo typeInfo = root.getTypeByClass(beanType);
if (typeInfo == null) {
throw new PersistenceException("Inheritance type for bean type [" + beanType.getName() + "] was not found?");
}
/**
* Return true if this is abstract node.
*/
public boolean isAbstract() {
return (discriminatorValue == null);
}
return typeInfo;
}
/**
* Return true if this is the root node.
*/
public boolean isRoot() {
return parent == null;
}
/**
* Create an EntityBean for this type.
*/
public EntityBean createEntityBean() {
return descriptor.createEntityBean();
}
/**
* For a discriminator get the inheritance information for this tree.
*/
public InheritInfo getType(String discValue) {
return discMap.get(discValue);
}
/**
* Return the InheritInfo for the given bean type.
*/
private InheritInfo getTypeByClass(Class<?> beanType) {
return typeMap.get(beanType.getName());
/**
* Return the IdBinder for this type.
*/
public IdBinder getIdBinder() {
return descriptor.getIdBinder();
}
/**
* return the type.
*/
public Class<?> getType() {
return type;
}
/**
* Return the root node of the tree.
* <p>
* The root has a map of discriminator values to types.
* </p>
*/
public InheritInfo getRoot() {
return root;
}
/**
* Return the parent node.
*/
public InheritInfo getParent() {
return parent;
}
/**
* Return true if this is abstract node.
*/
public boolean isAbstract() {
return (discriminatorValue == null);
}
/**
* Return true if this is the root node.
*/
public boolean isRoot() {
return parent == null;
}
/**
* For a discriminator get the inheritance information for this tree.
*/
public InheritInfo getType(String discValue) {
return discMap.get(discValue);
}
/**
* Return the InheritInfo for the given bean type.
*/
private InheritInfo getTypeByClass(Class<?> beanType) {
return typeMap.get(beanType.getName());
}
private void registerWithRoot(InheritInfo info) {
if (info.getDiscriminatorStringValue() != null) {
String stringDiscValue = info.getDiscriminatorStringValue();
discMap.put(stringDiscValue, info);
}
typeMap.put(info.getType().getName(), info);
}
private void registerWithRoot(InheritInfo info) {
if (info.getDiscriminatorStringValue() != null) {
String stringDiscValue = info.getDiscriminatorStringValue();
discMap.put(stringDiscValue, info);
}
typeMap.put(info.getType().getName(), info);
}
/**
* Add a child node.
*/
public void addChild(InheritInfo childInfo) {
children.add(childInfo);
}
/**
* Add a child node.
*/
public void addChild(InheritInfo childInfo) {
children.add(childInfo);
}
/**
* Return the derived where for the discriminator.
*/
public String getWhere() {
/**
* Return the derived where for the discriminator.
*/
public String getWhere() {
return where;
}
return where;
}
/**
* Return the column name of the discriminator.
*/
public String getDiscriminatorColumn() {
return discriminatorColumn;
}
/**
* Return the column name of the discriminator.
*/
public String getDiscriminatorColumn() {
return discriminatorColumn;
}
/**
* Return the sql type of the discriminator value.
*/
public int getDiscriminatorType() {
return discriminatorType;
}
/**
* Return the sql type of the discriminator value.
*/
public int getDiscriminatorType() {
return discriminatorType;
}
/**
* Return the length of the discriminator column.
*/
public int getDiscriminatorLength() {
return discriminatorLength;
}
/**
* Return the length of the discriminator column.
*/
public int getDiscriminatorLength() {
return discriminatorLength;
}
/**
* Return the discriminator value for this node.
*/
public String getDiscriminatorStringValue() {
return discriminatorStringValue;
}
/**
* Return the discriminator value for this node.
*/
public String getDiscriminatorStringValue() {
return discriminatorStringValue;
}
public Object getDiscriminatorValue() {
return discriminatorValue;
}
public String toString() {
return "InheritInfo[" + type.getName() + "] disc[" + discriminatorStringValue + "]";
}
public Object getDiscriminatorValue() {
return discriminatorValue;
}
public String toString() {
return "InheritInfo[" + type.getName() + "] disc[" + discriminatorStringValue + "]";
}
}
@@ -10,6 +10,7 @@ import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoin;
import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoinColumn;
import com.avaje.ebeaninternal.server.query.SplitName;
import com.avaje.ebeaninternal.server.query.SqlBeanLoad;
import com.avaje.ebeaninternal.server.query.SqlJoinType;
/**
* Represents a join to another table.
@@ -32,9 +33,9 @@ public final class TableJoin {
private final String table;
/**
* The type of join. LEFT OUTER etc.
* The type of join as per deployment (cardinality and optionality).
*/
private final String type;
private final SqlJoinType type;
/**
* The persist cascade info.
@@ -60,7 +61,7 @@ public final class TableJoin {
this.importedPrimaryKey = deploy.isImportedPrimaryKey();
this.table = InternString.intern(deploy.getTable());
this.type = InternString.intern(deploy.getType());
this.type = deploy.getType();
this.cascadeInfo = deploy.getCascadeInfo();
this.inheritInfo = deploy.getInheritInfo();
@@ -149,7 +150,7 @@ public final class TableJoin {
/**
* Return the type of join. LEFT OUTER JOIN etc.
*/
public String getType() {
public SqlJoinType getType() {
return type;
}
@@ -157,32 +158,26 @@ public final class TableJoin {
* Return true if this join is a left outer join.
*/
public boolean isOuterJoin() {
return type.equals(LEFT_OUTER);
return type == SqlJoinType.OUTER;
}
public boolean addJoin(boolean forceOuterJoin, String prefix, DbSqlContext ctx) {
public SqlJoinType addJoin(SqlJoinType joinType, String prefix, DbSqlContext ctx) {
String[] names = SplitName.split(prefix);
String a1 = ctx.getTableAlias(names[0]);
String a2 = ctx.getTableAlias(prefix);
return addJoin(forceOuterJoin, a1, a2, ctx);
return addJoin(joinType, a1, a2, ctx);
}
public boolean addJoin(boolean forceOuterJoin, String a1, String a2, DbSqlContext ctx) {
public SqlJoinType addJoin(SqlJoinType joinType, String a1, String a2, DbSqlContext ctx) {
String inheritance = inheritInfo != null ? inheritInfo.getWhere() : null;
ctx.addJoin(forceOuterJoin?LEFT_OUTER:type, table, columns(), a1, a2, inheritance);
String joinLiteral = joinType.getLiteral(type);
ctx.addJoin(joinLiteral, table, columns(), a1, a2, inheritance);
return forceOuterJoin || LEFT_OUTER.equals(type);
}
/**
* Explicitly add a (non-outer) join.
*/
public void addInnerJoin(String a1, String a2, DbSqlContext ctx) {
String inheritance = inheritInfo != null ? inheritInfo.getWhere() : null;
ctx.addJoin(JOIN, table, columns(), a1, a2, inheritance);
return joinType.autoToOuter(type);
}
}
@@ -133,9 +133,9 @@ public class DeployBeanDescriptor<T> {
*/
private Class<T> beanType;
private List<BeanPersistController> persistControllers = new ArrayList<BeanPersistController>();
private List<BeanPersistListener<T>> persistListeners = new ArrayList<BeanPersistListener<T>>();
private List<BeanQueryAdapter> queryAdapters = new ArrayList<BeanQueryAdapter>();
private List<BeanPersistController> persistControllers = new ArrayList<BeanPersistController>(2);
private List<BeanPersistListener<T>> persistListeners = new ArrayList<BeanPersistListener<T>>(2);
private List<BeanQueryAdapter> queryAdapters = new ArrayList<BeanQueryAdapter>(2);
private CacheOptions cacheOptions = new CacheOptions();
@@ -147,7 +147,7 @@ public class DeployBeanDescriptor<T> {
/**
* The table joins for this bean. Server side only.
*/
private ArrayList<DeployTableJoin> tableJoinList = new ArrayList<DeployTableJoin>();
private ArrayList<DeployTableJoin> tableJoinList = new ArrayList<DeployTableJoin>(2);
/**
* Inheritance information. Server side only.
@@ -86,6 +86,8 @@ public class DeployBeanProperty {
private boolean unique;
private boolean discriminator;
/**
* The length or precision of the DB column.
*/
@@ -336,6 +338,20 @@ public class DeployBeanProperty {
this.undirectionalShadow = undirectionalShadow;
}
/**
* Mark this property as mapping to the discriminator column.
*/
public void setDiscriminator(boolean discriminator) {
this.discriminator = discriminator;
}
/**
* Return true if this property maps to the inheritance discriminator column.s
*/
public boolean isDiscriminator() {
return discriminator;
}
/**
* Return true if the property is encrypted in java rather than in the DB.
*/
@@ -16,7 +16,9 @@ import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound;
import com.avaje.ebeaninternal.server.deploy.BeanPropertySimpleCollection;
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
import com.avaje.ebeaninternal.server.deploy.TableJoin;
import com.avaje.ebeaninternal.server.type.ScalarTypeString;
/**
* Helper object to classify BeanProperties into appropriate lists.
@@ -88,6 +90,21 @@ public class DeployBeanPropertyLists {
allocateToList(prop);
}
InheritInfo inheritInfo = deploy.getInheritInfo();
if (inheritInfo != null) {
// Create a BeanProperty for the discriminator column to support
// using RawSql queries with inheritance
String discriminatorColumn = inheritInfo.getDiscriminatorColumn();
DeployBeanProperty discDeployProp = new DeployBeanProperty(deploy, String.class, new ScalarTypeString(), null);
discDeployProp.setDiscriminator(true);
discDeployProp.setName(discriminatorColumn);
discDeployProp.setDbColumn(discriminatorColumn);
// create the discriminator BeanProperty and only register it in the propertyMap
BeanProperty dprop = new BeanProperty(owner, desc, discDeployProp);
propertyMap.put(dprop.getName(), dprop);
}
List<DeployTableJoin> deployTableJoins = deploy.getTableJoins();
tableJoins = new TableJoin[deployTableJoins.size()];
for (int i = 0; i < deployTableJoins.size(); i++) {
@@ -9,6 +9,7 @@ import com.avaje.ebeaninternal.server.deploy.BeanCascadeInfo;
import com.avaje.ebeaninternal.server.deploy.BeanTable;
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
import com.avaje.ebeaninternal.server.deploy.TableJoin;
import com.avaje.ebeaninternal.server.query.SqlJoinType;
/**
* Represents a join to another table during deployment phase.
@@ -32,7 +33,7 @@ public class DeployTableJoin {
/**
* The type of join. LEFT OUTER etc.
*/
private String type = TableJoin.JOIN;
private SqlJoinType type = SqlJoinType.INNER;
/**
* The list of properties mapped to this joined table.
@@ -162,7 +163,7 @@ public class DeployTableJoin {
/**
* Return the type of join. LEFT OUTER JOIN etc.
*/
public String getType() {
public SqlJoinType getType() {
return type;
}
@@ -170,7 +171,11 @@ public class DeployTableJoin {
* Return true if this join is a left outer join.
*/
public boolean isOuterJoin() {
return type.equals(TableJoin.LEFT_OUTER);
return type == SqlJoinType.OUTER;
}
private void setType(SqlJoinType type) {
this.type = type;
}
/**
@@ -179,13 +184,13 @@ public class DeployTableJoin {
public void setType(String joinType) {
joinType = joinType.toUpperCase();
if (joinType.equalsIgnoreCase(TableJoin.JOIN)) {
type = TableJoin.JOIN;
type = SqlJoinType.INNER;
} else if (joinType.indexOf("LEFT") > -1) {
type = TableJoin.LEFT_OUTER;
type = SqlJoinType.OUTER;
} else if (joinType.indexOf("OUTER") > -1) {
type = TableJoin.LEFT_OUTER;
type = SqlJoinType.OUTER;
} else if (joinType.indexOf("INNER") > -1) {
type = TableJoin.JOIN;
type = SqlJoinType.INNER;
} else {
throw new RuntimeException(Message.msg("join.type.unknown", joinType));
}
@@ -107,10 +107,13 @@ public class AnnotationAssocOnes extends AnnotationParser {
BeanTable beanTable = prop.getBeanTable();
JoinColumn joinColumn = get(prop, JoinColumn.class);
if (joinColumn != null) {
prop.getTableJoin().addJoinColumn(false, joinColumn, beanTable);
if (!joinColumn.updatable()){
prop.setDbUpdateable(false);
}
prop.getTableJoin().addJoinColumn(false, joinColumn, beanTable);
if (!joinColumn.updatable()) {
prop.setDbUpdateable(false);
}
if (!joinColumn.nullable()) {
prop.setNullable(false);
}
}
JoinColumns joinColumns = get(prop, JoinColumns.class);
@@ -40,355 +40,358 @@ public class DeployCreateProperties {
private final DetermineManyType determineManyType;
private final TypeManager typeManager;
public DeployCreateProperties(TypeManager typeManager) {
this.typeManager = typeManager;
this.determineManyType = new DetermineManyType();
}
/**
* Create the appropriate properties for a bean.
*/
public void createProperties(DeployBeanDescriptor<?> desc) {
createProperties(desc, desc.getBeanType(), 0);
desc.sortProperties();
// check the transient properties...
Iterator<DeployBeanProperty> it = desc.propertiesAll();
while (it.hasNext()) {
DeployBeanProperty prop = it.next();
if (prop.isTransient()){
if (prop.getWriteMethod() == null || prop.getReadMethod() == null){
// Typically a helper method ... this is expected
logger.trace("... transient: "+prop.getFullBeanName());
} else {
// dubious, possible error...
String msg = Message.msg("deploy.property.nofield", desc.getFullName(), prop.getName());
logger.warn(msg);
}
}
}
}
/**
* Return true if we should ignore this field.
* <p>
* We want to ignore ebean internal fields and some others as well.
* </p>
*/
private boolean ignoreFieldByName(String fieldName) {
if (fieldName.startsWith("_ebean_")){
// ignore Ebean internal fields
return true;
}
if (fieldName.startsWith("ajc$instance$")) {
// ignore AspectJ internal fields
return true;
}
public DeployCreateProperties(TypeManager typeManager) {
this.typeManager = typeManager;
this.determineManyType = new DetermineManyType();
}
// we are interested in this field
return false;
}
/**
* reflect the bean properties from Class. Some of these properties may not
* map to database columns.
*/
private void createProperties(DeployBeanDescriptor<?> desc, Class<?> beanType, int level) {
/**
* Create the appropriate properties for a bean.
*/
public void createProperties(DeployBeanDescriptor<?> desc) {
boolean scalaObject = desc.isScalaObject();
createProperties(desc, desc.getBeanType(), 0);
desc.sortProperties();
try {
Method[] declaredMethods = beanType.getDeclaredMethods();
Field[] fields = beanType.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
if (Modifier.isStatic(field.getModifiers())) {
// not interested in static fields
} else if (Modifier.isTransient(field.getModifiers())) {
// not interested in transient fields
logger.trace("Skipping transient field "+field.getName()+" in "+beanType.getName());
// check the transient properties...
Iterator<DeployBeanProperty> it = desc.propertiesAll();
} else if (ignoreFieldByName(field.getName())) {
// not interested this field (ebean or aspectJ field)
} else {
String fieldName = getFieldName(field, beanType);
String initFieldName = initCap(fieldName);
Method getter = findGetter(field, initFieldName, declaredMethods, scalaObject);
Method setter = findSetter(field, initFieldName, declaredMethods, scalaObject);
DeployBeanProperty prop = createProp(level, desc, field, beanType, getter, setter);
if (prop == null){
// transient annotation on unsupported type
} else {
// set a order that gives priority to inherited properties
// push Id/EmbeddedId up and CreatedTimestamp/UpdatedTimestamp down
int sortOverride = prop.getSortOverride();
prop.setSortOrder((level*10000+100-i + sortOverride));
DeployBeanProperty replaced = desc.addBeanProperty(prop);
if (replaced != null){
if (replaced.isTransient()) {
// expected for inheritance...
} else {
String msg = "Huh??? property "+prop.getFullBeanName()+" being defined twice";
msg += " but replaced property was not transient? This is not expected?";
logger.warn(msg);
}
}
}
}
}
Class<?> superClass = beanType.getSuperclass();
if (!superClass.equals(Object.class)) {
// recursively add any properties in the inheritance heirarchy
// up to the Object.class level...
createProperties(desc, superClass, level + 1);
}
} catch (PersistenceException ex) {
throw ex;
} catch (Exception ex) {
throw new PersistenceException(ex);
}
}
/**
* Make the first letter of the string upper case.
*/
private String initCap(String str){
if (str.length() > 1){
return Character.toUpperCase(str.charAt(0))+str.substring(1);
} else {
// only a single char
return str.toUpperCase();
}
}
/**
* Return the bean spec field name (trim of "is" from boolean types)
*/
private String getFieldName(Field field, Class<?> beanType){
String name = field.getName();
if ((Boolean.class.equals(field.getType()) || boolean.class.equals(field.getType()))
&& name.startsWith("is") && name.length() > 2){
// it is a boolean type field starting with "is"
char c = name.charAt(2);
if (Character.isUpperCase(c)){
String msg = "trimming off 'is' from boolean field name "+name+" in class "+beanType.getName();
logger.info(msg);
return name.substring(2);
}
}
return name;
}
/**
* Find a public non-static getter method that matches this field (according to bean-spec rules).
*/
private Method findGetter(Field field, String initFieldName, Method[] declaredMethods, boolean scalaObject){
String methGetName = "get"+initFieldName;
String methIsName = "is"+initFieldName;
String scalaGet = field.getName();
for (int i = 0; i < declaredMethods.length; i++) {
Method m = declaredMethods[i];
if ((scalaObject && m.getName().equals(scalaGet))
|| m.getName().equals(methGetName) || m.getName().equals(methIsName)){
Class<?>[] params = m.getParameterTypes();
if (params.length == 0){
if (field.getType().equals(m.getReturnType())){
int modifiers = m.getModifiers();
if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers)) {
// we find it...
return m;
}
}
}
}
}
return null;
}
/**
* Find a public non-static setter method that matches this field (according to bean-spec rules).
*/
private Method findSetter(Field field, String initFieldName, Method[] declaredMethods, boolean scalaObject){
String methSetName = "set"+initFieldName;
String scalaSetName = field.getName()+"_$eq";
for (int i = 0; i < declaredMethods.length; i++) {
Method m = declaredMethods[i];
if ((scalaObject && m.getName().equals(scalaSetName))
|| m.getName().equals(methSetName)){
Class<?>[] params = m.getParameterTypes();
if (params.length == 1 && field.getType().equals(params[0])){
if (void.class.equals(m.getReturnType())){
int modifiers = m.getModifiers();
if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers)) {
return m;
}
}
}
}
}
return null;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private DeployBeanProperty createManyType(DeployBeanDescriptor<?> desc, Class<?> targetType, ManyType manyType) {
try {
ScalarType<?> scalarType = typeManager.getScalarType(targetType);
if (scalarType != null) {
return new DeployBeanPropertySimpleCollection(desc, targetType, scalarType, manyType);
}
} catch (NullPointerException e) {
logger.debug("expected non-scalar type"+e.getMessage());
}
//TODO: Handle Collection of CompoundType and Embedded Type
return new DeployBeanPropertyAssocMany(desc, targetType, manyType);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private DeployBeanProperty createProp(DeployBeanDescriptor<?> desc, Field field) {
Class<?> propertyType = field.getType();
Class<?> innerType = propertyType;
// check for Collection type (list, set or map)
ManyType manyType = determineManyType.getManyType(propertyType);
if (manyType != null) {
// List, Set or Map based object
Class<?> targetType = determineTargetType(field);
if (targetType == null){
Transient transAnnotation = field.getAnnotation(Transient.class);
if (transAnnotation != null) {
// not supporting this field (generic type used)
return null;
}
logger.warn("Could not find parameter type (via reflection) on "+desc.getFullName()+" "+field.getName());
}
return createManyType(desc, targetType, manyType);
}
if (innerType.isEnum() || innerType.isPrimitive()){
return new DeployBeanProperty(desc, propertyType, null, null);
}
ScalarType<?> scalarType = typeManager.getScalarType(innerType);
if (scalarType != null) {
return new DeployBeanProperty(desc, propertyType, scalarType, null);
}
CtCompoundType<?> compoundType = typeManager.getCompoundType(innerType);
if (compoundType != null) {
return new DeployBeanPropertyCompound(desc, propertyType, compoundType, null);
}
if (!isTransientField(field)){
try {
CheckImmutableResponse checkImmutable = typeManager.checkImmutable(innerType);
if (checkImmutable.isImmutable()){
if (checkImmutable.isCompoundType()){
// use reflection to support compound immutable value objects
typeManager.recursiveCreateScalarDataReader(innerType);
compoundType = typeManager.getCompoundType(innerType);
if (compoundType != null) {
return new DeployBeanPropertyCompound(desc, propertyType, compoundType, null);
}
} else {
// use reflection to support simple immutable value objects
scalarType = typeManager.recursiveCreateScalarTypes(innerType);
return new DeployBeanProperty(desc, propertyType, scalarType, null);
}
}
} catch (Exception e){
logger.error("Error with " + desc + " field:" + field.getName(), e);
}
}
return new DeployBeanPropertyAssocOne(desc, propertyType);
}
private boolean isTransientField(Field field) {
Transient t = field.getAnnotation(Transient.class);
return (t != null);
}
private DeployBeanProperty createProp(int level, DeployBeanDescriptor<?> desc, Field field, Class<?> beanType, Method getter, Method setter) {
DeployBeanProperty prop = createProp(desc, field);
if (prop == null){
// transient annotation on unsupported type
return null;
while (it.hasNext()) {
DeployBeanProperty prop = it.next();
if (prop.isTransient()) {
if (prop.getWriteMethod() == null || prop.getReadMethod() == null) {
// Typically a helper method ... this is expected
logger.trace("... transient: " + prop.getFullBeanName());
} else {
prop.setOwningType(beanType);
prop.setName(field.getName());
// the getter or setter could be null if we are using
// javaagent type enhancement. If we are using subclass
// generation then we do need to find the getter and setter
prop.setReadMethod(getter);
prop.setWriteMethod(setter);
prop.setField(field);
return prop;
// dubious, possible error...
String msg = Message.msg("deploy.property.nofield", desc.getFullName(), prop.getName());
logger.warn(msg);
}
}
}
}
/**
* Return true if we should ignore this field.
* <p>
* We want to ignore ebean internal fields and some others as well.
* </p>
*/
private boolean ignoreFieldByName(String fieldName) {
if (fieldName.startsWith("_ebean_")) {
// ignore Ebean internal fields
return true;
}
if (fieldName.startsWith("ajc$instance$")) {
// ignore AspectJ internal fields
return true;
}
/**
* Determine the type of the List,Set or Map. Not been set explicitly so
* determine this from ParameterizedType.
*/
private Class<?> determineTargetType(Field field) {
Type genType = field.getGenericType();
if (genType instanceof ParameterizedType) {
ParameterizedType ptype = (ParameterizedType) genType;
// we are interested in this field
return false;
}
Type[] typeArgs = ptype.getActualTypeArguments();
if (typeArgs.length == 1) {
// probably a Set or List
if (typeArgs[0] instanceof Class<?>){
return (Class<?>) typeArgs[0];
}
//throw new RuntimeException("Unexpected Parameterised Type? "+typeArgs[0]);
return null;
}
if (typeArgs.length == 2) {
// this is probably a Map
if (typeArgs[1] instanceof ParameterizedType) {
// not supporting ParameterizedType on Map.
return null;
}
return (Class<?>) typeArgs[1];
}
}
// if targetType is null, then must be set in annotations
return null;
}
/**
* reflect the bean properties from Class. Some of these properties may not map to database
* columns.
*/
private void createProperties(DeployBeanDescriptor<?> desc, Class<?> beanType, int level) {
boolean scalaObject = desc.isScalaObject();
try {
Method[] declaredMethods = beanType.getDeclaredMethods();
Field[] fields = beanType.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
if (Modifier.isStatic(field.getModifiers())) {
// not interested in static fields
} else if (Modifier.isTransient(field.getModifiers())) {
// not interested in transient fields
logger.trace("Skipping transient field " + field.getName() + " in " + beanType.getName());
} else if (ignoreFieldByName(field.getName())) {
// not interested this field (ebean or aspectJ field)
} else {
String fieldName = getFieldName(field, beanType);
String initFieldName = initCap(fieldName);
Method getter = findGetter(field, initFieldName, declaredMethods, scalaObject);
Method setter = findSetter(field, initFieldName, declaredMethods, scalaObject);
DeployBeanProperty prop = createProp(level, desc, field, beanType, getter, setter);
if (prop == null) {
// transient annotation on unsupported type
} else {
// set a order that gives priority to inherited properties
// push Id/EmbeddedId up and CreatedTimestamp/UpdatedTimestamp down
int sortOverride = prop.getSortOverride();
prop.setSortOrder((level * 10000 + 100 - i + sortOverride));
DeployBeanProperty replaced = desc.addBeanProperty(prop);
if (replaced != null) {
if (replaced.isTransient()) {
// expected for inheritance...
} else {
String msg = "Huh??? property " + prop.getFullBeanName() + " being defined twice";
msg += " but replaced property was not transient? This is not expected?";
logger.warn(msg);
}
}
}
}
}
Class<?> superClass = beanType.getSuperclass();
if (!superClass.equals(Object.class)) {
// recursively add any properties in the inheritance heirarchy
// up to the Object.class level...
createProperties(desc, superClass, level + 1);
}
} catch (PersistenceException ex) {
throw ex;
} catch (Exception ex) {
throw new PersistenceException(ex);
}
}
/**
* Make the first letter of the string upper case.
*/
private String initCap(String str) {
if (str.length() > 1) {
return Character.toUpperCase(str.charAt(0)) + str.substring(1);
} else {
// only a single char
return str.toUpperCase();
}
}
/**
* Return the bean spec field name (trim of "is" from boolean types)
*/
private String getFieldName(Field field, Class<?> beanType) {
String name = field.getName();
if ((Boolean.class.equals(field.getType()) || boolean.class.equals(field.getType())) && name.startsWith("is")
&& name.length() > 2) {
// it is a boolean type field starting with "is"
char c = name.charAt(2);
if (Character.isUpperCase(c)) {
String msg = "trimming off 'is' from boolean field name " + name + " in class " + beanType.getName();
logger.info(msg);
return name.substring(2);
}
}
return name;
}
/**
* Find a public non-static getter method that matches this field (according to bean-spec rules).
*/
private Method findGetter(Field field, String initFieldName, Method[] declaredMethods, boolean scalaObject) {
String methGetName = "get" + initFieldName;
String methIsName = "is" + initFieldName;
String scalaGet = field.getName();
for (int i = 0; i < declaredMethods.length; i++) {
Method m = declaredMethods[i];
if ((scalaObject && m.getName().equals(scalaGet)) || m.getName().equals(methGetName)
|| m.getName().equals(methIsName)) {
Class<?>[] params = m.getParameterTypes();
if (params.length == 0) {
if (field.getType().equals(m.getReturnType())) {
int modifiers = m.getModifiers();
if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers)) {
// we find it...
return m;
}
}
}
}
}
return null;
}
/**
* Find a public non-static setter method that matches this field (according to bean-spec rules).
*/
private Method findSetter(Field field, String initFieldName, Method[] declaredMethods, boolean scalaObject) {
String methSetName = "set" + initFieldName;
String scalaSetName = field.getName() + "_$eq";
for (int i = 0; i < declaredMethods.length; i++) {
Method m = declaredMethods[i];
if ((scalaObject && m.getName().equals(scalaSetName)) || m.getName().equals(methSetName)) {
Class<?>[] params = m.getParameterTypes();
if (params.length == 1 && field.getType().equals(params[0])) {
if (void.class.equals(m.getReturnType())) {
int modifiers = m.getModifiers();
if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers)) {
return m;
}
}
}
}
}
return null;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private DeployBeanProperty createManyType(DeployBeanDescriptor<?> desc, Class<?> targetType, ManyType manyType) {
try {
ScalarType<?> scalarType = typeManager.getScalarType(targetType);
if (scalarType != null) {
return new DeployBeanPropertySimpleCollection(desc, targetType, scalarType, manyType);
}
} catch (NullPointerException e) {
logger.debug("expected non-scalar type" + e.getMessage());
}
// TODO: Handle Collection of CompoundType and Embedded Type
return new DeployBeanPropertyAssocMany(desc, targetType, manyType);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private DeployBeanProperty createProp(DeployBeanDescriptor<?> desc, Field field) {
Class<?> propertyType = field.getType();
Class<?> innerType = propertyType;
// check for Collection type (list, set or map)
ManyType manyType = determineManyType.getManyType(propertyType);
if (manyType != null) {
// List, Set or Map based object
Class<?> targetType = determineTargetType(field);
if (targetType == null) {
Transient transAnnotation = field.getAnnotation(Transient.class);
if (transAnnotation != null) {
// not supporting this field (generic type used)
return null;
}
logger.warn("Could not find parameter type (via reflection) on " + desc.getFullName() + " " + field.getName());
}
return createManyType(desc, targetType, manyType);
}
if (innerType.isEnum() || innerType.isPrimitive()) {
return new DeployBeanProperty(desc, propertyType, null, null);
}
ScalarType<?> scalarType = typeManager.getScalarType(innerType);
if (scalarType != null) {
return new DeployBeanProperty(desc, propertyType, scalarType, null);
}
CtCompoundType<?> compoundType = typeManager.getCompoundType(innerType);
if (compoundType != null) {
return new DeployBeanPropertyCompound(desc, propertyType, compoundType, null);
}
if (isTransientField(field)) {
return null;
}
try {
CheckImmutableResponse checkImmutable = typeManager.checkImmutable(innerType);
if (checkImmutable.isImmutable()) {
if (checkImmutable.isCompoundType()) {
// use reflection to support compound immutable value objects
typeManager.recursiveCreateScalarDataReader(innerType);
compoundType = typeManager.getCompoundType(innerType);
if (compoundType != null) {
return new DeployBeanPropertyCompound(desc, propertyType, compoundType, null);
}
} else {
// use reflection to support simple immutable value objects
scalarType = typeManager.recursiveCreateScalarTypes(innerType);
return new DeployBeanProperty(desc, propertyType, scalarType, null);
}
}
return new DeployBeanPropertyAssocOne(desc, propertyType);
} catch (Exception e) {
logger.error("Error with " + desc + " field:" + field.getName(), e);
return null;
}
}
private boolean isTransientField(Field field) {
Transient t = field.getAnnotation(Transient.class);
return (t != null);
}
private DeployBeanProperty createProp(int level, DeployBeanDescriptor<?> desc, Field field, Class<?> beanType,
Method getter, Method setter) {
DeployBeanProperty prop = createProp(desc, field);
if (prop == null) {
// transient annotation on unsupported type
return null;
} else {
prop.setOwningType(beanType);
prop.setName(field.getName());
// the getter or setter could be null if we are using
// javaagent type enhancement. If we are using subclass
// generation then we do need to find the getter and setter
prop.setReadMethod(getter);
prop.setWriteMethod(setter);
prop.setField(field);
return prop;
}
}
/**
* Determine the type of the List,Set or Map. Not been set explicitly so determine this from
* ParameterizedType.
*/
private Class<?> determineTargetType(Field field) {
Type genType = field.getGenericType();
if (genType instanceof ParameterizedType) {
ParameterizedType ptype = (ParameterizedType) genType;
Type[] typeArgs = ptype.getActualTypeArguments();
if (typeArgs.length == 1) {
// probably a Set or List
if (typeArgs[0] instanceof Class<?>) {
return (Class<?>) typeArgs[0];
}
// throw new RuntimeException("Unexpected Parameterised Type? "+typeArgs[0]);
return null;
}
if (typeArgs.length == 2) {
// this is probably a Map
if (typeArgs[1] instanceof ParameterizedType) {
// not supporting ParameterizedType on Map.
return null;
}
return (Class<?>) typeArgs[1];
}
}
// if targetType is null, then must be set in annotations
return null;
}
}
@@ -135,7 +135,8 @@ public class DeployInherit {
}
DiscriminatorColumn da = (DiscriminatorColumn) cls.getAnnotation(DiscriminatorColumn.class);
if (da != null) {
info.setDiscriminatorColumn(da.name());
// lowercase the discriminator column for RawSql and JSON
info.setDiscriminatorColumn(da.name().toLowerCase());
DiscriminatorType discriminatorType = da.discriminatorType();
if (discriminatorType.equals(DiscriminatorType.INTEGER)){
info.setDiscriminatorType(Types.INTEGER);
@@ -12,6 +12,7 @@ import com.avaje.ebean.FutureList;
import com.avaje.ebean.FutureRowCount;
import com.avaje.ebean.Junction;
import com.avaje.ebean.OrderBy;
import com.avaje.ebean.PagedList;
import com.avaje.ebean.PagingList;
import com.avaje.ebean.QueryIterator;
import com.avaje.ebean.QueryResultVisitor;
@@ -39,7 +40,7 @@ abstract class JunctionExpression<T> implements Junction<T>, SpiExpression, Expr
private static final long serialVersionUID = -645619859900030678L;
Conjunction(com.avaje.ebean.Query<T> query, ExpressionList<T> parent) {
super(AND, query, parent);
super(false, AND, query, parent);
}
}
@@ -48,17 +49,21 @@ abstract class JunctionExpression<T> implements Junction<T>, SpiExpression, Expr
private static final long serialVersionUID = -8464470066692221413L;
Disjunction(com.avaje.ebean.Query<T> query, ExpressionList<T> parent) {
super(OR, query, parent);
super(true, OR, query, parent);
}
}
// private final ArrayList<SpiExpression> list = new
// ArrayList<SpiExpression>();
private final DefaultExpressionList<T> exprList;
private final String joinType;
JunctionExpression(String joinType, com.avaje.ebean.Query<T> query, ExpressionList<T> parent) {
/**
* If true then a disjunction which means outer joins are required.
*/
private final boolean disjunction;
JunctionExpression(boolean disjunction, String joinType, com.avaje.ebean.Query<T> query, ExpressionList<T> parent) {
this.disjunction = disjunction;
this.joinType = joinType;
this.exprList = new DefaultExpressionList<T>(query, parent);
}
@@ -67,9 +72,20 @@ abstract class JunctionExpression<T> implements Junction<T>, SpiExpression, Expr
List<SpiExpression> list = exprList.internalList();
// get the current state for 'require outer joins'
boolean parentOuterJoins = manyWhereJoin.isRequireOuterJoins();
if (disjunction) {
// turn on outer joins required for disjunction expressions
manyWhereJoin.setRequireOuterJoins(true);
}
for (int i = 0; i < list.size(); i++) {
list.get(i).containsMany(desc, manyWhereJoin);
}
if (disjunction && !parentOuterJoins) {
// restore state to not forcing outer joins
manyWhereJoin.setRequireOuterJoins(false);
}
}
public Junction<T> add(Expression item) {
@@ -229,6 +245,11 @@ abstract class JunctionExpression<T> implements Junction<T>, SpiExpression, Expr
public PagingList<T> findPagingList(int pageSize) {
return exprList.findPagingList(pageSize);
}
@Override
public PagedList<T> findPagedList(int pageIndex, int pageSize) {
return exprList.findPagedList(pageIndex, pageSize);
}
public int findRowCount() {
return exprList.findRowCount();
@@ -56,6 +56,13 @@ public abstract class DLoadBaseContext {
if (queryProps == null) {
return batchSize;
}
int queryFetchBatch = queryProps.getQueryFetchBatch();
if (queryFetchBatch > 0) {
// property join was automatically set to a 'query join'
return queryFetchBatch;
}
FetchConfig fetchConfig = queryProps.getFetchConfig();
if (fetchConfig == null) {
return batchSize;
@@ -29,8 +29,8 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex
super(parent, desc, path, defaultBatchSize, queryProps);
this.bufferList = new ArrayList<DLoadBeanContext.LoadBuffer>();
this.currentBuffer = createBuffer(firstBatchSize);
this.bufferList = queryFetch ? new ArrayList<DLoadBeanContext.LoadBuffer>() : null;
}
protected void configureQuery(SpiQuery<?> query, String lazyLoadProperty) {
@@ -78,7 +78,7 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex
for (LoadBuffer loadBuffer : bufferList) {
if (!loadBuffer.list.isEmpty()) {
boolean loadCache = false;
LoadBeanRequest req = new LoadBeanRequest(loadBuffer, parentRequest.getTransaction(), false, null, loadCache);
LoadBeanRequest req = new LoadBeanRequest(loadBuffer, parentRequest, false, null, loadCache);
parent.getEbeanServer().loadBean(req);
if (!queryProps.isQueryFetchAll()) {
@@ -173,7 +173,7 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex
}
}
LoadBeanRequest req = new LoadBeanRequest(this, null, true, ebi.getLazyLoadProperty(), context.hitCache);
LoadBeanRequest req = new LoadBeanRequest(this, true, ebi.getLazyLoadProperty(), context.hitCache);
context.desc.getEbeanServer().loadBean(req);
}
@@ -91,7 +91,7 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
if (bufferList != null) {
for (LoadBuffer loadBuffer : bufferList) {
if (!loadBuffer.list.isEmpty()) {
LoadManyRequest req = new LoadManyRequest(loadBuffer, parentRequest.getTransaction(), requestedBatchSize, false, false, false);
LoadManyRequest req = new LoadManyRequest(loadBuffer, parentRequest, requestedBatchSize, false, false, false);
parent.getEbeanServer().loadMany(req);
if (!queryProps.isQueryFetchAll()) {
// Stop - only fetch the first batch ... the rest will be lazy loaded
@@ -187,7 +187,7 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
// Should reduce the list by checking each beanCollection in the L2 first before executing the query
LoadManyRequest req = new LoadManyRequest(this, null, batchSize, true, onlyIds, useCache);
LoadManyRequest req = new LoadManyRequest(this, batchSize, true, onlyIds, useCache);
context.parent.getEbeanServer().loadMany(req);
}
}
@@ -558,6 +558,7 @@ public final class DefaultPersister implements Persister {
// skip saving this bean
} else {
t.depth(+1);
prop.setParentBeanToChild(parentBean, detailBean);
saveRecurse(detailBean, t, parentBean);
t.depth(-1);
}
@@ -22,6 +22,7 @@ import com.avaje.ebean.bean.NodeUsageCollector;
import com.avaje.ebean.bean.NodeUsageListener;
import com.avaje.ebean.bean.ObjectGraphNode;
import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebeaninternal.api.LoadContext;
import com.avaje.ebeaninternal.api.SpiExpressionList;
import com.avaje.ebeaninternal.api.SpiQuery;
@@ -58,7 +59,7 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
private static final Logger logger = LoggerFactory.getLogger(CQuery.class);
private static final int GLOBAL_ROW_LIMIT = 1000000;
private static final int GLOBAL_ROW_LIMIT = GlobalProperties.getInt("query.globallimit",1000000);
/**
* The resultSet rows read.
@@ -362,6 +363,7 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
if (forwardOnlyHint) {
// Use forward only hints for large resultset processing (Issue 56, MySql specific)
pstmt = conn.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
pstmt.setFetchSize(Integer.MIN_VALUE);
} else {
pstmt = conn.prepareStatement(sql);
}
@@ -257,21 +257,21 @@ public class CQueryBuilder implements Constants {
ElPropertyValue el = descriptor.getElGetValue(propertyName);
if (el == null) {
String msg = "Property [" + propertyName + "] not found on " + descriptor.getFullName();
throw new PersistenceException(msg);
}
BeanProperty beanProperty = el.getBeanProperty();
if (beanProperty.isId()) {
// For @Id properties we chop off the last part of the path
propertyName = SplitName.parent(propertyName);
} else if (beanProperty instanceof BeanPropertyAssocOne<?>) {
String msg = "Column [" + column.getDbColumn() + "] mapped to complex Property[" + propertyName + "]";
msg += ". It should be mapped to a simple property (proably the Id property). ";
throw new PersistenceException(msg);
}
if (propertyName != null) {
String[] pathProp = SplitName.split(propertyName);
pathProps.addToPath(pathProp[0], pathProp[1]);
throw new PersistenceException("Property [" + propertyName + "] not found on " + descriptor.getFullName());
} else {
BeanProperty beanProperty = el.getBeanProperty();
if (beanProperty.isId() || beanProperty.isDiscriminator()) {
// For @Id properties we chop off the last part of the path
propertyName = SplitName.parent(propertyName);
} else if (beanProperty instanceof BeanPropertyAssocOne<?>) {
String msg = "Column [" + column.getDbColumn() + "] mapped to complex Property[" + propertyName + "]";
msg += ". It should be mapped to a simple property (proably the Id property). ";
throw new PersistenceException(msg);
}
if (propertyName != null) {
String[] pathProp = SplitName.split(propertyName);
pathProps.addToPath(pathProp[0], pathProp[1]);
}
}
}
}
@@ -305,6 +305,8 @@ public class CQueryBuilder implements Constants {
StringBuilder sb = new StringBuilder(500);
String dbOrderBy = predicates.getDbOrderBy();
if (selectClause != null) {
sb.append(selectClause);
@@ -320,6 +322,10 @@ public class CQueryBuilder implements Constants {
}
sb.append(select.getSelectSql());
if (query.isDistinct() && dbOrderBy != null) {
// add the orderby columns to the select clause (due to distinct)
sb.append(", ").append(convertDbOrderByForSelect(dbOrderBy));
}
}
sb.append(" from ");
@@ -375,7 +381,7 @@ public class CQueryBuilder implements Constants {
sb.append(dbFilterMany);
}
String dbOrderBy = predicates.getDbOrderBy();
if (dbOrderBy != null) {
sb.append(" order by ").append(dbOrderBy);
}
@@ -386,12 +392,20 @@ public class CQueryBuilder implements Constants {
return sqlLimiter.limit(r);
} else {
return new SqlLimitResponse(dbPlatform.completeSql(sb.toString(), query), false);
}
}
/**
* Convert the dbOrderBy clause to be safe for adding to select. This is done when 'distinct' is
* used.
*/
private String convertDbOrderByForSelect(String dbOrderBy) {
// just remove the ASC and DESC keywords
return dbOrderBy.replaceAll("(?i)\\b asc\\b|\\b desc\\b", "");
}
private boolean isEmpty(String s) {
return s == null || s.length() == 0;
}
@@ -6,39 +6,49 @@ import java.util.List;
import com.avaje.ebean.RawSql.ColumnMapping;
import com.avaje.ebean.config.dbplatform.SqlLimitResponse;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
import com.avaje.ebeaninternal.server.type.DataReader;
import com.avaje.ebeaninternal.server.type.RsetDataReaderIndexed;
/**
* RawSql based query plan.
*/
public class CQueryPlanRawSql extends CQueryPlan {
private final int[] rsetIndexPositions;
public CQueryPlanRawSql(OrmQueryRequest<?> request, SqlLimitResponse sqlRes, SqlTree sqlTree, String logWhereSql) {
super(request, sqlRes, sqlTree, true, logWhereSql);
this.rsetIndexPositions = createIndexPositions(request, sqlTree);
private final int[] rsetIndexPositions;
public CQueryPlanRawSql(OrmQueryRequest<?> request, SqlLimitResponse sqlRes, SqlTree sqlTree, String logWhereSql) {
super(request, sqlRes, sqlTree, true, logWhereSql);
this.rsetIndexPositions = createIndexPositions(request, sqlTree);
}
public DataReader createDataReader(ResultSet rset) {
return new RsetDataReaderIndexed(rset, rsetIndexPositions, isRowNumberIncluded());
}
private int[] createIndexPositions(OrmQueryRequest<?> request, SqlTree sqlTree) {
List<String> chain = sqlTree.buildSelectExpressionChain();
ColumnMapping columnMapping = request.getQuery().getRawSql().getColumnMapping();
InheritInfo inheritInfo = request.getBeanDescriptor().getInheritInfo();
boolean addDiscriminator = inheritInfo != null;
int offset = addDiscriminator ? 1 : 0;
int[] indexPositions = new int[chain.size() + offset];
if (addDiscriminator) {
// discriminator column must always be first in the query
indexPositions[0] = 1;
}
for (int i = 0; i < chain.size(); i++) {
String expr = chain.get(i);
int indexPos = 1 + columnMapping.getIndexPosition(expr);
indexPositions[i + offset] = indexPos;
}
public DataReader createDataReader(ResultSet rset){
return new RsetDataReaderIndexed(rset, rsetIndexPositions, isRowNumberIncluded());
}
private int[] createIndexPositions(OrmQueryRequest<?> request, SqlTree sqlTree) {
List<String> chain = sqlTree.buildSelectExpressionChain();
ColumnMapping columnMapping = request.getQuery().getRawSql().getColumnMapping();
int[] indexPositions = new int[chain.size()];
for (int i = 0; i < chain.size(); i++) {
String expr = chain.get(i);
int indexPos = 1 + columnMapping.getIndexPosition(expr);
indexPositions[i] = indexPos;
}
return indexPositions;
}
return indexPositions;
}
}
@@ -175,12 +175,11 @@ public class DefaultDbSqlContext implements DbSqlContext {
return this;
}
public void appendFormulaJoin(String sqlFormulaJoin, boolean forceOuterJoin) {
public void appendFormulaJoin(String sqlFormulaJoin, SqlJoinType joinType) {
// replace ${ta} place holder with the real table alias...
String tableAlias = tableAliasStack.peek();
String converted = StringHelper
.replaceString(sqlFormulaJoin, tableAliasPlaceHolder, tableAlias);
String converted = StringHelper.replaceString(sqlFormulaJoin, tableAliasPlaceHolder, tableAlias);
if (formulaJoins == null) {
formulaJoins = new HashSet<String>();
@@ -195,7 +194,7 @@ public class DefaultDbSqlContext implements DbSqlContext {
formulaJoins.add(converted);
sb.append(" ");
if (forceOuterJoin) {
if (joinType == SqlJoinType.OUTER) {
if ("join".equals(sqlFormulaJoin.substring(0, 4).toLowerCase())) {
// prepend left outer as we are in the 'many' part
append(" left outer ");
@@ -0,0 +1,110 @@
package com.avaje.ebeaninternal.server.query;
import java.util.List;
import java.util.concurrent.Future;
import javax.persistence.PersistenceException;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.PagedList;
import com.avaje.ebeaninternal.api.Monitor;
import com.avaje.ebeaninternal.api.SpiQuery;
/**
* PagedList implementation based on limit offset types of queries.
*
* @param <T>
* the entity bean type
*/
public class LimitOffsetPagedList<T> implements PagedList<T> {
private final transient EbeanServer server;
private final SpiQuery<T> query;
private final int pageSize;
private final int pageIndex;
private final Monitor monitor = new Monitor();
private Future<Integer> futureRowCount;
private List<T> list;
public LimitOffsetPagedList(EbeanServer server, SpiQuery<T> query, int pageIndex, int pageSize) {
this.server = server;
this.query = query;
this.pageSize = pageSize;
this.pageIndex = pageIndex;
}
public void loadRowCount() {
getFutureRowCount();
}
public Future<Integer> getFutureRowCount() {
synchronized (monitor) {
if (futureRowCount == null) {
futureRowCount = server.findFutureRowCount(query, null);
}
return futureRowCount;
}
}
public List<T> getList() {
synchronized (monitor) {
if (list == null) {
query.setFirstRow(pageIndex * pageSize);
query.setMaxRows(pageSize);
list = server.findList(query, null);
}
return list;
}
}
public int getTotalPageCount() {
int rowCount = getTotalRowCount();
if (rowCount == 0) {
return 0;
} else {
return ((rowCount - 1) / pageSize) + 1;
}
}
public int getTotalRowCount() {
try {
return getFutureRowCount().get();
} catch (Exception e) {
throw new PersistenceException(e);
}
}
public boolean hasNext() {
return pageIndex < (getTotalPageCount() - 1);
}
public boolean hasPrev() {
return pageIndex > 0;
}
public int getPageIndex() {
return pageIndex;
}
public int getPageSize() {
return pageSize;
}
public String getDisplayXtoYofZ(String to, String of) {
int first = pageIndex * pageSize + 1;
int last = first + getList().size() - 1;
int total = getTotalRowCount();
return first + to + last + of + total;
}
}
@@ -56,7 +56,7 @@ public class SqlBeanLoad {
public Object load(BeanProperty prop) throws SQLException {
if (!rawSql && prop.isTransient()) {
if (!rawSql && !prop.isLoadProperty()) {
return null;
}
@@ -0,0 +1,70 @@
package com.avaje.ebeaninternal.server.query;
/**
* Inner join, Outer join or automatic determination based on cardinality and optionality.
*/
public enum SqlJoinType {
/**
* It is an inner join.
*/
INNER("join"),
/**
* It is an outer join.
*/
OUTER("left outer join"),
/**
* It is automatically determined based on cardinality and optionality.
*/
AUTO("JOIN-TYPE-AUTO-LITERAL-NOT-USED");
String literal;
SqlJoinType(String literal) {
this.literal = literal;
}
/**
* Return the SQL join literal.
*/
public String getLiteral() {
return literal;
}
/**
* Return the actual SQL join literal taking into account the current join type and the 'default
* join type as per deployment cardinality and optionality'.
*/
public String getLiteral(SqlJoinType deploymentJoinType) {
if (this == SqlJoinType.AUTO) {
return deploymentJoinType.getLiteral();
}
return this.getLiteral();
}
/**
* If this is an AUTO join set it to OUTER as we are joining to a Many.
*/
public SqlJoinType autoToOuter() {
if (this == AUTO) {
return OUTER;
} else {
return this;
}
}
/**
* If join is AUTO but deploymentJoinType is OUTER then go into OUTER join mode.
*/
public SqlJoinType autoToOuter(SqlJoinType deploymentJoinType) {
if (this == AUTO && deploymentJoinType == OUTER) {
return OUTER;
} else {
return this;
}
}
}
@@ -18,206 +18,198 @@ import com.avaje.ebeaninternal.server.el.ElPropertyDeploy;
*/
public class SqlTreeAlias {
private int counter;
private int manyWhereCounter;
private TreeSet<String> joinProps = new TreeSet<String>();
private int counter;
private HashSet<String> embeddedPropertyJoins;
private int manyWhereCounter;
private TreeSet<String> manyWhereJoinProps = new TreeSet<String>();
private TreeSet<String> joinProps = new TreeSet<String>();
private HashMap<String,String> aliasMap = new HashMap<String,String>();
private HashSet<String> embeddedPropertyJoins;
private HashMap<String, String> manyWhereAliasMap = new HashMap<String, String>();
private TreeSet<String> manyWhereJoinProps = new TreeSet<String>();
private final String rootTableAlias;
public SqlTreeAlias(String rootTableAlias) {
this.rootTableAlias = rootTableAlias;
}
private HashMap<String, String> aliasMap = new HashMap<String, String>();
/**
* Add joins to support where predicates
* @param manyWhereJoins
*/
public void addManyWhereJoins(Set<String> manyWhereJoins) {
if (manyWhereJoins != null){
for (String include : manyWhereJoins) {
addPropertyJoin(include, manyWhereJoinProps);
}
}
private HashMap<String, String> manyWhereAliasMap = new HashMap<String, String>();
private final String rootTableAlias;
public SqlTreeAlias(String rootTableAlias) {
this.rootTableAlias = rootTableAlias;
}
/**
* Add joins to support where predicates
*/
public void addManyWhereJoins(Set<String> manyWhereJoins) {
if (manyWhereJoins != null) {
for (String include : manyWhereJoins) {
addPropertyJoin(include, manyWhereJoinProps);
}
}
private void addEmbeddedPropertyJoin(String embProp){
if (embeddedPropertyJoins == null){
embeddedPropertyJoins = new HashSet<String>();
}
embeddedPropertyJoins.add(embProp);
}
private void addEmbeddedPropertyJoin(String embProp) {
if (embeddedPropertyJoins == null) {
embeddedPropertyJoins = new HashSet<String>();
}
/**
* Add joins.
*/
public void addJoin(Set<String> propJoins, BeanDescriptor<?> desc) {
if (propJoins != null){
for (String propJoin : propJoins) {
ElPropertyDeploy elProp = desc.getElPropertyDeploy(propJoin);
if (elProp != null && elProp.getBeanProperty().isEmbedded()) {
String[] split = SplitName.split(propJoin);
addPropertyJoin(split[0], joinProps);
addEmbeddedPropertyJoin(propJoin);
} else {
addPropertyJoin(propJoin, joinProps);
}
}
}
}
embeddedPropertyJoins.add(embProp);
}
private void addPropertyJoin(String include, TreeSet<String> set){
if (set.add(include)) {
String[] split = SplitName.split(include);
if (split[0] != null){
addPropertyJoin(split[0], set);
}
}
}
/**
* Build a set of table alias for the given bean and fetch
* joined properties.
*/
public void buildAlias() {
Iterator<String> i = joinProps.iterator();
while (i.hasNext()) {
calcAlias(i.next());
}
/**
* Add joins.
*/
public void addJoin(Set<String> propJoins, BeanDescriptor<?> desc) {
if (propJoins != null) {
for (String propJoin : propJoins) {
ElPropertyDeploy elProp = desc.getElPropertyDeploy(propJoin);
if (elProp != null && elProp.getBeanProperty().isEmbedded()) {
addEmbeddedPropertyJoin(propJoin);
i = manyWhereJoinProps.iterator();
while (i.hasNext()) {
calcAliasManyWhere(i.next());
}
mapEmbeddedPropertyAlias();
}
private void mapEmbeddedPropertyAlias() {
if (embeddedPropertyJoins != null){
for (String propJoin : embeddedPropertyJoins) {
String[] split = SplitName.split(propJoin);
// the table alias of the parent path
String alias = getTableAlias(split[0]);
aliasMap.put(propJoin, alias);
}
}
}
private String calcAlias(String prefix) {
String alias = nextTableAlias();
aliasMap.put(prefix, alias);
return alias;
}
private String calcAliasManyWhere(String prefix) {
String alias = nextManyWhereTableAlias();
manyWhereAliasMap.put(prefix, alias);
return alias;
}
/**
* Return the table alias for a given property name.
*/
public String getTableAlias(String prefix){
if (prefix == null){
return rootTableAlias;
} else {
String s = aliasMap.get(prefix);
if (s == null){
return calcAlias(prefix);
}
return s;
}
}
/**
* Return an alias using "Many where joins".
*/
public String getTableAliasManyWhere(String prefix) {
if (prefix == null){
return rootTableAlias;
}
String s = manyWhereAliasMap.get(prefix);
if (s == null){
s = aliasMap.get(prefix);
}
if (s == null) {
String msg = "Could not determine table alias for [" + prefix + "] manyMap["
+ manyWhereAliasMap + "] aliasMap[" + aliasMap + "]";
throw new RuntimeException(msg);
}
return s;
}
/**
* Parse for where clauses that uses "Many where joins"
*/
public String parseWhere(String clause) {
clause = parseRootAlias(clause);
clause = parseAliasMap(clause, manyWhereAliasMap);
return parseAliasMap(clause, aliasMap);
}
/**
* Parse without using any extra "Many where joins".
*/
public String parse(String clause) {
clause = parseRootAlias(clause);
return parseAliasMap(clause, aliasMap);
}
/**
* Parse the clause replacing the table alias place holders.
*/
private String parseRootAlias(String clause) {
if (rootTableAlias == null){
return clause.replace("${}", "");
} else {
return clause.replace("${}", rootTableAlias+".");
addPropertyJoin(propJoin, joinProps);
}
}
}
/**
* Parse the clause replacing the table alias place holders.
*/
private String parseAliasMap(String clause, HashMap<String,String> parseAliasMap) {
}
Iterator<Entry<String, String>> i = parseAliasMap.entrySet().iterator();
while (i.hasNext()) {
Map.Entry<String,String> e = i.next();
String k = "${"+e.getKey()+"}";
clause = clause.replace(k, e.getValue()+".");
}
return clause;
}
/**
* Return the next valid table alias given the preferred table alias.
*/
private String nextTableAlias() {
return "t"+(++counter);
}
private String nextManyWhereTableAlias() {
return "u"+(++manyWhereCounter);
private void addPropertyJoin(String include, TreeSet<String> set) {
if (set.add(include)) {
String[] split = SplitName.split(include);
if (split[0] != null) {
addPropertyJoin(split[0], set);
}
}
}
/**
* Build a set of table alias for the given bean and fetch joined properties.
*/
public void buildAlias() {
Iterator<String> i = joinProps.iterator();
while (i.hasNext()) {
calcAlias(i.next());
}
i = manyWhereJoinProps.iterator();
while (i.hasNext()) {
calcAliasManyWhere(i.next());
}
mapEmbeddedPropertyAlias();
}
private void mapEmbeddedPropertyAlias() {
if (embeddedPropertyJoins != null) {
for (String propJoin : embeddedPropertyJoins) {
String[] split = SplitName.split(propJoin);
// the table alias of the parent path
String alias = getTableAlias(split[0]);
aliasMap.put(propJoin, alias);
}
}
}
private String calcAlias(String prefix) {
String alias = nextTableAlias();
aliasMap.put(prefix, alias);
return alias;
}
private String calcAliasManyWhere(String prefix) {
String alias = nextManyWhereTableAlias();
manyWhereAliasMap.put(prefix, alias);
return alias;
}
/**
* Return the table alias for a given property name.
*/
public String getTableAlias(String prefix) {
if (prefix == null) {
return rootTableAlias;
} else {
String s = aliasMap.get(prefix);
if (s == null) {
return calcAlias(prefix);
}
return s;
}
}
/**
* Return an alias using "Many where joins".
*/
public String getTableAliasManyWhere(String prefix) {
if (prefix == null) {
return rootTableAlias;
}
String s = manyWhereAliasMap.get(prefix);
if (s == null) {
s = aliasMap.get(prefix);
}
if (s == null) {
String msg = "Could not determine table alias for [" + prefix + "] manyMap[" + manyWhereAliasMap + "] aliasMap["+ aliasMap + "]";
throw new RuntimeException(msg);
}
return s;
}
/**
* Parse for where clauses that uses "Many where joins"
*/
public String parseWhere(String clause) {
clause = parseRootAlias(clause);
clause = parseAliasMap(clause, manyWhereAliasMap);
return parseAliasMap(clause, aliasMap);
}
/**
* Parse without using any extra "Many where joins".
*/
public String parse(String clause) {
clause = parseRootAlias(clause);
return parseAliasMap(clause, aliasMap);
}
/**
* Parse the clause replacing the table alias place holders.
*/
private String parseRootAlias(String clause) {
if (rootTableAlias == null) {
return clause.replace("${}", "");
} else {
return clause.replace("${}", rootTableAlias + ".");
}
}
/**
* Parse the clause replacing the table alias place holders.
*/
private String parseAliasMap(String clause, HashMap<String, String> parseAliasMap) {
Iterator<Entry<String, String>> i = parseAliasMap.entrySet().iterator();
while (i.hasNext()) {
Map.Entry<String, String> e = i.next();
String k = "${" + e.getKey() + "}";
clause = clause.replace(k, e.getValue() + ".");
}
return clause;
}
/**
* Return the next valid table alias given the preferred table alias.
*/
private String nextTableAlias() {
return "t" + (++counter);
}
private String nextManyWhereTableAlias() {
return "u" + (++manyWhereCounter);
}
}
@@ -10,7 +10,11 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebeaninternal.api.ManyWhereJoins;
import com.avaje.ebeaninternal.api.PropertyJoin;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.api.SpiQuery.Type;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
@@ -25,9 +29,6 @@ import com.avaje.ebeaninternal.server.el.ElPropertyValue;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Factory for SqlTree.
*/
@@ -69,8 +70,7 @@ public class SqlTreeBuilder {
/**
* Construct for RawSql query.
*/
public SqlTreeBuilder(OrmQueryRequest<?> request, CQueryPredicates predicates,
OrmQueryDetail queryDetail) {
public SqlTreeBuilder(OrmQueryRequest<?> request, CQueryPredicates predicates, OrmQueryDetail queryDetail) {
this.rawSql = true;
this.desc = request.getBeanDescriptor();
@@ -97,7 +97,7 @@ public class SqlTreeBuilder {
this.desc = request.getBeanDescriptor();
this.query = request.getQuery();
this.subQuery = Type.SUBQUERY.equals(query.getType());
this.subQuery = Type.SUBQUERY.equals(query.getType()) || Type.ID_LIST.equals(query.getType());
this.includeJoin = query.getIncludeTableJoin();
this.manyWhereJoins = query.getManyWhereJoins();
this.queryDetail = query.getDetail();
@@ -170,7 +170,7 @@ public class SqlTreeBuilder {
if (rawSql) {
return "Not Used";
}
rootNode.appendFrom(ctx, false);
rootNode.appendFrom(ctx, SqlJoinType.AUTO);
return ctx.getContent();
}
@@ -182,7 +182,7 @@ public class SqlTreeBuilder {
if (!rawSql) {
alias.addJoin(queryDetail.getIncludes(), desc);
alias.addJoin(predicates.getPredicateIncludes(), desc);
alias.addManyWhereJoins(manyWhereJoins.getJoins());
alias.addManyWhereJoins(manyWhereJoins.getPropertyNames());
// build set of table alias
alias.buildAlias();
@@ -238,12 +238,10 @@ public class SqlTreeBuilder {
*/
private void addManyWhereJoins(List<SqlTreeNode> myJoinList) {
Set<String> includes = manyWhereJoins.getJoins();
for (String joinProp : includes) {
BeanPropertyAssoc<?> beanProperty = (BeanPropertyAssoc<?>) desc
.getBeanPropertyFromPath(joinProp);
SqlTreeNodeManyWhereJoin nodeJoin = new SqlTreeNodeManyWhereJoin(joinProp, beanProperty);
Collection<PropertyJoin> includes = manyWhereJoins.getPropertyJoins();
for (PropertyJoin joinProp : includes) {
BeanPropertyAssoc<?> beanProperty = (BeanPropertyAssoc<?>) desc.getBeanPropertyFromPath(joinProp.getProperty());
SqlTreeNodeManyWhereJoin nodeJoin = new SqlTreeNodeManyWhereJoin(joinProp.getProperty(), beanProperty, joinProp.getSqlJoinType());
myJoinList.add(nodeJoin);
}
}
@@ -292,12 +290,11 @@ public class SqlTreeBuilder {
// support the predicates or order by clauses.
// remove ManyWhereJoins from the predicateIncludes
predicateIncludes.removeAll(manyWhereJoins.getJoins());
predicateIncludes.removeAll(manyWhereJoins.getPropertyNames());
// look for predicateIncludes that are not in selectIncludes and add
// them as extra joins to the query
IncludesDistiller extraJoinDistill = new IncludesDistiller(desc, selectIncludes,
predicateIncludes);
IncludesDistiller extraJoinDistill = new IncludesDistiller(desc, selectIncludes, predicateIncludes);
Collection<SqlTreeNodeExtraJoin> extraJoins = extraJoinDistill.getExtraJoinRootNodes();
if (extraJoins.isEmpty()) {
@@ -334,8 +331,7 @@ public class SqlTreeBuilder {
BeanProperty p = desc.findBeanProperty(propName);
if (p == null) {
logger
.error("property [" + propName + "]not found on " + desc + " for query - excluding it.");
logger.error("property [" + propName + "]not found on " + desc + " for query - excluding it.");
} else if (p instanceof BeanPropertyAssoc<?> && p.isEmbedded()) {
// if the property is embedded we need to lookup the real column name
@@ -368,9 +364,7 @@ public class SqlTreeBuilder {
if (!selectProps.containsProperty(baseName)) {
BeanProperty p = desc.findBeanProperty(baseName);
if (p == null) {
String m = "property [" + propName + "] not found on " + desc
+ " for query - excluding it.";
logger.error(m);
logger.error("property [" + propName + "] not found on " + desc + " for query - excluding it.");
} else if (p.isEmbedded()) {
// add the embedded bean (and effectively
@@ -378,8 +372,7 @@ public class SqlTreeBuilder {
selectProps.add(p);
} else {
String m = "property [" + p.getFullBeanName()
+ "] expected to be an embedded bean for query - excluding it.";
String m = "property [" + p.getFullBeanName() + "] expected to be an embedded bean for query - excluding it.";
logger.error(m);
}
}
@@ -389,8 +382,9 @@ public class SqlTreeBuilder {
// sub class hierarchy if required
BeanProperty p = desc.findBeanProperty(propName);
if (p == null) {
logger.error("property [" + propName + "] not found on " + desc
+ " for query - excluding it.");
logger.error("property [" + propName + "] not found on " + desc + " for query - excluding it.");
p = desc.findBeanProperty("id");
selectProps.add(p);
} else if (p.isId()) {
// do not bother to include id for normal queries as the
@@ -415,7 +409,7 @@ public class SqlTreeBuilder {
private SqlTreeProperties getBaseSelectPartial(BeanDescriptor<?> desc, OrmQueryProperties queryProps) {
SqlTreeProperties selectProps = new SqlTreeProperties(desc);
SqlTreeProperties selectProps = new SqlTreeProperties();
selectProps.setReadOnly(queryProps.isReadOnly());
// add properties in the order in which they appear
@@ -443,7 +437,7 @@ public class SqlTreeBuilder {
return getBaseSelectPartial(desc, queryProps);
}
SqlTreeProperties selectProps = new SqlTreeProperties(desc);
SqlTreeProperties selectProps = new SqlTreeProperties();
selectProps.setAllProperties(true);
// normal simple properties of the bean
@@ -486,9 +480,7 @@ public class SqlTreeBuilder {
if (manyProperty != null) {
// only one many associated allowed to be included in fetch
if (logger.isDebugEnabled()) {
String msg = "Not joining [" + propName + "] as already joined to a Many[" + manyProperty
+ "].";
logger.debug(msg);
logger.debug("Not joining [" + propName + "] as already joined to a Many[" + manyProperty + "].");
}
return false;
}
@@ -28,7 +28,7 @@ public interface SqlTreeNode {
/**
* Append to the FROM part of the sql.
*/
public void appendFrom(DbSqlContext ctx, boolean forceOuterJoin);
public void appendFrom(DbSqlContext ctx, SqlJoinType joinType);
/**
* Append any where predicates for inheritance.
@@ -28,50 +28,47 @@ public class SqlTreeNodeBean implements SqlTreeNode {
private static final SqlTreeNode[] NO_CHILDREN = new SqlTreeNode[0];
final BeanDescriptor<?> desc;
protected final BeanDescriptor<?> desc;
final IdBinder idBinder;
protected final IdBinder idBinder;
/**
* The children which will be other SelectBean or SelectProxyBean.
*/
final SqlTreeNode[] children;
protected final SqlTreeNode[] children;
final boolean readOnlyLeaf;
protected final boolean readOnlyLeaf;
/**
* Set to true if this is a partial object fetch.
*/
final boolean partialObject;
protected final boolean partialObject;
final BeanProperty[] properties;
protected final BeanProperty[] properties;
/**
* Extra where clause added by Where annotation on associated many.
*/
final String extraWhere;
protected final String extraWhere;
final BeanPropertyAssoc<?> nodeBeanProp;
protected final BeanPropertyAssoc<?> nodeBeanProp;
final TableJoin[] tableJoins;
protected final TableJoin[] tableJoins;
/**
* False if report bean and has no id property.
*/
final boolean readId;
protected final boolean readId;
final boolean disableLazyLoad;
protected final boolean disableLazyLoad;
final InheritInfo inheritInfo;
protected final InheritInfo inheritInfo;
final String prefix;
final Map<String, String> pathMap;
protected final String prefix;
protected final Map<String, String> pathMap;
final BeanPropertyAssocMany<?> lazyLoadParent;
protected final BeanPropertyAssocMany<?> lazyLoadParent;
public SqlTreeNodeBean(String prefix, BeanPropertyAssoc<?> beanProp, SqlTreeProperties props,
List<SqlTreeNode> myChildren, boolean withId) {
@@ -452,20 +449,21 @@ public class SqlTreeNodeBean implements SqlTreeNode {
/**
* Append to the FROM clause for this node.
*/
public void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) {
public void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
ctx.pushJoin(prefix);
ctx.pushTableAlias(prefix);
forceOuterJoin = appendFromBaseTable(ctx, forceOuterJoin);
// join and return SqlJoinType to use for child joins
joinType = appendFromBaseTable(ctx, joinType);
for (int i = 0; i < properties.length; i++) {
// usually nothing... except for 1-1 Exported
properties[i].appendFrom(ctx, forceOuterJoin);
properties[i].appendFrom(ctx, joinType);
}
for (int i = 0; i < children.length; i++) {
children[i].appendFrom(ctx, forceOuterJoin);
children[i].appendFrom(ctx, joinType);
}
ctx.popTableAlias();
@@ -476,7 +474,7 @@ public class SqlTreeNodeBean implements SqlTreeNode {
* Join to base table for this node. This includes a join to the intersection
* table if this is a ManyToMany node.
*/
public boolean appendFromBaseTable(DbSqlContext ctx, boolean forceOuterJoin) {
public SqlJoinType appendFromBaseTable(DbSqlContext ctx, SqlJoinType joinType) {
if (nodeBeanProp instanceof BeanPropertyAssocMany<?>) {
BeanPropertyAssocMany<?> manyProp = (BeanPropertyAssocMany<?>) nodeBeanProp;
@@ -488,14 +486,14 @@ public class SqlTreeNodeBean implements SqlTreeNode {
String alias2 = alias + "z_";
TableJoin manyToManyJoin = manyProp.getIntersectionTableJoin();
manyToManyJoin.addJoin(forceOuterJoin, parentAlias, alias2, ctx);
manyToManyJoin.addJoin(joinType, parentAlias, alias2, ctx);
return nodeBeanProp.addJoin(forceOuterJoin, alias2, alias, ctx);
return nodeBeanProp.addJoin(joinType, alias2, alias, ctx);
}
}
return nodeBeanProp.addJoin(forceOuterJoin, prefix, ctx);
return nodeBeanProp.addJoin(joinType, prefix, ctx);
}
/**
@@ -22,13 +22,13 @@ import com.avaje.ebeaninternal.server.deploy.TableJoin;
public class SqlTreeNodeExtraJoin implements SqlTreeNode {
final BeanPropertyAssoc<?> assocBeanProperty;
private final BeanPropertyAssoc<?> assocBeanProperty;
final String prefix;
private final String prefix;
final boolean manyJoin;
private final boolean manyJoin;
List<SqlTreeNodeExtraJoin> children;
private List<SqlTreeNodeExtraJoin> children;
public SqlTreeNodeExtraJoin(String prefix, BeanPropertyAssoc<?> assocBeanProperty) {
this.prefix = prefix;
@@ -66,7 +66,7 @@ public class SqlTreeNodeExtraJoin implements SqlTreeNode {
children.add(child);
}
public void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) {
public void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
boolean manyToMany = false;
@@ -82,29 +82,29 @@ public class SqlTreeNodeExtraJoin implements SqlTreeNode {
String alias2 = alias+"z_";
TableJoin manyToManyJoin = manyProp.getIntersectionTableJoin();
manyToManyJoin.addJoin(forceOuterJoin, parentAlias, alias2, ctx);
manyToManyJoin.addJoin(joinType, parentAlias, alias2, ctx);
assocBeanProperty.addJoin(forceOuterJoin, alias2, alias, ctx);
assocBeanProperty.addJoin(joinType, alias2, alias, ctx);
}
}
if (!manyToMany){
assocBeanProperty.addJoin(forceOuterJoin, prefix, ctx);
assocBeanProperty.addJoin(joinType, prefix, ctx);
}
if (children != null){
if (manyJoin){
// make sure all decendants use OUTER JOIN
forceOuterJoin = true;
}
for (int i = 0; i < children.size(); i++) {
SqlTreeNodeExtraJoin child = children.get(i);
child.appendFrom(ctx, forceOuterJoin);
}
}
}
if (children != null) {
if (manyJoin) {
// if AUTO then make all decendants use OUTER JOIN
joinType = joinType.autoToOuter();
}
for (int i = 0; i < children.size(); i++) {
SqlTreeNodeExtraJoin child = children.get(i);
child.appendFrom(ctx, joinType);
}
}
}
/**
* Does nothing.
@@ -34,8 +34,8 @@ public final class SqlTreeNodeManyRoot extends SqlTreeNodeBean {
* Force outer join for everything after the many property.
*/
@Override
public void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) {
super.appendFrom(ctx, true);
public void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
super.appendFrom(ctx, joinType.autoToOuter());
}
}
@@ -14,20 +14,30 @@ import com.avaje.ebeaninternal.server.deploy.TableJoin;
/**
* Join to Many (or child of a many) to support where clause predicates on many properties.
*
* @author rbygrave
*/
public class SqlTreeNodeManyWhereJoin implements SqlTreeNode {
private final String parentPrefix;
private final String prefix;
private final BeanPropertyAssoc<?> nodeBeanProp;
private final SqlTreeNode[] children;
public SqlTreeNodeManyWhereJoin(String prefix, BeanPropertyAssoc<?> prop) {
private final BeanPropertyAssoc<?> nodeBeanProp;
/**
* Child joins.
*/
private final SqlTreeNode[] children;
/**
* The many where join which is either INNER or OUTER.
*/
private final SqlJoinType manyJoinType;
public SqlTreeNodeManyWhereJoin(String prefix, BeanPropertyAssoc<?> prop, SqlJoinType manyJoinType) {
this.nodeBeanProp = prop;
this.prefix = prefix;
this.manyJoinType = manyJoinType;
String[] split = SplitName.split(prefix);
this.parentPrefix = split[0];
@@ -39,12 +49,15 @@ public class SqlTreeNodeManyWhereJoin implements SqlTreeNode {
/**
* Append to the FROM clause for this node.
*/
public void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) {
@Override
public void appendFrom(DbSqlContext ctx, SqlJoinType currentJoinType) {
appendFromBaseTable(ctx, forceOuterJoin);
// always use the join type as per this many where join
// (OUTER for disjunction and otherwise INNER)
appendFromBaseTable(ctx, manyJoinType);
for (int i = 0; i < children.length; i++) {
children[i].appendFrom(ctx, forceOuterJoin);
children[i].appendFrom(ctx, manyJoinType);
}
}
@@ -52,25 +65,25 @@ public class SqlTreeNodeManyWhereJoin implements SqlTreeNode {
* Join to base table for this node. This includes a join to the
* intersection table if this is a ManyToMany node.
*/
public void appendFromBaseTable(DbSqlContext ctx, boolean forceOuterJoin) {
public void appendFromBaseTable(DbSqlContext ctx, SqlJoinType joinType) {
String alias = ctx.getTableAliasManyWhere(prefix);
String parentAlias = ctx.getTableAliasManyWhere(parentPrefix);
if (nodeBeanProp instanceof BeanPropertyAssocOne<?>){
nodeBeanProp.addInnerJoin(parentAlias, alias, ctx);
nodeBeanProp.addJoin(joinType, parentAlias, alias, ctx);
} else {
BeanPropertyAssocMany<?> manyProp = (BeanPropertyAssocMany<?>)nodeBeanProp;
if (!manyProp.isManyToMany()) {
manyProp.addInnerJoin(parentAlias, alias, ctx);
manyProp.addJoin(joinType, parentAlias, alias, ctx);
} else {
String alias2 = alias + "z_";
TableJoin manyToManyJoin = manyProp.getIntersectionTableJoin();
manyToManyJoin.addInnerJoin(parentAlias, alias2, ctx);
manyProp.addInnerJoin(alias2, alias, ctx);
manyToManyJoin.addJoin(joinType, parentAlias, alias2, ctx);
manyProp.addJoin(joinType, alias2, alias, ctx);
}
}
}
@@ -40,7 +40,7 @@ public final class SqlTreeNodeRoot extends SqlTreeNodeBean {
* For the root node there is no join type or on clause etc.
*/
@Override
public boolean appendFromBaseTable(DbSqlContext ctx, boolean forceOuterJoin) {
public SqlJoinType appendFromBaseTable(DbSqlContext ctx, SqlJoinType joinType) {
ctx.append(desc.getBaseTable());
ctx.append(" ").append(ctx.getTableAlias(null));
@@ -48,10 +48,10 @@ public final class SqlTreeNodeRoot extends SqlTreeNodeBean {
if (includeJoin != null) {
String a1 = ctx.getTableAlias(null);
String a2 = "int_"; // unique alias for intersection join
includeJoin.addJoin(forceOuterJoin, a1, a2, ctx);
includeJoin.addJoin(joinType, a1, a2, ctx);
}
return forceOuterJoin;
return joinType;
}
}
@@ -3,9 +3,7 @@ package com.avaje.ebeaninternal.server.query;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.TableJoin;
@@ -15,44 +13,35 @@ import com.avaje.ebeaninternal.server.deploy.TableJoin;
public class SqlTreeProperties {
private static final TableJoin[] EMPTY_TABLE_JOINS = new TableJoin[0];
private final BeanDescriptor<?> desc;
// /**
// * The included Properties that will be used by EntityBeanIntercept
// * to determine lazy loading on partial objects.
// */
Set<String> includedProps;
/**
* True if this node of the tree should have read only entity beans.
*/
boolean readOnly;
private boolean readOnly;
/**
* set to false if the id field is not included.
*/
boolean includeId = true;
private boolean includeId = true;
TableJoin[] tableJoins = EMPTY_TABLE_JOINS;
private TableJoin[] tableJoins = EMPTY_TABLE_JOINS;
/**
* The bean properties in order.
*/
List<BeanProperty> propsList = new ArrayList<BeanProperty>();
private List<BeanProperty> propsList = new ArrayList<BeanProperty>();
/**
* Maintain a list of property names to detect embedded bean additions.
*/
LinkedHashSet<String> propNames = new LinkedHashSet<String>();
private LinkedHashSet<String> propNames = new LinkedHashSet<String>();
private boolean allProperties;
public SqlTreeProperties(BeanDescriptor<?> desc) {
this.desc = desc;
public SqlTreeProperties() {
}
public boolean containsProperty(String propName){
public boolean containsProperty(String propName){
return propNames.contains(propName);
}
@@ -18,6 +18,7 @@ import com.avaje.ebean.FutureList;
import com.avaje.ebean.FutureRowCount;
import com.avaje.ebean.OrderBy;
import com.avaje.ebean.OrderBy.Property;
import com.avaje.ebean.PagedList;
import com.avaje.ebean.PagingList;
import com.avaje.ebean.Query;
import com.avaje.ebean.QueryIterator;
@@ -190,6 +191,8 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
*/
private boolean autoFetchTuned;
private boolean logSecondaryQuery;
/**
* The node of the bean or collection that fired lazy loading. Not null if
* profiling is on and this query is for lazy loading. Used to hook back a
@@ -584,8 +587,31 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
public void setUsageProfiling(boolean usageProfiling) {
this.usageProfiling = usageProfiling;
}
public void setLogSecondaryQuery(boolean logSecondaryQuery) {
this.logSecondaryQuery = logSecondaryQuery;
}
public boolean isLogSecondaryQuery() {
return logSecondaryQuery;
}
public void setParentNode(ObjectGraphNode parentNode) {
private List<SpiQuery<?>> loggedSecondaryQueries;
@Override
public List<SpiQuery<?>> getLoggedSecondaryQueries() {
return loggedSecondaryQueries;
}
public void logSecondaryQuery(SpiQuery<?> query) {
if (loggedSecondaryQueries == null) {
loggedSecondaryQueries = new ArrayList<SpiQuery<?>>();
}
loggedSecondaryQueries.add(query);
}
public void setParentNode(ObjectGraphNode parentNode) {
this.parentNode = parentNode;
}
@@ -915,7 +941,12 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
return server.findPagingList(this, null, pageSize);
}
/**
@Override
public PagedList<T> findPagedList(int pageIndex, int pageSize) {
return server.findPagedList(this, null, pageIndex, pageSize);
}
/**
* Set an ordered bind parameter according to its position. Note that the
* position starts at 1 to be consistent with JDBC PreparedStatement. You
* need to set a parameter value for each ? you have in the query.
@@ -102,7 +102,6 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
private final ScalarType<?> timestampType = new ScalarTypeTimestamp();
private final ScalarType<?> uuidType = new ScalarTypeUUID();
private final ScalarType<?> urlType = new ScalarTypeURL();
private final ScalarType<?> uriType = new ScalarTypeURI();
private final ScalarType<?> localeType = new ScalarTypeLocale();
@@ -140,7 +139,7 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
this.extraTypeFactory = new DefaultTypeFactory(config);
initialiseStandard(clobType, blobType);
initialiseStandard(clobType, blobType, config.isUuidStoreAsBinary());
initialiseJodaTypes();
if (bootupClasses != null) {
@@ -613,7 +612,7 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
* Register all the standard types supported. This is the standard JDBC types
* plus some other common types such as java.util.Date and java.util.Calendar.
*/
protected void initialiseStandard(int platformClobType, int platformBlobType) {
protected void initialiseStandard(int platformClobType, int platformBlobType, boolean binaryUUID) {
ScalarType<?> utilDateType = extraTypeFactory.createUtilDate();
typeMap.put(java.util.Date.class, utilDateType);
@@ -637,13 +636,13 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
// boolean mapping to Types.Integer, Types.VARCHAR or Types.Boolean
}
// ScalarTypeScalaDouble scalaDoubleType = new ScalarTypeScalaDouble();
// typeMap.put(scala.Double.class, scalaDoubleType);
// Store UUID as binary(16) or varchar(40)
ScalarType<?> uuidType = (binaryUUID) ? new ScalarTypeUUIDBinary() : new ScalarTypeUUIDVarchar();
typeMap.put(UUID.class, uuidType);
typeMap.put(Locale.class, localeType);
typeMap.put(Currency.class, currencyType);
typeMap.put(TimeZone.class, timeZoneType);
typeMap.put(UUID.class, uuidType);
typeMap.put(URL.class, urlType);
typeMap.put(URI.class, uriType);
@@ -1,47 +0,0 @@
package com.avaje.ebeaninternal.server.type;
import java.util.UUID;
import com.avaje.ebeaninternal.server.core.BasicTypeConverter;
/**
* ScalarType for java.util.UUID which converts to and from a VARCHAR database column.
*/
public class ScalarTypeUUID extends ScalarTypeBaseVarchar<UUID> {
public ScalarTypeUUID() {
super(UUID.class);
}
@Override
public int getLength() {
return 40;
}
@Override
public UUID convertFromDbString(String dbValue) {
return UUID.fromString(dbValue);
}
@Override
public String convertToDbString(UUID beanValue) {
return formatValue(beanValue);
}
public UUID toBeanType(Object value) {
return BasicTypeConverter.toUUID(value);
}
public Object toJdbcType(Object value) {
return BasicTypeConverter.convert(value, jdbcType);
}
public String formatValue(UUID v) {
return v.toString();
}
public UUID parse(String value) {
return UUID.fromString(value);
}
}
@@ -0,0 +1,141 @@
package com.avaje.ebeaninternal.server.type;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.sql.SQLException;
import java.sql.Types;
import java.util.UUID;
public class ScalarTypeUUIDBinary extends ScalarTypeBase<UUID> {
protected ScalarTypeUUIDBinary() {
super(UUID.class, false, Types.BINARY);
}
@Override
public int getLength() {
return 16;
}
public Object toJdbcType(Object value) {
return convertToBytes(value);
}
@Override
public UUID toBeanType(Object value) {
return convertFromBytes((byte[]) value);
}
@Override
public String formatValue(UUID v) {
return v.toString();
}
@Override
public UUID parse(String value) {
return UUID.fromString(value);
}
@Override
public UUID parseDateTime(long dateTime) {
throw new IllegalStateException("Never called");
}
public boolean isDateTimeCapable() {
return false;
}
/**
* Convert from byte[] to UUID.
*/
public static UUID convertFromBytes(byte[] bytes) {
int usableBytes = Math.min(bytes.length, 16);
// Need exactly 16 bytes - pad the input if not enough bytes are provided
// Use provided bytes in the least significant position; if more than 16 bytes are given,
// then use the first 16 bytes from the array;
byte[] barr = new byte[16];
for (int i = 15, j = usableBytes - 1; j >= 0; i--, j--) {
barr[i] = bytes[j];
}
ByteArrayInputStream bais = new ByteArrayInputStream(barr);
DataInputStream inputStream = new DataInputStream(bais);
try {
long msb = inputStream.readLong();
long lsb = inputStream.readLong();
return new UUID(msb, lsb);
} catch (IOException e) {
throw new RuntimeException("Not Expecting this", e);
}
}
/**
* Convert from UUID to byte[].
*/
public static byte[] convertToBytes(Object value) {
UUID uuid = (UUID) value;
ByteArrayOutputStream baos = new ByteArrayOutputStream(16);
DataOutputStream outputStream = new DataOutputStream(baos);
try {
outputStream.writeLong(uuid.getMostSignificantBits());
outputStream.writeLong(uuid.getLeastSignificantBits());
} catch (IOException e) {
throw new RuntimeException("Not Expecting this", e);
}
return baos.toByteArray();
}
@Override
public void bind(DataBind b, UUID value) throws SQLException {
if (value == null) {
b.setNull(Types.BINARY);
} else {
b.setBytes(convertToBytes(value));
}
}
@Override
public UUID read(DataReader dataReader) throws SQLException {
byte[] bytes = dataReader.getBytes();
if (bytes == null) {
return null;
} else {
return convertFromBytes(bytes);
}
}
@Override
public Object readData(DataInput dataInput) throws IOException {
if (!dataInput.readBoolean()) {
return null;
} else {
return dataInput.readUTF();
}
}
@Override
public void writeData(DataOutput dataOutput, Object v) throws IOException {
String value = (String) v;
if (value == null) {
dataOutput.writeBoolean(false);
} else {
dataOutput.writeBoolean(true);
dataOutput.writeUTF(format(v));
}
}
}
@@ -0,0 +1,47 @@
package com.avaje.ebeaninternal.server.type;
import java.util.UUID;
import com.avaje.ebeaninternal.server.core.BasicTypeConverter;
/**
* ScalarType for java.util.UUID which converts to and from a VARCHAR database column.
*/
public class ScalarTypeUUIDVarchar extends ScalarTypeBaseVarchar<UUID> {
public ScalarTypeUUIDVarchar() {
super(UUID.class);
}
@Override
public int getLength() {
return 40;
}
@Override
public UUID convertFromDbString(String dbValue) {
return UUID.fromString(dbValue);
}
@Override
public String convertToDbString(UUID beanValue) {
return formatValue(beanValue);
}
public UUID toBeanType(Object value) {
return BasicTypeConverter.toUUID(value);
}
public Object toJdbcType(Object value) {
return BasicTypeConverter.convert(value, jdbcType);
}
public String formatValue(UUID v) {
return v.toString();
}
public UUID parse(String value) {
return UUID.fromString(value);
}
}
@@ -14,6 +14,7 @@ import com.avaje.ebean.FutureList;
import com.avaje.ebean.FutureRowCount;
import com.avaje.ebean.Junction;
import com.avaje.ebean.OrderBy;
import com.avaje.ebean.PagedList;
import com.avaje.ebean.PagingList;
import com.avaje.ebean.Query;
import com.avaje.ebean.QueryIterator;
@@ -152,6 +153,11 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
return query.findPagingList(pageSize);
}
@Override
public PagedList<T> findPagedList(int pageIndex, int pageSize) {
return query.findPagedList(pageIndex, pageSize);
}
public int findRowCount() {
return query.findRowCount();
}
@@ -2,8 +2,7 @@ package com.avaje.ebeaninternal.server.cache;
import java.sql.Timestamp;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -11,10 +10,13 @@ import com.avaje.ebean.Ebean;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import com.avaje.tests.model.basic.Address;
import com.avaje.tests.model.basic.Country;
import com.avaje.tests.model.basic.Customer;
import com.avaje.tests.model.basic.Customer.Status;
import com.avaje.tests.model.embedded.EAddress;
import com.avaje.tests.model.embedded.EPerson;
public class TestCacheBeanData extends BaseTestCase {
@@ -68,4 +70,54 @@ public class TestCacheBeanData extends BaseTestCase {
Assert.assertNotNull(newCustomer.getBillingAddress().getId());
}
@Test
public void testCacheBeanExtractAndLoadWithEmbdedded() {
SpiEbeanServer server = (SpiEbeanServer)Ebean.getServer(null);
BeanDescriptor<EPerson> desc = server.getBeanDescriptor(EPerson.class);
BeanPropertyAssocOne<?> addressBeanProperty = (BeanPropertyAssocOne<?>)desc.getBeanProperty("address");
EAddress address = new EAddress();
address.setStreet("92 Someplace Else");
address.setSuburb("Sandringham");
address.setCity("Auckland");
EPerson person = new EPerson();
person.setId(98989L);
person.setName("Rob");
person.setAddress(address);
CachedBeanData addressCacheData = (CachedBeanData)addressBeanProperty.getCacheDataValue((EntityBean) person);
EPerson newPersonCheck = new EPerson();
newPersonCheck.setId(98989L);
addressBeanProperty.setCacheDataValue((EntityBean) newPersonCheck, addressCacheData);
EAddress newAddress = newPersonCheck.getAddress();
Assert.assertEquals(address.getStreet(), newAddress.getStreet());
Assert.assertEquals(address.getCity(), newAddress.getCity());
Assert.assertEquals(address.getSuburb(), newAddress.getSuburb());
CachedBeanData cacheData = desc.cacheBeanExtractData((EntityBean)person);
Assert.assertNotNull(cacheData);
EPerson newPerson = new EPerson();
desc.cacheBeanLoadData((EntityBean)newPerson, cacheData);
Assert.assertNotNull(newPerson.getId());
Assert.assertNotNull(newPerson.getName());
Assert.assertNotNull(newPerson.getAddress());
Assert.assertEquals(person.getId(), newPerson.getId());
Assert.assertEquals(person.getName(), newPerson.getName());
Assert.assertEquals(person.getAddress().getStreet(), newPerson.getAddress().getStreet());
Assert.assertEquals(person.getAddress().getCity(), newPerson.getAddress().getCity());
}
}
@@ -0,0 +1,52 @@
package com.avaje.ebeaninternal.server.type;
import java.util.List;
import java.util.UUID;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.SqlRow;
import com.avaje.tests.model.basic.UUOne;
public class TestBinaryUUID extends BaseTestCase {
@Test
public void test() {
UUOne one0 = new UUOne();
one0.setName("first one");
UUID id = UUID.randomUUID();
UUOne one1 = new UUOne();
one1.setId(id);
one1.setName("second one");
Ebean.save(one0);
Ebean.save(one1);
UUOne fetch0 = Ebean.find(UUOne.class, one0.getId());
UUOne fetch1 = Ebean.find(UUOne.class, one1.getId());
Assert.assertEquals(one0.getId(), fetch0.getId());
Assert.assertEquals(one0.getName(), fetch0.getName());
Assert.assertEquals(one1.getId(), fetch1.getId());
Assert.assertEquals(one1.getName(), fetch1.getName());
String sql = "select id, name from uuone";
List<SqlRow> list = Ebean.createSqlQuery(sql).findList();
for (SqlRow sqlRow : list) {
Object sqlId = sqlRow.get("id");
Assert.assertNotNull(sqlId);
Assert.assertTrue(sqlId instanceof byte[]);
UUID uuid = sqlRow.getUUID("id");
Assert.assertNotNull(uuid);
}
}
}
@@ -0,0 +1,22 @@
package com.avaje.ebeaninternal.server.type;
import java.util.UUID;
import org.junit.Assert;
import org.junit.Test;
public class TestScalarTypeUUIDBinaryConversion {
@Test
public void testConversion() {
UUID id = UUID.randomUUID();
byte[] bytes = ScalarTypeUUIDBinary.convertToBytes(id);
Assert.assertEquals(16, bytes.length);
UUID id2 = (UUID)ScalarTypeUUIDBinary.convertFromBytes(bytes);
Assert.assertEquals(id, id2);
}
}
@@ -0,0 +1,70 @@
package com.avaje.tests.autofetch;
import java.util.List;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.BaseTestCase;
import com.avaje.ebean.Ebean;
import com.avaje.tests.model.embedded.EMain;
import com.avaje.tests.model.embedded.Eembeddable;
public class AutofetchEmbeddedTest extends BaseTestCase {
private final Logger logger = LoggerFactory.getLogger(AutofetchEmbeddedTest.class);
@Test
public void testEmbeddedBeanLazyLoadAndUpdate() {
EMain testBean = new EMain();
testBean.setName("test");
testBean.getEmbeddable().setDescription("test description");
Ebean.save(testBean);
EMain partialBean = Ebean.find(EMain.class).select("version").setId(testBean.getId()).findUnique();
logger.info(" -- invoke lazy loading of embedded bean");
Eembeddable embeddable = partialBean.getEmbeddable();
embeddable.setDescription("modified description");
logger.info(" -- update bean");
Ebean.save(partialBean);
}
@Test
public void testEmbeddedBeanQueryTuning() {
Ebean.getServer(null).getAdminAutofetch().setProfiling(true);
Ebean.getServer(null).getAdminAutofetch().setQueryTuning(true);
Ebean.getServer(null).getAdminAutofetch().setProfilingBase(1);
EMain testBean = new EMain();
testBean.setName("test");
testBean.getEmbeddable().setDescription("test description");
Ebean.save(testBean);
//This should not throw an exception
for (int i = 0; i < 5; i++) {
Ebean.beginTransaction();
try {
List<EMain> result = Ebean.find(EMain.class).setAutofetch(true).findList();
for (EMain e : result) {
e.getEmbeddable().setDescription("Test" + i);
Ebean.save(e);
}
Ebean.commitTransaction();
} finally {
Ebean.endTransaction();
logger.debug(Ebean.getServer(null).getAdminAutofetch().collectUsageViaGC());
}
}
}
@Test
public void testEmbeddedFetch() {
Ebean.find(EMain.class).fetch("embeddable").findList();
}
}
@@ -8,7 +8,6 @@ import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.Update;
import com.avaje.tests.model.basic.EBasic;
public class TestExplicitInsert extends BaseTestCase {
@@ -16,9 +15,6 @@ public class TestExplicitInsert extends BaseTestCase {
@Test
public void test() {
// GlobalProperties.put("ebean.classes",
// ""+LDPerson.class.toString()+","+EBasic.class.toString());
EBasic b = new EBasic();
b.setName("exp insert");
b.setDescription("explicit insert");
@@ -38,28 +34,9 @@ public class TestExplicitInsert extends BaseTestCase {
Assert.assertNotNull(b2.getId());
Assert.assertTrue(!b.getId().equals(b2.getId()));
List<EBasic> list = server.find(EBasic.class).setMaxRows(10).findList();
List<EBasic> list = server.find(EBasic.class).where().in("id",b.getId(), b2.getId()).findList();
Assert.assertTrue(list.size() >= 2);
int firstRow = 1;
List<EBasic> list2 = server.find(EBasic.class).order().asc("id").setFirstRow(firstRow)
.setMaxRows(10).findList();
int expectedCount = list.size() - firstRow;
if (expectedCount > 0) {
Assert.assertEquals(expectedCount, list2.size());
} else {
Assert.assertTrue(list2.isEmpty());
}
Update<EBasic> update = Ebean.createUpdate(EBasic.class,
"update ebasic set description = 'test'");
int rows = update.execute();
Assert.assertTrue(rows > 0);
Ebean.externalModification("e_basic", true, false, true);
Assert.assertEquals(2, list.size());
}
}
@@ -24,13 +24,14 @@ public class TestOrderTotalAmountFormula extends BaseTestCase {
.findList();
for (Customer c0 : l0) {
System.out.println("customer: " + c0.getId());
c0.getId();
List<Order> orders = c0.getOrders();
for (Order order : orders) {
System.out.println("... order:" + order);
order.getId();
}
}
}
}
@@ -7,6 +7,9 @@ import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.FetchConfig;
import com.avaje.ebean.Query;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.tests.model.basic.Customer;
import com.avaje.tests.model.basic.Order;
import com.avaje.tests.model.basic.ResetBasicData;
@@ -14,23 +17,93 @@ import com.avaje.tests.model.basic.ResetBasicData;
public class TestSecondaryQueries extends BaseTestCase {
@Test
public void testQueries() {
public void testSecQueryOneToMany() {
ResetBasicData.reset();
Order testOrder = ResetBasicData.createOrderCustAndOrder("testSecQry10");
Integer custId = testOrder.getCustomer().getId();
Customer cust = Ebean.find(Customer.class).select("name").fetch("contacts", "+query")
.setId(custId).findUnique();
Query<Customer> query = Ebean.find(Customer.class)
.select("name")
.fetch("contacts", "+query")
.setId(custId);
SpiQuery<?> spiQuery = (SpiQuery<?>)query;
spiQuery.setLogSecondaryQuery(true);
Customer cust = query.findUnique();
Assert.assertNotNull(cust);
String generatedSql = query.getGeneratedSql();
Assert.assertTrue(generatedSql.contains("from o_customer t0 where t0.id = ?"));
List<SpiQuery<?>> loggedSecondaryQueries = spiQuery.getLoggedSecondaryQueries();
Assert.assertEquals(1, loggedSecondaryQueries.size());
SpiQuery<?> secondaryQuery = loggedSecondaryQueries.get(0);
String secondarySql = secondaryQuery.getGeneratedSql();
Assert.assertTrue(secondarySql.contains("from contact t0 where (t0.customer_id) in (?)"));
}
@Test
public void testManyToOneWithManyPlusOneToMany() {
List<Order> list = Ebean.find(Order.class).select("status").fetch("details", "+query(10)")
.fetch("customer", "+query name, status").fetch("customer.contacts").where()
.eq("status", Order.Status.NEW).findList();
ResetBasicData.reset();
Query<Order> query = Ebean.find(Order.class)
.select("status")
.fetch("customer", "name, status", new FetchConfig().query())
.fetch("customer.contacts")
.fetch("details", new FetchConfig().query())
.where().eq("status", Order.Status.NEW)
.query();
// .fetch("customer", "+query name, status")
// .fetch("details", "+query(10)")
SpiQuery<?> spiQuery = (SpiQuery<?>)query;
spiQuery.setLogSecondaryQuery(true);
List<Order> list = query.findList();
Assert.assertTrue(list.size() > 0);
for (Order order : list) {
order.getCustomer().getStatus();
}
String generatedSql = spiQuery.getGeneratedSql();
//select t0.id c0, t0.status c1, t0.kcustomer_id c2 from o_order t0 where t0.status = ? ; --bind(NEW)
Assert.assertEquals("select t0.id c0, t0.status c1, t0.kcustomer_id c2 from o_order t0 where t0.status = ? ", generatedSql);
List<SpiQuery<?>> secondaryQueries = spiQuery.getLoggedSecondaryQueries();
Assert.assertEquals(2, secondaryQueries.size());
SpiQuery<?> custSecondaryQuery = secondaryQueries.get(0);
String custSecondarySql = custSecondaryQuery.getGeneratedSql();
// select t0.id c0, t0.name c1, t0.status c2,
// t1.id c3, t1.first_name c4, t1.last_name c5, t1.phone c6, t1.mobile c7, t1.email c8, t1.cretime c9, t1.updtime c10, t1.customer_id c11, t1.group_id c12
// from o_customer t0
// left outer join contact t1 on t1.customer_id = t0.id
// where t0.id = ? order by t0.id; --bind(1)
Assert.assertTrue(custSecondarySql.contains("from o_customer t0 "));
Assert.assertTrue(custSecondarySql.contains("left outer join contact t1 on t1.customer_id = t0.id "));
Assert.assertTrue(custSecondarySql.contains("where t0.id "));
SpiQuery<?> orderDetailsSecondaryQuery = secondaryQueries.get(1);
String ordSecondarySql = orderDetailsSecondaryQuery.getGeneratedSql();
// select ...
// from o_order_detail t0
// where (t0.order_id) in (?,?,?,?,?) ; --bind(1,4,1,1,1)
Assert.assertTrue(ordSecondarySql.contains(" from o_order_detail t0 where (t0.order_id) in (?"));
}
}
@@ -0,0 +1,34 @@
package com.avaje.tests.cascade;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
import com.avaje.ebean.Ebean;
import com.avaje.tests.model.basic.TSDetail;
import com.avaje.tests.model.basic.TSMaster;
public class TestPrivateOwnedAddRemoveOrphan extends BaseTestCase {
@Test
public void test(){
// setup
TSMaster master0 = new TSMaster();
Ebean.save(master0);
// act
TSMaster master1 = Ebean.find(master0.getClass(), master0.getId());
TSDetail tsDetail = new TSDetail();
// Add then remove a bean that was never saved (to the DB)
master1.getDetails().add(tsDetail);
master1.getDetails().remove(tsDetail);
Ebean.save(master1);
TSMaster master2 = Ebean.find(master1.getClass(), master1.getId());
Assert.assertTrue(master2.getDetails().isEmpty());
}
}
@@ -2,8 +2,7 @@ package com.avaje.tests.cascade;
import javax.persistence.OptimisticLockException;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -1,17 +1,17 @@
package com.avaje.tests.inheritance;
import junit.framework.Assert;
import junit.framework.TestCase;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.TxRunnable;
import com.avaje.tests.model.basic.AttributeHolder;
import com.avaje.tests.model.basic.ListAttribute;
import com.avaje.tests.model.basic.ListAttributeValue;
public class TestDuplcateKeyException extends TestCase {
public class TestDuplcateKeyException extends BaseTestCase {
/**
* Test query.
@@ -19,8 +19,9 @@ public class TestDuplcateKeyException extends TestCase {
* it was considered safe to skip as it didn't take into account any derived classes
* into account with e.g. collections and Cascade options </p>
*/
public void testQuery()
{
@Test
public void testQuery() {
// Setup the data first
final ListAttributeValue value1 = new ListAttributeValue();
@@ -2,9 +2,10 @@ package com.avaje.tests.inheritance;
import java.util.List;
import junit.framework.Assert;
import junit.framework.TestCase;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.Query;
import com.avaje.tests.model.basic.Car;
@@ -12,80 +13,77 @@ import com.avaje.tests.model.basic.Truck;
import com.avaje.tests.model.basic.Vehicle;
import com.avaje.tests.model.basic.VehicleDriver;
public class TestInheritInsert extends TestCase {
public void testCasting() {
Truck t = new Truck();
t.setCapacity(10d);
Ebean.save(t);
Vehicle v = Ebean.find(Vehicle.class, t.getId());
if (v instanceof Truck){
Truck t0 = (Truck)v;
Assert.assertEquals(10d, t0.getCapacity());
Assert.assertEquals(10d, ((Truck)v).getCapacity());
Assert.assertNotNull(t0.getId());
} else {
Assert.assertTrue("v not a Truck?", false);
}
VehicleDriver driver = new VehicleDriver();
driver.setName("Jim");
driver.setVehicle(v);
Ebean.save(driver);
VehicleDriver d1 = Ebean.find(VehicleDriver.class, driver.getId());
v = d1.getVehicle();
if (v instanceof Truck){
Double capacity = ((Truck)v).getCapacity();
Assert.assertEquals(10d, capacity);
Assert.assertNotNull(v.getId());
} else {
Assert.assertTrue("v not a Truck?", false);
}
List<VehicleDriver> list = Ebean.find(VehicleDriver.class).findList();
for (VehicleDriver vehicleDriver : list) {
if (vehicleDriver.getVehicle() instanceof Truck){
Double capacity = ((Truck)vehicleDriver.getVehicle()).getCapacity();
Assert.assertEquals(10d, capacity);
}
}
}
public class TestInheritInsert extends BaseTestCase {
public void testQuery()
{
// TODO: figure out whats wrong in the build process, then uncomment the lines again
Car car = new Car();
car.setLicenseNumber("MARIOS_CAR_LICENSE");
Ebean.save(car);
@Test
public void testCasting() {
VehicleDriver driver = new VehicleDriver();
driver.setName("Mario");
driver.setVehicle(car);
Ebean.save(driver);
Truck t = new Truck();
t.setCapacity(10d);
Ebean.save(t);
Query<VehicleDriver> query = Ebean.find(VehicleDriver.class);
query.where().eq("vehicle.licenseNumber", "MARIOS_CAR_LICENSE");
List<VehicleDriver> drivers = query.findList();
Assert.assertNotNull(drivers);
Assert.assertEquals(1, drivers.size());
Assert.assertNotNull(drivers.get(0));
Vehicle v = Ebean.find(Vehicle.class, t.getId());
if (v instanceof Truck) {
Truck t0 = (Truck) v;
Assert.assertEquals(Double.valueOf(10d), t0.getCapacity());
Assert.assertEquals(Double.valueOf(10d), ((Truck) v).getCapacity());
Assert.assertNotNull(t0.getId());
} else {
Assert.assertTrue("v not a Truck?", false);
}
Assert.assertEquals("Mario", drivers.get(0).getName());
Assert.assertEquals("MARIOS_CAR_LICENSE", drivers.get(0).getVehicle()
.getLicenseNumber());
VehicleDriver driver = new VehicleDriver();
driver.setName("Jim");
driver.setVehicle(v);
Vehicle car2 = Ebean.find(Vehicle.class, car.getId());
// FIXME~EMG - NoSuchMethodError
car2.setLicenseNumber("test");
Ebean.save(car);
Ebean.save(driver);
}
VehicleDriver d1 = Ebean.find(VehicleDriver.class, driver.getId());
v = d1.getVehicle();
if (v instanceof Truck) {
Double capacity = ((Truck) v).getCapacity();
Assert.assertEquals(Double.valueOf(10d), capacity);
Assert.assertNotNull(v.getId());
} else {
Assert.assertTrue("v not a Truck?", false);
}
List<VehicleDriver> list = Ebean.find(VehicleDriver.class).findList();
for (VehicleDriver vehicleDriver : list) {
if (vehicleDriver.getVehicle() instanceof Truck) {
Double capacity = ((Truck) vehicleDriver.getVehicle()).getCapacity();
Assert.assertEquals(Double.valueOf(10d), capacity);
}
}
}
@Test
public void testQuery() {
Car car = new Car();
car.setLicenseNumber("MARIOS_CAR_LICENSE");
Ebean.save(car);
VehicleDriver driver = new VehicleDriver();
driver.setName("Mario");
driver.setVehicle(car);
Ebean.save(driver);
Query<VehicleDriver> query = Ebean.find(VehicleDriver.class);
query.where().eq("vehicle.licenseNumber", "MARIOS_CAR_LICENSE");
List<VehicleDriver> drivers = query.findList();
Assert.assertNotNull(drivers);
Assert.assertEquals(1, drivers.size());
Assert.assertNotNull(drivers.get(0));
Assert.assertEquals("Mario", drivers.get(0).getName());
Assert.assertEquals("MARIOS_CAR_LICENSE", drivers.get(0).getVehicle().getLicenseNumber());
Vehicle car2 = Ebean.find(Vehicle.class, car.getId());
car2.setLicenseNumber("test");
Ebean.save(car);
}
}
@@ -2,8 +2,7 @@ package com.avaje.tests.inheritance;
import java.util.List;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -0,0 +1,47 @@
package com.avaje.tests.inheritance;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.bean.BeanCollection.ModifyListenMode;
import com.avaje.ebean.common.BeanList;
import com.avaje.tests.model.basic.Animal;
import com.avaje.tests.model.basic.AnimalShelter;
import com.avaje.tests.model.basic.Cat;
import com.avaje.tests.model.basic.Dog;
public class TestInheritanceOnMany extends BaseTestCase {
@Test
public void test() {
Cat cat = new Cat();
cat.setName("Puss");
Ebean.save(cat);
Dog dog = new Dog();
dog.setRegistrationNumber("DOGGIE");
Ebean.save(dog);
AnimalShelter shelter = new AnimalShelter();
shelter.setName("My Animal Shelter");
shelter.getAnimals().add(cat);
shelter.getAnimals().add(dog);
Ebean.save(shelter);
AnimalShelter shelter2 = Ebean.find(AnimalShelter.class, shelter.getId());
List<Animal> animals = shelter2.getAnimals();
BeanList<?> beanList = (BeanList<?>)animals;
ModifyListenMode modifyListenMode = beanList.getModifyListenMode();
Assert.assertNotNull(modifyListenMode);
}
}
@@ -0,0 +1,52 @@
package com.avaje.tests.inheritance;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.RawSql;
import com.avaje.ebean.RawSqlBuilder;
import com.avaje.tests.model.basic.Truck;
import com.avaje.tests.model.basic.Vehicle;
public class TestInheritanceRawSql extends BaseTestCase {
@Test
public void test() {
Truck truck = new Truck();
truck.setCapacity(50D);
truck.setLicenseNumber("ASB23");
Ebean.save(truck);
String sql = "select dtype, id, license_number from vehicle where id = :id";
RawSqlBuilder rawSqlBuilder = RawSqlBuilder.parse(sql);
RawSql rawSql = rawSqlBuilder.create();
List<Vehicle> list = Ebean.find(Vehicle.class)
.setRawSql(rawSql)
.setParameter("id", truck.getId())
.findList();
Assert.assertEquals(1, list.size());
Vehicle vehicle2 = list.get(0);
Assert.assertTrue(vehicle2 instanceof Truck);
Truck truck2 = (Truck)vehicle2;
Assert.assertEquals("ASB23", truck2.getLicenseNumber());
// invoke lazy loading and set the capacity
truck2.setCapacity(30D);
// and now save
Ebean.save(truck2);
}
}
@@ -1,14 +1,17 @@
package com.avaje.tests.inheritance;
import junit.framework.Assert;
import junit.framework.TestCase;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
import com.avaje.ebean.Ebean;
import com.avaje.tests.model.basic.TIntChild;
import com.avaje.tests.model.basic.TIntRoot;
public class TestIntInherit extends TestCase {
public class TestIntInherit extends BaseTestCase {
@Test
public void testMe() {
TIntRoot r = new TIntRoot();
@@ -1,17 +1,17 @@
package com.avaje.tests.inheritance;
import junit.framework.Assert;
import junit.framework.TestCase;
import org.junit.Assert;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.BaseTestCase;
import com.avaje.ebean.Ebean;
import com.avaje.tests.model.basic.AttributeHolder;
import com.avaje.tests.model.basic.ListAttribute;
import com.avaje.tests.model.basic.ListAttributeValue;
public class TestSkippable extends TestCase {
public class TestSkippable extends BaseTestCase {
private static final Logger logger = LoggerFactory.getLogger(TestSkippable.class);
@@ -21,6 +21,7 @@ public class TestSkippable extends TestCase {
* it was considered safe to skip as it didn't take into account any derived classes
* into account with e.g. collections and Cascade options </p>
*/
@Test
public void testQuery() {
// Setup the data first
@@ -0,0 +1,58 @@
package com.avaje.tests.insert;
import java.util.Date;
import java.util.TimeZone;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
import com.avaje.ebean.Ebean;
import com.avaje.tests.model.basic.EBasic;
public class TestSaveWithDaylightSavings extends BaseTestCase {
@Test
public void test() {
// For it to fail, the time has to match the time at which the daylight saving changes
// are applied in that time zone. Therefore specify it explicitly.
TimeZone defaultTimeZone = TimeZone.getDefault();
try {
TimeZone.setDefault(TimeZone.getTimeZone("EET"));
// Run the code and see how there is a 3600 second change
Date daylightSavingDate = new Date(1351382400000l);
// On a second run comment in the following date and see
// how there is a 0 second change
// daylightSavingDate = new Date(1361382400000l);
EBasic e = new EBasic();
e.setSomeDate(daylightSavingDate);
Ebean.save(e);
Assert.assertNotNull(e.getId());
// Reload the entity from database
EBasic e2 = Ebean.find(EBasic.class, e.getId());
long diffMillis = e2.getSomeDate().getTime() - e.getSomeDate().getTime();
System.out.println("The date I created " + daylightSavingDate);
System.out.println(" --- the date i put in : " + e.getSomeDate());
System.out.println(" as millis : " + e.getSomeDate().getTime());
System.out.println(" --- the date i get back : " + e2.getSomeDate());
System.out.println(" as millis : " + e2.getSomeDate().getTime());
System.out.println("The difference is " + diffMillis / 1000 + " seconds");
Assert.assertEquals(0L, diffMillis);
} finally {
TimeZone.setDefault(defaultTimeZone);
}
}
}
@@ -0,0 +1,34 @@
package com.avaje.tests.lifecycle;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
import com.avaje.ebean.Ebean;
import com.avaje.tests.model.basic.EBasicWithLifecycle;
public class TestLifecyleAnnotatedBean extends BaseTestCase {
@Test
public void test() {
EBasicWithLifecycle bean = new EBasicWithLifecycle();
bean.setName("hello there");
Ebean.getServerCacheManager();
Ebean.save(bean);
Assert.assertEquals("prePersist,postPersist,", bean.getBuffer());
EBasicWithLifecycle beanWasLoaded = Ebean.find(EBasicWithLifecycle.class, bean.getId());
Assert.assertEquals("postLoad", beanWasLoaded.getBuffer().toString());
bean.setName("Changed");
Ebean.save(bean);
Ebean.delete(bean);
Assert.assertEquals("prePersist,postPersist,preUpdate,postUpdate,preRemove,postRemove", bean.getBuffer());
}
}
@@ -5,6 +5,7 @@ import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Version;
@@ -20,6 +21,9 @@ public abstract class Animal {
@Version
Long version;
@ManyToOne
AnimalShelter shelter;
public Long getId() {
return id;
}
@@ -35,4 +39,13 @@ public abstract class Animal {
public void setVersion(Long version) {
this.version = version;
}
public AnimalShelter getShelter() {
return shelter;
}
public void setShelter(AnimalShelter shelter) {
this.shelter = shelter;
}
}
@@ -0,0 +1,60 @@
package com.avaje.tests.model.basic;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Version;
import com.avaje.ebean.annotation.PrivateOwned;
@Entity
public class AnimalShelter {
@Id
Long id;
@Version
Long version;
String name;
@OneToMany(cascade=CascadeType.PERSIST, mappedBy="shelter")
@PrivateOwned
List<Animal> animals;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Animal> getAnimals() {
return animals;
}
public void setAnimals(List<Animal> animals) {
this.animals = animals;
}
}
@@ -6,9 +6,9 @@ import javax.persistence.Entity;
@Entity
@DiscriminatorValue("CAT")
public class Cat extends Animal {
String name;
public String getName() {
return name;
}
@@ -28,7 +28,7 @@ public class Contact {
String mobile;
String email;
@ManyToOne
@ManyToOne(optional=false)
Customer customer;
@ManyToOne(optional=true)
@@ -1,5 +1,7 @@
package com.avaje.tests.model.basic;
import java.util.Date;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@@ -7,59 +9,69 @@ import javax.persistence.Table;
import com.avaje.ebean.annotation.EnumValue;
@Entity
@Table(name="e_basic")
@Table(name = "e_basic")
public class EBasic {
public enum Status {
@EnumValue("N")
NEW,
@EnumValue("A")
ACTIVE,
@EnumValue("I")
INACTIVE,
}
@Id
Integer id;
Status status;
public enum Status {
@EnumValue("N")
NEW,
String name;
@EnumValue("A")
ACTIVE,
String description;
public Integer getId() {
return id;
}
@EnumValue("I")
INACTIVE,
}
public void setId(Integer id) {
this.id = id;
}
@Id
Integer id;
public Status getStatus() {
return status;
}
Status status;
public void setStatus(Status status) {
this.status = status;
}
String name;
public String getName() {
return name;
}
String description;
public void setName(String name) {
this.name = name;
}
Date someDate;
public String getDescription() {
return description;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Date getSomeDate() {
return someDate;
}
public void setSomeDate(Date someDate) {
this.someDate = someDate;
}
public void setDescription(String description) {
this.description = description;
}
}
@@ -0,0 +1,92 @@
package com.avaje.tests.model.basic;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.PostLoad;
import javax.persistence.PostPersist;
import javax.persistence.PostRemove;
import javax.persistence.PostUpdate;
import javax.persistence.PrePersist;
import javax.persistence.PreRemove;
import javax.persistence.PreUpdate;
import javax.persistence.Table;
import javax.persistence.Version;
@Entity
@Table(name="e_basic_withlife")
public class EBasicWithLifecycle {
@Id
Long id;
String name;
@Version
Long version;
transient StringBuilder buffer = new StringBuilder();
@PrePersist
public void prePersist() {
buffer.append("prePersist,");
}
@PostPersist
public void postPersist() {
buffer.append("postPersist,");
}
@PreUpdate
public void preUpdate() {
buffer.append("preUpdate,");
}
@PostUpdate
public void postUpdate() {
buffer.append("postUpdate,");
}
@PreRemove
public void preRemove() {
buffer.append("preRemove,");
}
@PostRemove
public void postRemove() {
buffer.append("postRemove");
}
@PostLoad
public void postLoad() {
buffer.append("postLoad");
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
public String getBuffer() {
return buffer.toString();
}
}
@@ -7,6 +7,7 @@ import java.util.List;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.TxRunnable;
import com.avaje.tests.model.basic.Order.Status;
public class ResetBasicData {
@@ -273,6 +274,7 @@ public class ResetBasicData {
Product product1 = Ebean.getReference(Product.class, 1);
Order order = new Order();
order.setStatus(Status.SHIPPED);
order.setCustomer(customer);
List<OrderDetail> details = new ArrayList<OrderDetail>();
@@ -290,6 +292,7 @@ public class ResetBasicData {
Product product3 = Ebean.getReference(Product.class, 3);
Order order = new Order();
order.setStatus(Status.COMPLETE);
order.setCustomer(customer);
List<OrderDetail> details = new ArrayList<OrderDetail>();
@@ -301,15 +304,14 @@ public class ResetBasicData {
Ebean.save(order);
}
private void createOrder4(Customer customer) {
private void createOrder4(Customer customer) {
Order order = new Order();
order.setCustomer(customer);
Order order = new Order();
order.setCustomer(customer);
order.addShipment(new OrderShipment());
order.addShipment(new OrderShipment());
Ebean.save(order);
}
Ebean.save(order);
}
}
@@ -80,4 +80,5 @@ public class TSMaster {
}
details.add(detail);
}
}
@@ -0,0 +1,42 @@
package com.avaje.tests.model.carwheeltruck;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.OneToMany;
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "type")
@DiscriminatorValue("car")
public class TCar {
@Id
String plateNo;
@OneToMany(cascade = CascadeType.ALL)
List<TWheel> wheels;
public String getPlateNo() {
return plateNo;
}
public void setPlateNo(String plateNo) {
this.plateNo = plateNo;
}
public List<TWheel> getWheels() {
return wheels;
}
public void setWheels(List<TWheel> wheels) {
this.wheels = wheels;
}
}
@@ -0,0 +1,27 @@
package com.avaje.tests.model.carwheeltruck;
import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="type")
@DiscriminatorValue("truck")
public class TTruck extends TCar {
@Column(name="truckLoad")
Long load;
public Long getLoad() {
return load;
}
public void setLoad(Long load) {
this.load = load;
}
}
@@ -0,0 +1,32 @@
package com.avaje.tests.model.carwheeltruck;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
@Entity
public class TWheel {
@Id
Long id;
@ManyToOne(optional=false)
TCar owner;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public TCar getOwner() {
return owner;
}
public void setOwner(TCar owner) {
this.owner = owner;
}
}
@@ -0,0 +1,33 @@
package com.avaje.tests.model.carwheeltruck;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
import com.avaje.ebean.Ebean;
public class TestTruckCarWheelInhertiance extends BaseTestCase {
@Test
public void test() {
TTruck truck = new TTruck();
truck.setPlateNo("foo");
TWheel wheel = new TWheel();
wheel.setOwner(truck);
Ebean.save(truck);
// This save() works ok...
// But if then is added one more wheel
wheel = new TWheel();
wheel.setOwner(truck);
// And save() is called again
Ebean.save(truck);
// Then an exception is raised:
}
}
@@ -0,0 +1,38 @@
package com.avaje.tests.model.embedded;
import javax.persistence.Embeddable;
@Embeddable
public class EAddress {
String street;
String suburb;
String city;
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getSuburb() {
return suburb;
}
public void setSuburb(String suburb) {
this.suburb = suburb;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
}
@@ -0,0 +1,101 @@
package com.avaje.tests.model.embedded;
import java.util.Date;
import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Version;
@Entity
public class EInvoice {
public enum State {
New, Processing, Approved
}
@Id
Long id;
@Version
Long version;
Date date;
State state;
@ManyToOne
EPerson person;
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "street", column = @Column(name = "ship_street")),
@AttributeOverride(name = "suburb", column = @Column(name = "ship_suburb")),
@AttributeOverride(name = "city", column = @Column(name = "ship_city"))
})
EAddress shipAddress;
@Embedded
EAddress billAddress;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public EPerson getPerson() {
return person;
}
public void setPerson(EPerson person) {
this.person = person;
}
public EAddress getShipAddress() {
return shipAddress;
}
public void setShipAddress(EAddress shipAddress) {
this.shipAddress = shipAddress;
}
public EAddress getBillAddress() {
return billAddress;
}
public void setBillAddress(EAddress billAddress) {
this.billAddress = billAddress;
}
}
@@ -0,0 +1,64 @@
package com.avaje.tests.model.embedded;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Version;
@Entity
public class EPerson {
@Id
Long id;
@Version
Long version;
String name;
String notes;
@Embedded
EAddress address;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNotes() {
return notes;
}
public void setNotes(String notes) {
this.notes = notes;
}
public EAddress getAddress() {
return address;
}
public void setAddress(EAddress address) {
this.address = address;
}
}
@@ -3,16 +3,16 @@ package com.avaje.tests.model.embedded;
import javax.persistence.Embeddable;
@Embeddable
public class Eembeddable
{
String description;
public class Eembeddable {
String description;
public String getDescription() {
return description;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public void setDescription(String description) {
this.description = description;
}
}

Some files were not shown because too many files have changed in this diff Show More