Compare commits

...
Author SHA1 Message Date
rbygrave e937d36ca2 [maven-release-plugin] prepare release avaje-ebeanorm-3.4.2 2015-06-19 20:09:51 +12:00
rbygrave 32b7c1a755 Change tests to be more robust 2015-06-19 20:01:00 +12:00
Rob Bygrave c766793f2e Merge pull request #272 from rlamarche/develop-3.3
Backport fix for #154 - @OrderBy on a @OneToMany property is not used when lazy loading
2015-05-08 09:29:28 +12:00
Rob Bygraveandromlam b30b456dbd Fix for #154 - @OrderBy on a @OneToMany property is not used when lazy loading
Conflicts:
	src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java
	src/test/java/com/avaje/tests/query/orderby/TestOrderByWithMany.java
2015-04-07 14:09:19 +02:00
Rob Bygrave 6bc86efed7 [maven-release-plugin] prepare for next development iteration 2014-05-28 00:23:27 +12:00
Rob Bygrave 5c447511c1 [maven-release-plugin] prepare release avaje-ebeanorm-3.4.1 2014-05-28 00:23:04 +12:00
Rob Bygrave f91789886c Backport of fix to 3.x for #131 - Memory issue (Hard refs) when using findIterate 2014-05-28 00:21:57 +12:00
Rob Bygrave 7d14ede1b9 [maven-release-plugin] prepare for next development iteration 2014-05-22 20:02:42 +12:00
Rob Bygrave 1ba8bc4557 [maven-release-plugin] prepare release avaje-ebeanorm-3.3.4 2014-05-22 20:02:26 +12:00
Rob Bygrave a234bd1201 Merge pull request #129 from jroper/java16-compat
Backport to JDK 6
2014-05-22 19:36:32 +12:00
James Roper 1c0c0893b6 Backport to JDK 6
Replaced all uses of ju.Objects.hashCode and ju.Objects.equals with a
copy of their implementations inlined into the code.

Also changed source/target for compiler plugin.

