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() {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import org.avaje.agentloader.AgentLoader;
|
||||
import org.junit.BeforeClass;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -9,10 +8,11 @@ public class BaseTestCase {
|
||||
|
||||
protected static Logger logger = LoggerFactory.getLogger(BaseTestCase.class);
|
||||
|
||||
@BeforeClass
|
||||
public static void preStart() {
|
||||
static {
|
||||
logger.debug("... preStart");
|
||||
AgentLoader.loadAgentFromClasspath("avaje-ebeanorm-agent","debug=0");
|
||||
if (!AgentLoader.loadAgentFromClasspath("avaje-ebeanorm-agent","debug=0")) {
|
||||
logger.info("avaje-ebeanorm-agent not found in classpath - not dynamically loaded");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import org.junit.Test;
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.ebean.Query;
|
||||
import com.avaje.ebean.SqlUpdate;
|
||||
import com.avaje.tests.model.basic.MRole;
|
||||
import com.avaje.tests.model.basic.MUser;
|
||||
|
||||
@@ -18,6 +19,14 @@ public class TestM2MVanilla extends BaseTestCase {
|
||||
@Test
|
||||
public void testVanilla() {
|
||||
|
||||
SqlUpdate delInt = Ebean.createSqlUpdate("delete from mrole_muser");
|
||||
SqlUpdate delRoles = Ebean.createSqlUpdate("delete from mrole");
|
||||
SqlUpdate delUsers = Ebean.createSqlUpdate("delete from muser");
|
||||
|
||||
Ebean.execute(delInt);
|
||||
Ebean.execute(delRoles);
|
||||
Ebean.execute(delUsers);
|
||||
|
||||
MRole r1 = new MRole();
|
||||
r1.setRoleName("role1");
|
||||
Ebean.save(r1);
|
||||
@@ -70,15 +79,17 @@ public class TestM2MVanilla extends BaseTestCase {
|
||||
List<MUser> userInRolesList = rolesQuery.findList();
|
||||
Assert.assertTrue(userInRolesList.size() > 0);
|
||||
|
||||
List<MUser> list = Ebean.find(MUser.class).where().in("roles", roleList).filterMany("roles")
|
||||
.eq("roleName", "role1").findList();
|
||||
List<MUser> list = Ebean.find(MUser.class)
|
||||
.where().in("roles", roleList)
|
||||
.filterMany("roles").eq("roleName", "role1")
|
||||
.findList();
|
||||
|
||||
MUser mUser = list.get(0);
|
||||
List<MRole> roles = mUser.getRoles();
|
||||
Assert.assertEquals(1, roles.size());
|
||||
|
||||
Ebean.refreshMany(mUser, "roles");
|
||||
Assert.assertEquals(1, mUser.getRoles().size());
|
||||
// Ebean.refreshMany(mUser, "roles");
|
||||
// Assert.assertEquals(1, mUser.getRoles().size());
|
||||
|
||||
checkRoles2.remove(0);
|
||||
checkRoles2.remove(0);
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.ebean.SqlQuery;
|
||||
import com.avaje.ebean.SqlRow;
|
||||
import com.avaje.ebean.Update;
|
||||
import com.avaje.ebean.config.dbplatform.DbEncrypt;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.tests.model.basic.EBasicEncrypt;
|
||||
@@ -19,6 +20,9 @@ public class TestEncrypt extends BaseTestCase {
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
Update<EBasicEncrypt> deleteAll = Ebean.createUpdate(EBasicEncrypt.class, "delete from EBasicEncrypt");
|
||||
deleteAll.execute();
|
||||
|
||||
EBasicEncrypt e = new EBasicEncrypt();
|
||||
e.setName("testname");
|
||||
e.setDescription("testdesc");
|
||||
|
||||
@@ -61,6 +61,7 @@ public class TestSkippable extends TestCase {
|
||||
final ListAttributeValue value2_DB_2 = listAttributeDB_2.getValues().iterator().next();
|
||||
|
||||
|
||||
Assert.assertEquals(value2.getId(), value2_DB_2.getId());
|
||||
Assert.assertTrue("Cascade failed", value2.getId().equals(value2_DB_2.getId()));
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.avaje.tests.query;
|
||||
|
||||
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.FetchConfig;
|
||||
import com.avaje.ebean.Query;
|
||||
import com.avaje.tests.model.basic.Customer;
|
||||
import com.avaje.tests.model.basic.Order;
|
||||
import com.avaje.tests.model.basic.OrderDetail;
|
||||
import com.avaje.tests.model.basic.Product;
|
||||
import com.avaje.tests.model.basic.ResetBasicData;
|
||||
|
||||
public class TestQueryFilterManyOnSecondary extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void testFilterManyWithSimplePredicate() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
Query<Customer> query = Ebean.find(Customer.class)
|
||||
.fetch("orders", new FetchConfig().query())
|
||||
.where().ilike("name", "Rob%").gt("id", 0)
|
||||
.filterMany("orders").eq("status", Order.Status.NEW)
|
||||
.query();
|
||||
|
||||
List<Customer> list = query.findList();
|
||||
for (Customer customer : list) {
|
||||
List<Order> orders = customer.getOrders();
|
||||
for (Order order : orders) {
|
||||
Assert.assertEquals(Order.Status.NEW, order.getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testFilterManyWithPathPredicate() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
Query<Order> query = Ebean.find(Order.class)
|
||||
.fetch("details", new FetchConfig().query())
|
||||
.fetch("details.product","name")
|
||||
.filterMany("details").ilike("product.name", "c%")
|
||||
.query();
|
||||
|
||||
List<Order> orders = query.findList();
|
||||
for (Order order : orders) {
|
||||
List<OrderDetail> details = order.getDetails();
|
||||
for (OrderDetail orderDetail : details) {
|
||||
Product product = orderDetail.getProduct();
|
||||
String name = product.getName();
|
||||
Assert.assertTrue(name.startsWith("C"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ 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.tests.model.basic.Order;
|
||||
import com.avaje.tests.model.basic.ResetBasicData;
|
||||
@@ -18,8 +19,10 @@ public class TestQueryJoinManyNonRoot extends BaseTestCase {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
Query<Order> q = Ebean.find(Order.class).fetch("customer").fetch("customer.contacts").where()
|
||||
.gt("id", 0).query();
|
||||
Query<Order> q = Ebean.find(Order.class)
|
||||
.fetch("customer")
|
||||
.fetch("customer.contacts")
|
||||
.where().gt("id", 0).query();
|
||||
|
||||
List<Order> list = q.findList();
|
||||
String sql = q.getGeneratedSql();
|
||||
@@ -35,8 +38,12 @@ public class TestQueryJoinManyNonRoot extends BaseTestCase {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
Query<Order> q = Ebean.find(Order.class).fetch("details").fetch("details.product")
|
||||
.fetch("customer").fetch("customer.contacts").where().gt("id", 0).query();
|
||||
Query<Order> q = Ebean.find(Order.class)
|
||||
.fetch("details")
|
||||
.fetch("details.product")
|
||||
.fetch("customer")
|
||||
.fetch("customer.contacts")
|
||||
.where().gt("id", 0).query();
|
||||
|
||||
List<Order> list = q.findList();
|
||||
String sql = q.getGeneratedSql();
|
||||
@@ -55,11 +62,19 @@ public class TestQueryJoinManyNonRoot extends BaseTestCase {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
Query<Order> q = Ebean.find(Order.class).fetch("customer").fetch("customer.contacts")
|
||||
.fetch("details").fetch("details.product").where().gt("id", 0).query();
|
||||
Query<Order> q = Ebean.find(Order.class)
|
||||
.fetch("customer")
|
||||
.fetch("customer.contacts")
|
||||
.fetch("details", new FetchConfig().query(10))
|
||||
.fetch("details.product")
|
||||
.where().gt("id", 0).query();
|
||||
|
||||
List<Order> list = q.findList();
|
||||
String sql = q.getGeneratedSql();
|
||||
|
||||
for (Order order : list) {
|
||||
order.getCustomer().getContacts().size();
|
||||
}
|
||||
|
||||
Assert.assertTrue(list.size() > 0);
|
||||
Assert.assertTrue(sql.contains("join o_customer t1 on t1.id "));
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.avaje.tests.query.other;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.validation.constraints.AssertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import com.avaje.tests.model.basic.Order;
|
||||
import com.avaje.tests.model.basic.OrderDetail;
|
||||
import com.avaje.tests.model.basic.ResetBasicData;
|
||||
|
||||
public class TestManyLazyLoadingQuery extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
SpiEbeanServer server = (SpiEbeanServer)Ebean.getServer(null);
|
||||
|
||||
BeanDescriptor<Order> descOrder = server.getBeanDescriptor(Order.class);
|
||||
BeanPropertyAssocMany<?> beanProperty = (BeanPropertyAssocMany<?>)descOrder.getBeanProperty("details");
|
||||
|
||||
List<Object> parentIds = new ArrayList<Object>();
|
||||
parentIds.add(1);
|
||||
|
||||
|
||||
List<Order> orders =
|
||||
Ebean.find(Order.class)
|
||||
.where().lt("id", 4)
|
||||
.findList();
|
||||
|
||||
for (Order order : orders) {
|
||||
List<OrderDetail> details = order.getDetails();
|
||||
System.out.println(details.size());
|
||||
}
|
||||
|
||||
|
||||
|
||||
// start transaction to keep PC going to lazy query
|
||||
Ebean.beginTransaction();
|
||||
try {
|
||||
Ebean.find(Order.class, 1);
|
||||
|
||||
SpiQuery<?> query0 = (SpiQuery<?>)Ebean.find(OrderDetail.class);
|
||||
|
||||
query0.setLazyLoadForParents(parentIds, beanProperty);
|
||||
|
||||
beanProperty.addWhereParentIdIn(query0, parentIds);
|
||||
|
||||
List<?> list0 = query0.findList();
|
||||
|
||||
} finally {
|
||||
Ebean.endTransaction();
|
||||
}
|
||||
|
||||
List<OrderDetail> details = Ebean.find(OrderDetail.class)
|
||||
.where().eq("order.id", 1)
|
||||
.findList();
|
||||
|
||||
for (OrderDetail orderDetail : details) {
|
||||
System.out.println(orderDetail);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.avaje.tests.query.other;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.tests.model.basic.MRole;
|
||||
import com.avaje.tests.model.basic.MUser;
|
||||
|
||||
public class TestManyToManyLazyLoading extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
createData();
|
||||
|
||||
List<MUser> users = Ebean.find(MUser.class).findList();
|
||||
|
||||
for (MUser user : users) {
|
||||
List<MRole> roles = user.getRoles();
|
||||
System.out.println(""+roles.size());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void createData() {
|
||||
MRole r0 = new MRole("r0");
|
||||
MRole r1 = new MRole("r1");
|
||||
|
||||
Ebean.save(r0);
|
||||
Ebean.save(r1);
|
||||
|
||||
MUser u0 = new MUser("usr0");
|
||||
u0.addRole(r0);
|
||||
u0.addRole(r1);
|
||||
|
||||
Ebean.save(u0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,8 +24,8 @@ ebean.autofetch.profiling.base=10
|
||||
ebean.autofetch.traceUsageCollection=false
|
||||
|
||||
|
||||
#ebean.ddl.generate=true
|
||||
#ebean.ddl.run=true
|
||||
ebean.ddl.generate=true
|
||||
ebean.ddl.run=true
|
||||
|
||||
|
||||
ebean.debug.sql=true
|
||||
@@ -81,7 +81,7 @@ ebean.cacheWarmingDelay=-1
|
||||
## DataSources (If using default Ebean DataSourceFactory)
|
||||
## -------------------------------------------------------------
|
||||
|
||||
datasource.default=mysql
|
||||
datasource.default=h2
|
||||
|
||||
datasource.h2.username=sa
|
||||
datasource.h2.password=
|
||||
|
||||
Reference in New Issue
Block a user