Fix for #293 - Invalid result rows when ManyToMany "fetch joined" with additional child OneToMany or ManyToMany child relationship fetched

This commit is contained in:
rbygrave
2015-05-13 21:01:00 +12:00
parent 0da764ed0c
commit 9bf9338ba9
16 changed files with 186 additions and 337 deletions
@@ -69,7 +69,9 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
if (list == null) {
list = new ArrayList<E>();
}
list.add((E) bean);
if (bean != null) {
list.add((E) bean);
}
}
public boolean checkEmptyLazyLoad() {
@@ -52,12 +52,20 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
return !touched && (map == null || map.isEmpty());
}
@SuppressWarnings("unchecked")
public void internalPutNull() {
if (map == null) {
map = new LinkedHashMap<K, E>();
}
}
@SuppressWarnings("unchecked")
public void internalPut(Object key, Object bean) {
if (map == null) {
map = new LinkedHashMap<K, E>();
}
map.put((K)key, (E)bean);
if (key != null) {
map.put((K) key, (E) bean);
}
}
public void internalAdd(Object bean) {
@@ -62,7 +62,9 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
if (set == null) {
set = new LinkedHashSet<E>();
}
set.add((E) bean);
if (bean != null) {
set.add((E) bean);
}
}
/**
@@ -109,8 +109,12 @@ public final class BeanMapHelp<T> implements BeanCollectionHelp<T> {
@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
@@ -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.
*/
@@ -60,46 +60,22 @@ public class CQuery<T> 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<T> 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<T> 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<T> implements DbReadContext, CancelableQuery {
private final CQueryPlan queryPlan;
private final Mode queryMode;
private final boolean autoFetchProfiling;
@@ -197,17 +162,12 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
private final WeakReference<NodeUsageListener> 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<T> 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<T> 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<T> implements DbReadContext, CancelableQuery {
* the one/master and the second the many/detail.
* </p>
*/
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<T> 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<T> readCollection() throws SQLException {
readTheRows();
while (hasNext()) {
EntityBean bean = next();
help.add(collection, bean);
}
updateExecutionStatistics();
return collection;
}
@@ -605,13 +499,12 @@ public class CQuery<T> 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<T> 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<T> 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);
}
@@ -224,7 +224,7 @@ public class CQueryEngine {
}
if (cquery.readBean()) {
bean = cquery.getLoadedBean();
bean = cquery.next();
}
if (request.isLogSummary()) {
@@ -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
}
}
}
@@ -23,7 +23,7 @@ class CQueryIteratorSimple<T> implements QueryIterator<T> {
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<T> implements QueryIterator<T> {
@SuppressWarnings("unchecked")
public T next() {
return (T)cquery.getLoadedBean();
return (T)cquery.next();
}
public void close() {
@@ -36,8 +36,8 @@ class CQueryIteratorWithBuffer<T> implements QueryIterator<T> {
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;
}
@@ -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<String> selectChain);
void buildSelectExpressionChain(List<String> 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 {
* </p>
*
*/
public void load(DbReadContext ctx, EntityBean parentBean) throws SQLException;
EntityBean load(DbReadContext ctx, EntityBean localBean, EntityBean contextBean) throws SQLException;
}
@@ -69,7 +69,9 @@ public class SqlTreeNodeBean implements SqlTreeNode {
protected final Map<String, String> pathMap;
protected final BeanPropertyAssocMany<?> lazyLoadParent;
private final IdBinder lazyLoadParentIdBinder;
public SqlTreeNodeBean(String prefix, BeanPropertyAssoc<?> beanProp, SqlTreeProperties props,
List<SqlTreeNode> myChildren, boolean withId) {
@@ -83,6 +85,7 @@ public class SqlTreeNodeBean implements SqlTreeNode {
SqlTreeProperties props, List<SqlTreeNode> 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<String> 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) {
@@ -20,7 +20,6 @@ import com.avaje.ebeaninternal.server.deploy.TableJoin;
* </p>
*/
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<String> selectChain) {
// nothing to add
}
/**
/**
* Return true if the extra join is a many join.
* <p>
* 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;
}
}
@@ -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<SqlTreeNode> 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;
}
/**
@@ -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;
}
}
@@ -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.
*/