diff --git a/src/main/java/com/avaje/ebean/common/BeanList.java b/src/main/java/com/avaje/ebean/common/BeanList.java index e370441b5..717a27af1 100644 --- a/src/main/java/com/avaje/ebean/common/BeanList.java +++ b/src/main/java/com/avaje/ebean/common/BeanList.java @@ -69,7 +69,9 @@ public final class BeanList extends AbstractBeanCollection implements List if (list == null) { list = new ArrayList(); } - list.add((E) bean); + if (bean != null) { + list.add((E) bean); + } } public boolean checkEmptyLazyLoad() { diff --git a/src/main/java/com/avaje/ebean/common/BeanMap.java b/src/main/java/com/avaje/ebean/common/BeanMap.java index 814a14e93..7afd7dc39 100644 --- a/src/main/java/com/avaje/ebean/common/BeanMap.java +++ b/src/main/java/com/avaje/ebean/common/BeanMap.java @@ -52,12 +52,20 @@ public final class BeanMap extends AbstractBeanCollection implements Ma return !touched && (map == null || map.isEmpty()); } - @SuppressWarnings("unchecked") + public void internalPutNull() { + if (map == null) { + map = new LinkedHashMap(); + } + } + + @SuppressWarnings("unchecked") public void internalPut(Object key, Object bean) { if (map == null) { map = new LinkedHashMap(); } - map.put((K)key, (E)bean); + if (key != null) { + map.put((K) key, (E) bean); + } } public void internalAdd(Object bean) { diff --git a/src/main/java/com/avaje/ebean/common/BeanSet.java b/src/main/java/com/avaje/ebean/common/BeanSet.java index e3e6c166b..6faf7f426 100644 --- a/src/main/java/com/avaje/ebean/common/BeanSet.java +++ b/src/main/java/com/avaje/ebean/common/BeanSet.java @@ -62,7 +62,9 @@ public final class BeanSet extends AbstractBeanCollection implements Set(); } - set.add((E) bean); + if (bean != null) { + set.add((E) bean); + } } /** diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanMapHelp.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanMapHelp.java index 43e8ad313..e7cace23e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanMapHelp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanMapHelp.java @@ -109,8 +109,12 @@ public final class BeanMapHelp implements BeanCollectionHelp { @Override public void add(BeanCollection collection, EntityBean bean) { - Object keyValue = beanProperty.getValueIntercept(bean); - ((BeanMap) collection).internalPut(keyValue, bean); + if (bean == null) { + ((BeanMap) collection).internalPutNull(); + } else { + Object keyValue = beanProperty.getValueIntercept(bean); + ((BeanMap) collection).internalPut(keyValue, bean); + } } @Override diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/DbReadContext.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/DbReadContext.java index cc14c396b..4893272cb 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/DbReadContext.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/DbReadContext.java @@ -73,13 +73,8 @@ public interface DbReadContext { /** * Set back the bean that has just been loaded with its id. */ - public void setLoadedBean(EntityBean loadedBean, Object id, Object lazyLoadParentId); + public void setLazyLoadedChildBean(EntityBean loadedBean, Object parentId); - /** - * Set back the 'detail' bean that has just been loaded. - */ - public void setLoadedManyBean(EntityBean loadedBean); - /** * Return the query mode. */ diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java index 7c9de5e82..656e37ab7 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java @@ -60,46 +60,22 @@ public class CQuery implements DbReadContext, CancelableQuery { * Flag set when no more rows are in the resultSet. */ private boolean noMoreRows; - /** - * Id of loaded 'master' bean. - */ - private Object loadedBeanId; - /** - * Flag set when 'master' bean changed. - */ - private boolean loadedBeanChanged; /** * The 'master' bean just loaded. */ - private EntityBean loadedBean; + private EntityBean nextBean; + + /** + * Holds the previous loaded bean. + */ + private EntityBean currentBean; private final BeanPropertyAssocMany lazyLoadManyProperty; private Object lazyLoadParentId; - + private EntityBean lazyLoadParentBean; - - /** - * Holds the previous loaded bean. - */ - private EntityBean prevLoadedBean; - - /** - * The detail bean just loaded. - */ - private EntityBean loadedManyBean; - - /** - * The previous 'detail' collection remembered so that for manyToMany we can - * turn on the modify listening. - */ - private Object prevDetailCollection; - - /** - * The current 'detail' collection being populated. - */ - private Object currentDetailCollection; /** * The 'master' collection being populated. @@ -123,11 +99,6 @@ public class CQuery implements DbReadContext, CancelableQuery { private String currentPrefix; - /** - * Flag set true when reading 'master' and 'detail' beans. - */ - private final boolean manyIncluded; - /** * Where clause predicates. */ @@ -166,11 +137,6 @@ public class CQuery implements DbReadContext, CancelableQuery { */ private final BeanPropertyAssocMany manyProperty; - /** - * The many property Expression language object. - */ - private final ElPropertyValue manyPropertyEl; - private final int maxRowsLimit; private DataReader dataReader; @@ -186,7 +152,6 @@ public class CQuery implements DbReadContext, CancelableQuery { private final CQueryPlan queryPlan; - private final Mode queryMode; private final boolean autoFetchProfiling; @@ -197,17 +162,12 @@ public class CQuery implements DbReadContext, CancelableQuery { private final WeakReference autoFetchManagerRef; - private final Boolean readOnly; - private final SpiExpressionList filterMany; - private long startNano; private long executionTimeMicros; - private BeanCollectionAdd currentDetailAdd; - /** * Create the Sql select based on the request. */ @@ -233,19 +193,7 @@ public class CQuery implements DbReadContext, CancelableQuery { this.sqlTree = queryPlan.getSqlTree(); this.rootNode = sqlTree.getRootNode(); - this.manyProperty = sqlTree.getManyProperty(); - this.manyPropertyEl = sqlTree.getManyPropertyEl(); - this.manyIncluded = sqlTree.isManyIncluded(); - if (manyIncluded) { - // get filter to put on the collection for reuse with refresh - String manyPropertyName = sqlTree.getManyPropertyName(); - OrmQueryProperties chunk = query.getDetail().getChunk(manyPropertyName, false); - this.filterMany = (chunk == null) ? null : chunk.getFilterMany(); - } else { - this.filterMany = null; - } - this.sql = queryPlan.getSql(); this.rawSql = queryPlan.isRawSql(); this.rowNumberIncluded = queryPlan.isRowNumberIncluded(); @@ -414,58 +362,18 @@ public class CQuery implements DbReadContext, CancelableQuery { return request.getPersistenceContext(); } - public void setLoadedBean(EntityBean 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 - // with equals not being implemented + public void setLazyLoadedChildBean(EntityBean bean, Object lazyLoadParentId) { - } else { - if (manyIncluded) { - if (rowCount > 1) { - loadedBeanChanged = true; - } - 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 = (EntityBean)getPersistenceContext().get(lazyLoadManyProperty.getBeanDescriptor().getBeanType(), lazyLoadParentId); this.lazyLoadParentId = lazyLoadParentId; } - + // add the loadedBean to the appropriate collection of lazyLoadParentBean - lazyLoadManyProperty.addBeanToCollectionWithCreate(lazyLoadParentBean, loadedBean); + lazyLoadManyProperty.addBeanToCollectionWithCreate(lazyLoadParentBean, bean); } - } - } - - public void setLoadedManyBean(EntityBean manyValue) { - this.loadedManyBean = manyValue; - } - - /** - * Return the last read bean. - */ - public EntityBean getLoadedBean() { - if (manyIncluded) { - if (prevDetailCollection instanceof BeanCollection) { - ((BeanCollection) prevDetailCollection).setModifyListening(manyProperty.getModifyListenMode()); - - } else if (currentDetailCollection instanceof BeanCollection) { - ((BeanCollection) currentDetailCollection).setModifyListening(manyProperty.getModifyListenMode()); - } - } - - if (prevLoadedBean != null) { - return prevLoadedBean; - } else { - return loadedBean; - } } /** @@ -475,29 +383,78 @@ public class CQuery implements DbReadContext, CancelableQuery { * the one/master and the second the many/detail. *

*/ - private boolean readRow() throws SQLException { + private boolean readNextBean() throws SQLException { - synchronized (this) { - if (cancelled) { + if (!moveToNextRow()) { + if (currentBean == null) { return false; + } else { + // the last bean + nextBean = currentBean; + loadedBeanCount++; + return true; } + } - if (!dataReader.next()) { - return false; - } - - rowCount++; - dataReader.resetColumnPosition(); - - if (rowNumberIncluded) { - // row_number() column used for limit features - dataReader.incrementPos(1); - } - - rootNode.load(this, null); + loadedBeanCount++; + if (manyProperty == null) { + // only single resultSet row required to build object so we are done + // read a single resultSet row into single bean + nextBean = rootNode.load(this, null, null); return true; } + + if (nextBean == null) { + // very first read + nextBean = rootNode.load(this, null, null); + } else { + // nextBean set to previously read currentBean + nextBean = currentBean; + // check the current row we have just moved to + if (checkForDifferentBean()) { + return true; + } + } + readUntilDifferentBeanStarted(); + return true; + } + + /** + * Read resultSet rows until we hit the end or get a different bean. + */ + private void readUntilDifferentBeanStarted() throws SQLException { + while (moveToNextRow()) { + if (checkForDifferentBean()) return; + } + } + + /** + * Read the currentBean from the row data returning true if the bean + * is different to the nextBean (false if we need to read more rows). + */ + private boolean checkForDifferentBean() throws SQLException { + currentBean = rootNode.load(this, null, null); + return currentBean != nextBean; + } + + /** + * Return true if we can move to the next resultSet row. + */ + private boolean moveToNextRow() throws SQLException { + + if (!dataReader.next()) { + noMoreRows = true; + return false; + } + + rowCount++; + dataReader.resetColumnPosition(); + if (rowNumberIncluded) { + // row_number() column used for limit features + dataReader.incrementPos(1); + } + return true; } public long getQueryExecutionTimeMicros() { @@ -506,96 +463,33 @@ public class CQuery implements DbReadContext, CancelableQuery { public boolean readBean() throws SQLException { - boolean result = readBeanInternal(); - + boolean result = hasNext(); updateExecutionStatistics(); - return result; } - private boolean readBeanInternal() throws SQLException { + protected EntityBean next() { + return nextBean; + } - if (loadedBeanCount >= maxRowsLimit) { - return false; - } + protected boolean hasNext() throws SQLException { - if (!manyIncluded) { - // simple query... no details... - return readRow(); - } - - if (noMoreRows) { - return false; - } - - if (rowCount == 0) { - if (!readRow()) { - // no rows at all... + synchronized (this) { + if (noMoreRows || cancelled || loadedBeanCount >= maxRowsLimit) { return false; - } else { - createNewDetailCollection(); } - } - - if (readIntoCurrentDetailCollection()) { - createNewDetailCollection(); - // return prevLoadedBean - return true; - - } else { - // return loadedBean - prevDetailCollection = null; - prevLoadedBean = null; - noMoreRows = true; - return true; - } - } - - private boolean readIntoCurrentDetailCollection() throws SQLException { - while (readRow()) { - if (loadedBeanChanged) { - loadedBeanChanged = false; - return true; - } else { - addToCurrentDetailCollection(); - } - } - return false; - } - - private void createNewDetailCollection() { - prevDetailCollection = currentDetailCollection; - if (queryMode.equals(Mode.LAZYLOAD_MANY)) { - // just populate the current collection - currentDetailCollection = manyPropertyEl.elGetValue(loadedBean); - } else { - // create a new collection to populate and assign to the bean - currentDetailCollection = manyProperty.createEmpty(loadedBean); - manyPropertyEl.elSetValue(loadedBean, currentDetailCollection, false); - } - - if (filterMany != null) { - // remember the for use with a refresh - ((BeanCollection) currentDetailCollection).setFilterMany(filterMany); - } - - // the manyKey is always null for this case, just using default mapKey on the property - currentDetailAdd = manyProperty.getBeanCollectionAdd(currentDetailCollection, null); - addToCurrentDetailCollection(); - } - - private void addToCurrentDetailCollection() { - if (loadedManyBean != null) { - currentDetailAdd.addBean(loadedManyBean); + return readNextBean(); } } public BeanCollection readCollection() throws SQLException { - readTheRows(); + while (hasNext()) { + EntityBean bean = next(); + help.add(collection, bean); + } updateExecutionStatistics(); - return collection; } @@ -605,13 +499,12 @@ public class CQuery implements DbReadContext, CancelableQuery { executionTimeMicros = TimeUnit.NANOSECONDS.toMicros(exeNano); if (autoFetchProfiling) { - autoFetchManager - .collectQueryInfo(objectGraphNode, loadedBeanCount, executionTimeMicros); + autoFetchManager.collectQueryInfo(objectGraphNode, loadedBeanCount, executionTimeMicros); } queryPlan.executionTime(loadedBeanCount, executionTimeMicros, objectGraphNode); } catch (Exception e) { - logger.error(null, e); + logger.error("Error updating execution statistics", e); } } @@ -625,26 +518,8 @@ public class CQuery implements DbReadContext, CancelableQuery { } } - private void readTheRows() throws SQLException { - while (hasNextBean()) { - // add to the list/set/map - help.add(collection, getLoadedBean()); - } - } - - protected boolean hasNextBean() throws SQLException { - - if (!readBeanInternal()) { - return false; - - } else { - loadedBeanCount++; - return true; - } - } - public String getLoadedRowDetail() { - if (!manyIncluded) { + if (manyProperty == null) { return String.valueOf(rowCount); } else { return loadedBeanCount + ":" + rowCount; @@ -743,8 +618,7 @@ public class CQuery implements DbReadContext, CancelableQuery { if (t.isLogSummary()) { // log the error to the transaction log - String errMsg = StringHelper.replaceStringMulti(e.getMessage(), new String[] { "\r", "\n" }, - "\\n "); + String errMsg = StringHelper.replaceStringMulti(e.getMessage(), new String[] { "\r", "\n" }, "\\n "); String msg = "ERROR executing query: bindLog[" + bindLog + "] error[" + errMsg + "]"; t.logSummary(msg); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryEngine.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryEngine.java index e186546b9..a7262eeb8 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryEngine.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryEngine.java @@ -224,7 +224,7 @@ public class CQueryEngine { } if (cquery.readBean()) { - bean = cquery.getLoadedBean(); + bean = cquery.next(); } if (request.isLogSummary()) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryFetchIds.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryFetchIds.java index fd76b783f..f6ed21636 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryFetchIds.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryFetchIds.java @@ -270,14 +270,10 @@ public class CQueryFetchIds { // no-op } - public void setLoadedBean(EntityBean loadedBean, Object id, Object lazyLoadParentId) { + public void setLazyLoadedChildBean(EntityBean loadedBean, Object lazyLoadParentId) { // no-op } - public void setLoadedManyBean(EntityBean loadedBean) { - // no-op - } - } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryIteratorSimple.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryIteratorSimple.java index a0fea37c3..bf4b877f8 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryIteratorSimple.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryIteratorSimple.java @@ -23,7 +23,7 @@ class CQueryIteratorSimple implements QueryIterator { public boolean hasNext() { try { request.flushPersistenceContextOnIterate(); - return cquery.hasNextBean(); + return cquery.hasNext(); } catch (SQLException e) { throw cquery.createPersistenceException(e); } @@ -31,7 +31,7 @@ class CQueryIteratorSimple implements QueryIterator { @SuppressWarnings("unchecked") public T next() { - return (T)cquery.getLoadedBean(); + return (T)cquery.next(); } public void close() { diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryIteratorWithBuffer.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryIteratorWithBuffer.java index 7d39b14e5..673c0e2e0 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryIteratorWithBuffer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryIteratorWithBuffer.java @@ -36,8 +36,8 @@ class CQueryIteratorWithBuffer implements QueryIterator { int i = -1; while (moreToLoad && ++i < bufferSize) { - if (cquery.hasNextBean()) { - buffer.add((T)cquery.getLoadedBean()); + if (cquery.hasNext()) { + buffer.add((T)cquery.next()); } else { moreToLoad = false; } diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNode.java b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNode.java index 60ee2a566..55587b738 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNode.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNode.java @@ -9,31 +9,25 @@ import com.avaje.ebeaninternal.server.deploy.DbSqlContext; public interface SqlTreeNode { - public static final String PERIOD = "."; + String COMMA = ", "; - public static final String COMMA = ", "; - - public static final int NORMAL = 0; - public static final int SHARED = 1; - public static final int READONLY = 2; - - public void buildSelectExpressionChain(List selectChain); + void buildSelectExpressionChain(List selectChain); /** * Append the required column information to the SELECT part of the sql * statement. */ - public void appendSelect(DbSqlContext ctx, boolean subQuery); + void appendSelect(DbSqlContext ctx, boolean subQuery); /** * Append to the FROM part of the sql. */ - public void appendFrom(DbSqlContext ctx, SqlJoinType joinType); + void appendFrom(DbSqlContext ctx, SqlJoinType joinType); /** * Append any where predicates for inheritance. */ - public void appendWhere(DbSqlContext ctx); + void appendWhere(DbSqlContext ctx); /** * Load the appropriate information from the SqlSelectReader. @@ -43,6 +37,6 @@ public interface SqlTreeNode { *

* */ - public void load(DbReadContext ctx, EntityBean parentBean) throws SQLException; + EntityBean load(DbReadContext ctx, EntityBean localBean, EntityBean contextBean) throws SQLException; } diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeBean.java b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeBean.java index f81ea6ef2..f464e3050 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeBean.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeBean.java @@ -69,7 +69,9 @@ public class SqlTreeNodeBean implements SqlTreeNode { protected final Map pathMap; protected final BeanPropertyAssocMany lazyLoadParent; - + + private final IdBinder lazyLoadParentIdBinder; + public SqlTreeNodeBean(String prefix, BeanPropertyAssoc beanProp, SqlTreeProperties props, List myChildren, boolean withId) { @@ -83,6 +85,7 @@ public class SqlTreeNodeBean implements SqlTreeNode { SqlTreeProperties props, List myChildren, boolean withId, BeanPropertyAssocMany lazyLoadParent) { this.lazyLoadParent = lazyLoadParent; + this.lazyLoadParentIdBinder = (lazyLoadParent == null) ? null : lazyLoadParent.getBeanDescriptor().getIdBinder(); this.prefix = prefix; this.nodeBeanProp = beanProp; this.desc = desc; @@ -134,9 +137,6 @@ public class SqlTreeNodeBean implements SqlTreeNode { } } - protected void postLoad(DbReadContext cquery, EntityBean loadedBean, Object id, Object lazyLoadParentId) { - } - public void buildSelectExpressionChain(List selectChain) { if (readId) { idBinder.buildSelectExpressionChain(prefix, selectChain); @@ -155,11 +155,11 @@ public class SqlTreeNodeBean implements SqlTreeNode { /** * read the properties from the resultSet. */ - public void load(DbReadContext ctx, EntityBean parentBean) throws SQLException { + public EntityBean load(DbReadContext ctx, EntityBean parentBean, EntityBean contextParent) throws SQLException { Object lazyLoadParentId = null; - if (lazyLoadParent != null) { - lazyLoadParentId = lazyLoadParent.getBeanDescriptor().getIdBinder().read(ctx); + if (lazyLoadParentIdBinder != null) { + lazyLoadParentId = lazyLoadParentIdBinder.read(ctx); } // bean already existing in the persistence context @@ -196,12 +196,8 @@ public class SqlTreeNodeBean implements SqlTreeNode { PersistenceContext persistenceContext = !readId ? null : ctx.getPersistenceContext(); - Object id = null; - if (!readId) { - // report type bean... or perhaps excluding the id for SqlSelect? - - } else { - id = localIdBinder.readSet(ctx, localBean); + if (readId) { + Object id = localIdBinder.readSet(ctx, localBean); if (id == null) { // bean must be null... localBean = null; @@ -265,7 +261,7 @@ public class SqlTreeNodeBean implements SqlTreeNode { for (int i = 0; i < children.length; i++) { // read each child... and let them set their // values back to this localBean - children[i].load(ctx, localBean); + children[i].load(ctx, localBean, contextBean); } if (lazyLoadMany) { @@ -279,36 +275,33 @@ public class SqlTreeNodeBean implements SqlTreeNode { } localDesc.postLoad(localBean, null); - if (localBean instanceof EntityBean) { - EntityBeanIntercept ebi = ((EntityBean) localBean)._ebean_getIntercept(); - ebi.setPersistenceContext(persistenceContext); - if (Mode.LAZYLOAD_BEAN.equals(queryMode)) { - // Lazy Load does not reset the dirty state - ebi.setLoadedLazy(); - } else { - // normal bean loading - ebi.setLoaded(); - } - - if (partialObject) { - if (readId) { - // register for lazy loading - ctx.register(null, ebi); - } - } else { - ebi.setFullyLoadedBean(true); - } - - if (disableLazyLoad) { - // bean does not have an Id or is SqlSelect based - ebi.setDisableLazyLoad(true); - } - if (ctx.isAutoFetchProfiling()) { - // collect autofetch profiling for this bean... - ctx.profileBean(ebi, prefix); - } + EntityBeanIntercept ebi = localBean._ebean_getIntercept(); + ebi.setPersistenceContext(persistenceContext); + if (Mode.LAZYLOAD_BEAN.equals(queryMode)) { + // Lazy Load does not reset the dirty state + ebi.setLoadedLazy(); + } else { + // normal bean loading + ebi.setLoaded(); } + if (partialObject) { + if (readId) { + // register for lazy loading + ctx.register(null, ebi); + } + } else { + ebi.setFullyLoadedBean(true); + } + + if (disableLazyLoad) { + // bean does not have an Id or is SqlSelect based + ebi.setDisableLazyLoad(true); + } + if (ctx.isAutoFetchProfiling()) { + // collect autofetch profiling for this bean... + ctx.profileBean(ebi, prefix); + } } if (parentBean != null) { // set this back to the parentBean @@ -317,13 +310,13 @@ public class SqlTreeNodeBean implements SqlTreeNode { if (!readId) { // a bean with no Id (never found in context) - postLoad(ctx, localBean, id, null); + return localBean; } 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, lazyLoadParentId); + if (lazyLoadParentId != null) { + ctx.setLazyLoadedChildBean(contextBean, lazyLoadParentId); + } + return contextBean; } } @@ -339,10 +332,7 @@ public class SqlTreeNodeBean implements SqlTreeNode { BeanPropertyAssocMany[] manys = localDesc.propertiesMany(); for (int i = 0; i < manys.length; i++) { - if (fetchedMany != null && fetchedMany.equals(manys[i])) { - // this many property is included in the query... - // it is being loaded with real row data (result[1]) - } else { + if (fetchedMany == null || !fetchedMany.equals(manys[i])) { // create a proxy for the many (deferred fetching) BeanCollection ref = manys[i].createReferenceIfNull(localBean); if (ref != null && !ref.isRegisteredWithLoadContext()) { @@ -418,13 +408,9 @@ public class SqlTreeNodeBean implements SqlTreeNode { public void appendWhere(DbSqlContext ctx) { - // Only apply inheritance to root node as any join will alreay have the inheritance join include - see TableJoin - if (inheritInfo != null && nodeBeanProp == null) { - if (inheritInfo.isRoot()) { - // at root of hierarchy so don't bother - // adding a where clause because we want - // all the types... - } else { + // Only apply inheritance to root node as any join will alreay have the inheritance join include - see TableJoin + if (inheritInfo != null && nodeBeanProp == null) { + if (!inheritInfo.isRoot()) { // restrict to this type and // sub types of this type. if (ctx.length() > 0) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeExtraJoin.java b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeExtraJoin.java index 573586329..add74e788 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeExtraJoin.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeExtraJoin.java @@ -20,7 +20,6 @@ import com.avaje.ebeaninternal.server.deploy.TableJoin; *

*/ public class SqlTreeNodeExtraJoin implements SqlTreeNode { - private final BeanPropertyAssoc assocBeanProperty; @@ -35,16 +34,12 @@ public class SqlTreeNodeExtraJoin implements SqlTreeNode { this.assocBeanProperty = assocBeanProperty; this.manyJoin = assocBeanProperty instanceof BeanPropertyAssocMany; } - - - + public void buildSelectExpressionChain(List selectChain) { // nothing to add } - - - /** + /** * Return true if the extra join is a many join. *

* This means we need to add distinct to the sql query. @@ -95,7 +90,7 @@ public class SqlTreeNodeExtraJoin implements SqlTreeNode { if (children != null) { if (manyJoin) { - // if AUTO then make all decendants use OUTER JOIN + // if AUTO then make all descendants use OUTER JOIN joinType = joinType.autoToOuter(); } @@ -121,7 +116,8 @@ public class SqlTreeNodeExtraJoin implements SqlTreeNode { /** * Does nothing. */ - public void load(DbReadContext ctx, EntityBean parentBean) throws SQLException { + public EntityBean load(DbReadContext ctx, EntityBean localBean, EntityBean parentBean) throws SQLException { + return null; } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeManyRoot.java b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeManyRoot.java index c875e80aa..ca19f16bd 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeManyRoot.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeManyRoot.java @@ -10,24 +10,22 @@ import com.avaje.ebeaninternal.server.deploy.DbSqlContext; public final class SqlTreeNodeManyRoot extends SqlTreeNodeBean { + final BeanPropertyAssocMany manyProp; + public SqlTreeNodeManyRoot(String prefix, BeanPropertyAssocMany prop, SqlTreeProperties props, List myList) { super(prefix, prop, prop.getTargetDescriptor(), props, myList, true, null); + this.manyProp = prop; } @Override - protected void postLoad(DbReadContext cquery, EntityBean loadedBean, Object id, Object lazyLoadParentId) { - - // put the localBean into the manyValue so that it - // is added to the collection/map - cquery.setLoadedManyBean(loadedBean); - } - - @Override - public void load(DbReadContext cquery, EntityBean parentBean) throws SQLException { + public EntityBean load(DbReadContext cquery, EntityBean parentBean, EntityBean contextParent) throws SQLException { // pass in null for parentBean because the localBean // that is built is added to a collection rather than // being set to the parentBean directly - super.load(cquery, null); + EntityBean detailBean = super.load(cquery, null, null); + // initialise the collection and add detailBean if it is not null + manyProp.addBeanToCollectionWithCreate(contextParent, detailBean); + return detailBean; } /** diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeManyWhereJoin.java b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeManyWhereJoin.java index 4c54619ee..9a84dc546 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeManyWhereJoin.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeManyWhereJoin.java @@ -100,8 +100,9 @@ public class SqlTreeNodeManyWhereJoin implements SqlTreeNode { // nothing to do here } - public void load(DbReadContext ctx, EntityBean parentBean) throws SQLException { + public EntityBean load(DbReadContext ctx, EntityBean localBean, EntityBean parentBean) throws SQLException { // nothing to do here + return null; } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeRoot.java b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeRoot.java index 83344b06b..86a3e7fac 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeRoot.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeRoot.java @@ -28,14 +28,7 @@ public final class SqlTreeNodeRoot extends SqlTreeNodeBean { super(null, null, desc, props, myList, withId, null); this.includeJoin = null; } - - @Override - protected void postLoad(DbReadContext cquery, EntityBean loadedBean, Object id, Object lazyLoadParentId) { - - // set the current bean with id... - cquery.setLoadedBean(loadedBean, id, lazyLoadParentId); - } - + /** * For the root node there is no join type or on clause etc. */