mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
40
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
33d7e10ad8 | ||
|
|
2201f9220e | ||
|
|
b8743a5ef2 | ||
|
|
b587aa8172 | ||
|
|
c37fa83675 | ||
|
|
08602afcff | ||
|
|
5b98d82f58 | ||
|
|
22cd5b0e82 | ||
|
|
93855478dd | ||
|
|
59b86d5b19 | ||
|
|
27afb9ee2e | ||
|
|
a49cd42a0c | ||
|
|
8c8442ec58 | ||
|
|
41f3173adf | ||
|
|
1a4192b40f | ||
|
|
fffda7c789 | ||
|
|
b7f6596bcb | ||
|
|
1ea49dd206 | ||
|
|
32e4f114b4 | ||
|
|
fcbedda597 | ||
|
|
2055349d76 | ||
|
|
44ee64cde5 | ||
|
|
9f916998c9 | ||
|
|
2ddef37632 | ||
|
|
538ebdc899 | ||
|
|
d6e0d661fb | ||
|
|
289deb397a | ||
|
|
68b4aab837 | ||
|
|
563293f7a9 | ||
|
|
5049694fd3 | ||
|
|
a9c8d1960c | ||
|
|
066b409a65 | ||
|
|
54ad2e0ad0 | ||
|
|
77f7bc2a7e | ||
|
|
f773d74597 | ||
|
|
90b23c4985 | ||
|
|
60b4ad410b | ||
|
|
d8391ba90c | ||
|
|
c4d41ecb00 | ||
|
|
39f5284a76 |
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
+10
@@ -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())) {
|
||||
|
||||
+22
-11
@@ -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);
|
||||
|
||||
+344
-341
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+22
@@ -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
Reference in New Issue
Block a user