mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Fix for #293 - Invalid result rows when ManyToMany "fetch joined" with additional child OneToMany or ManyToMany child relationship fetched
This commit is contained in:
@@ -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.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user