To test, I compiled my own avaje launchagent against 6, and changed to
use that in the pom, compiled/tested the whole project using JDK 7,
then ran mvn surefire:test using JDK 6 - running surefire:test ensures
that mvn doesn't try to recompile everything against 6, since that's not
possible because of some of the delegate classes having delegate methods
to JDK 7 jdbc classes.
2014-05-22 15:20:21 +10:00
Rob Bygrave ebbc560ae8 [maven-release-plugin] prepare for next development iteration 2014-05-19 01:03:43 +12:00
Rob Bygrave fcb9df05ad [maven-release-plugin] prepare release avaje-ebeanorm-3.3.3 2014-05-19 01:03:31 +12:00
Rob Bygrave 3de79b211a Change version in pom to 3.3.3-SNAPSHOT in preparation for release 2014-05-19 01:02:13 +12:00
Rob Bygrave d93952a920 Backport of fix for #56 - Using findIterate with MySQL streams 2014-05-19 00:59:05 +12:00
29 changed files with 326 additions and 397 deletions
+2
View File
@@ -7,3 +7,5 @@ target/
logs/
log/
/mydb.db
*.iml
.idea
+10 -1
View File
@@ -9,7 +9,7 @@
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm</artifactId>
<version>3.3.2</version>
<version>3.4.2</version>
<packaging>jar</packaging>
<name>avaje-ebeanorm</name>
@@ -167,6 +167,15 @@
<!-- Enhance the meta beans -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm-mavenenhancer</artifactId>
@@ -244,14 +244,6 @@ public interface ExpressionList<T> extends Serializable {
*/
public Query<T> setMaxRows(int maxRows);
/**
* Set the number of rows after which the fetching should continue in a
* background thread.
*
* @see Query#setBackgroundFetchAfter(int)
*/
public Query<T> setBackgroundFetchAfter(int backgroundFetchAfter);
/**
* Set the name of the property which values become the key of a map.
*
@@ -259,15 +251,6 @@ public interface ExpressionList<T> extends Serializable {
*/
public Query<T> setMapKey(String mapKey);
/**
* Please migrate to using {@link #findIterate()} or {@link #findVisit(QueryResultVisitor)}.
* Set a QueryListener for bean by bean processing.
*
* @see Query#setListener(QueryListener)
* @deprecated Migrate to {@link #findIterate()} or {@link #findVisit(QueryResultVisitor)}
*/
public Query<T> setListener(QueryListener<T> queryListener);
/**
* Set to true to use the query for executing this query.
*
-37
View File
@@ -663,36 +663,6 @@ public interface Query<T> extends Serializable {
*/
public Query<T> setParameter(int position, Object value);
/**
* Please migrate to using {@link #findIterate()} or {@link #findVisit(QueryResultVisitor)}
* <p>
* Set a listener to process the query on a row by row basis.
* </p>
* <p>
* Use this when you want to process a large query and do not want to hold the
* entire query result in memory.
* </p>
* <p>
* It this case the rows are not loaded into the persistence context and
* instead are processed by the query listener.
* </p>
*
* <pre class="code">
* QueryListener&lt;Order&gt; listener = ...;
*
* Query&lt;Order&gt; query = Ebean.createQuery(Order.class);
*
* // set the listener that will process each order one at a time
* query.setListener(listener);
*
* // execute the query. Note that the returned
* // list (emptyList) will be empty ...
* List&lt;Order&gt; emtyList = query.findList();
* </pre>
* @deprecated Deprecated in favor of {@link #findIterate()} and {@link #findVisit(QueryResultVisitor)}
*/
public Query<T> setListener(QueryListener<T> queryListener);
/**
* Set the Id value to query. This is used with findUnique().
* <p>
@@ -966,13 +936,6 @@ public interface Query<T> extends Serializable {
*/
public Query<T> setMaxRows(int maxRows);
/**
* Set the rows after which fetching should continue in a background thread.
*
* @param backgroundFetchAfter
*/
public Query<T> setBackgroundFetchAfter(int backgroundFetchAfter);
/**
* Set the property to use as keys for a map.
* <p>
@@ -1,7 +1,6 @@
package com.avaje.ebean.bean;
import java.io.Serializable;
import java.util.Objects;
/**
* Identifies a unique node of an object graph.
@@ -70,7 +69,7 @@ public final class ObjectGraphNode implements Serializable {
public int hashCode() {
int hc = 31 * originQueryPoint.hashCode();
hc = 31 * hc + Objects.hashCode(path);
hc = 31 * hc + (path == null ? 0 : path.hashCode());
return hc;
}
@@ -83,7 +82,7 @@ public final class ObjectGraphNode implements Serializable {
}
ObjectGraphNode e = (ObjectGraphNode) obj;
return Objects.equals(e.path, path)
return ((e.path == path) || (e.path != null && e.path.equals(path)))
&& e.originQueryPoint.equals(originQueryPoint);
}
}
@@ -1,7 +1,5 @@
package com.avaje.ebeaninternal.api;
import java.util.Objects;
/**
* A hash for a query plan.
*/
@@ -26,7 +24,7 @@ public class HashQueryPlan {
public int hashCode() {
int hc = planHash;
hc = hc * 31 + bindCount;
hc = hc * 31 + Objects.hashCode(rawSql);
hc = hc * 31 + (rawSql == null ? 0 : rawSql.hashCode());
return hc;
}
@@ -41,6 +39,6 @@ public class HashQueryPlan {
HashQueryPlan e = (HashQueryPlan) obj;
return e.planHash == planHash
&& e.bindCount == bindCount
&& Objects.equals(e.rawSql, rawSql);
&& ((e.rawSql == rawSql) || (e.rawSql != null && e.rawSql.equals(rawSql)));
}
}
@@ -1,7 +1,5 @@
package com.avaje.ebeaninternal.api;
import java.util.Objects;
/**
* Used to build HashQueryPlan instances.
*/
@@ -33,7 +31,7 @@ public class HashQueryPlanBuilder {
* Add an object to the hash calculation.
*/
public HashQueryPlanBuilder add(Object object) {
planHash = planHash * 31 + Objects.hashCode(object);
planHash = planHash * 31 + (object == null ? 0 : object.hashCode());
return this;
}
@@ -6,7 +6,6 @@ import java.util.List;
import com.avaje.ebean.ExpressionList;
import com.avaje.ebean.OrderBy;
import com.avaje.ebean.Query;
import com.avaje.ebean.QueryListener;
import com.avaje.ebean.bean.BeanCollectionTouched;
import com.avaje.ebean.bean.CallStack;
import com.avaje.ebean.bean.EntityBean;
@@ -509,12 +508,6 @@ public interface SpiQuery<T> extends Query<T> {
*/
public String getMapKey();
/**
* Return the number of rows after which fetching should occur in a
* background thread.
*/
public int getBackgroundFetchAfter();
/**
* Return the maximum number of rows to return in the query.
*/
@@ -545,19 +538,6 @@ public interface SpiQuery<T> extends Query<T> {
*/
public Object getId();
/**
* Return the queryListener.
*/
public QueryListener<T> getListener();
/**
* Return true if this query should use its own transaction.
* <p>
* This is true for background fetching and when using QueryListener.
* </p>
*/
public boolean createOwnTransaction();
/**
* Set the generated sql for debug purposes.
*
@@ -107,7 +107,10 @@ public class DefaultBeanLoader {
BeanDescriptor<?> desc = ctx.getBeanDescriptor();
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(many.getTargetType());
String orderBy = many.getLazyFetchOrderBy();
if (orderBy != null) {
query.orderBy(orderBy);
}
query.setLazyLoadForParents(idList, many);
many.addWhereParentIdIn(query, idList);
@@ -90,7 +90,6 @@ import com.avaje.ebeaninternal.server.deploy.InheritInfo;
import com.avaje.ebeaninternal.server.el.ElFilter;
import com.avaje.ebeaninternal.server.jmx.MAdminAutofetch;
import com.avaje.ebeaninternal.server.lib.ShutdownManager;
import com.avaje.ebeaninternal.server.loadcontext.DLoadContext;
import com.avaje.ebeaninternal.server.query.CQuery;
import com.avaje.ebeaninternal.server.query.CQueryEngine;
import com.avaje.ebeaninternal.server.query.CallableQueryIds;
@@ -1133,7 +1132,7 @@ public final class DefaultServer implements SpiEbeanServer {
if (Mode.LAZYLOAD_MANY.equals(query.getMode())) {
allowOneManyFetch = false;
} else if (query.hasMaxRowsOrFirstRow() && !query.isRawSql() && !query.isSqlSelect() && query.getBackgroundFetchAfter() == 0) {
} else if (query.hasMaxRowsOrFirstRow() && !query.isRawSql() && !query.isSqlSelect()) {
// convert ALL fetch joins to Many's to be query joins
// so that limit offset type SQL clauses work
allowOneManyFetch = false;
@@ -1194,16 +1193,15 @@ public final class DefaultServer implements SpiEbeanServer {
if (cachedBean != null) {
if (context == null) {
context = new DefaultPersistenceContext();
}
context.put(query.getId(), cachedBean);
DLoadContext loadContext = new DLoadContext(this, beanDescriptor, query.isReadOnly(), query);
loadContext.setPersistenceContext(context);
EntityBeanIntercept ebi = ((EntityBean) cachedBean)._ebean_getIntercept();
// Not using a loadContext for beans coming out of L2 cache
// so that means no batch lazy loading for these beans
EntityBean entityBean = (EntityBean) cachedBean;
EntityBeanIntercept ebi = entityBean._ebean_getIntercept();
ebi.setPersistenceContext(context);
loadContext.register(null, ebi);
Object id = beanDescriptor.getId(entityBean);
context.put(id, entityBean);
}
return (T) cachedBean;
@@ -29,6 +29,7 @@ import com.avaje.ebeaninternal.server.deploy.DeployPropertyParserMap;
import com.avaje.ebeaninternal.server.loadcontext.DLoadContext;
import com.avaje.ebeaninternal.server.query.CQueryPlan;
import com.avaje.ebeaninternal.server.query.CancelableQuery;
import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
/**
* Wraps the objects involved in executing a Query.
@@ -43,7 +44,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
private final BeanFinder<T> finder;
private final LoadContext graphContext;
private LoadContext graphContext;
private final Boolean readOnly;
@@ -75,9 +76,6 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
this.queryEngine = queryEngine;
this.query = query;
this.readOnly = query.isReadOnly();
this.graphContext = new DLoadContext(ebeanServer, beanDescriptor, readOnly, query);
graphContext.registerSecondaryQueries(query);
}
public void executeSecondaryQueries(int defaultQueryBatch) {
@@ -163,12 +161,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
@Override
public void initTransIfRequired() {
// first check if the query requires its own transaction
if (query.createOwnTransaction()) {
// using background fetch or query listener etc
transaction = ebeanServer.createQueryTransaction();
createdTransaction = true;
} else if (transaction == null) {
if (transaction == null) {
// maybe a current one
transaction = ebeanServer.getCurrentServerTransaction();
if (transaction == null) {
@@ -177,8 +170,11 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
createdTransaction = true;
}
}
this.persistenceContext = getPersistenceContext(query, transaction);
this.graphContext.setPersistenceContext(persistenceContext);
this.graphContext = new DLoadContext(this);
this.graphContext.registerSecondaryQueries(query);
}
/**
@@ -381,7 +377,8 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
}
public void flushPersistenceContextOnIterate() {
beanDescriptor.flushPersistenceContextOnIterate(persistenceContext);
persistenceContext = new DefaultPersistenceContext();
graphContext.setPersistenceContext(persistenceContext);
}
}
@@ -2444,13 +2444,5 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
return false;
}
public void flushPersistenceContextOnIterate(PersistenceContext persistenceContext) {
persistenceContext.clear(beanType);
for (int i = 0; i < propertiesMany.length; i++) {
persistenceContext.clear(propertiesMany[i].getBeanDescriptor().getBeanType());
}
}
}
@@ -54,9 +54,17 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
*/
final boolean manyToMany;
final String fetchOrderBy;
/**
* Order by used when fetch joining the associated many.
*/
private final String fetchOrderBy;
final String mapKey;
/**
* Order by used when lazy loading the associated many.
*/
private String lazyFetchOrderBy;
private final String mapKey;
/**
* The type of the many, set, list or map.
@@ -132,6 +140,22 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
exportedProperties = createExported();
if (exportedProperties.length > 0){
embeddedExportedProperties = exportedProperties[0].isEmbedded();
if (fetchOrderBy != null) {
// derive lazyFetchOrderBy
StringBuilder sb = new StringBuilder(50);
for (int i = 0; i < exportedProperties.length; i++) {
if (i > 0) {
sb.append(", ");
}
// these fkcolumns always on base table hence t0 as alias
sb.append("t0.").append(exportedProperties[i].getForeignDbColumn());
}
if (fetchOrderBy != null) {
sb.append(", ").append(fetchOrderBy);
}
lazyFetchOrderBy = sb.toString().trim();
}
}
String delStmt;
@@ -462,6 +486,13 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
}
/**
* Return the order by for use when lazy loading the associated collection.
*/
public String getLazyFetchOrderBy() {
return lazyFetchOrderBy;
}
/**
* Return the default mapKey when returning a Map.
*/
public String getMapKey() {
@@ -3,7 +3,6 @@ package com.avaje.ebeaninternal.server.expression;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import com.avaje.ebean.event.BeanQueryRequest;
@@ -115,7 +114,7 @@ class AllEqualsExpression implements SpiExpression {
int hc = 31;
for (Object value : propMap.values()) {
hc = hc * 31 + Objects.hashCode(value);
hc = hc * 31 + (value == null ? 0 : value.hashCode());
}
return hc;
@@ -14,7 +14,6 @@ import com.avaje.ebean.Junction;
import com.avaje.ebean.OrderBy;
import com.avaje.ebean.PagingList;
import com.avaje.ebean.QueryIterator;
import com.avaje.ebean.QueryListener;
import com.avaje.ebean.QueryResultVisitor;
import com.avaje.ebean.event.BeanQueryRequest;
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
@@ -375,19 +374,10 @@ abstract class JunctionExpression<T> implements Junction<T>, SpiExpression, Expr
return exprList.select(properties);
}
public com.avaje.ebean.Query<T> setBackgroundFetchAfter(int backgroundFetchAfter) {
return exprList.setBackgroundFetchAfter(backgroundFetchAfter);
}
public com.avaje.ebean.Query<T> setFirstRow(int firstRow) {
return exprList.setFirstRow(firstRow);
}
@Deprecated
public com.avaje.ebean.Query<T> setListener(QueryListener<T> queryListener) {
return exprList.setListener(queryListener);
}
public com.avaje.ebean.Query<T> setMapKey(String mapKey) {
return exprList.setMapKey(mapKey);
}
@@ -28,8 +28,9 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex
super(parent, desc, path, defaultBatchSize, queryProps);
this.currentBuffer = createBuffer(firstBatchSize);
this.bufferList = queryFetch ? new ArrayList<DLoadBeanContext.LoadBuffer>() : null;
// bufferList only required when using query joins (queryFetch)
this.bufferList = (!queryFetch) ? null : new ArrayList<DLoadBeanContext.LoadBuffer>();
this.currentBuffer = createBuffer(firstBatchSize);
}
protected void configureQuery(SpiQuery<?> query, String lazyLoadProperty) {
@@ -51,11 +52,11 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex
protected void register(EntityBeanIntercept ebi){
ebi.setBeanLoader(0, currentBuffer, getPersistenceContext());
if (currentBuffer.add(ebi)) {
// the currentBuffer is full so create another one
if (currentBuffer.isFull()) {
currentBuffer = createBuffer(secondaryBatchSize);
}
currentBuffer.add(ebi);
ebi.setBeanLoader(0, currentBuffer, getPersistenceContext());
}
private LoadBuffer createBuffer(int size) {
@@ -98,22 +99,29 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex
*/
public static class LoadBuffer implements BeanLoader, LoadBeanBuffer {
private final PersistenceContext persistenceContext;
private final DLoadBeanContext context;
private final int batchSize;
private final List<EntityBeanIntercept> list;
public LoadBuffer(DLoadBeanContext context, int batchSize) {
this.context = context;
// set the persistence context as at this moment in
// case it changes as part of a findIterate etc
this.persistenceContext = context.getPersistenceContext();
this.batchSize = batchSize;
this.list = new ArrayList<EntityBeanIntercept>(batchSize);
}
public boolean isFull() {
return batchSize == list.size();
}
/**
* Return true if the buffer is full.
*/
public boolean add(EntityBeanIntercept ebi) {
public void add(EntityBeanIntercept ebi) {
list.add(ebi);
return batchSize == list.size();
}
@Override
@@ -138,7 +146,7 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex
@Override
public PersistenceContext getPersistenceContext() {
return context.getPersistenceContext();
return persistenceContext;
}
@Override
@@ -52,12 +52,14 @@ public class DLoadContext implements LoadContext {
private List<OrmQueryProperties> secQuery;
public DLoadContext(SpiEbeanServer ebeanServer, BeanDescriptor<?> rootDescriptor, Boolean readOnly, SpiQuery<?> query) {
public DLoadContext(OrmQueryRequest<?> request) {
this.ebeanServer = ebeanServer;
this.persistenceContext = request.getPersistenceContext();
this.ebeanServer = request.getServer();
this.defaultBatchSize = ebeanServer.getLazyLoadBatchSize();
this.rootDescriptor = rootDescriptor;
this.readOnly = readOnly;
this.rootDescriptor = request.getBeanDescriptor();
SpiQuery<?> query = request.getQuery();
this.readOnly = query.isReadOnly();
this.excludeBeanCache = Boolean.FALSE.equals(query.isUseBeanCache());
this.useAutofetchManager = query.getAutoFetchManager() != null;
@@ -17,70 +17,71 @@ import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties;
public class DLoadManyContext extends DLoadBaseContext implements LoadManyContext {
protected final BeanPropertyAssocMany<?> property;
private List<LoadBuffer> bufferList;
private LoadBuffer currentBuffer;
public DLoadManyContext(DLoadContext parent, BeanPropertyAssocMany<?> property,
String path, int defaultBatchSize, OrmQueryProperties queryProps) {
super(parent, property.getBeanDescriptor(), path, defaultBatchSize, queryProps);
public DLoadManyContext(DLoadContext parent, BeanPropertyAssocMany<?> property, String path, int defaultBatchSize,
OrmQueryProperties queryProps) {
this.property = property;
this.bufferList = new ArrayList<DLoadManyContext.LoadBuffer>();
super(parent, property.getBeanDescriptor(), path, defaultBatchSize, queryProps);
this.property = property;
// bufferList only required when using query joins (queryFetch)
this.bufferList = (!queryFetch) ? null : new ArrayList<DLoadManyContext.LoadBuffer>();
this.currentBuffer = createBuffer(firstBatchSize);
}
}
private LoadBuffer createBuffer(int size) {
LoadBuffer buffer = new LoadBuffer(this, size);
bufferList.add(buffer);
if (bufferList != null) {
bufferList.add(buffer);
}
return buffer;
}
public void configureQuery(SpiQuery<?> query){
// propagate the readOnly state
if (parent.isReadOnly() != null){
query.setReadOnly(parent.isReadOnly());
}
query.setParentNode(objectGraphNode);
if (queryProps != null){
queryProps.configureBeanQuery(query);
}
if (parent.isUseAutofetchManager()){
query.setAutofetch(true);
}
}
public BeanPropertyAssocMany<?> getBeanProperty() {
return property;
}
public void configureQuery(SpiQuery<?> query) {
public BeanDescriptor<?> getBeanDescriptor() {
return desc;
}
// propagate the readOnly state
if (parent.isReadOnly() != null) {
query.setReadOnly(parent.isReadOnly());
}
query.setParentNode(objectGraphNode);
public String getName() {
return parent.getEbeanServer().getName();
}
if (queryProps != null) {
queryProps.configureBeanQuery(query);
}
public void register(BeanCollection<?> bc){
bc.setLoader(0, currentBuffer);
if (currentBuffer.add(bc)) {
// the currentBuffer is full so create another one
if (parent.isUseAutofetchManager()) {
query.setAutofetch(true);
}
}
public BeanPropertyAssocMany<?> getBeanProperty() {
return property;
}
public BeanDescriptor<?> getBeanDescriptor() {
return desc;
}
public String getName() {
return parent.getEbeanServer().getName();
}
public void register(BeanCollection<?> bc) {
if (currentBuffer.isFull()) {
currentBuffer = createBuffer(secondaryBatchSize);
}
}
currentBuffer.add(bc);
bc.setLoader(0, currentBuffer);
}
public void loadSecondaryQuery(OrmQueryRequest<?> parentRequest, int requestedBatchSize, boolean all){
public void loadSecondaryQuery(OrmQueryRequest<?> parentRequest, int requestedBatchSize, boolean all) {
if (!queryFetch) {
throw new IllegalStateException("Not expecting loadSecondaryQuery() to be called?");
@@ -89,33 +90,38 @@ 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);
parent.getEbeanServer().loadMany(req);
LoadManyRequest req = new LoadManyRequest(loadBuffer, parentRequest.getTransaction(), requestedBatchSize,
false, false, false);
parent.getEbeanServer().loadMany(req);
if (!queryProps.isQueryFetchAll()) {
// Stop - only fetch the first batch ... the rest will be lazy loaded
break;
}
}
}
// this is only run once - secondary query is a one shot deal
this.bufferList = null;
}
}
}
}
/**
* A buffer for batch loading bean collections on a given path.
* Supports batch lazy loading and secondary query loading.
* A buffer for batch loading bean collections on a given path. Supports batch lazy loading and
* secondary query loading.
*/
public static class LoadBuffer implements BeanCollectionLoader, LoadManyBuffer {
private final PersistenceContext persistenceContext;
private final DLoadManyContext context;
private final int batchSize;
private final List<BeanCollection<?>> list;
public LoadBuffer(DLoadManyContext context, int batchSize) {
this.context = context;
// set the persistence context as at this moment in
// case it changes as part of a findIterate etc
this.persistenceContext = context.getPersistenceContext();
this.batchSize = batchSize;
this.list = new ArrayList<BeanCollection<?>>(batchSize);
}
@@ -123,11 +129,17 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
/**
* Return true if the buffer is full.
*/
public boolean add(BeanCollection<?> bc) {
list.add(bc);
public boolean isFull() {
return batchSize == list.size();
}
/**
* Return true if the buffer is full.
*/
public void add(BeanCollection<?> bc) {
list.add(bc);
}
@Override
public List<BeanCollection<?>> getBatch() {
return list;
@@ -137,22 +149,22 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
public BeanPropertyAssocMany<?> getBeanProperty() {
return context.property;
}
@Override
public ObjectGraphNode getObjectGraphNode() {
return context.objectGraphNode;
}
@Override
public void configureQuery(SpiQuery<?> query){
public void configureQuery(SpiQuery<?> query) {
context.configureQuery(query);
}
@Override
public String getName() {
return context.serverName;
}
@Override
public BeanDescriptor<?> getBeanDescriptor() {
return context.desc;
@@ -160,9 +172,9 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
@Override
public PersistenceContext getPersistenceContext() {
return context.getPersistenceContext();
return persistenceContext;
}
@Override
public String getFullPath() {
return context.fullPath;
@@ -182,9 +194,10 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
return;
}
}
// Should reduce the list by checking each beanCollection in the L2 first before executing the query
// 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);
context.parent.getEbeanServer().loadMany(req);
}
@@ -14,7 +14,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.QueryIterator;
import com.avaje.ebean.QueryListener;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.BeanCollectionAdd;
import com.avaje.ebean.bean.EntityBean;
@@ -24,7 +23,6 @@ import com.avaje.ebean.bean.NodeUsageListener;
import com.avaje.ebean.bean.ObjectGraphNode;
import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebeaninternal.api.LoadContext;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.SpiExpressionList;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.api.SpiQuery.Mode;
@@ -41,7 +39,6 @@ import com.avaje.ebeaninternal.server.deploy.DbReadContext;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties;
import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
import com.avaje.ebeaninternal.server.type.DataBind;
import com.avaje.ebeaninternal.server.type.DataReader;
@@ -135,8 +132,6 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
private final SpiQuery<T> query;
private final QueryListener<T> queryListener;
private Map<String, String> currentPathMap;
private String currentPrefix;
@@ -189,8 +184,6 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
*/
private final ElPropertyValue manyPropertyEl;
private final int backgroundFetchAfter;
private final int maxRowsLimit;
/**
@@ -198,8 +191,6 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
*/
private boolean hasHitBackgroundFetchAfter;
private final PersistenceContext persistenceContext;
private DataReader dataReader;
/**
@@ -276,20 +267,7 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
this.desc = request.getBeanDescriptor();
this.predicates = predicates;
this.queryListener = query.getListener();
if (queryListener == null) {
// normal, use the one from the transaction
this.persistenceContext = request.getPersistenceContext();
} else {
// 'Row Level Transaction Context'...
// local transaction context that will be reset
// after each 'master' bean is sent to the listener
this.persistenceContext = new DefaultPersistenceContext();
}
this.maxRowsLimit = query.getMaxRows() > 0 ? query.getMaxRows() : GLOBAL_ROW_LIMIT;
this.backgroundFetchAfter = query.getBackgroundFetchAfter() > 0 ? query
.getBackgroundFetchAfter() : Integer.MAX_VALUE;
this.help = createHelp(request);
this.collection = (BeanCollection<T>) (help != null ? help.createEmpty(false) : null);
@@ -386,6 +364,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);
}
@@ -442,7 +421,7 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
* Return the persistence context.
*/
public PersistenceContext getPersistenceContext() {
return persistenceContext;
return request.getPersistenceContext();
}
public void setLoadedBean(Object bean, Object id, Object lazyLoadParentId) {
@@ -465,7 +444,7 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
if (lazyLoadParentId != null) {
if (!lazyLoadParentId.equals(this.lazyLoadParentId)) {
// get the appropriate parent bean from the persistence context
this.lazyLoadParentBean = persistenceContext.get(lazyLoadManyProperty.getBeanDescriptor().getBeanType(), lazyLoadParentId);
this.lazyLoadParentBean = getPersistenceContext().get(lazyLoadManyProperty.getBeanDescriptor().getBeanType(), lazyLoadParentId);
this.lazyLoadParentId = lazyLoadParentId;
}
@@ -563,12 +542,6 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
return false;
}
if (inForeground && loadedBeanCount >= backgroundFetchAfter) {
hasHitBackgroundFetchAfter = true;
collection.setFinishedFetch(false);
return false;
}
if (!manyIncluded) {
// simple query... no details...
return readRow();
@@ -686,13 +659,8 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
private void readTheRows(boolean inForeground) throws SQLException {
while (hasNextBean(inForeground)) {
if (queryListener != null) {
queryListener.process(getLoadedBean());
} else {
// add to the list/set/map
help.add(collection, getLoadedBean());
}
// add to the list/set/map
help.add(collection, getLoadedBean());
}
}
@@ -73,7 +73,6 @@ public class CQueryFetchIds {
private int rowCount;
private final int maxRows;
private final int bgFetchAfter;
/**
* Create the Sql select based on the request.
@@ -86,7 +85,6 @@ public class CQueryFetchIds {
this.query = request.getQuery();
this.sql = sql;
this.maxRows = query.getMaxRows();
this.bgFetchAfter = query.getBackgroundFetchAfter();
query.setGeneratedSql(sql);
@@ -184,9 +182,6 @@ public class CQueryFetchIds {
hasMoreRows = rset.next();
break;
} else if (bgFetchAfter > 0 && rowCount >= bgFetchAfter) {
useBackgroundToContinueFetch = true;
break;
}
}
@@ -21,7 +21,6 @@ import com.avaje.ebean.OrderBy.Property;
import com.avaje.ebean.PagingList;
import com.avaje.ebean.Query;
import com.avaje.ebean.QueryIterator;
import com.avaje.ebean.QueryListener;
import com.avaje.ebean.QueryResultVisitor;
import com.avaje.ebean.RawSql;
import com.avaje.ebean.bean.BeanCollectionTouched;
@@ -68,8 +67,6 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
* Used to add beans to the PersistanceContext prior to query.
*/
private transient ArrayList<EntityBean> contextAdditions;
private transient QueryListener<T> queryListener;
/**
* For lazy loading of ManyToMany we need to add a join to the intersection
@@ -142,11 +139,6 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
private boolean futureFetch;
private List<Object> partialIds;
/**
* The rows after which the fetch continues in a bg thread.
*/
private int backgroundFetchAfter;
private int timeout = -1;
@@ -437,7 +429,6 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
copy.additionalWhere = additionalWhere;
copy.additionalHaving = additionalHaving;
copy.distinct = distinct;
copy.backgroundFetchAfter = backgroundFetchAfter;
copy.timeout = timeout;
copy.mapKey = mapKey;
copy.id = id;
@@ -1016,26 +1007,6 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
return this;
}
/**
* Return the findListener is one has been set.
*/
public QueryListener<T> getListener() {
return queryListener;
}
/**
* Set a FindListener. This is designed for large fetches where lots are
* rows are to be processed and instead of returning all the rows they are
* processed one at a time.
* <p>
* Note that the returning List Set or Map will be empty.
* </p>
*/
public DefaultOrmQuery<T> setListener(QueryListener<T> queryListener) {
this.queryListener = queryListener;
return this;
}
public Class<T> getBeanType() {
return beanType;
}
@@ -1112,15 +1083,6 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
return this;
}
public int getBackgroundFetchAfter() {
return backgroundFetchAfter;
}
public DefaultOrmQuery<T> setBackgroundFetchAfter(int backgroundFetchAfter) {
this.backgroundFetchAfter = backgroundFetchAfter;
return this;
}
public Object getId() {
return id;
}
@@ -1235,23 +1197,6 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
return whereExpressions;
}
/**
* Return true if using background fetching or a queryListener.
*/
public boolean createOwnTransaction() {
if (futureFetch){
// the future fetches have already created
// their own transaction
return false;
}
if (backgroundFetchAfter > 0 || queryListener != null) {
// run in own transaction as we can't know how long
// the background fetching will continue etc
return true;
}
return false;
}
public String getGeneratedSql() {
return generatedSql;
}
@@ -17,7 +17,6 @@ import com.avaje.ebean.OrderBy;
import com.avaje.ebean.PagingList;
import com.avaje.ebean.Query;
import com.avaje.ebean.QueryIterator;
import com.avaje.ebean.QueryListener;
import com.avaje.ebean.QueryResultVisitor;
import com.avaje.ebean.event.BeanQueryRequest;
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
@@ -213,19 +212,10 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
return query.setMaxRows(maxRows);
}
public Query<T> setBackgroundFetchAfter(int backgroundFetchAfter) {
return query.setBackgroundFetchAfter(backgroundFetchAfter);
}
public Query<T> setMapKey(String mapKey) {
return query.setMapKey(mapKey);
}
@Deprecated
public Query<T> setListener(QueryListener<T> queryListener) {
return query.setListener(queryListener);
}
public Query<T> setUseCache(boolean useCache) {
return query.setUseCache(useCache);
}
@@ -14,7 +14,6 @@ import com.avaje.ebean.FutureRowCount;
import com.avaje.ebean.OrderBy;
import com.avaje.ebean.PagingList;
import com.avaje.ebean.Query;
import com.avaje.ebean.QueryListener;
import com.avaje.ebeaninternal.api.SpiExpressionList;
import com.avaje.ebeaninternal.server.expression.FilterExprPath;
@@ -128,19 +127,10 @@ public class FilterExpressionList<T> extends DefaultExpressionList<T> {
throw new PersistenceException(notAllowedMessage);
}
public Query<T> setBackgroundFetchAfter(int backgroundFetchAfter) {
return rootQuery.setBackgroundFetchAfter(backgroundFetchAfter);
}
public Query<T> setFirstRow(int firstRow) {
return rootQuery.setFirstRow(firstRow);
}
@Deprecated
public Query<T> setListener(QueryListener<T> queryListener) {
return rootQuery.setListener(queryListener);
}
public Query<T> setMapKey(String mapKey) {
return rootQuery.setMapKey(mapKey);
}
@@ -1,63 +0,0 @@
package com.avaje.tests.basic;
import junit.framework.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.Query;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.tests.model.basic.Order;
import com.avaje.tests.model.basic.ResetBasicData;
public class TestBackgroundFetchAfter extends BaseTestCase {
@Test
public void testWrtJoin() {
ResetBasicData.reset();
SpiEbeanServer server = (SpiEbeanServer)Ebean.getServer(null);
boolean h2Db = "h2".equals(server.getDatabasePlatform().getName());
// limit not in sql as join to many
Query<Order> q = Ebean.find(Order.class)
.fetch("details")
.setBackgroundFetchAfter(3)
.setMaxRows(10);
q.findList();
String sql = q.getGeneratedSql();
if (h2Db){
Assert.assertTrue(sql.indexOf("limit") == -1);
}
// allows limit use as no join to many
q = Ebean.find(Order.class)
.setBackgroundFetchAfter(3)
.setMaxRows(10);
q.findList();
sql = q.getGeneratedSql();
if (h2Db){
Assert.assertTrue(sql.indexOf("limit") > -1);
}
// allows limit use as join to one (not many)
q = Ebean.find(Order.class)
.fetch("customer")
.setBackgroundFetchAfter(3)
.setMaxRows(10);
q.findList();
sql = q.getGeneratedSql();
if (h2Db){
Assert.assertTrue(sql.indexOf("limit") > -1);
}
}
}
@@ -1,5 +1,6 @@
package com.avaje.tests.basic;
import java.util.ArrayList;
import java.util.List;
import junit.framework.Assert;
@@ -28,9 +29,14 @@ public class TestInheritRef extends BaseTestCase {
t.setLicenseNumber("T1098");
t.setCapacity(20D);
Ebean.save(t);
List<Integer> ids = new ArrayList<Integer>();
ids.add(c.getId());
ids.add(t.getId());
List<Vehicle> list = Ebean.find(Vehicle.class)
.setAutofetch(false)
.where().idIn(ids)
.findList();
Assert.assertTrue(list.size() > 0);
@@ -39,7 +45,6 @@ public class TestInheritRef extends BaseTestCase {
Truck truck = (Truck)vehicle;
Assert.assertTrue(truck.getLicenseNumber().equals("T1098"));
Assert.assertTrue(truck.getCapacity() == 20D);
}
}
@@ -3,6 +3,8 @@ package com.avaje.tests.basic;
import java.sql.Date;
import java.util.List;
import com.avaje.ebean.cache.ServerCache;
import com.avaje.ebean.cache.ServerCacheManager;
import junit.framework.Assert;
import org.junit.Test;
@@ -19,7 +21,10 @@ public class TestManyLazyLoad extends BaseTestCase {
public void testLazyLoadRef() {
ResetBasicData.reset();
ServerCacheManager serverCacheManager = Ebean.getServerCacheManager();
serverCacheManager.clearAll();
List<Order> list = Ebean.find(Order.class).order().asc("id").findList();
Assert.assertTrue(list.size()+" > 0", list.size() > 0);
@@ -40,8 +45,7 @@ public class TestManyLazyLoad extends BaseTestCase {
Assert.assertTrue(sz+" > 0", sz > 0);
Order o = details.get(0).getOrder();
Assert.assertTrue("same instance", o == order1);
Assert.assertSame("same instance", o, order1);
// change order... list before a scalar property
Order order2 = Ebean.getReference(Order.class, order.getId());
@@ -2,8 +2,7 @@ package com.avaje.tests.basic;
import java.util.List;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -25,6 +24,8 @@ public class TestSharedInstancePropagation extends BaseTestCase {
ResetBasicData.reset();
Ebean.getServerCacheManager().clearAll();
Order order = Ebean.find(Order.class)
.setAutofetch(false)
.setUseCache(true)
@@ -1,5 +1,8 @@
package com.avaje.tests.query.orderby;
import java.util.List;
import org.avaje.ebeantest.LoggedSqlCollector;
import org.junit.Assert;
import org.junit.Test;
@@ -7,6 +10,7 @@ import com.avaje.ebean.BaseTestCase;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.Query;
import com.avaje.tests.model.basic.Order;
import com.avaje.tests.model.basic.OrderDetail;
import com.avaje.tests.model.basic.ResetBasicData;
public class TestOrderByWithMany extends BaseTestCase {
@@ -16,6 +20,8 @@ public class TestOrderByWithMany extends BaseTestCase {
ResetBasicData.reset();
checkWithLazyLoadingOnBuiltInMany();
checkWithBuiltInManyBasic();
checkWithBuiltInMany();
checkAppendId();
checkNone();
@@ -25,6 +31,42 @@ public class TestOrderByWithMany extends BaseTestCase {
checkAlreadyIncluded2();
}
private void checkWithLazyLoadingOnBuiltInMany() {
LoggedSqlCollector.start();
Query<Order> query = Ebean.find(Order.class);
// a query that ensures we are going to lazy load on the details
List<Order> orders = query.findList();
for (Order order : orders) {
// invoke lazy loading
List<OrderDetail> details = order.getDetails();
details.size();
}
// first one is the main query and others are lazy loading queries
List<String> loggedSql = LoggedSqlCollector.stop();
Assert.assertTrue(loggedSql.size() > 1);
String lazyLoadSql = loggedSql.get(1);
// contains the foreign key back to the parent bean (t0.order_id)
Assert.assertTrue(lazyLoadSql.contains("select t0.order_id c0, t0.id"));
Assert.assertTrue(lazyLoadSql.contains("order by t0.order_id, t0.id, t0.order_qty, t0.cretime desc"));
}
private void checkWithBuiltInManyBasic() {
Query<Order> query = Ebean.find(Order.class).fetch("details");
query.findList();
String sql = query.getGeneratedSql();
Assert.assertTrue(sql.contains("order by t0.id, t1.id asc, t1.order_qty asc, t1.cretime desc"));
}
private void checkWithBuiltInMany() {
Query<Order> query = Ebean.find(Order.class).fetch("details").order().desc("customer.name");
@@ -0,0 +1,87 @@
package org.avaje.ebeantest;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.UnsynchronizedAppenderBase;
/**
* Helper that can collect the SQL that is logged via SLF4J.
* <p>
* Used {@link #start()} and {@link #stop()} to collect the logged messages that contain the
* executed SQL statements.
* <p>
* Internally this uses a Logback Appender to collect messages for org.avaje.ebean.SQL.
*/
public class LoggedSqlCollector {
private static BasicAppender basicAppender = new BasicAppender();
static {
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
basicAppender.setContext(lc);
Logger logger = (Logger) LoggerFactory.getLogger("org.avaje.ebean.SQL");
logger.addAppender(basicAppender);
logger.setLevel(Level.TRACE);
logger.setAdditive(true);
}
/**
* Start collection of the logged SQL statements.
*/
public static List<String> start() {
return basicAppender.collectStart();
}
/**
* Stop collection of the logged SQL statements return the list of captured messages that contain
* the SQL.
*/
public static List<String> stop() {
return basicAppender.collectEnd();
}
private static class BasicAppender extends UnsynchronizedAppenderBase<ILoggingEvent> {
List<String> messages = new ArrayList<String>();
@Override
protected void append(ILoggingEvent eventObject) {
if (started) {
messages.add(eventObject.getMessage());
}
}
/**
* Start collection.
*/
List<String> collectStart() {
List<String> tempMessages = messages;
messages = new ArrayList<String>();
// set started flag
start();
return tempMessages;
}
/**
* End collection.
*/
List<String> collectEnd() {
// set stopped state
stop();
List<String> tempMessages = messages;
messages = new ArrayList<String>();
return tempMessages;
}
}
}