mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Fix for #37 - disjunction expression should not produce inner join - left outer join instead
This commit is contained in:
@@ -2,8 +2,8 @@ package com.avaje.ebeaninternal.api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.avaje.ebean.Transaction;
|
||||
import com.avaje.ebean.bean.EntityBeanIntercept;
|
||||
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
|
||||
|
||||
/**
|
||||
* Request for loading ManyToOne and OneToOne relationships.
|
||||
@@ -18,10 +18,12 @@ public class LoadBeanRequest extends LoadRequest {
|
||||
|
||||
private final boolean loadCache;
|
||||
|
||||
public LoadBeanRequest(LoadBeanBuffer LoadBuffer, Transaction transaction, boolean lazy, String lazyLoadProperty,
|
||||
boolean loadCache) {
|
||||
|
||||
super(transaction, lazy);
|
||||
public LoadBeanRequest(LoadBeanBuffer LoadBuffer, boolean lazy, String lazyLoadProperty, boolean loadCache) {
|
||||
this(LoadBuffer, null, lazy, lazyLoadProperty, loadCache);
|
||||
}
|
||||
|
||||
public LoadBeanRequest(LoadBeanBuffer LoadBuffer, OrmQueryRequest<?> parentRequest, boolean lazy, String lazyLoadProperty, boolean loadCache) {
|
||||
super(parentRequest, lazy);
|
||||
this.LoadBuffer = LoadBuffer;
|
||||
this.batch = LoadBuffer.getBatch();
|
||||
this.lazyLoadProperty = lazyLoadProperty;
|
||||
|
||||
@@ -2,69 +2,71 @@ package com.avaje.ebeaninternal.api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.avaje.ebean.Transaction;
|
||||
import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
|
||||
|
||||
/**
|
||||
* Request for loading Associated One Beans.
|
||||
* Request for loading Associated Many Beans.
|
||||
*/
|
||||
public class LoadManyRequest extends LoadRequest {
|
||||
|
||||
private final List<BeanCollection<?>> batch;
|
||||
|
||||
private final List<BeanCollection<?>> batch;
|
||||
private final LoadManyBuffer loadContext;
|
||||
|
||||
private final LoadManyBuffer loadContext;
|
||||
private final boolean onlyIds;
|
||||
|
||||
private final boolean onlyIds;
|
||||
|
||||
private final boolean loadCache;
|
||||
|
||||
public LoadManyRequest(LoadManyBuffer loadContext, Transaction transaction, int batchSize, boolean lazy,
|
||||
private final boolean loadCache;
|
||||
|
||||
public LoadManyRequest(LoadManyBuffer loadContext, int batchSize, boolean lazy,boolean onlyIds, boolean loadCache) {
|
||||
this(loadContext, null, batchSize, lazy, onlyIds, loadCache);
|
||||
}
|
||||
|
||||
public LoadManyRequest(LoadManyBuffer loadContext, OrmQueryRequest<?> parentRequest, int batchSize, boolean lazy,
|
||||
boolean onlyIds, boolean loadCache) {
|
||||
|
||||
super(transaction, lazy);
|
||||
this.loadContext = loadContext;
|
||||
this.batch = loadContext.getBatch();
|
||||
this.onlyIds = onlyIds;
|
||||
this.loadCache = loadCache;
|
||||
}
|
||||
super(parentRequest, lazy);
|
||||
this.loadContext = loadContext;
|
||||
this.batch = loadContext.getBatch();
|
||||
this.onlyIds = onlyIds;
|
||||
this.loadCache = loadCache;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return "path:" + loadContext.getFullPath() + " size:"+ batch.size();
|
||||
}
|
||||
public String getDescription() {
|
||||
return "path:" + loadContext.getFullPath() + " size:" + batch.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the batch of collections to actually load.
|
||||
*/
|
||||
public List<BeanCollection<?>> getBatch() {
|
||||
return batch;
|
||||
}
|
||||
/**
|
||||
* Return the batch of collections to actually load.
|
||||
*/
|
||||
public List<BeanCollection<?>> getBatch() {
|
||||
return batch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the load context.
|
||||
*/
|
||||
public LoadManyBuffer getLoadContext() {
|
||||
return loadContext;
|
||||
}
|
||||
/**
|
||||
* Return the load context.
|
||||
*/
|
||||
public LoadManyBuffer getLoadContext() {
|
||||
return loadContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if lazy loading should only load the id values.
|
||||
* <p>
|
||||
* This for use when lazy loading is invoked on methods such
|
||||
* as clear() and removeAll() where it generally makes sense to
|
||||
* only fetch the Id values as the other property information is
|
||||
* not used.
|
||||
* </p>
|
||||
*/
|
||||
public boolean isOnlyIds() {
|
||||
return onlyIds;
|
||||
}
|
||||
/**
|
||||
* Return true if lazy loading should only load the id values.
|
||||
* <p>
|
||||
* This for use when lazy loading is invoked on methods such as clear() and removeAll() where it
|
||||
* generally makes sense to only fetch the Id values as the other property information is not
|
||||
* used.
|
||||
* </p>
|
||||
*/
|
||||
public boolean isOnlyIds() {
|
||||
return onlyIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if we should load the Collection ids into the cache.
|
||||
*/
|
||||
public boolean isLoadCache() {
|
||||
return loadCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if we should load the Collection ids into the cache.
|
||||
*/
|
||||
public boolean isLoadCache() {
|
||||
return loadCache;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,22 +1,36 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import com.avaje.ebean.Transaction;
|
||||
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
|
||||
|
||||
/**
|
||||
* Request for loading Associated One Beans.
|
||||
*/
|
||||
public abstract class LoadRequest {
|
||||
|
||||
protected final boolean lazy;
|
||||
protected final OrmQueryRequest<?> parentRequest;
|
||||
|
||||
protected final Transaction transaction;
|
||||
protected final Transaction transaction;
|
||||
|
||||
public LoadRequest(Transaction transaction, boolean lazy) {
|
||||
protected final boolean lazy;
|
||||
|
||||
this.transaction = transaction;
|
||||
public LoadRequest(OrmQueryRequest<?> parentRequest, boolean lazy) {
|
||||
|
||||
this.parentRequest = parentRequest;
|
||||
this.transaction = parentRequest == null ? null : parentRequest.getTransaction();
|
||||
this.lazy = lazy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the just executed secondary query with the 'root' query if 'logSecondaryQuery' is set to
|
||||
* true. This is for testing purposes to confirm the secondary query executes etc.
|
||||
*/
|
||||
public void logSecondaryQuery(SpiQuery<?> query) {
|
||||
if (parentRequest != null && parentRequest.isLogSecondaryQuery()) {
|
||||
parentRequest.getQuery().logSecondaryQuery(query);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is a lazy load and false if it is a secondary query.
|
||||
*/
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Set;
|
||||
import java.util.Collection;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyDeploy;
|
||||
import com.avaje.ebeaninternal.server.query.SplitName;
|
||||
import com.avaje.ebeaninternal.server.query.SqlJoinType;
|
||||
|
||||
/**
|
||||
* Holds the joins needs to support the many where predicates.
|
||||
@@ -17,12 +19,32 @@ public class ManyWhereJoins implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -6490181101871795417L;
|
||||
|
||||
private final TreeSet<String> joins = new TreeSet<String>();
|
||||
private final TreeMap<String,PropertyJoin> joins = new TreeMap<String,PropertyJoin>();
|
||||
|
||||
private StringBuilder formulaProperties = new StringBuilder();
|
||||
|
||||
private boolean formulaWithJoin;
|
||||
|
||||
/**
|
||||
* 'Mode' indicating that joins added while this is true are required to be outer joins.
|
||||
*/
|
||||
private boolean requireOuterJoins;
|
||||
|
||||
/**
|
||||
* Return the current 'mode' indicating if outer joins are currently required or not.
|
||||
*/
|
||||
public boolean isRequireOuterJoins() {
|
||||
return requireOuterJoins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the 'mode' to be that joins added are required to be outer joins.
|
||||
* This is set during the evaluation of disjunction predicates.
|
||||
*/
|
||||
public void setRequireOuterJoins(boolean requireOuterJoins) {
|
||||
this.requireOuterJoins = requireOuterJoins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a many where join.
|
||||
*/
|
||||
@@ -34,10 +56,10 @@ public class ManyWhereJoins implements Serializable {
|
||||
join = addManyToJoin(join, p.getName());
|
||||
}
|
||||
if (join != null){
|
||||
joins.add(join);
|
||||
addJoin(join);
|
||||
String secondaryTableJoinPrefix = p.getSecondaryTableJoinPrefix();
|
||||
if (secondaryTableJoinPrefix != null) {
|
||||
joins.add(join+"."+secondaryTableJoinPrefix);
|
||||
addJoin(join+"."+secondaryTableJoinPrefix);
|
||||
}
|
||||
addParentJoins(join);
|
||||
}
|
||||
@@ -58,11 +80,16 @@ public class ManyWhereJoins implements Serializable {
|
||||
private void addParentJoins(String join) {
|
||||
String[] split = SplitName.split(join);
|
||||
if (split[0] != null){
|
||||
joins.add(split[0]);
|
||||
addJoin(split[0]);
|
||||
addParentJoins(split[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private void addJoin(String property) {
|
||||
SqlJoinType joinType = (requireOuterJoins) ? SqlJoinType.OUTER: SqlJoinType.INNER;
|
||||
joins.put(property, new PropertyJoin(property, joinType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there are no extra many where joins.
|
||||
*/
|
||||
@@ -73,10 +100,22 @@ public class ManyWhereJoins implements Serializable {
|
||||
/**
|
||||
* Return the set of many where joins.
|
||||
*/
|
||||
public Set<String> getJoins() {
|
||||
return joins;
|
||||
public Collection<PropertyJoin> getPropertyJoins() {
|
||||
return joins.values();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the set of property names for the many where joins.
|
||||
*/
|
||||
public TreeSet<String> getPropertyNames() {
|
||||
|
||||
TreeSet<String> propertyNames = new TreeSet<String>();
|
||||
for (PropertyJoin join : joins.values()) {
|
||||
propertyNames.add(join.getProperty());
|
||||
}
|
||||
return propertyNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* In findRowCount query found a formula property with a join clause so building a select clause
|
||||
* specifically for the findRowCount query.
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import com.avaje.ebeaninternal.server.query.SqlJoinType;
|
||||
|
||||
/**
|
||||
* Represents a join required for a given property and whether than needs to be an outer join.
|
||||
*/
|
||||
public class PropertyJoin {
|
||||
|
||||
/**
|
||||
* The property name.
|
||||
*/
|
||||
private final String property;
|
||||
|
||||
/**
|
||||
* Set to true if the property needs to be an outer join.
|
||||
*/
|
||||
private final SqlJoinType joinType;
|
||||
|
||||
public PropertyJoin(String property, SqlJoinType joinType) {
|
||||
this.property = property;
|
||||
this.joinType = joinType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the property that should be joined.
|
||||
*/
|
||||
public String getProperty() {
|
||||
return property;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this join is required to be an outer join.
|
||||
*/
|
||||
public SqlJoinType getSqlJoinType() {
|
||||
return joinType;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,13 +10,12 @@ import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
*/
|
||||
public interface SpiExpression extends Expression {
|
||||
|
||||
/**
|
||||
* Process "Many" properties populating ManyWhereJoins.
|
||||
* <p>
|
||||
* Predicates on Many properties require an extra independent
|
||||
* join clause.
|
||||
* </p>
|
||||
*/
|
||||
/**
|
||||
* Process "Many" properties populating ManyWhereJoins.
|
||||
* <p>
|
||||
* Predicates on Many properties require an extra independent join clause.
|
||||
* </p>
|
||||
*/
|
||||
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins whereManyJoins);
|
||||
|
||||
/**
|
||||
|
||||
@@ -260,10 +260,25 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
*/
|
||||
public Boolean isAutofetch();
|
||||
|
||||
// /**
|
||||
// * Return explicit forUpdate setting or null.
|
||||
// */
|
||||
// public boolean isForUpdate();
|
||||
/**
|
||||
* Set to true if you want to capture executed secondary queries.
|
||||
*/
|
||||
public void setLogSecondaryQuery(boolean logSecondaryQuery);
|
||||
|
||||
/**
|
||||
* Return true if executed secondary queries should be captured.
|
||||
*/
|
||||
public boolean isLogSecondaryQuery();
|
||||
|
||||
/**
|
||||
* Return the list of secondary queries that were executed.
|
||||
*/
|
||||
public List<SpiQuery<?>> getLoggedSecondaryQueries();
|
||||
|
||||
/**
|
||||
* Log an executed secondary query.
|
||||
*/
|
||||
public void logSecondaryQuery(SpiQuery<?> query);
|
||||
|
||||
/**
|
||||
* If return null then no autoFetch profiling for this query. If a
|
||||
|
||||
@@ -140,6 +140,9 @@ public class DefaultBeanLoader {
|
||||
desc.cacheManyPropPut(many, bc, parentId);
|
||||
}
|
||||
}
|
||||
|
||||
// log the query (for testing secondary queries)
|
||||
loadRequest.logSecondaryQuery(query);
|
||||
}
|
||||
|
||||
public void loadMany(BeanCollection<?> bc, boolean onlyIds) {
|
||||
@@ -322,6 +325,9 @@ public class DefaultBeanLoader {
|
||||
// necessary but allow processing to continue until it is accessed by client code
|
||||
ebis[i].checkLazyLoadFailure();
|
||||
}
|
||||
|
||||
// log the query (for testing secondary queries)
|
||||
loadRequest.logSecondaryQuery(query);
|
||||
}
|
||||
|
||||
public void refresh(EntityBean bean) {
|
||||
|
||||
@@ -54,7 +54,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
private HashQuery cacheKey;
|
||||
|
||||
private HashQueryPlan queryPlanHash;
|
||||
|
||||
|
||||
/**
|
||||
* Create the InternalQueryRequest.
|
||||
*/
|
||||
@@ -363,4 +363,11 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
beanDescriptor.flushPersistenceContextOnIterate(persistenceContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the request wants to log the secondary queries (test purpose).
|
||||
*/
|
||||
public boolean isLogSecondaryQuery() {
|
||||
return query.isLogSecondaryQuery();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
|
||||
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
|
||||
import com.avaje.ebeaninternal.server.query.SqlBeanLoad;
|
||||
import com.avaje.ebeaninternal.server.query.SqlJoinType;
|
||||
import com.avaje.ebeaninternal.server.reflect.BeanReflectGetter;
|
||||
import com.avaje.ebeaninternal.server.reflect.BeanReflectSetter;
|
||||
import com.avaje.ebeaninternal.server.text.json.ReadJsonContext;
|
||||
@@ -509,14 +510,14 @@ public class BeanProperty implements ElPropertyValue {
|
||||
* Add any extra joins required to support this property. Generally a no
|
||||
* operation except for a OneToOne exported.
|
||||
*/
|
||||
public void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) {
|
||||
public void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
|
||||
if (formula && sqlFormulaJoin != null) {
|
||||
ctx.appendFormulaJoin(sqlFormulaJoin, forceOuterJoin);
|
||||
ctx.appendFormulaJoin(sqlFormulaJoin, joinType);
|
||||
|
||||
} else if (secondaryTableJoin != null) {
|
||||
|
||||
String relativePrefix = ctx.getRelativePrefix(secondaryTableJoinPrefix);
|
||||
secondaryTableJoin.addJoin(forceOuterJoin, relativePrefix, ctx);
|
||||
secondaryTableJoin.addJoin(joinType, relativePrefix, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import com.avaje.ebeaninternal.server.deploy.id.ImportedIdSimple;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
|
||||
import com.avaje.ebeaninternal.server.query.SqlJoinType;
|
||||
|
||||
/**
|
||||
* Abstract base for properties mapped to an associated bean, list, set or map.
|
||||
@@ -129,22 +130,15 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
|
||||
/**
|
||||
* Add table join with table alias based on prefix.
|
||||
*/
|
||||
public boolean addJoin(boolean forceOuterJoin, String prefix, DbSqlContext ctx) {
|
||||
return tableJoin.addJoin(forceOuterJoin, prefix, ctx);
|
||||
public SqlJoinType addJoin(SqlJoinType joinType, String prefix, DbSqlContext ctx) {
|
||||
return tableJoin.addJoin(joinType, prefix, ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add table join with explicit table alias.
|
||||
*/
|
||||
public boolean addJoin(boolean forceOuterJoin, String a1, String a2, DbSqlContext ctx) {
|
||||
return tableJoin.addJoin(forceOuterJoin, a1, a2, ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add table join with explicit table alias.
|
||||
*/
|
||||
public void addInnerJoin(String a1, String a2, DbSqlContext ctx) {
|
||||
tableJoin.addInnerJoin(a1, a2, ctx);
|
||||
public SqlJoinType addJoin(SqlJoinType joinType, String a1, String a2, DbSqlContext ctx) {
|
||||
return tableJoin.addJoin(joinType, a1, a2, ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
|
||||
import com.avaje.ebeaninternal.server.query.SplitName;
|
||||
import com.avaje.ebeaninternal.server.query.SqlBeanLoad;
|
||||
import com.avaje.ebeaninternal.server.query.SqlJoinType;
|
||||
import com.avaje.ebeaninternal.server.text.json.ReadJsonContext;
|
||||
import com.avaje.ebeaninternal.server.text.json.WriteJsonContext;
|
||||
|
||||
@@ -508,9 +509,9 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) {
|
||||
public void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
|
||||
if (!isTransient) {
|
||||
localHelp.appendFrom(ctx, forceOuterJoin);
|
||||
localHelp.appendFrom(ctx, joinType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -587,7 +588,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
|
||||
abstract void appendSelect(DbSqlContext ctx, boolean subQuery);
|
||||
|
||||
abstract void appendFrom(DbSqlContext ctx, boolean forceOuterJoin);
|
||||
abstract void appendFrom(DbSqlContext ctx, SqlJoinType joinType);
|
||||
|
||||
}
|
||||
|
||||
@@ -633,7 +634,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) {
|
||||
void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -729,11 +730,11 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) {
|
||||
void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
|
||||
if (targetInheritInfo != null) {
|
||||
// add join to support the discriminator column
|
||||
String relativePrefix = ctx.getRelativePrefix(name);
|
||||
tableJoin.addJoin(forceOuterJoin, relativePrefix, ctx);
|
||||
tableJoin.addJoin(joinType, relativePrefix, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -822,10 +823,10 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) {
|
||||
void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
|
||||
|
||||
String relativePrefix = ctx.getRelativePrefix(getName());
|
||||
tableJoin.addJoin(forceOuterJoin, relativePrefix, ctx);
|
||||
tableJoin.addJoin(joinType, relativePrefix, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.avaje.ebeaninternal.server.deploy;
|
||||
|
||||
import com.avaje.ebeaninternal.server.query.SqlJoinType;
|
||||
|
||||
/**
|
||||
* Used to provide context during sql construction.
|
||||
*/
|
||||
@@ -72,7 +74,7 @@ public interface DbSqlContext {
|
||||
* Append a Sql Formula join. This converts the "${ta}" keyword to the current
|
||||
* table alias.
|
||||
*/
|
||||
public void appendFormulaJoin(String sqlFormulaJoin, boolean forceOuterJoin);
|
||||
public void appendFormulaJoin(String sqlFormulaJoin, SqlJoinType joinType);
|
||||
|
||||
/**
|
||||
* Return the current content length.
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoin;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoinColumn;
|
||||
import com.avaje.ebeaninternal.server.query.SplitName;
|
||||
import com.avaje.ebeaninternal.server.query.SqlBeanLoad;
|
||||
import com.avaje.ebeaninternal.server.query.SqlJoinType;
|
||||
|
||||
/**
|
||||
* Represents a join to another table.
|
||||
@@ -32,9 +33,9 @@ public final class TableJoin {
|
||||
private final String table;
|
||||
|
||||
/**
|
||||
* The type of join. LEFT OUTER etc.
|
||||
* The type of join as per deployment (cardinality and optionality).
|
||||
*/
|
||||
private final String type;
|
||||
private final SqlJoinType type;
|
||||
|
||||
/**
|
||||
* The persist cascade info.
|
||||
@@ -60,7 +61,7 @@ public final class TableJoin {
|
||||
|
||||
this.importedPrimaryKey = deploy.isImportedPrimaryKey();
|
||||
this.table = InternString.intern(deploy.getTable());
|
||||
this.type = InternString.intern(deploy.getType());
|
||||
this.type = deploy.getType();
|
||||
this.cascadeInfo = deploy.getCascadeInfo();
|
||||
this.inheritInfo = deploy.getInheritInfo();
|
||||
|
||||
@@ -149,7 +150,7 @@ public final class TableJoin {
|
||||
/**
|
||||
* Return the type of join. LEFT OUTER JOIN etc.
|
||||
*/
|
||||
public String getType() {
|
||||
public SqlJoinType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@@ -157,32 +158,26 @@ public final class TableJoin {
|
||||
* Return true if this join is a left outer join.
|
||||
*/
|
||||
public boolean isOuterJoin() {
|
||||
return type.equals(LEFT_OUTER);
|
||||
return type == SqlJoinType.OUTER;
|
||||
}
|
||||
|
||||
public boolean addJoin(boolean forceOuterJoin, String prefix, DbSqlContext ctx) {
|
||||
public SqlJoinType addJoin(SqlJoinType joinType, String prefix, DbSqlContext ctx) {
|
||||
|
||||
String[] names = SplitName.split(prefix);
|
||||
String a1 = ctx.getTableAlias(names[0]);
|
||||
String a2 = ctx.getTableAlias(prefix);
|
||||
|
||||
return addJoin(forceOuterJoin, a1, a2, ctx);
|
||||
return addJoin(joinType, a1, a2, ctx);
|
||||
}
|
||||
|
||||
public boolean addJoin(boolean forceOuterJoin, String a1, String a2, DbSqlContext ctx) {
|
||||
public SqlJoinType addJoin(SqlJoinType joinType, String a1, String a2, DbSqlContext ctx) {
|
||||
|
||||
String inheritance = inheritInfo != null ? inheritInfo.getWhere() : null;
|
||||
|
||||
ctx.addJoin(forceOuterJoin?LEFT_OUTER:type, table, columns(), a1, a2, inheritance);
|
||||
String joinLiteral = joinType.getLiteral(type);
|
||||
ctx.addJoin(joinLiteral, table, columns(), a1, a2, inheritance);
|
||||
|
||||
return forceOuterJoin || LEFT_OUTER.equals(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicitly add a (non-outer) join.
|
||||
*/
|
||||
public void addInnerJoin(String a1, String a2, DbSqlContext ctx) {
|
||||
String inheritance = inheritInfo != null ? inheritInfo.getWhere() : null;
|
||||
ctx.addJoin(JOIN, table, columns(), a1, a2, inheritance);
|
||||
return joinType.autoToOuter(type);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.avaje.ebeaninternal.server.deploy.BeanCascadeInfo;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanTable;
|
||||
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
|
||||
import com.avaje.ebeaninternal.server.deploy.TableJoin;
|
||||
import com.avaje.ebeaninternal.server.query.SqlJoinType;
|
||||
|
||||
/**
|
||||
* Represents a join to another table during deployment phase.
|
||||
@@ -32,7 +33,7 @@ public class DeployTableJoin {
|
||||
/**
|
||||
* The type of join. LEFT OUTER etc.
|
||||
*/
|
||||
private String type = TableJoin.JOIN;
|
||||
private SqlJoinType type = SqlJoinType.INNER;
|
||||
|
||||
/**
|
||||
* The list of properties mapped to this joined table.
|
||||
@@ -162,7 +163,7 @@ public class DeployTableJoin {
|
||||
/**
|
||||
* Return the type of join. LEFT OUTER JOIN etc.
|
||||
*/
|
||||
public String getType() {
|
||||
public SqlJoinType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@@ -170,7 +171,11 @@ public class DeployTableJoin {
|
||||
* Return true if this join is a left outer join.
|
||||
*/
|
||||
public boolean isOuterJoin() {
|
||||
return type.equals(TableJoin.LEFT_OUTER);
|
||||
return type == SqlJoinType.OUTER;
|
||||
}
|
||||
|
||||
private void setType(SqlJoinType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,13 +184,13 @@ public class DeployTableJoin {
|
||||
public void setType(String joinType) {
|
||||
joinType = joinType.toUpperCase();
|
||||
if (joinType.equalsIgnoreCase(TableJoin.JOIN)) {
|
||||
type = TableJoin.JOIN;
|
||||
type = SqlJoinType.INNER;
|
||||
} else if (joinType.indexOf("LEFT") > -1) {
|
||||
type = TableJoin.LEFT_OUTER;
|
||||
type = SqlJoinType.OUTER;
|
||||
} else if (joinType.indexOf("OUTER") > -1) {
|
||||
type = TableJoin.LEFT_OUTER;
|
||||
type = SqlJoinType.OUTER;
|
||||
} else if (joinType.indexOf("INNER") > -1) {
|
||||
type = TableJoin.JOIN;
|
||||
type = SqlJoinType.INNER;
|
||||
} else {
|
||||
throw new RuntimeException(Message.msg("join.type.unknown", joinType));
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ abstract class JunctionExpression<T> implements Junction<T>, SpiExpression, Expr
|
||||
private static final long serialVersionUID = -645619859900030678L;
|
||||
|
||||
Conjunction(com.avaje.ebean.Query<T> query, ExpressionList<T> parent) {
|
||||
super(AND, query, parent);
|
||||
super(false, AND, query, parent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,17 +49,21 @@ abstract class JunctionExpression<T> implements Junction<T>, SpiExpression, Expr
|
||||
private static final long serialVersionUID = -8464470066692221413L;
|
||||
|
||||
Disjunction(com.avaje.ebean.Query<T> query, ExpressionList<T> parent) {
|
||||
super(OR, query, parent);
|
||||
super(true, OR, query, parent);
|
||||
}
|
||||
}
|
||||
|
||||
// private final ArrayList<SpiExpression> list = new
|
||||
// ArrayList<SpiExpression>();
|
||||
private final DefaultExpressionList<T> exprList;
|
||||
|
||||
private final String joinType;
|
||||
|
||||
JunctionExpression(String joinType, com.avaje.ebean.Query<T> query, ExpressionList<T> parent) {
|
||||
/**
|
||||
* If true then a disjunction which means outer joins are required.
|
||||
*/
|
||||
private final boolean disjunction;
|
||||
|
||||
JunctionExpression(boolean disjunction, String joinType, com.avaje.ebean.Query<T> query, ExpressionList<T> parent) {
|
||||
this.disjunction = disjunction;
|
||||
this.joinType = joinType;
|
||||
this.exprList = new DefaultExpressionList<T>(query, parent);
|
||||
}
|
||||
@@ -68,9 +72,20 @@ abstract class JunctionExpression<T> implements Junction<T>, SpiExpression, Expr
|
||||
|
||||
List<SpiExpression> list = exprList.internalList();
|
||||
|
||||
// get the current state for 'require outer joins'
|
||||
boolean parentOuterJoins = manyWhereJoin.isRequireOuterJoins();
|
||||
if (disjunction) {
|
||||
// turn on outer joins required for disjunction expressions
|
||||
manyWhereJoin.setRequireOuterJoins(true);
|
||||
}
|
||||
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
list.get(i).containsMany(desc, manyWhereJoin);
|
||||
}
|
||||
if (disjunction && !parentOuterJoins) {
|
||||
// restore state to not forcing outer joins
|
||||
manyWhereJoin.setRequireOuterJoins(false);
|
||||
}
|
||||
}
|
||||
|
||||
public Junction<T> add(Expression item) {
|
||||
|
||||
@@ -56,6 +56,13 @@ public abstract class DLoadBaseContext {
|
||||
if (queryProps == null) {
|
||||
return batchSize;
|
||||
}
|
||||
|
||||
int queryFetchBatch = queryProps.getQueryFetchBatch();
|
||||
if (queryFetchBatch > 0) {
|
||||
// property join was automatically set to a 'query join'
|
||||
return queryFetchBatch;
|
||||
}
|
||||
|
||||
FetchConfig fetchConfig = queryProps.getFetchConfig();
|
||||
if (fetchConfig == null) {
|
||||
return batchSize;
|
||||
|
||||
@@ -29,8 +29,8 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex
|
||||
|
||||
super(parent, desc, path, defaultBatchSize, queryProps);
|
||||
|
||||
this.bufferList = new ArrayList<DLoadBeanContext.LoadBuffer>();
|
||||
this.currentBuffer = createBuffer(firstBatchSize);
|
||||
this.bufferList = queryFetch ? new ArrayList<DLoadBeanContext.LoadBuffer>() : null;
|
||||
}
|
||||
|
||||
protected void configureQuery(SpiQuery<?> query, String lazyLoadProperty) {
|
||||
@@ -78,7 +78,7 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex
|
||||
for (LoadBuffer loadBuffer : bufferList) {
|
||||
if (!loadBuffer.list.isEmpty()) {
|
||||
boolean loadCache = false;
|
||||
LoadBeanRequest req = new LoadBeanRequest(loadBuffer, parentRequest.getTransaction(), false, null, loadCache);
|
||||
LoadBeanRequest req = new LoadBeanRequest(loadBuffer, parentRequest, false, null, loadCache);
|
||||
|
||||
parent.getEbeanServer().loadBean(req);
|
||||
if (!queryProps.isQueryFetchAll()) {
|
||||
@@ -173,7 +173,7 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex
|
||||
}
|
||||
}
|
||||
|
||||
LoadBeanRequest req = new LoadBeanRequest(this, null, true, ebi.getLazyLoadProperty(), context.hitCache);
|
||||
LoadBeanRequest req = new LoadBeanRequest(this, true, ebi.getLazyLoadProperty(), context.hitCache);
|
||||
context.desc.getEbeanServer().loadBean(req);
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
|
||||
if (bufferList != null) {
|
||||
for (LoadBuffer loadBuffer : bufferList) {
|
||||
if (!loadBuffer.list.isEmpty()) {
|
||||
LoadManyRequest req = new LoadManyRequest(loadBuffer, parentRequest.getTransaction(), requestedBatchSize, false, false, false);
|
||||
LoadManyRequest req = new LoadManyRequest(loadBuffer, parentRequest, requestedBatchSize, false, false, false);
|
||||
parent.getEbeanServer().loadMany(req);
|
||||
if (!queryProps.isQueryFetchAll()) {
|
||||
// Stop - only fetch the first batch ... the rest will be lazy loaded
|
||||
@@ -187,7 +187,7 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
|
||||
|
||||
// Should reduce the list by checking each beanCollection in the L2 first before executing the query
|
||||
|
||||
LoadManyRequest req = new LoadManyRequest(this, null, batchSize, true, onlyIds, useCache);
|
||||
LoadManyRequest req = new LoadManyRequest(this, batchSize, true, onlyIds, useCache);
|
||||
context.parent.getEbeanServer().loadMany(req);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,12 +175,11 @@ public class DefaultDbSqlContext implements DbSqlContext {
|
||||
return this;
|
||||
}
|
||||
|
||||
public void appendFormulaJoin(String sqlFormulaJoin, boolean forceOuterJoin) {
|
||||
public void appendFormulaJoin(String sqlFormulaJoin, SqlJoinType joinType) {
|
||||
|
||||
// replace ${ta} place holder with the real table alias...
|
||||
String tableAlias = tableAliasStack.peek();
|
||||
String converted = StringHelper
|
||||
.replaceString(sqlFormulaJoin, tableAliasPlaceHolder, tableAlias);
|
||||
String converted = StringHelper.replaceString(sqlFormulaJoin, tableAliasPlaceHolder, tableAlias);
|
||||
|
||||
if (formulaJoins == null) {
|
||||
formulaJoins = new HashSet<String>();
|
||||
@@ -195,7 +194,7 @@ public class DefaultDbSqlContext implements DbSqlContext {
|
||||
formulaJoins.add(converted);
|
||||
|
||||
sb.append(" ");
|
||||
if (forceOuterJoin) {
|
||||
if (joinType == SqlJoinType.OUTER) {
|
||||
if ("join".equals(sqlFormulaJoin.substring(0, 4).toLowerCase())) {
|
||||
// prepend left outer as we are in the 'many' part
|
||||
append(" left outer ");
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.avaje.ebeaninternal.server.query;
|
||||
|
||||
/**
|
||||
* Inner join, Outer join or automatic determination based on cardinality and optionality.
|
||||
*/
|
||||
public enum SqlJoinType {
|
||||
|
||||
/**
|
||||
* It is an inner join.
|
||||
*/
|
||||
INNER("join"),
|
||||
|
||||
/**
|
||||
* It is an outer join.
|
||||
*/
|
||||
OUTER("left outer join"),
|
||||
|
||||
/**
|
||||
* It is automatically determined based on cardinality and optionality.
|
||||
*/
|
||||
AUTO("JOIN-TYPE-AUTO-LITERAL-NOT-USED");
|
||||
|
||||
String literal;
|
||||
|
||||
SqlJoinType(String literal) {
|
||||
this.literal = literal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SQL join literal.
|
||||
*/
|
||||
public String getLiteral() {
|
||||
return literal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the actual SQL join literal taking into account the current join type and the 'default
|
||||
* join type as per deployment cardinality and optionality'.
|
||||
*/
|
||||
public String getLiteral(SqlJoinType deploymentJoinType) {
|
||||
if (this == SqlJoinType.AUTO) {
|
||||
return deploymentJoinType.getLiteral();
|
||||
}
|
||||
return this.getLiteral();
|
||||
}
|
||||
|
||||
/**
|
||||
* If this is an AUTO join set it to OUTER as we are joining to a Many.
|
||||
*/
|
||||
public SqlJoinType autoToOuter() {
|
||||
if (this == AUTO) {
|
||||
return OUTER;
|
||||
} else {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If join is AUTO but deploymentJoinType is OUTER then go into OUTER join mode.
|
||||
*/
|
||||
public SqlJoinType autoToOuter(SqlJoinType deploymentJoinType) {
|
||||
if (this == AUTO && deploymentJoinType == OUTER) {
|
||||
return OUTER;
|
||||
} else {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,7 +10,11 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.avaje.ebeaninternal.api.ManyWhereJoins;
|
||||
import com.avaje.ebeaninternal.api.PropertyJoin;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery.Type;
|
||||
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
|
||||
@@ -25,9 +29,6 @@ import com.avaje.ebeaninternal.server.el.ElPropertyValue;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Factory for SqlTree.
|
||||
*/
|
||||
@@ -169,7 +170,7 @@ public class SqlTreeBuilder {
|
||||
if (rawSql) {
|
||||
return "Not Used";
|
||||
}
|
||||
rootNode.appendFrom(ctx, false);
|
||||
rootNode.appendFrom(ctx, SqlJoinType.AUTO);
|
||||
return ctx.getContent();
|
||||
}
|
||||
|
||||
@@ -181,7 +182,7 @@ public class SqlTreeBuilder {
|
||||
if (!rawSql) {
|
||||
alias.addJoin(queryDetail.getIncludes(), desc);
|
||||
alias.addJoin(predicates.getPredicateIncludes(), desc);
|
||||
alias.addManyWhereJoins(manyWhereJoins.getJoins());
|
||||
alias.addManyWhereJoins(manyWhereJoins.getPropertyNames());
|
||||
|
||||
// build set of table alias
|
||||
alias.buildAlias();
|
||||
@@ -237,10 +238,10 @@ public class SqlTreeBuilder {
|
||||
*/
|
||||
private void addManyWhereJoins(List<SqlTreeNode> myJoinList) {
|
||||
|
||||
Set<String> includes = manyWhereJoins.getJoins();
|
||||
for (String joinProp : includes) {
|
||||
BeanPropertyAssoc<?> beanProperty = (BeanPropertyAssoc<?>) desc.getBeanPropertyFromPath(joinProp);
|
||||
SqlTreeNodeManyWhereJoin nodeJoin = new SqlTreeNodeManyWhereJoin(joinProp, beanProperty);
|
||||
Collection<PropertyJoin> includes = manyWhereJoins.getPropertyJoins();
|
||||
for (PropertyJoin joinProp : includes) {
|
||||
BeanPropertyAssoc<?> beanProperty = (BeanPropertyAssoc<?>) desc.getBeanPropertyFromPath(joinProp.getProperty());
|
||||
SqlTreeNodeManyWhereJoin nodeJoin = new SqlTreeNodeManyWhereJoin(joinProp.getProperty(), beanProperty, joinProp.getSqlJoinType());
|
||||
myJoinList.add(nodeJoin);
|
||||
}
|
||||
}
|
||||
@@ -289,12 +290,11 @@ public class SqlTreeBuilder {
|
||||
// support the predicates or order by clauses.
|
||||
|
||||
// remove ManyWhereJoins from the predicateIncludes
|
||||
predicateIncludes.removeAll(manyWhereJoins.getJoins());
|
||||
predicateIncludes.removeAll(manyWhereJoins.getPropertyNames());
|
||||
|
||||
// look for predicateIncludes that are not in selectIncludes and add
|
||||
// them as extra joins to the query
|
||||
IncludesDistiller extraJoinDistill = new IncludesDistiller(desc, selectIncludes,
|
||||
predicateIncludes);
|
||||
IncludesDistiller extraJoinDistill = new IncludesDistiller(desc, selectIncludes, predicateIncludes);
|
||||
|
||||
Collection<SqlTreeNodeExtraJoin> extraJoins = extraJoinDistill.getExtraJoinRootNodes();
|
||||
if (extraJoins.isEmpty()) {
|
||||
|
||||
@@ -28,7 +28,7 @@ public interface SqlTreeNode {
|
||||
/**
|
||||
* Append to the FROM part of the sql.
|
||||
*/
|
||||
public void appendFrom(DbSqlContext ctx, boolean forceOuterJoin);
|
||||
public void appendFrom(DbSqlContext ctx, SqlJoinType joinType);
|
||||
|
||||
/**
|
||||
* Append any where predicates for inheritance.
|
||||
|
||||
@@ -28,50 +28,47 @@ public class SqlTreeNodeBean implements SqlTreeNode {
|
||||
|
||||
private static final SqlTreeNode[] NO_CHILDREN = new SqlTreeNode[0];
|
||||
|
||||
final BeanDescriptor<?> desc;
|
||||
protected final BeanDescriptor<?> desc;
|
||||
|
||||
final IdBinder idBinder;
|
||||
protected final IdBinder idBinder;
|
||||
|
||||
/**
|
||||
* The children which will be other SelectBean or SelectProxyBean.
|
||||
*/
|
||||
final SqlTreeNode[] children;
|
||||
protected final SqlTreeNode[] children;
|
||||
|
||||
final boolean readOnlyLeaf;
|
||||
protected final boolean readOnlyLeaf;
|
||||
|
||||
/**
|
||||
* Set to true if this is a partial object fetch.
|
||||
*/
|
||||
final boolean partialObject;
|
||||
protected final boolean partialObject;
|
||||
|
||||
|
||||
final BeanProperty[] properties;
|
||||
protected final BeanProperty[] properties;
|
||||
|
||||
/**
|
||||
* Extra where clause added by Where annotation on associated many.
|
||||
*/
|
||||
final String extraWhere;
|
||||
protected final String extraWhere;
|
||||
|
||||
final BeanPropertyAssoc<?> nodeBeanProp;
|
||||
protected final BeanPropertyAssoc<?> nodeBeanProp;
|
||||
|
||||
final TableJoin[] tableJoins;
|
||||
protected final TableJoin[] tableJoins;
|
||||
|
||||
/**
|
||||
* False if report bean and has no id property.
|
||||
*/
|
||||
final boolean readId;
|
||||
protected final boolean readId;
|
||||
|
||||
final boolean disableLazyLoad;
|
||||
protected final boolean disableLazyLoad;
|
||||
|
||||
final InheritInfo inheritInfo;
|
||||
protected final InheritInfo inheritInfo;
|
||||
|
||||
final String prefix;
|
||||
|
||||
|
||||
final Map<String, String> pathMap;
|
||||
protected final String prefix;
|
||||
|
||||
protected final Map<String, String> pathMap;
|
||||
|
||||
final BeanPropertyAssocMany<?> lazyLoadParent;
|
||||
protected final BeanPropertyAssocMany<?> lazyLoadParent;
|
||||
|
||||
public SqlTreeNodeBean(String prefix, BeanPropertyAssoc<?> beanProp, SqlTreeProperties props,
|
||||
List<SqlTreeNode> myChildren, boolean withId) {
|
||||
@@ -452,20 +449,21 @@ public class SqlTreeNodeBean implements SqlTreeNode {
|
||||
/**
|
||||
* Append to the FROM clause for this node.
|
||||
*/
|
||||
public void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) {
|
||||
public void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
|
||||
|
||||
ctx.pushJoin(prefix);
|
||||
ctx.pushTableAlias(prefix);
|
||||
|
||||
forceOuterJoin = appendFromBaseTable(ctx, forceOuterJoin);
|
||||
// join and return SqlJoinType to use for child joins
|
||||
joinType = appendFromBaseTable(ctx, joinType);
|
||||
|
||||
for (int i = 0; i < properties.length; i++) {
|
||||
// usually nothing... except for 1-1 Exported
|
||||
properties[i].appendFrom(ctx, forceOuterJoin);
|
||||
properties[i].appendFrom(ctx, joinType);
|
||||
}
|
||||
|
||||
for (int i = 0; i < children.length; i++) {
|
||||
children[i].appendFrom(ctx, forceOuterJoin);
|
||||
children[i].appendFrom(ctx, joinType);
|
||||
}
|
||||
|
||||
ctx.popTableAlias();
|
||||
@@ -476,7 +474,7 @@ public class SqlTreeNodeBean implements SqlTreeNode {
|
||||
* Join to base table for this node. This includes a join to the intersection
|
||||
* table if this is a ManyToMany node.
|
||||
*/
|
||||
public boolean appendFromBaseTable(DbSqlContext ctx, boolean forceOuterJoin) {
|
||||
public SqlJoinType appendFromBaseTable(DbSqlContext ctx, SqlJoinType joinType) {
|
||||
|
||||
if (nodeBeanProp instanceof BeanPropertyAssocMany<?>) {
|
||||
BeanPropertyAssocMany<?> manyProp = (BeanPropertyAssocMany<?>) nodeBeanProp;
|
||||
@@ -488,14 +486,14 @@ public class SqlTreeNodeBean implements SqlTreeNode {
|
||||
String alias2 = alias + "z_";
|
||||
|
||||
TableJoin manyToManyJoin = manyProp.getIntersectionTableJoin();
|
||||
manyToManyJoin.addJoin(forceOuterJoin, parentAlias, alias2, ctx);
|
||||
manyToManyJoin.addJoin(joinType, parentAlias, alias2, ctx);
|
||||
|
||||
return nodeBeanProp.addJoin(forceOuterJoin, alias2, alias, ctx);
|
||||
return nodeBeanProp.addJoin(joinType, alias2, alias, ctx);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nodeBeanProp.addJoin(forceOuterJoin, prefix, ctx);
|
||||
return nodeBeanProp.addJoin(joinType, prefix, ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,13 +22,13 @@ import com.avaje.ebeaninternal.server.deploy.TableJoin;
|
||||
public class SqlTreeNodeExtraJoin implements SqlTreeNode {
|
||||
|
||||
|
||||
final BeanPropertyAssoc<?> assocBeanProperty;
|
||||
private final BeanPropertyAssoc<?> assocBeanProperty;
|
||||
|
||||
final String prefix;
|
||||
private final String prefix;
|
||||
|
||||
final boolean manyJoin;
|
||||
private final boolean manyJoin;
|
||||
|
||||
List<SqlTreeNodeExtraJoin> children;
|
||||
private List<SqlTreeNodeExtraJoin> children;
|
||||
|
||||
public SqlTreeNodeExtraJoin(String prefix, BeanPropertyAssoc<?> assocBeanProperty) {
|
||||
this.prefix = prefix;
|
||||
@@ -66,7 +66,7 @@ public class SqlTreeNodeExtraJoin implements SqlTreeNode {
|
||||
children.add(child);
|
||||
}
|
||||
|
||||
public void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) {
|
||||
public void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
|
||||
|
||||
boolean manyToMany = false;
|
||||
|
||||
@@ -82,29 +82,29 @@ public class SqlTreeNodeExtraJoin implements SqlTreeNode {
|
||||
String alias2 = alias+"z_";
|
||||
|
||||
TableJoin manyToManyJoin = manyProp.getIntersectionTableJoin();
|
||||
manyToManyJoin.addJoin(forceOuterJoin, parentAlias, alias2, ctx);
|
||||
manyToManyJoin.addJoin(joinType, parentAlias, alias2, ctx);
|
||||
|
||||
assocBeanProperty.addJoin(forceOuterJoin, alias2, alias, ctx);
|
||||
assocBeanProperty.addJoin(joinType, alias2, alias, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
if (!manyToMany){
|
||||
assocBeanProperty.addJoin(forceOuterJoin, prefix, ctx);
|
||||
assocBeanProperty.addJoin(joinType, prefix, ctx);
|
||||
}
|
||||
|
||||
if (children != null){
|
||||
|
||||
if (manyJoin){
|
||||
// make sure all decendants use OUTER JOIN
|
||||
forceOuterJoin = true;
|
||||
}
|
||||
|
||||
for (int i = 0; i < children.size(); i++) {
|
||||
SqlTreeNodeExtraJoin child = children.get(i);
|
||||
child.appendFrom(ctx, forceOuterJoin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (children != null) {
|
||||
|
||||
if (manyJoin) {
|
||||
// if AUTO then make all decendants use OUTER JOIN
|
||||
joinType = joinType.autoToOuter();
|
||||
}
|
||||
|
||||
for (int i = 0; i < children.size(); i++) {
|
||||
SqlTreeNodeExtraJoin child = children.get(i);
|
||||
child.appendFrom(ctx, joinType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing.
|
||||
|
||||
@@ -34,8 +34,8 @@ public final class SqlTreeNodeManyRoot extends SqlTreeNodeBean {
|
||||
* Force outer join for everything after the many property.
|
||||
*/
|
||||
@Override
|
||||
public void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) {
|
||||
super.appendFrom(ctx, true);
|
||||
public void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
|
||||
super.appendFrom(ctx, joinType.autoToOuter());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,20 +14,30 @@ import com.avaje.ebeaninternal.server.deploy.TableJoin;
|
||||
|
||||
/**
|
||||
* Join to Many (or child of a many) to support where clause predicates on many properties.
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public class SqlTreeNodeManyWhereJoin implements SqlTreeNode {
|
||||
|
||||
private final String parentPrefix;
|
||||
|
||||
private final String prefix;
|
||||
private final BeanPropertyAssoc<?> nodeBeanProp;
|
||||
private final SqlTreeNode[] children;
|
||||
|
||||
public SqlTreeNodeManyWhereJoin(String prefix, BeanPropertyAssoc<?> prop) {
|
||||
private final BeanPropertyAssoc<?> nodeBeanProp;
|
||||
|
||||
/**
|
||||
* Child joins.
|
||||
*/
|
||||
private final SqlTreeNode[] children;
|
||||
|
||||
/**
|
||||
* The many where join which is either INNER or OUTER.
|
||||
*/
|
||||
private final SqlJoinType manyJoinType;
|
||||
|
||||
public SqlTreeNodeManyWhereJoin(String prefix, BeanPropertyAssoc<?> prop, SqlJoinType manyJoinType) {
|
||||
|
||||
this.nodeBeanProp = prop;
|
||||
this.prefix = prefix;
|
||||
this.manyJoinType = manyJoinType;
|
||||
|
||||
String[] split = SplitName.split(prefix);
|
||||
this.parentPrefix = split[0];
|
||||
@@ -39,12 +49,15 @@ public class SqlTreeNodeManyWhereJoin implements SqlTreeNode {
|
||||
/**
|
||||
* Append to the FROM clause for this node.
|
||||
*/
|
||||
public void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) {
|
||||
@Override
|
||||
public void appendFrom(DbSqlContext ctx, SqlJoinType currentJoinType) {
|
||||
|
||||
appendFromBaseTable(ctx, forceOuterJoin);
|
||||
// always use the join type as per this many where join
|
||||
// (OUTER for disjunction and otherwise INNER)
|
||||
appendFromBaseTable(ctx, manyJoinType);
|
||||
|
||||
for (int i = 0; i < children.length; i++) {
|
||||
children[i].appendFrom(ctx, forceOuterJoin);
|
||||
children[i].appendFrom(ctx, manyJoinType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,25 +65,25 @@ public class SqlTreeNodeManyWhereJoin implements SqlTreeNode {
|
||||
* Join to base table for this node. This includes a join to the
|
||||
* intersection table if this is a ManyToMany node.
|
||||
*/
|
||||
public void appendFromBaseTable(DbSqlContext ctx, boolean forceOuterJoin) {
|
||||
public void appendFromBaseTable(DbSqlContext ctx, SqlJoinType joinType) {
|
||||
|
||||
String alias = ctx.getTableAliasManyWhere(prefix);
|
||||
String parentAlias = ctx.getTableAliasManyWhere(parentPrefix);
|
||||
|
||||
if (nodeBeanProp instanceof BeanPropertyAssocOne<?>){
|
||||
nodeBeanProp.addInnerJoin(parentAlias, alias, ctx);
|
||||
nodeBeanProp.addJoin(joinType, parentAlias, alias, ctx);
|
||||
|
||||
} else {
|
||||
BeanPropertyAssocMany<?> manyProp = (BeanPropertyAssocMany<?>)nodeBeanProp;
|
||||
if (!manyProp.isManyToMany()) {
|
||||
manyProp.addInnerJoin(parentAlias, alias, ctx);
|
||||
manyProp.addJoin(joinType, parentAlias, alias, ctx);
|
||||
|
||||
} else {
|
||||
String alias2 = alias + "z_";
|
||||
|
||||
TableJoin manyToManyJoin = manyProp.getIntersectionTableJoin();
|
||||
manyToManyJoin.addInnerJoin(parentAlias, alias2, ctx);
|
||||
manyProp.addInnerJoin(alias2, alias, ctx);
|
||||
manyToManyJoin.addJoin(joinType, parentAlias, alias2, ctx);
|
||||
manyProp.addJoin(joinType, alias2, alias, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ public final class SqlTreeNodeRoot extends SqlTreeNodeBean {
|
||||
* For the root node there is no join type or on clause etc.
|
||||
*/
|
||||
@Override
|
||||
public boolean appendFromBaseTable(DbSqlContext ctx, boolean forceOuterJoin) {
|
||||
public SqlJoinType appendFromBaseTable(DbSqlContext ctx, SqlJoinType joinType) {
|
||||
|
||||
ctx.append(desc.getBaseTable());
|
||||
ctx.append(" ").append(ctx.getTableAlias(null));
|
||||
@@ -48,10 +48,10 @@ public final class SqlTreeNodeRoot extends SqlTreeNodeBean {
|
||||
if (includeJoin != null) {
|
||||
String a1 = ctx.getTableAlias(null);
|
||||
String a2 = "int_"; // unique alias for intersection join
|
||||
includeJoin.addJoin(forceOuterJoin, a1, a2, ctx);
|
||||
includeJoin.addJoin(joinType, a1, a2, ctx);
|
||||
}
|
||||
|
||||
return forceOuterJoin;
|
||||
return joinType;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -191,6 +191,8 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
*/
|
||||
private boolean autoFetchTuned;
|
||||
|
||||
private boolean logSecondaryQuery;
|
||||
|
||||
/**
|
||||
* The node of the bean or collection that fired lazy loading. Not null if
|
||||
* profiling is on and this query is for lazy loading. Used to hook back a
|
||||
@@ -585,8 +587,31 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
public void setUsageProfiling(boolean usageProfiling) {
|
||||
this.usageProfiling = usageProfiling;
|
||||
}
|
||||
|
||||
public void setLogSecondaryQuery(boolean logSecondaryQuery) {
|
||||
this.logSecondaryQuery = logSecondaryQuery;
|
||||
}
|
||||
|
||||
public boolean isLogSecondaryQuery() {
|
||||
return logSecondaryQuery;
|
||||
}
|
||||
|
||||
public void setParentNode(ObjectGraphNode parentNode) {
|
||||
private List<SpiQuery<?>> loggedSecondaryQueries;
|
||||
|
||||
@Override
|
||||
public List<SpiQuery<?>> getLoggedSecondaryQueries() {
|
||||
return loggedSecondaryQueries;
|
||||
}
|
||||
|
||||
public void logSecondaryQuery(SpiQuery<?> query) {
|
||||
if (loggedSecondaryQueries == null) {
|
||||
loggedSecondaryQueries = new ArrayList<SpiQuery<?>>();
|
||||
}
|
||||
loggedSecondaryQueries.add(query);
|
||||
}
|
||||
|
||||
|
||||
public void setParentNode(ObjectGraphNode parentNode) {
|
||||
this.parentNode = parentNode;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user