From 9bf9338ba9a358375f2dfd9f5fb1f4d7a3660998 Mon Sep 17 00:00:00 2001
From: rbygrave
Date: Wed, 13 May 2015 21:01:00 +1200
Subject: [PATCH] Fix for #293 - Invalid result rows when ManyToMany "fetch
joined" with additional child OneToMany or ManyToMany child relationship
fetched
---
.../java/com/avaje/ebean/common/BeanList.java | 4 +-
.../java/com/avaje/ebean/common/BeanMap.java | 12 +-
.../java/com/avaje/ebean/common/BeanSet.java | 4 +-
.../server/deploy/BeanMapHelp.java | 8 +-
.../server/deploy/DbReadContext.java | 7 +-
.../ebeaninternal/server/query/CQuery.java | 308 ++++++------------
.../server/query/CQueryEngine.java | 2 +-
.../server/query/CQueryFetchIds.java | 6 +-
.../server/query/CQueryIteratorSimple.java | 4 +-
.../query/CQueryIteratorWithBuffer.java | 4 +-
.../server/query/SqlTreeNode.java | 18 +-
.../server/query/SqlTreeNodeBean.java | 102 +++---
.../server/query/SqlTreeNodeExtraJoin.java | 14 +-
.../server/query/SqlTreeNodeManyRoot.java | 18 +-
.../query/SqlTreeNodeManyWhereJoin.java | 3 +-
.../server/query/SqlTreeNodeRoot.java | 9 +-
16 files changed, 186 insertions(+), 337 deletions(-)
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.
*/