mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Change *ToMany lazy loading to not use the parent entity/table
This commit is contained in:
@@ -51,6 +51,9 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void internalAdd(Object bean) {
|
||||
if (list == null) {
|
||||
list = new ArrayList<E>();
|
||||
}
|
||||
list.add((E) bean);
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,14 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
|
||||
super(ebeanServer, ownerBean, propertyName);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void internalPut(Object key, Object bean) {
|
||||
if (map == null) {
|
||||
map = new LinkedHashMap<K, E>();
|
||||
}
|
||||
map.put((K)key, (E)bean);
|
||||
}
|
||||
|
||||
public void internalAdd(Object bean) {
|
||||
throw new RuntimeException("Not allowed for map");
|
||||
}
|
||||
|
||||
@@ -45,6 +45,9 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void internalAdd(Object bean) {
|
||||
if (set == null) {
|
||||
set = new LinkedHashSet<E>();
|
||||
}
|
||||
set.add((E) bean);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,9 @@ package com.avaje.ebean.meta;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Provides access to the meta data in EbeanServer such as query execution statistics.
|
||||
*/
|
||||
public interface MetaInfoManager {
|
||||
|
||||
/**
|
||||
@@ -26,9 +29,9 @@ public interface MetaInfoManager {
|
||||
/**
|
||||
* Collect and return the ObjectGraphNode statistics.
|
||||
* <p>
|
||||
* These show query executions for based on an origin point and paths. This is
|
||||
* used to look at the amount of lazy loading occurring for a given query
|
||||
* origin point.
|
||||
* These show query executions based on an origin point and relative path.
|
||||
* This is used to look at the amount of lazy loading occurring for a given
|
||||
* query origin point and highlight potential for tuning a query.
|
||||
* </p>
|
||||
*
|
||||
* @param reset
|
||||
|
||||
@@ -8,6 +8,6 @@ public interface SpiExpressionFactory extends ExpressionFactory {
|
||||
/**
|
||||
* Create another expression factory with a given sub path.
|
||||
*/
|
||||
public ExpressionFactory createExpressionFactory(FilterExprPath prefix);
|
||||
public ExpressionFactory createExpressionFactory();//FilterExprPath prefix);
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import com.avaje.ebeaninternal.server.deploy.TableJoin;
|
||||
import com.avaje.ebeaninternal.server.query.CancelableQuery;
|
||||
import com.avaje.ebeaninternal.server.querydefn.NaturalKeyBindParam;
|
||||
@@ -153,11 +154,23 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
*/
|
||||
public String getLoadMode();
|
||||
|
||||
/**
|
||||
* This becomes a lazy loading query for a many relationship.
|
||||
*/
|
||||
public void setLazyLoadForParents(List<Object> parentIds, BeanPropertyAssocMany<?> many);
|
||||
|
||||
/**
|
||||
* Return the lazy loading 'many' property.
|
||||
*/
|
||||
public BeanPropertyAssocMany<?> getLazyLoadForParentsProperty();
|
||||
|
||||
/**
|
||||
* Return the list of parent Id's for lazy loading.
|
||||
*/
|
||||
public List<Object> getLazyLoadForParentIds();
|
||||
|
||||
/**
|
||||
* Set the load mode (+lazy or +query) and the load description.
|
||||
*
|
||||
* @param loadMode
|
||||
* @param loadDescription
|
||||
*/
|
||||
public void setLoadDescription(String loadMode, String loadDescription);
|
||||
|
||||
|
||||
@@ -106,20 +106,12 @@ public class DefaultBeanLoader {
|
||||
|
||||
BeanDescriptor<?> desc = ctx.getBeanDescriptor();
|
||||
|
||||
String idProperty = desc.getIdBinder().getIdProperty();
|
||||
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(many.getTargetType());
|
||||
|
||||
query.setLazyLoadForParents(idList, many);
|
||||
many.addWhereParentIdIn(query, idList);
|
||||
|
||||
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(desc.getBeanType());
|
||||
query.setMode(Mode.LAZYLOAD_MANY);
|
||||
query.setLazyLoadManyPath(many.getName());
|
||||
query.setPersistenceContext(pc);
|
||||
query.select(idProperty);
|
||||
query.fetch(many.getName());
|
||||
|
||||
if (idList.size() == 1) {
|
||||
query.where().idEq(idList.get(0));
|
||||
} else {
|
||||
query.where().idIn(idList);
|
||||
}
|
||||
|
||||
String mode = loadRequest.isLazy() ? "+lazy" : "+query";
|
||||
query.setLoadDescription(mode, loadRequest.getDescription());
|
||||
@@ -129,7 +121,7 @@ public class DefaultBeanLoader {
|
||||
|
||||
if (loadRequest.isOnlyIds()) {
|
||||
// override to just select the Id values
|
||||
query.fetch(many.getName(), many.getTargetIdProperty());
|
||||
query.select(many.getTargetIdProperty());
|
||||
}
|
||||
|
||||
server.findList(query, loadRequest.getTransaction());
|
||||
|
||||
@@ -1198,7 +1198,7 @@ public final class DefaultServer implements SpiEbeanServer {
|
||||
}
|
||||
context.put(query.getId(), cachedBean);
|
||||
|
||||
DLoadContext loadContext = new DLoadContext(this, beanDescriptor, query.isReadOnly(), false, null, false);
|
||||
DLoadContext loadContext = new DLoadContext(this, beanDescriptor, query.isReadOnly(), query);
|
||||
loadContext.setPersistenceContext(context);
|
||||
|
||||
EntityBeanIntercept ebi = ((EntityBean) cachedBean)._ebean_getIntercept();
|
||||
|
||||
@@ -36,6 +36,9 @@ public final class BeanListHelp<T> implements BeanCollectionHelp<T> {
|
||||
this.loader = loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal add bypassing any modify listening.
|
||||
*/
|
||||
public void add(BeanCollection<?> collection, Object bean) {
|
||||
collection.internalAdd(bean);
|
||||
}
|
||||
@@ -77,12 +80,21 @@ public final class BeanListHelp<T> implements BeanCollectionHelp<T> {
|
||||
}
|
||||
|
||||
public Object createEmpty(boolean vanilla) {
|
||||
return vanilla ? new ArrayList<T>() : new BeanList<T>();
|
||||
if (vanilla) {
|
||||
return new ArrayList<T>();
|
||||
}
|
||||
BeanList<T> beanList = new BeanList<T>();
|
||||
if (many != null) {
|
||||
beanList.setModifyListening(many.getModifyListenMode());
|
||||
}
|
||||
return beanList;
|
||||
}
|
||||
|
||||
public BeanCollection<T> createReference(Object parentBean, String propertyName) {
|
||||
|
||||
return new BeanList<T>(loader, parentBean, propertyName);
|
||||
BeanList<T> beanList = new BeanList<T>(loader, parentBean, propertyName);
|
||||
beanList.setModifyListening(many.getModifyListenMode());
|
||||
return beanList;
|
||||
}
|
||||
|
||||
public void refresh(EbeanServer server, Query<?> query, Transaction t, Object parentBean) {
|
||||
|
||||
@@ -101,22 +101,34 @@ public final class BeanMapHelp<T> implements BeanCollectionHelp<T> {
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public Object createEmpty(boolean vanilla) {
|
||||
return vanilla ? new LinkedHashMap() : new BeanMap();
|
||||
if (vanilla) {
|
||||
return new LinkedHashMap();
|
||||
}
|
||||
BeanMap beanMap = new BeanMap();
|
||||
if (many != null) {
|
||||
beanMap.setModifyListening(many.getModifyListenMode());
|
||||
}
|
||||
return beanMap;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
/**
|
||||
* Internal add bypassing any modify listening.
|
||||
*/
|
||||
public void add(BeanCollection<?> collection, Object bean) {
|
||||
|
||||
Object keyValue = beanProperty.getValueIntercept(bean);
|
||||
|
||||
Map<Object, Object> map = (Map<Object, Object>) collection;
|
||||
map.put(keyValue, bean);
|
||||
((BeanMap<?,?>) collection).internalPut(keyValue, bean);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public BeanCollection<T> createReference(Object parentBean, String propertyName) {
|
||||
|
||||
return new BeanMap(loader, parentBean, propertyName);
|
||||
BeanMap beanMap = new BeanMap(loader, parentBean, propertyName);
|
||||
if (many != null) {
|
||||
beanMap.setModifyListening(many.getModifyListenMode());
|
||||
}
|
||||
return beanMap;
|
||||
}
|
||||
|
||||
public void refresh(EbeanServer server, Query<?> query, Transaction t, Object parentBean) {
|
||||
|
||||
@@ -140,11 +140,23 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
} else {
|
||||
delStmt = "delete from "+targetDescriptor.getBaseTable()+" where ";
|
||||
}
|
||||
deleteByParentIdSql = delStmt + deriveWhereParentIdSql(false);
|
||||
deleteByParentIdInSql = delStmt + deriveWhereParentIdSql(true);
|
||||
deleteByParentIdSql = delStmt + deriveWhereParentIdSql(false,"");
|
||||
deleteByParentIdInSql = delStmt + deriveWhereParentIdSql(true,"");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add the bean to the appropriate collection on the parent bean.
|
||||
*/
|
||||
public void addBeanToCollectionWithCreate(Object parentBean, Object detailBean) {
|
||||
BeanCollection<?> bc = (BeanCollection<?>)super.getValue(parentBean);
|
||||
if (bc == null) {
|
||||
bc = (BeanCollection<?>)help.createEmpty(false);
|
||||
setValue(parentBean, bc);
|
||||
}
|
||||
help.add(bc, detailBean);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(Object bean) {
|
||||
return super.getValue(bean);
|
||||
@@ -196,7 +208,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
|
||||
private List<Object> findIdsByParentId(Object parentId, Transaction t, ArrayList<Object> excludeDetailIds) {
|
||||
|
||||
String rawWhere = deriveWhereParentIdSql(false);
|
||||
String rawWhere = deriveWhereParentIdSql(false,"");
|
||||
|
||||
EbeanServer server = getBeanDescriptor().getEbeanServer();
|
||||
Query<?> q = server.find(getPropertyType())
|
||||
@@ -212,9 +224,29 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
return server.findIds(q, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a where clause to the query for a given list of parent Id's.
|
||||
*/
|
||||
public void addWhereParentIdIn(SpiQuery<?> query, List<Object> parentIds) {
|
||||
|
||||
String tableAlias = manyToMany ? "int_." : "t0.";
|
||||
if (manyToMany) {
|
||||
query.setIncludeTableJoin(inverseJoin);
|
||||
}
|
||||
String rawWhere = deriveWhereParentIdSql(true, tableAlias);
|
||||
String inClause = descriptor.getIdBinder().getIdInValueExpr(parentIds.size());
|
||||
|
||||
String expr = rawWhere+inClause;
|
||||
|
||||
// Flatten the bind values if needed (embeddedId)
|
||||
List<Object> bindValues = getBindParentIds(parentIds);
|
||||
|
||||
query.where().raw(expr, bindValues.toArray());
|
||||
}
|
||||
|
||||
private List<Object> findIdsByParentIdList(List<Object> parentIdist, Transaction t, ArrayList<Object> excludeDetailIds) {
|
||||
|
||||
String rawWhere = deriveWhereParentIdSql(true);
|
||||
String rawWhere = deriveWhereParentIdSql(true,"");
|
||||
String inClause = targetIdBinder.getIdInValueExpr(parentIdist.size());
|
||||
|
||||
String expr = rawWhere+inClause;
|
||||
@@ -465,6 +497,20 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
public Object getParentId(Object parentBean) {
|
||||
return descriptor.getId(parentBean);
|
||||
}
|
||||
|
||||
public List<Object> getBindParentIds(List<Object> parentIds) {
|
||||
if (exportedProperties.length == 1){
|
||||
return parentIds;
|
||||
}
|
||||
List<Object> expandedList = new ArrayList<Object>(parentIds.size()*exportedProperties.length);
|
||||
for (int i=0; i < parentIds.size(); i++) {
|
||||
for (int y = 0; y < exportedProperties.length; y++) {
|
||||
Object compId = parentIds.get(i);
|
||||
expandedList.add(exportedProperties[y].getValue(compId));
|
||||
}
|
||||
}
|
||||
return expandedList;
|
||||
}
|
||||
|
||||
private void bindWhereParendId(DefaultSqlUpdate sqlUpd, Object parentId){
|
||||
|
||||
@@ -493,7 +539,18 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
return pos;
|
||||
}
|
||||
|
||||
private String deriveWhereParentIdSql(boolean inClause) {
|
||||
public void addSelectExported(DbSqlContext ctx, String tableAlias) {
|
||||
|
||||
String alias = manyToMany ? "int_" : tableAlias;
|
||||
if (alias == null) {
|
||||
alias = "t0";
|
||||
}
|
||||
for (int i = 0; i < exportedProperties.length; i++) {
|
||||
ctx.appendColumn(alias, exportedProperties[i].getForeignDbColumn());
|
||||
}
|
||||
}
|
||||
|
||||
private String deriveWhereParentIdSql(boolean inClause, String tableAlias) {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
@@ -506,7 +563,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
String s = inClause ? "," : " and ";
|
||||
sb.append(s);
|
||||
}
|
||||
sb.append(fkColumn);
|
||||
sb.append(tableAlias).append(fkColumn);
|
||||
if (!inClause){
|
||||
sb.append("=? ");
|
||||
}
|
||||
|
||||
@@ -77,17 +77,29 @@ public final class BeanSetHelp<T> implements BeanCollectionHelp<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal add bypassing any modify listening.
|
||||
*/
|
||||
public void add(BeanCollection<?> collection, Object bean) {
|
||||
collection.internalAdd(bean);
|
||||
}
|
||||
|
||||
public Object createEmpty(boolean vanilla) {
|
||||
return vanilla ? new LinkedHashSet<T>() : new BeanSet<T>();
|
||||
if (vanilla) {
|
||||
return new LinkedHashSet<T>();
|
||||
}
|
||||
BeanSet<T> beanSet = new BeanSet<T>();
|
||||
if (many != null) {
|
||||
beanSet.setModifyListening(many.getModifyListenMode());
|
||||
}
|
||||
return beanSet;
|
||||
}
|
||||
|
||||
public BeanCollection<T> createReference(Object parentBean, String propertyName) {
|
||||
|
||||
return new BeanSet<T>(loader, parentBean, propertyName);
|
||||
BeanSet<T> beanSet = new BeanSet<T>(loader, parentBean, propertyName);
|
||||
beanSet.setModifyListening(many.getModifyListenMode());
|
||||
return beanSet;
|
||||
}
|
||||
|
||||
public void refresh(EbeanServer server, Query<?> query, Transaction t, Object parentBean) {
|
||||
|
||||
@@ -72,7 +72,7 @@ public interface DbReadContext {
|
||||
/**
|
||||
* Set back the bean that has just been loaded with its id.
|
||||
*/
|
||||
public void setLoadedBean(Object loadedBean, Object id);
|
||||
public void setLoadedBean(Object loadedBean, Object id, Object lazyLoadParentId);
|
||||
|
||||
/**
|
||||
* Set back the 'detail' bean that has just been loaded.
|
||||
|
||||
+8
-7
@@ -21,18 +21,19 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
|
||||
|
||||
private static final Object[] EMPTY_ARRAY = new Object[] {};
|
||||
|
||||
private final FilterExprPath prefix;
|
||||
private final FilterExprPath prefix = null;
|
||||
|
||||
public DefaultExpressionFactory() {
|
||||
this(null);
|
||||
//this();//null);
|
||||
}
|
||||
|
||||
public DefaultExpressionFactory(FilterExprPath prefix) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
// public DefaultExpressionFactory(FilterExprPath prefix) {
|
||||
// this.prefix = prefix;
|
||||
// }
|
||||
|
||||
public ExpressionFactory createExpressionFactory(FilterExprPath prefix) {
|
||||
return new DefaultExpressionFactory(prefix);
|
||||
public ExpressionFactory createExpressionFactory(){//FilterExprPath prefix) {
|
||||
return this;
|
||||
//return new DefaultExpressionFactory(prefix);
|
||||
}
|
||||
|
||||
public String getLang() {
|
||||
|
||||
@@ -27,6 +27,9 @@ public class FilterExprPath implements Serializable {
|
||||
* due to a proceeding (earlier) query join etc.
|
||||
*/
|
||||
public FilterExprPath trimPath(int prefixTrim) {
|
||||
if (prefixTrim >= path.length()) {
|
||||
return new FilterExprPath(null);
|
||||
}
|
||||
return new FilterExprPath(path.substring(prefixTrim));
|
||||
}
|
||||
|
||||
|
||||
@@ -51,23 +51,17 @@ public class DLoadContext implements LoadContext {
|
||||
private PersistenceContext persistenceContext;
|
||||
private List<OrmQueryProperties> secQuery;
|
||||
|
||||
|
||||
public DLoadContext(SpiEbeanServer ebeanServer, BeanDescriptor<?> rootDescriptor, Boolean readOnly, SpiQuery<?> query) {
|
||||
this(ebeanServer, rootDescriptor, readOnly,
|
||||
Boolean.FALSE.equals(query.isUseBeanCache()),
|
||||
query.getParentNode(),
|
||||
query.getAutoFetchManager() != null);
|
||||
}
|
||||
|
||||
public DLoadContext(SpiEbeanServer ebeanServer, BeanDescriptor<?> rootDescriptor, Boolean readOnly,
|
||||
boolean excludeBeanCache, ObjectGraphNode parentNode, boolean useAutofetchManager) {
|
||||
|
||||
this.ebeanServer = ebeanServer;
|
||||
this.defaultBatchSize = ebeanServer.getLazyLoadBatchSize();
|
||||
this.rootDescriptor = rootDescriptor;
|
||||
this.readOnly = readOnly;
|
||||
this.excludeBeanCache = excludeBeanCache;
|
||||
this.useAutofetchManager = useAutofetchManager;
|
||||
this.excludeBeanCache = Boolean.FALSE.equals(query.isUseBeanCache());
|
||||
this.useAutofetchManager = query.getAutoFetchManager() != null;
|
||||
|
||||
ObjectGraphNode parentNode = query.getParentNode();
|
||||
if (parentNode != null){
|
||||
this.origin = parentNode.getOriginQueryPoint();
|
||||
this.relativePath = parentNode.getPath();
|
||||
@@ -80,29 +74,29 @@ public class DLoadContext implements LoadContext {
|
||||
this.rootBeanContext = new DLoadBeanContext(this, rootDescriptor, null, defaultBatchSize, null);
|
||||
}
|
||||
|
||||
protected boolean isExcludeBeanCache() {
|
||||
return excludeBeanCache;
|
||||
protected boolean isExcludeBeanCache() {
|
||||
return excludeBeanCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the minimum batch size when using QueryIterator with query joins.
|
||||
*/
|
||||
public int getSecondaryQueriesMinBatchSize(OrmQueryRequest<?> parentRequest, int defaultQueryBatch) {
|
||||
|
||||
if (secQuery == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the minimum batch size when using QueryIterator with query joins.
|
||||
*/
|
||||
public int getSecondaryQueriesMinBatchSize(OrmQueryRequest<?> parentRequest, int defaultQueryBatch) {
|
||||
|
||||
if (secQuery == null){
|
||||
return -1;
|
||||
}
|
||||
|
||||
int maxBatch = 0;
|
||||
for (int i = 0; i < secQuery.size(); i++) {
|
||||
int batchSize = secQuery.get(i).getQueryFetchBatch();
|
||||
if (batchSize == 0){
|
||||
batchSize = defaultQueryBatch;
|
||||
}
|
||||
maxBatch = Math.max(maxBatch, batchSize);
|
||||
}
|
||||
return maxBatch;
|
||||
int maxBatch = 0;
|
||||
for (int i = 0; i < secQuery.size(); i++) {
|
||||
int batchSize = secQuery.get(i).getQueryFetchBatch();
|
||||
if (batchSize == 0) {
|
||||
batchSize = defaultQueryBatch;
|
||||
}
|
||||
maxBatch = Math.max(maxBatch, batchSize);
|
||||
}
|
||||
return maxBatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute all the secondary queries.
|
||||
|
||||
@@ -30,15 +30,13 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
|
||||
super(parent, property.getBeanDescriptor(), path, defaultBatchSize, queryProps);
|
||||
|
||||
this.property = property;
|
||||
this.currentBuffer = createBuffer(firstBatchSize);
|
||||
this.bufferList = queryFetch ? new ArrayList<DLoadManyContext.LoadBuffer>() : null;
|
||||
this.bufferList = new ArrayList<DLoadManyContext.LoadBuffer>();
|
||||
this.currentBuffer = createBuffer(firstBatchSize);
|
||||
}
|
||||
|
||||
private LoadBuffer createBuffer(int size) {
|
||||
LoadBuffer buffer = new LoadBuffer(this, size);
|
||||
if (bufferList != null) {
|
||||
bufferList.add(buffer);
|
||||
}
|
||||
bufferList.add(buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@@ -48,10 +46,10 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
|
||||
if (parent.isReadOnly() != null){
|
||||
query.setReadOnly(parent.isReadOnly());
|
||||
}
|
||||
query.setParentNode(getObjectGraphNode());
|
||||
query.setParentNode(objectGraphNode);
|
||||
|
||||
if (queryProps != null){
|
||||
queryProps.configureManyQuery(query);
|
||||
queryProps.configureBeanQuery(query);
|
||||
}
|
||||
|
||||
if (parent.isUseAutofetchManager()){
|
||||
@@ -59,20 +57,6 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
|
||||
}
|
||||
}
|
||||
|
||||
public ObjectGraphNode getObjectGraphNode() {
|
||||
|
||||
// we return the parent node ... as we actually
|
||||
// query on the parent selecting just it's id
|
||||
|
||||
int pos = path.lastIndexOf('.');
|
||||
if (pos == -1){
|
||||
return parent.getObjectGraphNode(null);
|
||||
} else {
|
||||
String parentPath = path.substring(0, pos);
|
||||
return parent.getObjectGraphNode(parentPath);
|
||||
}
|
||||
}
|
||||
|
||||
public BeanPropertyAssocMany<?> getBeanProperty() {
|
||||
return property;
|
||||
}
|
||||
@@ -156,7 +140,7 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
|
||||
|
||||
@Override
|
||||
public ObjectGraphNode getObjectGraphNode() {
|
||||
return context.getObjectGraphNode();
|
||||
return context.objectGraphNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -84,12 +84,18 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
|
||||
/**
|
||||
* Flag set when 'master' bean changed.
|
||||
*/
|
||||
boolean loadedBeanChanged;
|
||||
private boolean loadedBeanChanged;
|
||||
/**
|
||||
* The 'master' bean just loaded.
|
||||
*/
|
||||
private Object loadedBean;
|
||||
|
||||
private final BeanPropertyAssocMany<?> lazyLoadManyProperty;
|
||||
|
||||
private Object lazyLoadParentId;
|
||||
|
||||
private Object lazyLoadParentBean;
|
||||
|
||||
/**
|
||||
* Holds the previous loaded bean.
|
||||
*/
|
||||
@@ -234,6 +240,7 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
|
||||
this.queryPlan = queryPlan;
|
||||
this.query = request.getQuery();
|
||||
this.queryMode = query.getMode();
|
||||
this.lazyLoadManyProperty = query.getLazyLoadForParentsProperty();
|
||||
|
||||
this.readOnly = request.isReadOnly();
|
||||
|
||||
@@ -438,7 +445,7 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
|
||||
return persistenceContext;
|
||||
}
|
||||
|
||||
public void setLoadedBean(Object bean, Object id) {
|
||||
public void setLoadedBean(Object bean, Object id, Object lazyLoadParentId) {
|
||||
if (id != null && id.equals(loadedBeanId)) {
|
||||
// master/detail loading with master bean
|
||||
// unchanged. NB Using id to avoid any issue
|
||||
@@ -452,7 +459,19 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
|
||||
this.prevLoadedBean = loadedBean;
|
||||
this.loadedBeanId = id;
|
||||
}
|
||||
|
||||
this.loadedBean = bean;
|
||||
|
||||
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.lazyLoadParentId = lazyLoadParentId;
|
||||
}
|
||||
|
||||
// add the loadedBean to the appropriate collection of lazyLoadParentBean
|
||||
lazyLoadManyProperty.addBeanToCollectionWithCreate(lazyLoadParentBean, loadedBean);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -299,7 +299,7 @@ public class CQueryFetchIds {
|
||||
// no-op
|
||||
}
|
||||
|
||||
public void setLoadedBean(Object loadedBean, Object id) {
|
||||
public void setLoadedBean(Object loadedBean, Object id, Object lazyLoadParentId) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.avaje.ebeaninternal.server.deploy.TableJoin;
|
||||
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;
|
||||
|
||||
@@ -256,7 +257,10 @@ public class SqlTreeBuilder {
|
||||
|
||||
if (prefix == null) {
|
||||
buildExtraJoins(desc, myList);
|
||||
return new SqlTreeNodeRoot(desc, props, myList, !subQuery, includeJoin);
|
||||
|
||||
// Optional many property for lazy loading query
|
||||
BeanPropertyAssocMany<?> lazyLoadMany = (query == null) ? null : query.getLazyLoadForParentsProperty();
|
||||
return new SqlTreeNodeRoot(desc, props, myList, !subQuery, includeJoin, lazyLoadMany);
|
||||
|
||||
} else if (prop instanceof BeanPropertyAssocMany<?>) {
|
||||
return new SqlTreeNodeManyRoot(prefix, (BeanPropertyAssocMany<?>) prop, props, myList);
|
||||
|
||||
@@ -82,19 +82,22 @@ public class SqlTreeNodeBean implements SqlTreeNode {
|
||||
Set<String> includedProps;
|
||||
|
||||
final Map<String, String> pathMap;
|
||||
|
||||
|
||||
final BeanPropertyAssocMany<?> lazyLoadParent;
|
||||
|
||||
public SqlTreeNodeBean(String prefix, BeanPropertyAssoc<?> beanProp, SqlTreeProperties props,
|
||||
List<SqlTreeNode> myChildren, boolean withId) {
|
||||
|
||||
this(prefix, beanProp, beanProp.getTargetDescriptor(), props, myChildren, withId);
|
||||
this(prefix, beanProp, beanProp.getTargetDescriptor(), props, myChildren, withId, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create with the appropriate node.
|
||||
*/
|
||||
public SqlTreeNodeBean(String prefix, BeanPropertyAssoc<?> beanProp, BeanDescriptor<?> desc,
|
||||
SqlTreeProperties props, List<SqlTreeNode> myChildren, boolean withId) {
|
||||
SqlTreeProperties props, List<SqlTreeNode> myChildren, boolean withId, BeanPropertyAssocMany<?> lazyLoadParent) {
|
||||
|
||||
this.lazyLoadParent = lazyLoadParent;
|
||||
this.prefix = prefix;
|
||||
this.nodeBeanProp = beanProp;
|
||||
this.desc = desc;
|
||||
@@ -156,7 +159,7 @@ public class SqlTreeNodeBean implements SqlTreeNode {
|
||||
}
|
||||
}
|
||||
|
||||
protected void postLoad(DbReadContext cquery, Object loadedBean, Object id) {
|
||||
protected void postLoad(DbReadContext cquery, Object loadedBean, Object id, Object lazyLoadParentId) {
|
||||
}
|
||||
|
||||
public void buildSelectExpressionChain(List<String> selectChain) {
|
||||
@@ -179,6 +182,11 @@ public class SqlTreeNodeBean implements SqlTreeNode {
|
||||
*/
|
||||
public void load(DbReadContext ctx, Object parentBean) throws SQLException {
|
||||
|
||||
Object lazyLoadParentId = null;
|
||||
if (lazyLoadParent != null) {
|
||||
lazyLoadParentId = lazyLoadParent.getBeanDescriptor().getIdBinder().read(ctx);
|
||||
}
|
||||
|
||||
// bean already existing in the persistence context
|
||||
Object contextBean = null;
|
||||
|
||||
@@ -333,13 +341,13 @@ public class SqlTreeNodeBean implements SqlTreeNode {
|
||||
|
||||
if (!readId) {
|
||||
// a bean with no Id (never found in context)
|
||||
postLoad(ctx, localBean, id);
|
||||
postLoad(ctx, localBean, id, null);
|
||||
|
||||
} else {
|
||||
// return the contextBean which is either the localBean
|
||||
// read from the resultSet and put into the context OR
|
||||
// the 'matching' bean that already existed in the context
|
||||
postLoad(ctx, contextBean, id);
|
||||
postLoad(ctx, contextBean, id, lazyLoadParentId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,6 +383,10 @@ public class SqlTreeNodeBean implements SqlTreeNode {
|
||||
|
||||
ctx.pushJoin(prefix);
|
||||
ctx.pushTableAlias(prefix);
|
||||
|
||||
if (lazyLoadParent != null) {
|
||||
lazyLoadParent.addSelectExported(ctx, prefix);
|
||||
}
|
||||
|
||||
if (!subQuery && inheritInfo != null) {
|
||||
ctx.appendColumn(inheritInfo.getDiscriminatorColumn());
|
||||
|
||||
@@ -10,11 +10,11 @@ import com.avaje.ebeaninternal.server.deploy.DbSqlContext;
|
||||
public final class SqlTreeNodeManyRoot extends SqlTreeNodeBean {
|
||||
|
||||
public SqlTreeNodeManyRoot(String prefix, BeanPropertyAssocMany<?> prop, SqlTreeProperties props, List<SqlTreeNode> myList) {
|
||||
super(prefix, prop, prop.getTargetDescriptor(), props, myList, true);
|
||||
super(prefix, prop, prop.getTargetDescriptor(), props, myList, true, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postLoad(DbReadContext cquery, Object loadedBean, Object id) {
|
||||
protected void postLoad(DbReadContext cquery, Object loadedBean, Object id, Object lazyLoadParentId) {
|
||||
|
||||
// put the localBean into the manyValue so that it
|
||||
// is added to the collection/map
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.avaje.ebeaninternal.server.query;
|
||||
import java.util.List;
|
||||
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import com.avaje.ebeaninternal.server.deploy.DbReadContext;
|
||||
import com.avaje.ebeaninternal.server.deploy.DbSqlContext;
|
||||
import com.avaje.ebeaninternal.server.deploy.TableJoin;
|
||||
@@ -13,43 +14,43 @@ import com.avaje.ebeaninternal.server.deploy.TableJoin;
|
||||
public final class SqlTreeNodeRoot extends SqlTreeNodeBean {
|
||||
|
||||
private final TableJoin includeJoin;
|
||||
|
||||
|
||||
/**
|
||||
* Specify for SqlSelect to include an Id property or not.
|
||||
*/
|
||||
public SqlTreeNodeRoot(BeanDescriptor<?> desc, SqlTreeProperties props, List<SqlTreeNode> myList, boolean withId, TableJoin includeJoin){
|
||||
super(null, null, desc, props, myList, withId);
|
||||
public SqlTreeNodeRoot(BeanDescriptor<?> desc, SqlTreeProperties props, List<SqlTreeNode> myList, boolean withId, TableJoin includeJoin, BeanPropertyAssocMany<?> many){
|
||||
super(null, null, desc, props, myList, withId, many);
|
||||
this.includeJoin = includeJoin;
|
||||
}
|
||||
|
||||
public SqlTreeNodeRoot(BeanDescriptor<?> desc, SqlTreeProperties props, List<SqlTreeNode> myList, boolean withId) {
|
||||
super(null, null, desc, props, myList, withId);
|
||||
super(null, null, desc, props, myList, withId, null);
|
||||
this.includeJoin = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postLoad(DbReadContext cquery, Object loadedBean, Object id) {
|
||||
protected void postLoad(DbReadContext cquery, Object loadedBean, Object id, Object lazyLoadParentId) {
|
||||
|
||||
// set the current bean with id...
|
||||
cquery.setLoadedBean(loadedBean, id);
|
||||
cquery.setLoadedBean(loadedBean, id, lazyLoadParentId);
|
||||
}
|
||||
|
||||
/**
|
||||
* For the root node there is no join type or on clause etc.
|
||||
*/
|
||||
@Override
|
||||
public boolean appendFromBaseTable(DbSqlContext ctx, boolean forceOuterJoin) {
|
||||
|
||||
ctx.append(desc.getBaseTable());
|
||||
ctx.append(" ").append(ctx.getTableAlias(null));
|
||||
|
||||
if (includeJoin != null){
|
||||
String a1 = ctx.getTableAlias(null);
|
||||
String a2 = "int_"; // unique alias for intersection join
|
||||
includeJoin.addJoin(forceOuterJoin, a1, a2, ctx);
|
||||
}
|
||||
|
||||
return forceOuterJoin;
|
||||
}
|
||||
/**
|
||||
* For the root node there is no join type or on clause etc.
|
||||
*/
|
||||
@Override
|
||||
public boolean appendFromBaseTable(DbSqlContext ctx, boolean forceOuterJoin) {
|
||||
|
||||
ctx.append(desc.getBaseTable());
|
||||
ctx.append(" ").append(ctx.getTableAlias(null));
|
||||
|
||||
if (includeJoin != null) {
|
||||
String a1 = ctx.getTableAlias(null);
|
||||
String a2 = "int_"; // unique alias for intersection join
|
||||
includeJoin.addJoin(forceOuterJoin, a1, a2, ctx);
|
||||
}
|
||||
|
||||
return forceOuterJoin;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ import com.avaje.ebeaninternal.api.SpiExpressionList;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import com.avaje.ebeaninternal.server.deploy.DRawSqlSelect;
|
||||
import com.avaje.ebeaninternal.server.deploy.DeployNamedQuery;
|
||||
import com.avaje.ebeaninternal.server.deploy.TableJoin;
|
||||
@@ -207,6 +208,10 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
*/
|
||||
private ObjectGraphNode parentNode;
|
||||
|
||||
private BeanPropertyAssocMany<?> lazyLoadForParentsProperty;
|
||||
|
||||
private List<Object> lazyLoadForParentsIds;
|
||||
|
||||
/**
|
||||
* Hash of final query after AutoFetch tuning.
|
||||
*/
|
||||
@@ -530,9 +535,25 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
*/
|
||||
public void setPersistenceContext(PersistenceContext persistenceContext) {
|
||||
this.persistenceContext = persistenceContext;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@Override
|
||||
public void setLazyLoadForParents(List<Object> parentIds, BeanPropertyAssocMany<?> many) {
|
||||
this.lazyLoadForParentsIds = parentIds;
|
||||
this.lazyLoadForParentsProperty = many;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Object> getLazyLoadForParentIds() {
|
||||
return lazyLoadForParentsIds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanPropertyAssocMany<?> getLazyLoadForParentsProperty() {
|
||||
return lazyLoadForParentsProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the query detail has neither select or joins specified.
|
||||
*/
|
||||
public boolean isDetailEmpty() {
|
||||
|
||||
@@ -15,6 +15,7 @@ import com.avaje.ebean.OrderBy;
|
||||
import com.avaje.ebean.Query;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionFactory;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionList;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
@@ -170,7 +171,7 @@ public class OrmQueryProperties implements Serializable {
|
||||
if (filterMany == null){
|
||||
FilterExprPath exprPath = new FilterExprPath(path);
|
||||
SpiExpressionFactory queryEf = (SpiExpressionFactory)rootQuery.getExpressionFactory();
|
||||
ExpressionFactory filterEf = queryEf.createExpressionFactory(exprPath);
|
||||
ExpressionFactory filterEf = queryEf.createExpressionFactory();//exprPath);
|
||||
filterMany = new FilterExpressionList(exprPath, filterEf, rootQuery);
|
||||
// by default we need to make this a 'query join' now
|
||||
queryFetchAll = true;
|
||||
@@ -229,12 +230,17 @@ public class OrmQueryProperties implements Serializable {
|
||||
public void configureBeanQuery(SpiQuery<?> query) {
|
||||
|
||||
if (trimmedProperties != null && trimmedProperties.length() > 0) {
|
||||
query.select(trimmedProperties);
|
||||
if (filterMany != null){
|
||||
query.setFilterMany(path, filterMany);
|
||||
}
|
||||
query.select(trimmedProperties);
|
||||
}
|
||||
|
||||
|
||||
if (filterMany != null){
|
||||
SpiExpressionList<?> trimPath = filterMany.trimPath(path.length()+1);
|
||||
List<SpiExpression> underlyingList = trimPath.getUnderlyingList();
|
||||
for (SpiExpression spiExpression : underlyingList) {
|
||||
query.where().add(spiExpression);
|
||||
}
|
||||
}
|
||||
|
||||
if (secondaryChildren != null) {
|
||||
int trimPath = path.length() + 1;
|
||||
for (int i = 0; i < secondaryChildren.size(); i++) {
|
||||
@@ -251,36 +257,6 @@ public class OrmQueryProperties implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the select and joins for this query.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void configureManyQuery(SpiQuery<?> query) {
|
||||
|
||||
if (trimmedProperties != null && trimmedProperties.length() > 0) {
|
||||
query.fetch(query.getLazyLoadManyPath(), trimmedProperties);
|
||||
}
|
||||
if (filterMany != null){
|
||||
query.setFilterMany(path, filterMany);
|
||||
}
|
||||
if (secondaryChildren != null) {
|
||||
|
||||
int trimlen = path.length() - query.getLazyLoadManyPath().length();
|
||||
|
||||
for (int i = 0; i < secondaryChildren.size(); i++) {
|
||||
OrmQueryProperties p = secondaryChildren.get(i);
|
||||
String path = p.getPath();
|
||||
path = path.substring(trimlen);
|
||||
query.fetch(path, p.getProperties(), p.getFetchConfig());
|
||||
query.setFilterMany(path, p.getFilterManyTrimPath(trimlen));
|
||||
}
|
||||
}
|
||||
|
||||
if (orderBy != null){
|
||||
query.setOrder(orderBy);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy of the OrmQueryProperties.
|
||||
*/
|
||||
|
||||
@@ -34,7 +34,7 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
|
||||
private static final long serialVersionUID = -6992345500247035947L;
|
||||
|
||||
protected final ArrayList<SpiExpression> list = new ArrayList<SpiExpression>();
|
||||
protected final List<SpiExpression> list;
|
||||
|
||||
protected final Query<T> query;
|
||||
|
||||
@@ -52,6 +52,11 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
}
|
||||
|
||||
public DefaultExpressionList(Query<T> query, ExpressionFactory expr, ExpressionList<T> parentExprList) {
|
||||
this(query, expr, parentExprList, new ArrayList<SpiExpression>());
|
||||
}
|
||||
|
||||
protected DefaultExpressionList(Query<T> query, ExpressionFactory expr, ExpressionList<T> parentExprList, List<SpiExpression> list) {
|
||||
this.list = list;
|
||||
this.query = query;
|
||||
this.expr = expr;
|
||||
this.exprLang = expr.getLang();
|
||||
|
||||
@@ -25,6 +25,12 @@ public class FilterExpressionList<T> extends DefaultExpressionList<T> {
|
||||
private final Query<T> rootQuery;
|
||||
|
||||
private final FilterExprPath pathPrefix;
|
||||
|
||||
public FilterExpressionList(FilterExprPath pathPrefix, FilterExpressionList<T> original) {
|
||||
super(null, original.expr, null, original.getUnderlyingList());
|
||||
this.pathPrefix = pathPrefix;
|
||||
this.rootQuery = original.rootQuery;
|
||||
}
|
||||
|
||||
public FilterExpressionList(FilterExprPath pathPrefix, ExpressionFactory expr, Query<T> rootQuery) {
|
||||
super(null, expr, null);
|
||||
@@ -33,7 +39,7 @@ public class FilterExpressionList<T> extends DefaultExpressionList<T> {
|
||||
}
|
||||
|
||||
public SpiExpressionList<?> trimPath(int prefixTrim) {
|
||||
return new FilterExpressionList<T>(pathPrefix.trimPath(prefixTrim), expr, rootQuery);
|
||||
return new FilterExpressionList<T>(pathPrefix.trimPath(prefixTrim), this);
|
||||
}
|
||||
|
||||
public FilterExprPath getPathPrefix() {
|
||||
|
||||
Reference in New Issue
Block a user