diff --git a/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java index 41b497550..a9d08db0d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -54,79 +54,79 @@ import com.avaje.ebeaninternal.util.DefaultExpressionList; */ public class DefaultOrmQuery implements SpiQuery { - private static final long serialVersionUID = 6838006264714672460L; + private static final long serialVersionUID = 6838006264714672460L; - private final Class beanType; - - private transient final EbeanServer server; - - private transient BeanCollectionTouched beanCollectionTouched; - - private transient final ExpressionFactory expressionFactory; - - /** - * Used to add beans to the PersistanceContext prior to query. - */ - private transient ArrayList contextAdditions; - - /** - * For lazy loading of ManyToMany we need to add a join to the intersection - * table. This is that join to the intersection table. - */ - private transient TableJoin includeTableJoin; + private final Class beanType; - private transient AutoFetchManager autoFetchManager; + private transient final EbeanServer server; - private transient BeanDescriptor beanDescriptor; - - private boolean cancelled; - - private transient CancelableQuery cancelableQuery; - - /** - * The name of the query. - */ - private String name; - - private Type type; - - private Mode mode = Mode.NORMAL; - - /** - * Holds query in structured form. - */ - private OrmQueryDetail detail; + private transient BeanCollectionTouched beanCollectionTouched; - private int maxRows; + private transient final ExpressionFactory expressionFactory; - private int firstRow; + /** + * Used to add beans to the PersistanceContext prior to query. + */ + private transient ArrayList contextAdditions; - private int totalHits; - - /** - * The where clause from a parsed query string. - */ - private String rawWhereClause; - - private OrderBy orderBy; + /** + * For lazy loading of ManyToMany we need to add a join to the intersection table. This is that + * join to the intersection table. + */ + private transient TableJoin includeTableJoin; - private String loadMode; - - private String loadDescription; - - private String generatedSql; + private transient AutoFetchManager autoFetchManager; - /** - * Query language version of the query. - */ - private String query; + private transient BeanDescriptor beanDescriptor; - private String additionalWhere; + private boolean cancelled; - private String additionalHaving; + private transient CancelableQuery cancelableQuery; + + /** + * The name of the query. + */ + private String name; + + private Type type; + + private Mode mode = Mode.NORMAL; + + /** + * Holds query in structured form. + */ + private OrmQueryDetail detail; + + private int maxRows; + + private int firstRow; + + private int totalHits; + + /** + * The where clause from a parsed query string. + */ + private String rawWhereClause; + + private OrderBy orderBy; + + private String loadMode; + + private String loadDescription; + + private String generatedSql; + + /** + * Query language version of the query. + */ + private String query; + + private String additionalWhere; + + private String additionalHaving; + + private String lazyLoadProperty; - private String lazyLoadProperty; - private String lazyLoadManyPath; /** @@ -134,397 +134,398 @@ public class DefaultOrmQuery implements SpiQuery { */ private boolean distinct; - /** - * Set to true internally by Ebean when it needs the DISTINCT keyword added to the query (id property still expected). - */ - private boolean sqlDistinct; - - /** - * Set to true if this is a future fetch using background threads. - */ - private boolean futureFetch; - - private List partialIds; - - private int timeout = -1; - - /** - * The property used to get the key value for a Map. - */ - private String mapKey; + /** + * Set to true internally by Ebean when it needs the DISTINCT keyword added to the query (id + * property still expected). + */ + private boolean sqlDistinct; - /** - * Used for find by id type query. - */ - private Object id; + /** + * Set to true if this is a future fetch using background threads. + */ + private boolean futureFetch; - /** - * Bind parameters when using the query language. - */ - private BindParams bindParams; + private List partialIds; - private DefaultExpressionList whereExpressions; + private int timeout = -1; - private DefaultExpressionList havingExpressions; + /** + * The property used to get the key value for a Map. + */ + private String mapKey; - private int bufferFetchSizeHint; - - private boolean usageProfiling = true; + /** + * Used for find by id type query. + */ + private Object id; - private boolean loadBeanCache; - - private Boolean useBeanCache; - - private Boolean useQueryCache; + /** + * Bind parameters when using the query language. + */ + private BindParams bindParams; - private Boolean readOnly; - - private boolean sqlSelect; + private DefaultExpressionList whereExpressions; - /** - * Allow for explicit on off or null for default. - */ - private Boolean autoFetch; + private DefaultExpressionList havingExpressions; + + private int bufferFetchSizeHint; + + private boolean usageProfiling = true; + + private boolean loadBeanCache; + + private Boolean useBeanCache; + + private Boolean useQueryCache; + + private Boolean readOnly; + + private boolean sqlSelect; + + /** + * Allow for explicit on off or null for default. + */ + private Boolean autoFetch; /** * Allow to fetch a record "for update" which should lock it on read */ private boolean forUpdate; - - /** - * Set to true if this query has been tuned by autoFetch. - */ - 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 - * lazy loading query to the "original" query point. - */ - private ObjectGraphNode parentNode; - private BeanPropertyAssocMany lazyLoadForParentsProperty; - - private List lazyLoadForParentsIds; - - /** - * Hash of final query after AutoFetch tuning. - */ - private HashQueryPlan queryPlanHash; - - private transient PersistenceContext persistenceContext; + /** + * Set to true if this query has been tuned by autoFetch. + */ + 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 lazy loading query to the "original" query + * point. + */ + private ObjectGraphNode parentNode; + + private BeanPropertyAssocMany lazyLoadForParentsProperty; + + private List lazyLoadForParentsIds; + + /** + * Hash of final query after AutoFetch tuning. + */ + private HashQueryPlan queryPlanHash; + + private transient PersistenceContext persistenceContext; private ManyWhereJoins manyWhereJoins; - + private RawSql rawSql; - - public DefaultOrmQuery(Class beanType, EbeanServer server, ExpressionFactory expressionFactory, String query) { - this.beanType = beanType; - this.server = server; - this.expressionFactory = expressionFactory; - this.detail = new OrmQueryDetail(); - this.name = ""; - if (query != null){ - setQuery(query); - } - } - /** - * Additional supply a query which is parsed. - */ - public DefaultOrmQuery(Class beanType, EbeanServer server, ExpressionFactory expressionFactory, - DeployNamedQuery namedQuery) throws PersistenceException { - - this.beanType = beanType; - this.server = server; - this.expressionFactory = expressionFactory; - this.detail = new OrmQueryDetail(); - if (namedQuery == null){ - this.name = ""; - } else { - this.name = namedQuery.getName(); - this.sqlSelect = namedQuery.isSqlSelect(); - if (sqlSelect) { - // potentially with where and having clause... - DRawSqlSelect sqlSelect = namedQuery.getSqlSelect(); - additionalWhere = sqlSelect.getWhereClause(); - additionalHaving = sqlSelect.getHavingClause(); - } else if (namedQuery.isRawSql()){ - rawSql = namedQuery.getRawSql(); - - } else { - // parse the entire query... - setQuery(namedQuery.getQuery()); - } - } - } - - public int getTotalHits() { - return totalHits; + public DefaultOrmQuery(Class beanType, EbeanServer server, ExpressionFactory expressionFactory, String query) { + this.beanType = beanType; + this.server = server; + this.expressionFactory = expressionFactory; + this.detail = new OrmQueryDetail(); + this.name = ""; + if (query != null) { + setQuery(query); } + } - public void setTotalHits(int totalHits) { - this.totalHits = totalHits; + /** + * Additional supply a query which is parsed. + */ + public DefaultOrmQuery(Class beanType, EbeanServer server, ExpressionFactory expressionFactory, + DeployNamedQuery namedQuery) throws PersistenceException { + + this.beanType = beanType; + this.server = server; + this.expressionFactory = expressionFactory; + this.detail = new OrmQueryDetail(); + if (namedQuery == null) { + this.name = ""; + } else { + this.name = namedQuery.getName(); + this.sqlSelect = namedQuery.isSqlSelect(); + if (sqlSelect) { + // potentially with where and having clause... + DRawSqlSelect sqlSelect = namedQuery.getSqlSelect(); + additionalWhere = sqlSelect.getWhereClause(); + additionalHaving = sqlSelect.getHavingClause(); + } else if (namedQuery.isRawSql()) { + rawSql = namedQuery.getRawSql(); + + } else { + // parse the entire query... + setQuery(namedQuery.getQuery()); + } } + } - /** - * Set the BeanDescriptor for the root type of this query. - */ - public void setBeanDescriptor(BeanDescriptor beanDescriptor) { - this.beanDescriptor = beanDescriptor; - } + public int getTotalHits() { + return totalHits; + } - /** - * Return true if select all properties was used to ensure the - * property invoking a lazy load was included in the query. - */ - public boolean selectAllForLazyLoadProperty() { - if (lazyLoadProperty != null){ - if (!detail.containsProperty(lazyLoadProperty)){ - detail.select("*"); - return true; - } - } - return false; - } - - public RawSql getRawSql() { - return rawSql; + public void setTotalHits(int totalHits) { + this.totalHits = totalHits; + } + + /** + * Set the BeanDescriptor for the root type of this query. + */ + public void setBeanDescriptor(BeanDescriptor beanDescriptor) { + this.beanDescriptor = beanDescriptor; + } + + /** + * Return true if select all properties was used to ensure the property invoking a lazy load was + * included in the query. + */ + public boolean selectAllForLazyLoadProperty() { + if (lazyLoadProperty != null) { + if (!detail.containsProperty(lazyLoadProperty)) { + detail.select("*"); + return true; + } } + return false; + } - public DefaultOrmQuery setRawSql(RawSql rawSql) { - this.rawSql = rawSql; - return this; + public RawSql getRawSql() { + return rawSql; + } + + public DefaultOrmQuery setRawSql(RawSql rawSql) { + this.rawSql = rawSql; + return this; + } + + public String getLazyLoadProperty() { + return lazyLoadProperty; + } + + public void setLazyLoadProperty(String lazyLoadProperty) { + this.lazyLoadProperty = lazyLoadProperty; + } + + public String getLazyLoadManyPath() { + return lazyLoadManyPath; + } + + public ExpressionFactory getExpressionFactory() { + return expressionFactory; + } + + /** + * Return true if the where expressions contains a many property. + */ + public boolean initManyWhereJoins() { + manyWhereJoins = new ManyWhereJoins(); + if (whereExpressions != null) { + whereExpressions.containsMany(beanDescriptor, manyWhereJoins); } + return !manyWhereJoins.isEmpty(); + } - public String getLazyLoadProperty() { - return lazyLoadProperty; - } + public ManyWhereJoins getManyWhereJoins() { + return manyWhereJoins; + } - public void setLazyLoadProperty(String lazyLoadProperty) { - this.lazyLoadProperty = lazyLoadProperty; - } + public List removeQueryJoins() { + List queryJoins = detail.removeSecondaryQueries(); + if (queryJoins != null) { + if (orderBy != null) { + // remove any orderBy properties that relate to + // paths of the secondary queries + for (int i = 0; i < queryJoins.size(); i++) { + OrmQueryProperties joinPath = queryJoins.get(i); - public String getLazyLoadManyPath() { - return lazyLoadManyPath; - } - - public ExpressionFactory getExpressionFactory() { - return expressionFactory; - } - - /** - * Return true if the where expressions contains a many property. - */ - public boolean initManyWhereJoins() { - manyWhereJoins = new ManyWhereJoins(); - if (whereExpressions != null){ - whereExpressions.containsMany(beanDescriptor, manyWhereJoins); - } - return !manyWhereJoins.isEmpty(); - } - - public ManyWhereJoins getManyWhereJoins() { - return manyWhereJoins; - } - - public List removeQueryJoins() { - List queryJoins = detail.removeSecondaryQueries(); - if (queryJoins != null){ - if (orderBy != null){ - // remove any orderBy properties that relate to - // paths of the secondary queries - for (int i = 0; i < queryJoins.size(); i++) { - OrmQueryProperties joinPath = queryJoins.get(i); - - // loop through the orderBy properties and - // move any ones related to the query join - List properties = orderBy.getProperties(); - Iterator it = properties.iterator(); - while (it.hasNext()) { - OrderBy.Property property = it.next(); - if (property.getProperty().startsWith(joinPath.getPath())){ - // remove this orderBy segment and - // add it to the secondary join - it.remove(); - joinPath.addSecJoinOrderProperty(property); - } - } - } + // loop through the orderBy properties and + // move any ones related to the query join + List properties = orderBy.getProperties(); + Iterator it = properties.iterator(); + while (it.hasNext()) { + OrderBy.Property property = it.next(); + if (property.getProperty().startsWith(joinPath.getPath())) { + // remove this orderBy segment and + // add it to the secondary join + it.remove(); + joinPath.addSecJoinOrderProperty(property); } + } } - return queryJoins; + } } - - public List removeLazyJoins() { - return detail.removeSecondaryLazyQueries(); - } - - public void setLazyLoadManyPath(String lazyLoadManyPath) { - this.lazyLoadManyPath = lazyLoadManyPath; + return queryJoins; + } + + public List removeLazyJoins() { + return detail.removeSecondaryLazyQueries(); + } + + public void setLazyLoadManyPath(String lazyLoadManyPath) { + this.lazyLoadManyPath = lazyLoadManyPath; + } + + /** + * Convert any many joins fetch joins to query joins. + */ + public void convertManyFetchJoinsToQueryJoins(boolean allowOne, int queryBatch) { + detail.convertManyFetchJoinsToQueryJoins(beanDescriptor, lazyLoadManyPath, allowOne, queryBatch); + } + + /** + * Set the select clause to select the Id property. + */ + public void setSelectId() { + // clear select and fetch joins.. + detail.clear(); + + select(beanDescriptor.getIdBinder().getIdProperty()); + } + + public void convertWhereNaturalKeyToId(Object idValue) { + whereExpressions = new DefaultExpressionList(this, null); + setId(idValue); + } + + public NaturalKeyBindParam getNaturalKeyBindParam() { + NaturalKeyBindParam namedBind = null; + if (bindParams != null) { + namedBind = bindParams.getNaturalKeyBindParam(); + if (namedBind == null) { + return null; + } } - /** - * Convert any many joins fetch joins to query joins. - */ - public void convertManyFetchJoinsToQueryJoins(boolean allowOne, int queryBatch) { - detail.convertManyFetchJoinsToQueryJoins(beanDescriptor, lazyLoadManyPath, allowOne, queryBatch); + if (whereExpressions != null) { + List exprList = whereExpressions.internalList(); + if (exprList.size() > 1) { + return null; + } else if (exprList.size() == 0) { + return namedBind; + } else { + if (namedBind != null) { + return null; + } + SpiExpression se = exprList.get(0); + if (se instanceof SimpleExpression) { + SimpleExpression e = (SimpleExpression) se; + if (e.isOpEquals()) { + return new NaturalKeyBindParam(e.getPropertyName(), e.getValue()); + } + } + } } - - /** - * Set the select clause to select the Id property. - */ - public void setSelectId() { - // clear select and fetch joins.. - detail.clear(); - - select(beanDescriptor.getIdBinder().getIdProperty()); - } - - public void convertWhereNaturalKeyToId(Object idValue) { - whereExpressions = new DefaultExpressionList(this, null); - setId(idValue); - } - - public NaturalKeyBindParam getNaturalKeyBindParam() { - NaturalKeyBindParam namedBind = null; - if (bindParams != null){ - namedBind = bindParams.getNaturalKeyBindParam(); - if (namedBind == null) { - return null; - } - } - - if (whereExpressions != null){ - List exprList = whereExpressions.internalList(); - if (exprList.size() > 1){ - return null; - } else if (exprList.size() == 0) { - return namedBind; - } else { - if (namedBind != null){ - return null; - } - SpiExpression se = exprList.get(0); - if (se instanceof SimpleExpression){ - SimpleExpression e = (SimpleExpression)se; - if (e.isOpEquals()){ - return new NaturalKeyBindParam(e.getPropertyName(), e.getValue()); - } - } - } - } - return null; - } - - public DefaultOrmQuery copy() { - // Not including these in the copy: - // contextAdditions - // queryListener - // transactionContext - // autoFetchTuned - // autoFetchQueryPlanHash - // copy.generatedSql - - DefaultOrmQuery copy = new DefaultOrmQuery(beanType, server, expressionFactory, (String)null); - copy.name = name; - copy.includeTableJoin = includeTableJoin; - copy.autoFetchManager = autoFetchManager; + return null; + } - copy.query = query; - copy.additionalWhere = additionalWhere; - copy.additionalHaving = additionalHaving; + public DefaultOrmQuery copy() { + // Not including these in the copy: + // contextAdditions + // queryListener + // transactionContext + // autoFetchTuned + // autoFetchQueryPlanHash + // copy.generatedSql + + DefaultOrmQuery copy = new DefaultOrmQuery(beanType, server, expressionFactory, (String) null); + copy.name = name; + copy.includeTableJoin = includeTableJoin; + copy.autoFetchManager = autoFetchManager; + + copy.query = query; + copy.additionalWhere = additionalWhere; + copy.additionalHaving = additionalHaving; copy.distinct = distinct; - copy.sqlDistinct = sqlDistinct; - copy.timeout = timeout; - copy.mapKey = mapKey; - copy.id = id; - copy.loadBeanCache = loadBeanCache; - copy.useBeanCache = useBeanCache; - copy.useQueryCache = useQueryCache; - copy.readOnly = readOnly; - copy.sqlSelect = sqlSelect; - if (detail != null){ - copy.detail = detail.copy(); - } + copy.sqlDistinct = sqlDistinct; + copy.timeout = timeout; + copy.mapKey = mapKey; + copy.id = id; + copy.loadBeanCache = loadBeanCache; + copy.useBeanCache = useBeanCache; + copy.useQueryCache = useQueryCache; + copy.readOnly = readOnly; + copy.sqlSelect = sqlSelect; + if (detail != null) { + copy.detail = detail.copy(); + } - copy.firstRow = firstRow; - copy.maxRows = maxRows; - copy.rawWhereClause = rawWhereClause; - if (orderBy != null){ - copy.orderBy = orderBy.copy(); - } - if (bindParams != null){ - copy.bindParams = bindParams.copy(); - } - if (whereExpressions != null){ - copy.whereExpressions = whereExpressions.copy(copy); - } - if (havingExpressions != null){ - copy.havingExpressions = havingExpressions.copy(copy); - } - copy.usageProfiling = usageProfiling; - copy.autoFetch = autoFetch; - copy.parentNode = parentNode; + copy.firstRow = firstRow; + copy.maxRows = maxRows; + copy.rawWhereClause = rawWhereClause; + if (orderBy != null) { + copy.orderBy = orderBy.copy(); + } + if (bindParams != null) { + copy.bindParams = bindParams.copy(); + } + if (whereExpressions != null) { + copy.whereExpressions = whereExpressions.copy(copy); + } + if (havingExpressions != null) { + copy.havingExpressions = havingExpressions.copy(copy); + } + copy.usageProfiling = usageProfiling; + copy.autoFetch = autoFetch; + copy.parentNode = parentNode; copy.forUpdate = forUpdate; copy.rawSql = rawSql; copy.rawWhereClause = rawWhereClause; - return copy; - } + return copy; + } - public Type getType() { - return type; - } - - public void setType(Type type) { - this.type = type; - } + public Type getType() { + return type; + } + + public void setType(Type type) { + this.type = type; + } public String getLoadDescription() { return loadDescription; } - public String getLoadMode() { - return loadMode; - } + public String getLoadMode() { + return loadMode; + } - public void setLoadDescription(String loadMode, String loadDescription) { - this.loadMode = loadMode; - this.loadDescription = loadDescription; - } + public void setLoadDescription(String loadMode, String loadDescription) { + this.loadMode = loadMode; + this.loadDescription = loadDescription; + } - /** - * Return the TransactionContext. - *

- * If no TransactionContext is present on the query then the TransactionContext - * from the Transaction is used (transaction scoped persistence context). - *

- */ - public PersistenceContext getPersistenceContext() { - return persistenceContext; - } + /** + * Return the TransactionContext. + *

+ * If no TransactionContext is present on the query then the TransactionContext from the + * Transaction is used (transaction scoped persistence context). + *

+ */ + public PersistenceContext getPersistenceContext() { + return persistenceContext; + } - /** - * Set an explicit TransactionContext (typically for a refresh query). - *

- * If no TransactionContext is present on the query then the TransactionContext - * from the Transaction is used (transaction scoped persistence context). - *

- */ - public void setPersistenceContext(PersistenceContext persistenceContext) { - this.persistenceContext = persistenceContext; - } + /** + * Set an explicit TransactionContext (typically for a refresh query). + *

+ * If no TransactionContext is present on the query then the TransactionContext from the + * Transaction is used (transaction scoped persistence context). + *

+ */ + public void setPersistenceContext(PersistenceContext persistenceContext) { + this.persistenceContext = persistenceContext; + } @Override public void setLazyLoadForParents(List parentIds, BeanPropertyAssocMany many) { - this.lazyLoadForParentsIds = parentIds; - this.lazyLoadForParentsProperty = many; + this.lazyLoadForParentsIds = parentIds; + this.lazyLoadForParentsProperty = many; } - + @Override - public List getLazyLoadForParentIds() { + public List getLazyLoadForParentIds() { return lazyLoadForParentsIds; } @@ -534,137 +535,135 @@ public class DefaultOrmQuery implements SpiQuery { } /** - * Return true if the query detail has neither select or joins specified. - */ - public boolean isDetailEmpty() { - return detail.isEmpty(); - } + * Return true if the query detail has neither select or joins specified. + */ + public boolean isDetailEmpty() { + return detail.isEmpty(); + } - public boolean isAutofetchTuned() { - return autoFetchTuned; - } + public boolean isAutofetchTuned() { + return autoFetchTuned; + } - public void setAutoFetchTuned(boolean autoFetchTuned) { - this.autoFetchTuned = autoFetchTuned; - } + public void setAutoFetchTuned(boolean autoFetchTuned) { + this.autoFetchTuned = autoFetchTuned; + } - public Boolean isAutofetch() { - return sqlSelect ? Boolean.FALSE : autoFetch; - } + public Boolean isAutofetch() { + return sqlSelect ? Boolean.FALSE : autoFetch; + } public boolean isForUpdate() { return forUpdate; } - public DefaultOrmQuery setAutoFetch(boolean autoFetch) { - return setAutofetch(autoFetch); - } - - public DefaultOrmQuery setAutofetch(boolean autoFetch) { - this.autoFetch = autoFetch; - return this; - } + public DefaultOrmQuery setAutoFetch(boolean autoFetch) { + return setAutofetch(autoFetch); + } + + public DefaultOrmQuery setAutofetch(boolean autoFetch) { + this.autoFetch = autoFetch; + return this; + } public DefaultOrmQuery setForUpdate(boolean forUpdate) { this.forUpdate = forUpdate; return this; } - - public AutoFetchManager getAutoFetchManager() { - return autoFetchManager; - } - public void setAutoFetchManager(AutoFetchManager autoFetchManager) { - this.autoFetchManager = autoFetchManager; - } + public AutoFetchManager getAutoFetchManager() { + return autoFetchManager; + } - public Mode getMode() { - return mode; - } + public void setAutoFetchManager(AutoFetchManager autoFetchManager) { + this.autoFetchManager = autoFetchManager; + } - public void setMode(Mode mode) { - this.mode = mode; - } + public Mode getMode() { + return mode; + } - public boolean isUsageProfiling() { - return usageProfiling; - } + public void setMode(Mode mode) { + this.mode = mode; + } + + public boolean isUsageProfiling() { + return usageProfiling; + } + + public void setUsageProfiling(boolean usageProfiling) { + this.usageProfiling = usageProfiling; + } - public void setUsageProfiling(boolean usageProfiling) { - this.usageProfiling = usageProfiling; - } - public void setLogSecondaryQuery(boolean logSecondaryQuery) { this.logSecondaryQuery = logSecondaryQuery; - } - - public boolean isLogSecondaryQuery() { + } + + public boolean isLogSecondaryQuery() { return logSecondaryQuery; } - private List> loggedSecondaryQueries; - + private List> loggedSecondaryQueries; + @Override public List> getLoggedSecondaryQueries() { return loggedSecondaryQueries; } - + public void logSecondaryQuery(SpiQuery query) { if (loggedSecondaryQueries == null) { loggedSecondaryQueries = new ArrayList>(); } loggedSecondaryQueries.add(query); } - public void setParentNode(ObjectGraphNode parentNode) { - this.parentNode = parentNode; - } + this.parentNode = parentNode; + } - public ObjectGraphNode getParentNode() { - return parentNode; - } + public ObjectGraphNode getParentNode() { + return parentNode; + } - - public ObjectGraphNode setOrigin(CallStack callStack) { + public ObjectGraphNode setOrigin(CallStack callStack) { - // create a 'origin' which links this query to the profiling information - ObjectGraphOrigin o = new ObjectGraphOrigin(calculateOriginQueryHash(), callStack, beanType.getName()); - parentNode = new ObjectGraphNode(o, null); - return parentNode; - } + // create a 'origin' which links this query to the profiling information + ObjectGraphOrigin o = new ObjectGraphOrigin(calculateOriginQueryHash(), callStack, beanType.getName()); + parentNode = new ObjectGraphNode(o, null); + return parentNode; + } - /** - * Calculate a hash for use in determining the ObjectGraphOrigin. - *

- * This should be quite a stable hash as most uniqueness is determined - * by the CallStack, so we only use the bean type and overall query type. - *

- *

- * This stable hash allows the query to be changed (joins added etc) without - * losing the already collected usage profiling. - *

- */ - private int calculateOriginQueryHash() { - int hc = beanType.getName().hashCode(); - hc = hc * 31 + (type == null ? 0 : type.ordinal()); - return hc; + /** + * Calculate a hash for use in determining the ObjectGraphOrigin. + *

+ * This should be quite a stable hash as most uniqueness is determined by the CallStack, so we + * only use the bean type and overall query type. + *

+ *

+ * This stable hash allows the query to be changed (joins added etc) without losing the already + * collected usage profiling. + *

+ */ + private int calculateOriginQueryHash() { + int hc = beanType.getName().hashCode(); + hc = hc * 31 + (type == null ? 0 : type.ordinal()); + return hc; + } + + /** + * Calculate the query hash for either AutoFetch query tuning or Query Plan caching. + */ + private HashQueryPlan calculateHash(BeanQueryRequest request, HashQueryPlanBuilder builder) { + + // exclude bind values and things unrelated to + // the sql being generated + + if (builder == null) { + builder = new HashQueryPlanBuilder(); } - - /** - * Calculate the query hash for either AutoFetch query tuning or Query Plan caching. - */ - private HashQueryPlan calculateHash(BeanQueryRequest request, HashQueryPlanBuilder builder) { - // exclude bind values and things unrelated to - // the sql being generated - - if (builder == null) { - builder = new HashQueryPlanBuilder(); - } - - builder.add((type == null ? 0 : type.ordinal()+1)); - builder.add(autoFetchTuned).add(distinct).add(sqlDistinct).add(query); + builder.add((type == null ? 0 : type.ordinal() + 1)); + builder.add(autoFetchTuned).add(distinct).add(sqlDistinct).add(query); builder.add(firstRow).add(maxRows).add(orderBy).add(forUpdate); builder.add(rawWhereClause).add(additionalWhere).add(additionalHaving); builder.add(mapKey); @@ -672,12 +671,12 @@ public class DefaultOrmQuery implements SpiQuery { builder.add(rawSql == null ? 0 : rawSql.queryHash()); if (detail != null) { - detail.queryPlanHash(request, builder); + detail.queryPlanHash(request, builder); } if (bindParams != null) { bindParams.buildQueryPlanHash(builder); } - + if (request == null) { // for AutoFetch... builder.add(true); @@ -700,333 +699,332 @@ public class DefaultOrmQuery implements SpiQuery { } return builder.build(); - } - - /** - * Calculate a hash used by AutoFetch to identify when a query has changed - * (and hence potentially needs a new tuned query plan to be developed). - */ - public HashQueryPlan queryAutofetchHash(HashQueryPlanBuilder builder) { - - return calculateHash(null, builder); - } - - /** - * Calculate a hash that should be unique for the generated SQL across a - * given bean type. - *

- * This can used to enable the caching and reuse of a 'query plan'. - *

- *

- * This is calculated AFTER AutoFetch query tuning has occurred. - *

- */ - public HashQueryPlan queryPlanHash(BeanQueryRequest request) { + } - queryPlanHash = calculateHash(request, null); - return queryPlanHash; - } + /** + * Calculate a hash used by AutoFetch to identify when a query has changed (and hence potentially + * needs a new tuned query plan to be developed). + */ + public HashQueryPlan queryAutofetchHash(HashQueryPlanBuilder builder) { - /** - * Calculate a hash based on the bind values used in the query. - *

- * Used with queryPlanHash() to get a unique hash for a query. - *

- */ - public int queryBindHash() { - int hc = (id == null ? 0 : id.hashCode()); - hc = hc * 31 + (whereExpressions == null ? 0 : whereExpressions.queryBindHash()); - hc = hc * 31 + (havingExpressions == null ? 0 : havingExpressions.queryBindHash()); - hc = hc * 31 + (bindParams == null ? 0 : bindParams.queryBindHash()); - hc = hc * 31 + (contextAdditions == null ? 0 : contextAdditions.hashCode()); + return calculateHash(null, builder); + } - return hc; - } + /** + * Calculate a hash that should be unique for the generated SQL across a given bean type. + *

+ * This can used to enable the caching and reuse of a 'query plan'. + *

+ *

+ * This is calculated AFTER AutoFetch query tuning has occurred. + *

+ */ + public HashQueryPlan queryPlanHash(BeanQueryRequest request) { - /** - * Return a hash that includes the query plan and bind values. - *

- * This hash can be used to identify if we have executed the exact same query - * (including bind values) before. - *

- */ - public HashQuery queryHash() { - // calculateQueryPlanHash is called just after potential AutoFetch tuning - // so queryPlanHash is calculated well before this method is called - int hc = queryBindHash(); - - return new HashQuery(queryPlanHash, hc); - } + queryPlanHash = calculateHash(request, null); + return queryPlanHash; + } - /** - * Return the query name. - */ - public String getName() { - return name; - } + /** + * Calculate a hash based on the bind values used in the query. + *

+ * Used with queryPlanHash() to get a unique hash for a query. + *

+ */ + public int queryBindHash() { + int hc = (id == null ? 0 : id.hashCode()); + hc = hc * 31 + (whereExpressions == null ? 0 : whereExpressions.queryBindHash()); + hc = hc * 31 + (havingExpressions == null ? 0 : havingExpressions.queryBindHash()); + hc = hc * 31 + (bindParams == null ? 0 : bindParams.queryBindHash()); + hc = hc * 31 + (contextAdditions == null ? 0 : contextAdditions.hashCode()); - public boolean isSqlSelect() { - return sqlSelect; - } + return hc; + } - public boolean isRawSql() { - return rawSql != null; - } - - /** - * Return any additional where clauses. - */ - public String getAdditionalWhere() { - return additionalWhere; - } + /** + * Return a hash that includes the query plan and bind values. + *

+ * This hash can be used to identify if we have executed the exact same query (including bind + * values) before. + *

+ */ + public HashQuery queryHash() { + // calculateQueryPlanHash is called just after potential AutoFetch tuning + // so queryPlanHash is calculated well before this method is called + int hc = queryBindHash(); - /** - * Return the timeout. - */ - public int getTimeout() { - return timeout; - } + return new HashQuery(queryPlanHash, hc); + } - /** - * Return any additional having clauses. - */ - public String getAdditionalHaving() { - return additionalHaving; - } + /** + * Return the query name. + */ + public String getName() { + return name; + } - public boolean hasMaxRowsOrFirstRow() { - return maxRows > 0 || firstRow > 0; - } + public boolean isSqlSelect() { + return sqlSelect; + } + + public boolean isRawSql() { + return rawSql != null; + } + + /** + * Return any additional where clauses. + */ + public String getAdditionalWhere() { + return additionalWhere; + } + + /** + * Return the timeout. + */ + public int getTimeout() { + return timeout; + } + + /** + * Return any additional having clauses. + */ + public String getAdditionalHaving() { + return additionalHaving; + } + + public boolean hasMaxRowsOrFirstRow() { + return maxRows > 0 || firstRow > 0; + } public Boolean isReadOnly() { - return readOnly; - } + return readOnly; + } - public DefaultOrmQuery setReadOnly(boolean readOnly) { - this.readOnly = readOnly; - return this; - } + public DefaultOrmQuery setReadOnly(boolean readOnly) { + this.readOnly = readOnly; + return this; + } - public Boolean isUseBeanCache() { - return useBeanCache; - } + public Boolean isUseBeanCache() { + return useBeanCache; + } - public boolean isUseQueryCache() { - return Boolean.TRUE.equals(useQueryCache); - } + public boolean isUseQueryCache() { + return Boolean.TRUE.equals(useQueryCache); + } - public DefaultOrmQuery setUseCache(boolean useBeanCache) { - this.useBeanCache = useBeanCache; - return this; - } - - public DefaultOrmQuery setUseQueryCache(boolean useQueryCache) { - this.useQueryCache = useQueryCache; - return this; - } + public DefaultOrmQuery setUseCache(boolean useBeanCache) { + this.useBeanCache = useBeanCache; + return this; + } - public boolean isLoadBeanCache() { - return loadBeanCache; - } + public DefaultOrmQuery setUseQueryCache(boolean useQueryCache) { + this.useQueryCache = useQueryCache; + return this; + } - public DefaultOrmQuery setLoadBeanCache(boolean loadBeanCache) { - this.loadBeanCache = loadBeanCache; - return this; - } - - public DefaultOrmQuery setTimeout(int secs) { - this.timeout = secs; - return this; - } + public boolean isLoadBeanCache() { + return loadBeanCache; + } - public DefaultOrmQuery setQuery(String queryString) throws PersistenceException { + public DefaultOrmQuery setLoadBeanCache(boolean loadBeanCache) { + this.loadBeanCache = loadBeanCache; + return this; + } - this.query = queryString; + public DefaultOrmQuery setTimeout(int secs) { + this.timeout = secs; + return this; + } - OrmQueryDetailParser parser = new OrmQueryDetailParser(queryString); - parser.parse(); - parser.assign(this); - - return this; - } - - protected void setOrmQueryDetail(OrmQueryDetail detail){ - this.detail = detail; - } - protected void setRawWhereClause(String rawWhereClause){ - this.rawWhereClause = rawWhereClause; - } + public DefaultOrmQuery setQuery(String queryString) throws PersistenceException { - public DefaultOrmQuery setProperties(String columns) { - return select(columns); - } - - public void setDefaultSelectClause(){ - detail.setDefaultSelectClause(beanDescriptor); - } + this.query = queryString; - public DefaultOrmQuery select(String columns) { - detail.select(columns); - return this; - } + OrmQueryDetailParser parser = new OrmQueryDetailParser(queryString); + parser.parse(); + parser.assign(this); - public DefaultOrmQuery fetch(String property) { - return fetch(property, null, null); - } + return this; + } - public DefaultOrmQuery fetch(String property, FetchConfig joinConfig) { - return fetch(property, null, joinConfig); - } + protected void setOrmQueryDetail(OrmQueryDetail detail) { + this.detail = detail; + } - public DefaultOrmQuery fetch(String property, String columns) { - return fetch(property, columns, null); - } - - public DefaultOrmQuery fetch(String property, String columns, FetchConfig config) { - detail.addFetch(property, columns, config); - return this; - } - - public List findIds() { - // a copy of this query is made in the server - // as the query needs to modified (so we modify - // the copy rather than this query instance) - return server.findIds(this, null); - } - - public int findRowCount(){ - // a copy of this query is made in the server - // as the query needs to modified (so we modify - // the copy rather than this query instance) - return server.findRowCount(this, null); - } - - public void findVisit(QueryResultVisitor visitor) { - server.findVisit(this, visitor, null); - } + protected void setRawWhereClause(String rawWhereClause) { + this.rawWhereClause = rawWhereClause; + } - public QueryIterator findIterate() { - return server.findIterate(this, null); - } + public DefaultOrmQuery setProperties(String columns) { + return select(columns); + } - public List findList() { - return server.findList(this, null); - } + public void setDefaultSelectClause() { + detail.setDefaultSelectClause(beanDescriptor); + } - public Set findSet() { - return server.findSet(this, null); - } + public DefaultOrmQuery select(String columns) { + detail.select(columns); + return this; + } - public Map findMap() { - return server.findMap(this, null); - } - - @SuppressWarnings("unchecked") - public Map findMap(String keyProperty, Class keyType) { - setMapKey(keyProperty); - return (Map)findMap(); - } + public DefaultOrmQuery fetch(String property) { + return fetch(property, null, null); + } - public T findUnique() { - return server.findUnique(this, null); - } - - public FutureIds findFutureIds() { - return server.findFutureIds(this, null); - } + public DefaultOrmQuery fetch(String property, FetchConfig joinConfig) { + return fetch(property, null, joinConfig); + } - public FutureList findFutureList() { - return server.findFutureList(this, null); - } + public DefaultOrmQuery fetch(String property, String columns) { + return fetch(property, columns, null); + } - public FutureRowCount findFutureRowCount() { - return server.findFutureRowCount(this, null); - } - - public PagingList findPagingList(int pageSize) { - return server.findPagingList(this, null, pageSize); - } + public DefaultOrmQuery fetch(String property, String columns, FetchConfig config) { + detail.addFetch(property, columns, config); + return this; + } - @Override + public List findIds() { + // a copy of this query is made in the server + // as the query needs to modified (so we modify + // the copy rather than this query instance) + return server.findIds(this, null); + } + + public int findRowCount() { + // a copy of this query is made in the server + // as the query needs to modified (so we modify + // the copy rather than this query instance) + return server.findRowCount(this, null); + } + + public void findVisit(QueryResultVisitor visitor) { + server.findVisit(this, visitor, null); + } + + public QueryIterator findIterate() { + return server.findIterate(this, null); + } + + public List findList() { + return server.findList(this, null); + } + + public Set findSet() { + return server.findSet(this, null); + } + + public Map findMap() { + return server.findMap(this, null); + } + + @SuppressWarnings("unchecked") + public Map findMap(String keyProperty, Class keyType) { + setMapKey(keyProperty); + return (Map) findMap(); + } + + public T findUnique() { + return server.findUnique(this, null); + } + + public FutureIds findFutureIds() { + return server.findFutureIds(this, null); + } + + public FutureList findFutureList() { + return server.findFutureList(this, null); + } + + public FutureRowCount findFutureRowCount() { + return server.findFutureRowCount(this, null); + } + + public PagingList findPagingList(int pageSize) { + return server.findPagingList(this, null, pageSize); + } + + @Override public PagedList findPagedList(int pageIndex, int pageSize) { return server.findPagedList(this, null, pageIndex, pageSize); } /** - * Set an ordered bind parameter according to its position. Note that the - * position starts at 1 to be consistent with JDBC PreparedStatement. You - * need to set a parameter value for each ? you have in the query. - */ - public DefaultOrmQuery setParameter(int position, Object value) { - if (bindParams == null) { - bindParams = new BindParams(); - } - bindParams.setParameter(position, value); - return this; - } + * Set an ordered bind parameter according to its position. Note that the position starts at 1 to + * be consistent with JDBC PreparedStatement. You need to set a parameter value for each ? you + * have in the query. + */ + public DefaultOrmQuery setParameter(int position, Object value) { + if (bindParams == null) { + bindParams = new BindParams(); + } + bindParams.setParameter(position, value); + return this; + } - /** - * Set a named bind parameter. Named parameters have a colon to prefix the - * name. - */ - public DefaultOrmQuery setParameter(String name, Object value) { - if (bindParams == null) { - bindParams = new BindParams(); - } - bindParams.setParameter(name, value); - return this; - } - - public OrderBy getOrderBy() { - return orderBy; - } + /** + * Set a named bind parameter. Named parameters have a colon to prefix the name. + */ + public DefaultOrmQuery setParameter(String name, Object value) { + if (bindParams == null) { + bindParams = new BindParams(); + } + bindParams.setParameter(name, value); + return this; + } - /** - * Return the order by clause. - */ - public String getRawWhereClause() { - return rawWhereClause; - } + public OrderBy getOrderBy() { + return orderBy; + } - public OrderBy orderBy() { - return order(); - } - - public OrderBy order() { - if (orderBy == null){ - orderBy = new OrderBy(this, null); - } - return orderBy; - } + /** + * Return the order by clause. + */ + public String getRawWhereClause() { + return rawWhereClause; + } - public DefaultOrmQuery setOrderBy(String orderByClause) { - return order(orderByClause); - } - - public DefaultOrmQuery orderBy(String orderByClause) { - return order(orderByClause); - } + public OrderBy orderBy() { + return order(); + } - public DefaultOrmQuery order(String orderByClause) { - if (orderByClause == null || orderByClause.trim().length() == 0){ - this.orderBy = null; - } else { - this.orderBy = new OrderBy(this, orderByClause); - } - return this; - } + public OrderBy order() { + if (orderBy == null) { + orderBy = new OrderBy(this, null); + } + return orderBy; + } - public DefaultOrmQuery setOrderBy(OrderBy orderBy) { - return setOrder(orderBy); - } - - public DefaultOrmQuery setOrder(OrderBy orderBy) { - this.orderBy = orderBy; - if (orderBy != null){ - orderBy.setQuery(this); - } - return this; - } + public DefaultOrmQuery setOrderBy(String orderByClause) { + return order(orderByClause); + } + + public DefaultOrmQuery orderBy(String orderByClause) { + return order(orderByClause); + } + + public DefaultOrmQuery order(String orderByClause) { + if (orderByClause == null || orderByClause.trim().length() == 0) { + this.orderBy = null; + } else { + this.orderBy = new OrderBy(this, orderByClause); + } + return this; + } + + public DefaultOrmQuery setOrderBy(OrderBy orderBy) { + return setOrder(orderBy); + } + + public DefaultOrmQuery setOrder(OrderBy orderBy) { + this.orderBy = orderBy; + if (orderBy != null) { + orderBy.setQuery(this); + } + return this; + } /** * return true if user specified to use SQL DISTINCT (effectively excludes id property). @@ -1043,29 +1041,30 @@ public class DefaultOrmQuery implements SpiQuery { return this; } - /** - * Return true if this query uses SQL DISTINCT either explicitly by the user or internally defined by ebean. - */ - public boolean isDistinctQuery() { - return distinct || sqlDistinct; - } + /** + * Return true if this query uses SQL DISTINCT either explicitly by the user or internally defined + * by ebean. + */ + public boolean isDistinctQuery() { + return distinct || sqlDistinct; + } - /** - * Internally set to use SQL DISTINCT on the query but still have id property included. - */ - public DefaultOrmQuery setSqlDistinct(boolean sqlDistinct) { - this.sqlDistinct = sqlDistinct; - return this; - } + /** + * Internally set to use SQL DISTINCT on the query but still have id property included. + */ + public DefaultOrmQuery setSqlDistinct(boolean sqlDistinct) { + this.sqlDistinct = sqlDistinct; + return this; + } - public Class getBeanType() { - return beanType; - } + public Class getBeanType() { + return beanType; + } + + public void setDetail(OrmQueryDetail detail) { + this.detail = detail; + } - public void setDetail(OrmQueryDetail detail) { - this.detail = detail; - } - public boolean tuneFetchProperties(OrmQueryDetail tunedDetail) { return detail.tuneFetchProperties(tunedDetail); } @@ -1074,241 +1073,238 @@ public class DefaultOrmQuery implements SpiQuery { return detail; } - /** - * Return any beans that should be added to the persistence context prior to - * executing the query. - */ - public final ArrayList getContextAdditions() { - return contextAdditions; - } + /** + * Return any beans that should be added to the persistence context prior to executing the query. + */ + public final ArrayList getContextAdditions() { + return contextAdditions; + } - /** - * Add a bean to the context additions. - *

- * These are added to the persistence context before executing the query. - *

- */ - public void contextAdd(EntityBean bean) { - if (contextAdditions == null) { - contextAdditions = new ArrayList(); - } - contextAdditions.add(bean); - } + /** + * Add a bean to the context additions. + *

+ * These are added to the persistence context before executing the query. + *

+ */ + public void contextAdd(EntityBean bean) { + if (contextAdditions == null) { + contextAdditions = new ArrayList(); + } + contextAdditions.add(bean); + } - public String toString() { - return "Query [" + whereExpressions + "]"; - } + public String toString() { + return "Query [" + whereExpressions + "]"; + } - public TableJoin getIncludeTableJoin() { - return includeTableJoin; - } + public TableJoin getIncludeTableJoin() { + return includeTableJoin; + } - public void setIncludeTableJoin(TableJoin includeTableJoin) { - this.includeTableJoin = includeTableJoin; - } + public void setIncludeTableJoin(TableJoin includeTableJoin) { + this.includeTableJoin = includeTableJoin; + } - public int getFirstRow() { - return firstRow; - } + public int getFirstRow() { + return firstRow; + } - public DefaultOrmQuery setFirstRow(int firstRow) { - this.firstRow = firstRow; - return this; - } + public DefaultOrmQuery setFirstRow(int firstRow) { + this.firstRow = firstRow; + return this; + } - public int getMaxRows() { - return maxRows; - } + public int getMaxRows() { + return maxRows; + } - public DefaultOrmQuery setMaxRows(int maxRows) { - this.maxRows = maxRows; - return this; - } + public DefaultOrmQuery setMaxRows(int maxRows) { + this.maxRows = maxRows; + return this; + } - public String getMapKey() { - return mapKey; - } + public String getMapKey() { + return mapKey; + } - public DefaultOrmQuery setMapKey(String mapKey) { - this.mapKey = mapKey; - return this; - } + public DefaultOrmQuery setMapKey(String mapKey) { + this.mapKey = mapKey; + return this; + } - public Object getId() { - return id; - } + public Object getId() { + return id; + } - public DefaultOrmQuery setId(Object id) { - if (id == null) { - throw new NullPointerException("The id is null"); - } - this.id = id; - return this; - } + public DefaultOrmQuery setId(Object id) { + if (id == null) { + throw new NullPointerException("The id is null"); + } + this.id = id; + return this; + } - public BindParams getBindParams() { - return bindParams; - } + public BindParams getBindParams() { + return bindParams; + } - public String getQuery() { - return query; - } + public String getQuery() { + return query; + } - public DefaultOrmQuery addWhere(String addToWhereClause) { - return where(addToWhereClause); - } + public DefaultOrmQuery addWhere(String addToWhereClause) { + return where(addToWhereClause); + } - public DefaultOrmQuery addWhere(Expression expression) { - return where(expression); - } + public DefaultOrmQuery addWhere(Expression expression) { + return where(expression); + } - public ExpressionList addWhere() { - return where(); - } + public ExpressionList addWhere() { + return where(); + } - public DefaultOrmQuery where(String addToWhereClause) { - if (additionalWhere == null) { - additionalWhere = addToWhereClause; - } else { - additionalWhere += " " + addToWhereClause; - } - return this; - } + public DefaultOrmQuery where(String addToWhereClause) { + if (additionalWhere == null) { + additionalWhere = addToWhereClause; + } else { + additionalWhere += " " + addToWhereClause; + } + return this; + } - public DefaultOrmQuery where(Expression expression) { - if (whereExpressions == null) { - whereExpressions = new DefaultExpressionList(this, null); - } - whereExpressions.add(expression); - return this; - } + public DefaultOrmQuery where(Expression expression) { + if (whereExpressions == null) { + whereExpressions = new DefaultExpressionList(this, null); + } + whereExpressions.add(expression); + return this; + } - public ExpressionList where() { - if (whereExpressions == null) { - whereExpressions = new DefaultExpressionList(this, null); - } - return whereExpressions; - } - - public ExpressionList filterMany(String prop){ - - OrmQueryProperties chunk = detail.getChunk(prop, true); - return chunk.filterMany(this); - } - + public ExpressionList where() { + if (whereExpressions == null) { + whereExpressions = new DefaultExpressionList(this, null); + } + return whereExpressions; + } - public void setFilterMany(String prop, ExpressionList filterMany){ - if (filterMany != null){ - OrmQueryProperties chunk = detail.getChunk(prop, true); - chunk.setFilterMany((SpiExpressionList)filterMany); - } - } + public ExpressionList filterMany(String prop) { - public DefaultOrmQuery addHaving(String addToHavingClause) { - return having(addToHavingClause); - } + OrmQueryProperties chunk = detail.getChunk(prop, true); + return chunk.filterMany(this); + } - public DefaultOrmQuery addHaving(Expression expression) { - return having(expression); - } + public void setFilterMany(String prop, ExpressionList filterMany) { + if (filterMany != null) { + OrmQueryProperties chunk = detail.getChunk(prop, true); + chunk.setFilterMany((SpiExpressionList) filterMany); + } + } - public ExpressionList addHaving() { - return having(); - } + public DefaultOrmQuery addHaving(String addToHavingClause) { + return having(addToHavingClause); + } - public DefaultOrmQuery having(String addToHavingClause) { - if (additionalHaving == null) { - additionalHaving = addToHavingClause; - } else { - additionalHaving += " " + addToHavingClause; - } - return this; - } + public DefaultOrmQuery addHaving(Expression expression) { + return having(expression); + } - public DefaultOrmQuery having(Expression expression) { - if (havingExpressions == null) { - havingExpressions = new DefaultExpressionList(this, null); - } - havingExpressions.add(expression); - return this; - } + public ExpressionList addHaving() { + return having(); + } - public ExpressionList having() { - if (havingExpressions == null) { - havingExpressions = new DefaultExpressionList(this, null); - } - return havingExpressions; - } + public DefaultOrmQuery having(String addToHavingClause) { + if (additionalHaving == null) { + additionalHaving = addToHavingClause; + } else { + additionalHaving += " " + addToHavingClause; + } + return this; + } - public SpiExpressionList getHavingExpressions() { - return havingExpressions; - } + public DefaultOrmQuery having(Expression expression) { + if (havingExpressions == null) { + havingExpressions = new DefaultExpressionList(this, null); + } + havingExpressions.add(expression); + return this; + } - public SpiExpressionList getWhereExpressions() { - return whereExpressions; - } + public ExpressionList having() { + if (havingExpressions == null) { + havingExpressions = new DefaultExpressionList(this, null); + } + return havingExpressions; + } - public String getGeneratedSql() { - return generatedSql; - } + public SpiExpressionList getHavingExpressions() { + return havingExpressions; + } - public void setGeneratedSql(String generatedSql) { - this.generatedSql = generatedSql; - } + public SpiExpressionList getWhereExpressions() { + return whereExpressions; + } - public Query setBufferFetchSizeHint(int bufferFetchSizeHint){ - this.bufferFetchSizeHint = bufferFetchSizeHint; - return this; - } - - public int getBufferFetchSizeHint() { - return bufferFetchSizeHint; - } + public String getGeneratedSql() { + return generatedSql; + } - public void setBeanCollectionTouched(BeanCollectionTouched notify) { - this.beanCollectionTouched = notify; - } + public void setGeneratedSql(String generatedSql) { + this.generatedSql = generatedSql; + } - public BeanCollectionTouched getBeanCollectionTouched() { - return beanCollectionTouched; - } + public Query setBufferFetchSizeHint(int bufferFetchSizeHint) { + this.bufferFetchSizeHint = bufferFetchSizeHint; + return this; + } - public List getIdList() { - return partialIds; - } + public int getBufferFetchSizeHint() { + return bufferFetchSizeHint; + } - public void setIdList(List partialIds) { - this.partialIds = partialIds; - } + public void setBeanCollectionTouched(BeanCollectionTouched notify) { + this.beanCollectionTouched = notify; + } - public boolean isFutureFetch() { - return futureFetch; - } + public BeanCollectionTouched getBeanCollectionTouched() { + return beanCollectionTouched; + } - public void setFutureFetch(boolean backgroundFetch) { - this.futureFetch = backgroundFetch; - } + public List getIdList() { + return partialIds; + } - public void setCancelableQuery(CancelableQuery cancelableQuery) { - synchronized (this) { - this.cancelableQuery = cancelableQuery; - } - } - - public void cancel() { - synchronized (this) { - cancelled = true; - if (cancelableQuery != null){ - cancelableQuery.cancel(); - } - } - } + public void setIdList(List partialIds) { + this.partialIds = partialIds; + } - public boolean isCancelled() { - synchronized (this) { - return cancelled; - } - } - + public boolean isFutureFetch() { + return futureFetch; + } + + public void setFutureFetch(boolean backgroundFetch) { + this.futureFetch = backgroundFetch; + } + + public void setCancelableQuery(CancelableQuery cancelableQuery) { + synchronized (this) { + this.cancelableQuery = cancelableQuery; + } + } + + public void cancel() { + synchronized (this) { + cancelled = true; + if (cancelableQuery != null) { + cancelableQuery.cancel(); + } + } + } + + public boolean isCancelled() { + synchronized (this) { + return cancelled; + } + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmUpdate.java b/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmUpdate.java index e562149a9..4e94cba4a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmUpdate.java +++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmUpdate.java @@ -71,8 +71,7 @@ public final class DefaultOrmUpdate implements SpiUpdate, Serializable { this.name = namedUpdate.getName(); this.notifyCache = namedUpdate.isNotifyCache(); - // named updates are always converted to sql as part - // of the initialisation + // named updates are always converted to sql as part of the initialisation this.updateStatement = namedUpdate.getSqlUpdateStatement(); this.type = deriveType(updateStatement); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultRelationalQuery.java b/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultRelationalQuery.java index 9dd26d3fa..a4c148340 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultRelationalQuery.java +++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultRelationalQuery.java @@ -223,7 +223,5 @@ public class DefaultRelationalQuery implements SpiSqlQuery { return cancelled; } } - - - + } diff --git a/src/main/java/com/avaje/ebeaninternal/server/querydefn/NaturalKeyBindParam.java b/src/main/java/com/avaje/ebeaninternal/server/querydefn/NaturalKeyBindParam.java index 44012c22a..9aa75f67d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/NaturalKeyBindParam.java +++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/NaturalKeyBindParam.java @@ -2,21 +2,21 @@ package com.avaje.ebeaninternal.server.querydefn; public class NaturalKeyBindParam { - private final String name; - - private final Object value; - - public NaturalKeyBindParam(String name, Object value) { - this.name = name; - this.value = value; - } + private final String name; - public String getName() { - return name; - } + private final Object value; + + public NaturalKeyBindParam(String name, Object value) { + this.name = name; + this.value = value; + } + + public String getName() { + return name; + } + + public Object getValue() { + return value; + } - public Object getValue() { - return value; - } - } diff --git a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetail.java b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetail.java index 3e70e6fc7..486809b68 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetail.java +++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetail.java @@ -27,461 +27,456 @@ import com.avaje.ebeaninternal.server.query.SplitName; * Holds the select() and join() details of a ORM query. *

*

- * It is worth noting that for autoFetch a "tuned fetch info" builds an instance - * of OrmQueryDetail. Tuning a query is a matter of replacing an instance of - * this class with one that has been tuned with select() and join() set. + * It is worth noting that for autoFetch a "tuned fetch info" builds an instance of OrmQueryDetail. + * Tuning a query is a matter of replacing an instance of this class with one that has been tuned + * with select() and join() set. *

*/ public class OrmQueryDetail implements Serializable { - private static final long serialVersionUID = -2510486880141461807L; + private static final long serialVersionUID = -2510486880141461807L; - /** - * Root level properties. - */ - private OrmQueryProperties baseProps = new OrmQueryProperties(); + /** + * Root level properties. + */ + private OrmQueryProperties baseProps = new OrmQueryProperties(); - /** - * Contains the fetch/lazy/query joins and their properties. - */ - private LinkedHashMap fetchPaths = new LinkedHashMap(8); + /** + * Contains the fetch/lazy/query joins and their properties. + */ + private LinkedHashMap fetchPaths = new LinkedHashMap(8); - private LinkedHashSet includes = new LinkedHashSet(8); + private LinkedHashSet includes = new LinkedHashSet(8); - /** - * Return a deep copy of the OrmQueryDetail. - */ - public OrmQueryDetail copy() { - OrmQueryDetail copy = new OrmQueryDetail(); - copy.baseProps = baseProps.copy(); - for (Map.Entry entry : fetchPaths.entrySet()) { - copy.fetchPaths.put(entry.getKey(), entry.getValue().copy()); - } - copy.includes = new LinkedHashSet(includes); - return copy; + /** + * Return a deep copy of the OrmQueryDetail. + */ + public OrmQueryDetail copy() { + OrmQueryDetail copy = new OrmQueryDetail(); + copy.baseProps = baseProps.copy(); + for (Map.Entry entry : fetchPaths.entrySet()) { + copy.fetchPaths.put(entry.getKey(), entry.getValue().copy()); + } + copy.includes = new LinkedHashSet(includes); + return copy; + } + + /** + * Calculate the hash for the query plan. + */ + public void queryPlanHash(BeanQueryRequest request, HashQueryPlanBuilder builder) { + if (baseProps == null) { + builder.add(false); + } else { + builder.add(true); + baseProps.queryPlanHash(request, builder); } - /** - * Calculate the hash for the query plan. - */ - public void queryPlanHash(BeanQueryRequest request, HashQueryPlanBuilder builder) { - if (baseProps == null) { - builder.add(false); + if (fetchPaths != null) { + for (OrmQueryProperties p : fetchPaths.values()) { + p.queryPlanHash(request, builder); + } + } + } + + /** + * Return true if equal in terms of autofetch (select and joins). + */ + public boolean isAutoFetchEqual(OrmQueryDetail otherDetail) { + return autofetchPlanHash() == otherDetail.autofetchPlanHash(); + } + + /** + * Calculate the hash for the query plan. + */ + private int autofetchPlanHash() { + + int hc = (baseProps == null ? 1 : baseProps.autofetchPlanHash()); + + if (fetchPaths != null) { + for (OrmQueryProperties p : fetchPaths.values()) { + hc = hc * 31 + p.autofetchPlanHash(); + } + } + + return hc; + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + if (baseProps != null) { + sb.append("select ").append(baseProps); + } + if (fetchPaths != null) { + for (OrmQueryProperties join : fetchPaths.values()) { + sb.append(" fetch ").append(join); + } + } + return sb.toString(); + } + + public int hashCode() { + throw new RuntimeException("should not use"); + } + + /** + * set the properties to include on the base / root entity. + */ + public void select(String columns) { + baseProps = new OrmQueryProperties(null, columns); + } + + public boolean containsProperty(String property) { + if (baseProps == null) { + return true; + } else { + return baseProps.isIncluded(property); + } + } + + /** + * Set the base / root query properties. + */ + public void setBase(OrmQueryProperties baseProps) { + this.baseProps = baseProps; + } + + public List removeSecondaryQueries() { + return removeSecondaryQueries(false); + } + + public List removeSecondaryLazyQueries() { + return removeSecondaryQueries(true); + } + + private List removeSecondaryQueries(boolean lazyQuery) { + + ArrayList matchingPaths = new ArrayList(2); + + for (OrmQueryProperties chunk : fetchPaths.values()) { + boolean match = lazyQuery ? chunk.isLazyFetch() : chunk.isQueryFetch(); + if (match) { + matchingPaths.add(chunk.getPath()); + } + } + + if (matchingPaths.size() == 0) { + return null; + } + + // sort into depth order to remove + Collections.sort(matchingPaths); + + // the list of secondary queries + ArrayList props = new ArrayList(2); + + for (int i = 0; i < matchingPaths.size(); i++) { + String path = matchingPaths.get(i); + includes.remove(path); + OrmQueryProperties secQuery = fetchPaths.remove(path); + if (secQuery == null) { + // the path has already been removed by another + // secondary query + } else { - builder.add(true); - baseProps.queryPlanHash(request, builder); - } - - if (fetchPaths != null) { - for (OrmQueryProperties p : fetchPaths.values()) { - p.queryPlanHash(request, builder); + props.add(secQuery); + + // remove any child properties for this path + Iterator pass2It = fetchPaths.values().iterator(); + while (pass2It.hasNext()) { + OrmQueryProperties pass2Prop = pass2It.next(); + if (secQuery.isChild(pass2Prop)) { + // remove join to secondary query from the main query + // and add to this secondary query + pass2It.remove(); + includes.remove(pass2Prop.getPath()); + secQuery.add(pass2Prop); + } } } } - /** - * Return true if equal in terms of autofetch (select and joins). - */ - public boolean isAutoFetchEqual(OrmQueryDetail otherDetail) { - return autofetchPlanHash() == otherDetail.autofetchPlanHash(); + // Add the secondary queries as select properties + // to the parent chunk to ensure the foreign keys + // are included in the query + for (int i = 0; i < props.size(); i++) { + String path = props.get(i).getPath(); + // split into parent and property + String[] split = SplitName.split(path); + // add property to parent chunk + OrmQueryProperties chunk = getChunk(split[0], true); + chunk.addSecondaryQueryJoin(split[1]); } - /** - * Calculate the hash for the query plan. - */ - private int autofetchPlanHash() { + return props; + } - int hc = (baseProps == null ? 1 : baseProps.autofetchPlanHash()); + public boolean tuneFetchProperties(OrmQueryDetail tunedDetail) { - if (fetchPaths != null) { - for (OrmQueryProperties p : fetchPaths.values()) { - hc = hc * 31 + p.autofetchPlanHash(); - } - } + boolean tuned = false; - return hc; - } + OrmQueryProperties tunedRoot = tunedDetail.getChunk(null, false); + if (tunedRoot != null && tunedRoot.hasProperties()) { + tuned = true; + baseProps.setTunedProperties(tunedRoot); - public String toString() { - StringBuilder sb = new StringBuilder(); - if (baseProps != null) { - sb.append("select ").append(baseProps); - } - if (fetchPaths != null) { - for (OrmQueryProperties join : fetchPaths.values()) { - sb.append(" fetch ").append(join); - } - } - return sb.toString(); - } - - public int hashCode() { - throw new RuntimeException("should not use"); - } - - /** - * set the properties to include on the base / root entity. - */ - public void select(String columns) { - baseProps = new OrmQueryProperties(null, columns); - } - - public boolean containsProperty(String property) { - if (baseProps == null) { - return true; + for (OrmQueryProperties tunedChunk : tunedDetail.fetchPaths.values()) { + OrmQueryProperties chunk = getChunk(tunedChunk.getPath(), false); + if (chunk != null) { + // set the properties to select + chunk.setTunedProperties(tunedChunk); } else { - return baseProps.isIncluded(property); + // add a missing join + putFetchPath(tunedChunk.copy()); } + } + } + return tuned; + } + + /** + * Matches a join() method of the query. + */ + public void putFetchPath(OrmQueryProperties chunk) { + String path = chunk.getPath(); + fetchPaths.put(path, chunk); + includes.add(path); + } + + /** + * Remove all joins and properties. + *

+ * Typically for the row count query. + *

+ */ + public void clear() { + includes.clear(); + fetchPaths.clear(); + } + + /** + * Set the fetch properties and configuration for a given path. + * + * @param path + * the property to join + * @param partialProps + * the properties on the join property to include + */ + public OrmQueryProperties addFetch(String path, String partialProps, FetchConfig fetchConfig) { + + OrmQueryProperties chunk = getChunk(path, true); + chunk.setProperties(partialProps); + chunk.setFetchConfig(fetchConfig); + return chunk; + } + + public void sortFetchPaths(BeanDescriptor d) { + + LinkedHashMap sorted = new LinkedHashMap(fetchPaths.size()); + + for (OrmQueryProperties p : fetchPaths.values()) { + sortFetchPaths(d, p, sorted); } - /** - * Set the base / root query properties. - */ - public void setBase(OrmQueryProperties baseProps) { - this.baseProps = baseProps; - } + fetchPaths = sorted; + } - public List removeSecondaryQueries() { - return removeSecondaryQueries(false); - } + private void sortFetchPaths(BeanDescriptor d, OrmQueryProperties p, + LinkedHashMap sorted) { - public List removeSecondaryLazyQueries() { - return removeSecondaryQueries(true); - } - - private List removeSecondaryQueries(boolean lazyQuery) { - - ArrayList matchingPaths = new ArrayList(2); - - for (OrmQueryProperties chunk : fetchPaths.values()) { - boolean match = lazyQuery ? chunk.isLazyFetch() : chunk.isQueryFetch(); - if (match) { - matchingPaths.add(chunk.getPath()); - } + String path = p.getPath(); + if (!sorted.containsKey(path)) { + String parentPath = p.getParentPath(); + if (parentPath == null || sorted.containsKey(parentPath)) { + // off root path or parent already ahead in fetch order + sorted.put(path, p); + } else { + OrmQueryProperties parentProp = fetchPaths.get(parentPath); + if (parentProp == null) { + ElPropertyValue el = d.getElGetValue(parentPath); + if (el == null) { + String msg = "Path [" + parentPath + "] not valid from " + d.getFullName(); + throw new PersistenceException(msg); + } + // add a missing parent path just fetching the Id property + BeanPropertyAssoc assocOne = (BeanPropertyAssoc) el.getBeanProperty(); + parentProp = new OrmQueryProperties(parentPath, assocOne.getTargetIdProperty()); } - - if (matchingPaths.size() == 0) { - return null; + if (parentProp != null) { + sortFetchPaths(d, parentProp, sorted); } + sorted.put(path, p); + } + } + } - // sort into depth order to remove - Collections.sort(matchingPaths); + /** + * Convert 'fetch joins' to 'many' properties over to 'query joins'. + */ + public void convertManyFetchJoinsToQueryJoins(BeanDescriptor beanDescriptor, String lazyLoadManyPath, + boolean allowOne, int queryBatch) { - // the list of secondary queries - ArrayList props = new ArrayList(2); + ArrayList manyChunks = new ArrayList(3); - for (int i = 0; i < matchingPaths.size(); i++) { - String path = matchingPaths.get(i); - includes.remove(path); - OrmQueryProperties secQuery = fetchPaths.remove(path); - if (secQuery == null) { - // the path has already been removed by another - // secondary query + // the name of the many fetch property if there is one + String manyFetchProperty = null; - } else { - props.add(secQuery); + // flag that is set once the many fetch property is chosen + boolean fetchJoinFirstMany = allowOne; - // remove any child properties for this path - Iterator pass2It = fetchPaths.values().iterator(); - while (pass2It.hasNext()) { - OrmQueryProperties pass2Prop = pass2It.next(); - if (secQuery.isChild(pass2Prop)) { - // remove join to secondary query from the main query - // and add to this secondary query - pass2It.remove(); - includes.remove(pass2Prop.getPath()); - secQuery.add(pass2Prop); - } - } - } + sortFetchPaths(beanDescriptor); + + for (String fetchPath : fetchPaths.keySet()) { + ElPropertyDeploy elProp = beanDescriptor.getElPropertyDeploy(fetchPath); + if (elProp.containsManySince(manyFetchProperty)) { + + // this is a join to a *ToMany + OrmQueryProperties chunk = fetchPaths.get(fetchPath); + if (chunk.isFetchJoin() && !isLazyLoadManyRoot(lazyLoadManyPath, chunk) + && !hasParentSecJoin(lazyLoadManyPath, chunk)) { + // this is a 'fetch join' (included in main query) + if (fetchJoinFirstMany) { + // letting the first one remain a 'fetch join' + fetchJoinFirstMany = false; + manyFetchProperty = fetchPath; + } else { + // convert this one over to a 'query join' + manyChunks.add(chunk); + } } - - // Add the secondary queries as select properties - // to the parent chunk to ensure the foreign keys - // are included in the query - for (int i = 0; i < props.size(); i++) { - String path = props.get(i).getPath(); - // split into parent and property - String[] split = SplitName.split(path); - // add property to parent chunk - OrmQueryProperties chunk = getChunk(split[0], true); - chunk.addSecondaryQueryJoin(split[1]); - } - - return props; + } } - public boolean tuneFetchProperties(OrmQueryDetail tunedDetail) { - - boolean tuned = false; - - OrmQueryProperties tunedRoot = tunedDetail.getChunk(null, false); - if (tunedRoot != null && tunedRoot.hasProperties()) { - tuned = true; - baseProps.setTunedProperties(tunedRoot); - - for (OrmQueryProperties tunedChunk : tunedDetail.fetchPaths.values()) { - OrmQueryProperties chunk = getChunk(tunedChunk.getPath(), false); - if (chunk != null) { - // set the properties to select - chunk.setTunedProperties(tunedChunk); - } else { - // add a missing join - putFetchPath(tunedChunk.copy()); - } - } - } - return tuned; + for (int i = 0; i < manyChunks.size(); i++) { + // convert 'fetch joins' over to 'query joins' + manyChunks.get(i).setQueryFetch(queryBatch, true); } + } - /** - * Matches a join() method of the query. - */ - public void putFetchPath(OrmQueryProperties chunk) { - String path = chunk.getPath(); - fetchPaths.put(path, chunk); - includes.add(path); + /** + * Return true if this is actually the root level of a +query/+lazy loading query. + */ + private boolean isLazyLoadManyRoot(String lazyLoadManyPath, OrmQueryProperties chunk) { + if (lazyLoadManyPath != null && lazyLoadManyPath.equals(chunk.getPath())) { + return true; } + return false; + } - /** - * Remove all joins and properties. - *

- * Typically for the row count query. - *

- */ - public void clear() { - includes.clear(); - fetchPaths.clear(); - } - - /** - * Set the fetch properties and configuration for a given path. - * - * @param path - * the property to join - * @param partialProps - * the properties on the join property to include - */ - public OrmQueryProperties addFetch(String path, String partialProps, FetchConfig fetchConfig) { - - OrmQueryProperties chunk = getChunk(path, true); - chunk.setProperties(partialProps); - chunk.setFetchConfig(fetchConfig); - return chunk; - } - - public void sortFetchPaths(BeanDescriptor d) { - - LinkedHashMap sorted = new LinkedHashMap(fetchPaths.size()); - - for (OrmQueryProperties p : fetchPaths.values()) { - sortFetchPaths(d, p, sorted); - } - - fetchPaths = sorted; - } - - private void sortFetchPaths(BeanDescriptor d, OrmQueryProperties p, - LinkedHashMap sorted) { - - String path = p.getPath(); - if (!sorted.containsKey(path)) { - String parentPath = p.getParentPath(); - if (parentPath == null || sorted.containsKey(parentPath)) { - // off root path or parent already ahead in fetch order - sorted.put(path, p); - } else { - OrmQueryProperties parentProp = fetchPaths.get(parentPath); - if (parentProp == null) { - ElPropertyValue el = d.getElGetValue(parentPath); - if (el == null) { - String msg = "Path [" + parentPath + "] not valid from " + d.getFullName(); - throw new PersistenceException(msg); - } - // add a missing parent path just fetching the Id property - BeanPropertyAssoc assocOne = (BeanPropertyAssoc) el.getBeanProperty(); - parentProp = new OrmQueryProperties(parentPath, assocOne.getTargetIdProperty()); - } - if (parentProp != null) { - sortFetchPaths(d, parentProp, sorted); - } - sorted.put(path, p); - } - } - } - - /** - * Convert 'fetch joins' to 'many' properties over to 'query joins'. - */ - public void convertManyFetchJoinsToQueryJoins(BeanDescriptor beanDescriptor, String lazyLoadManyPath, - boolean allowOne, int queryBatch) { - - ArrayList manyChunks = new ArrayList(3); - - // the name of the many fetch property if there is one - String manyFetchProperty = null; - - // flag that is set once the many fetch property is chosen - boolean fetchJoinFirstMany = allowOne; - - sortFetchPaths(beanDescriptor); - - for (String fetchPath : fetchPaths.keySet()) { - ElPropertyDeploy elProp = beanDescriptor.getElPropertyDeploy(fetchPath); - if (elProp.containsManySince(manyFetchProperty)) { - - // this is a join to a *ToMany - OrmQueryProperties chunk = fetchPaths.get(fetchPath); - if (chunk.isFetchJoin() - && !isLazyLoadManyRoot(lazyLoadManyPath, chunk) - && !hasParentSecJoin(lazyLoadManyPath, chunk)) { - // this is a 'fetch join' (included in main query) - if (fetchJoinFirstMany) { - // letting the first one remain a 'fetch join' - fetchJoinFirstMany = false; - manyFetchProperty = fetchPath; - } else { - // convert this one over to a 'query join' - manyChunks.add(chunk); - } - } - } - } - - for (int i = 0; i < manyChunks.size(); i++) { - // convert 'fetch joins' over to 'query joins' - manyChunks.get(i).setQueryFetch(queryBatch, true); - } - } - - /** - * Return true if this is actually the root level of a +query/+lazy loading - * query. - */ - private boolean isLazyLoadManyRoot(String lazyLoadManyPath, OrmQueryProperties chunk) { - if (lazyLoadManyPath != null && lazyLoadManyPath.equals(chunk.getPath())) { - return true; - } + /** + * If the chunk has a parent that is a query or lazy join. In this case it does not need to be + * converted. + */ + private boolean hasParentSecJoin(String lazyLoadManyPath, OrmQueryProperties chunk) { + OrmQueryProperties parent = getParent(chunk); + if (parent == null) { + return false; + } else { + if (lazyLoadManyPath != null && lazyLoadManyPath.equals(parent.getPath())) { return false; + } else if (!parent.isFetchJoin()) { + return true; + } else { + return hasParentSecJoin(lazyLoadManyPath, parent); + } + } + } + + /** + * Return the parent chunk. + */ + private OrmQueryProperties getParent(OrmQueryProperties chunk) { + String parentPath = chunk.getParentPath(); + return parentPath == null ? null : fetchPaths.get(parentPath); + } + + /** + * Set any default select clauses for the main bean and any joins that have not explicitly defined + * a select clause. + *

+ * That is this will use FetchType.LAZY to exclude some properties by default. + *

+ */ + public void setDefaultSelectClause(BeanDescriptor desc) { + + if (desc.hasDefaultSelectClause() && !hasSelectClause()) { + if (baseProps == null) { + baseProps = new OrmQueryProperties(); + } + baseProps.setDefaultProperties(desc.getDefaultSelectClause(), desc.getDefaultSelectClauseSet()); } - /** - * If the chunk has a parent that is a query or lazy join. In this case it - * does not need to be converted. - */ - private boolean hasParentSecJoin(String lazyLoadManyPath, OrmQueryProperties chunk) { - OrmQueryProperties parent = getParent(chunk); - if (parent == null) { - return false; - } else { - if (lazyLoadManyPath != null && lazyLoadManyPath.equals(parent.getPath())) { - return false; - } else if (!parent.isFetchJoin()) { - return true; - } else { - return hasParentSecJoin(lazyLoadManyPath, parent); - } + for (OrmQueryProperties joinProps : fetchPaths.values()) { + if (!joinProps.hasSelectClause()) { + BeanDescriptor assocDesc = desc.getBeanDescriptor(joinProps.getPath()); + if (assocDesc.hasDefaultSelectClause()) { + // use the default select clause + joinProps.setDefaultProperties(assocDesc.getDefaultSelectClause(), assocDesc.getDefaultSelectClauseSet()); } + } } + } - /** - * Return the parent chunk. - */ - private OrmQueryProperties getParent(OrmQueryProperties chunk) { - String parentPath = chunk.getParentPath(); - return parentPath == null ? null : fetchPaths.get(parentPath); + public boolean hasSelectClause() { + return (baseProps != null && baseProps.hasSelectClause()); + } + + /** + * Return true if the query detail has neither select properties specified or any joins defined. + */ + public boolean isEmpty() { + return fetchPaths.isEmpty() && (baseProps == null || !baseProps.hasProperties()); + } + + /** + * Return true if there are no joins. + */ + public boolean isJoinsEmpty() { + return fetchPaths.isEmpty(); + } + + /** + * Add the explicit bean join. + *

+ * This is also used to Exclude the matching property from the parent select (aka remove the + * foreign key) because it is now included in it's on node in the SqlTree. + *

+ */ + public void includeBeanJoin(String parentPath, String propertyName) { + OrmQueryProperties parentChunk = getChunk(parentPath, true); + parentChunk.includeBeanJoin(propertyName); + } + + public OrmQueryProperties getChunk(String path, boolean create) { + if (path == null) { + return baseProps; } + OrmQueryProperties props = fetchPaths.get(path); + if (create && props == null) { + props = new OrmQueryProperties(path); + putFetchPath(props); + return props; - /** - * Set any default select clauses for the main bean and any joins that have - * not explicitly defined a select clause. - *

- * That is this will use FetchType.LAZY to exclude some properties by - * default. - *

- */ - public void setDefaultSelectClause(BeanDescriptor desc) { - - if (desc.hasDefaultSelectClause() && !hasSelectClause()) { - if (baseProps == null) { - baseProps = new OrmQueryProperties(); - } - baseProps.setDefaultProperties(desc.getDefaultSelectClause(), desc.getDefaultSelectClauseSet()); - } - - for (OrmQueryProperties joinProps : fetchPaths.values()) { - if (!joinProps.hasSelectClause()) { - BeanDescriptor assocDesc = desc.getBeanDescriptor(joinProps.getPath()); - if (assocDesc.hasDefaultSelectClause()) { - // use the default select clause - joinProps.setDefaultProperties(assocDesc.getDefaultSelectClause(), assocDesc.getDefaultSelectClauseSet()); - } - } - } + } else { + return props; } + } - public boolean hasSelectClause() { - return (baseProps != null && baseProps.hasSelectClause()); - } + /** + * Return true if the property is included. + */ + public boolean includes(String path) { - /** - * Return true if the query detail has neither select properties specified - * or any joins defined. - */ - public boolean isEmpty() { - return fetchPaths.isEmpty() && (baseProps == null || !baseProps.hasProperties()); - } + OrmQueryProperties chunk = fetchPaths.get(path); - /** - * Return true if there are no joins. - */ - public boolean isJoinsEmpty() { - return fetchPaths.isEmpty(); - } + // may not have fetch properties if just +cache etc + return chunk != null && !chunk.isCache(); + } - /** - * Add the explicit bean join. - *

- * This is also used to Exclude the matching property from the parent select - * (aka remove the foreign key) because it is now included in it's on node - * in the SqlTree. - *

- */ - public void includeBeanJoin(String parentPath, String propertyName) { - OrmQueryProperties parentChunk = getChunk(parentPath, true); - parentChunk.includeBeanJoin(propertyName); - } - - public OrmQueryProperties getChunk(String path, boolean create) { - if (path == null) { - return baseProps; - } - OrmQueryProperties props = fetchPaths.get(path); - if (create && props == null) { - props = new OrmQueryProperties(path); - putFetchPath(props); - return props; - - } else { - return props; - } - } - - /** - * Return true if the property is included. - */ - public boolean includes(String path) { - - OrmQueryProperties chunk = fetchPaths.get(path); - - // may not have fetch properties if just +cache etc - return chunk != null && !chunk.isCache(); - } - - /** - * Return the property includes for this detail. - */ - public HashSet getIncludes() { - return includes; - } + /** + * Return the property includes for this detail. + */ + public HashSet getIncludes() { + return includes; + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetailParser.java b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetailParser.java index fff9f9da6..b1dfbd5a8 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetailParser.java +++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetailParser.java @@ -3,198 +3,196 @@ package com.avaje.ebeaninternal.server.querydefn; import javax.persistence.PersistenceException; /** - * Parses a Object relational query statement into a OrmQueryDetail and - * OrmQueryAttributes. + * Parses a Object relational query statement into a OrmQueryDetail and OrmQueryAttributes. *

- * The reason they are split into detail and attributes is that the autoFetch - * feature is used to replace the OrmQueryDetail leaving the attributes - * unchanged. + * The reason they are split into detail and attributes is that the autoFetch feature is used to + * replace the OrmQueryDetail leaving the attributes unchanged. *

*/ public class OrmQueryDetailParser { - private final OrmQueryDetail detail = new OrmQueryDetail(); + private final OrmQueryDetail detail = new OrmQueryDetail(); - private int maxRows; + private int maxRows; - private int firstRow; + private int firstRow; - private String rawWhereClause; + private String rawWhereClause; - private String rawOrderBy; + private String rawOrderBy; - private final SimpleTextParser parser; + private final SimpleTextParser parser; - public OrmQueryDetailParser(String oql) { - this.parser = new SimpleTextParser(oql); + public OrmQueryDetailParser(String oql) { + this.parser = new SimpleTextParser(oql); + } + + public void parse() throws PersistenceException { + + parser.nextWord(); + processInitial(); + } + + protected void assign(DefaultOrmQuery query) { + query.setOrmQueryDetail(detail); + query.setFirstRow(firstRow); + query.setMaxRows(maxRows); + query.setRawWhereClause(rawWhereClause); + query.order(rawOrderBy); + } + + private void processInitial() { + if (parser.isMatch("find")) { + OrmQueryProperties props = readFindFetch(); + detail.setBase(props); + } else { + process(); } + while (!parser.isFinished()) { + process(); + } + } - public void parse() throws PersistenceException { + private boolean isFetch() { + return parser.isMatch("fetch") || parser.isMatch("join"); + } + private void process() { + if (isFetch()) { + OrmQueryProperties props = readFindFetch(); + detail.putFetchPath(props); + + } else if (parser.isMatch("where")) { + readWhere(); + + } else if (parser.isMatch("order", "by")) { + readOrderBy(); + + } else if (parser.isMatch("limit")) { + readLimit(); + + } else { + throw new PersistenceException("Query expected 'fetch', 'where','order by' or 'limit' keyword but got [" + + parser.getWord() + "] \r " + parser.getOql()); + } + } + + private void readLimit() { + try { + String maxLimit = parser.nextWord(); + maxRows = Integer.parseInt(maxLimit); + + String offsetKeyword = parser.nextWord(); + if (offsetKeyword != null) { + if (!parser.isMatch("offset")) { + throw new PersistenceException("expected offset keyword but got " + parser.getWord()); + } + String firstRowLimit = parser.nextWord(); + firstRow = Integer.parseInt(firstRowLimit); parser.nextWord(); - processInitial(); + } + } catch (NumberFormatException e) { + String msg = "Expected an integer for maxRows or firstRows in limit offset clause"; + throw new PersistenceException(msg, e); } + } - protected void assign(DefaultOrmQuery query) { - query.setOrmQueryDetail(detail); - query.setFirstRow(firstRow); - query.setMaxRows(maxRows); - query.setRawWhereClause(rawWhereClause); - query.order(rawOrderBy); - } + private void readOrderBy() { + // read the by + parser.nextWord(); - private void processInitial() { - if (parser.isMatch("find")) { - OrmQueryProperties props = readFindFetch(); - detail.setBase(props); - } else { - process(); - } - while (!parser.isFinished()) { - process(); + StringBuilder sb = new StringBuilder(); + while (parser.nextWord() != null) { + if (parser.isMatch("limit")) { + break; + } else { + String w = parser.getWord(); + if (!w.startsWith("(")) { + sb.append(" "); } + sb.append(w); + } + } + rawOrderBy = sb.toString().trim(); + + if (!parser.isFinished()) { + readLimit(); + } + } + + private void readWhere() { + + int nextMode = 0; + StringBuilder sb = new StringBuilder(); + while ((parser.nextWord()) != null) { + if (parser.isMatch("order", "by")) { + nextMode = 1; + break; + + } else if (parser.isMatch("limit")) { + nextMode = 2; + break; + + } else { + sb.append(" ").append(parser.getWord()); + } + } + String whereClause = sb.toString().trim(); + if (whereClause.length() > 0) { + rawWhereClause = whereClause; } - private boolean isFetch() { - return parser.isMatch("fetch") || parser.isMatch("join"); + if (nextMode == 1) { + readOrderBy(); + } else if (nextMode == 2) { + readLimit(); } + } - private void process() { - if (isFetch()) { - OrmQueryProperties props = readFindFetch(); - detail.putFetchPath(props); + private OrmQueryProperties readFindFetch() { - } else if (parser.isMatch("where")) { - readWhere(); + boolean readAlias = false; - } else if (parser.isMatch("order", "by")) { - readOrderBy(); - - } else if (parser.isMatch("limit")) { - readLimit(); - - } else { - throw new PersistenceException("Query expected 'fetch', 'where','order by' or 'limit' keyword but got [" - + parser.getWord() + "] \r " + parser.getOql()); - } - } - - private void readLimit() { - try { - String maxLimit = parser.nextWord(); - maxRows = Integer.parseInt(maxLimit); - - String offsetKeyword = parser.nextWord(); - if (offsetKeyword != null) { - if (!parser.isMatch("offset")) { - throw new PersistenceException("expected offset keyword but got " + parser.getWord()); - } - String firstRowLimit = parser.nextWord(); - firstRow = Integer.parseInt(firstRowLimit); - parser.nextWord(); - } - } catch (NumberFormatException e) { - String msg = "Expected an integer for maxRows or firstRows in limit offset clause"; - throw new PersistenceException(msg, e); - } - } - - private void readOrderBy() { - // read the by + String props = null; + String path = parser.nextWord(); + String token = null; + while ((token = parser.nextWord()) != null) { + if (!readAlias && parser.isMatch("as")) { + // next token is alias parser.nextWord(); + readAlias = true; - StringBuilder sb = new StringBuilder(); - while (parser.nextWord() != null) { - if (parser.isMatch("limit")) { - break; - } else { - String w = parser.getWord(); - if (!w.startsWith("(")) { - sb.append(" "); - } - sb.append(w); - } - } - rawOrderBy = sb.toString().trim(); + } else if ('(' == token.charAt(0)) { + props = token; + parser.nextWord(); + break; - if (!parser.isFinished()) { - readLimit(); - } + } else if (isFindFetchEnd()) { + break; + + } else if (!readAlias) { + readAlias = true; + + } else { + throw new PersistenceException("Expected (props) or new 'fetch' 'where' but got " + token); + } } - - private void readWhere() { - - int nextMode = 0; - StringBuilder sb = new StringBuilder(); - while ((parser.nextWord()) != null) { - if (parser.isMatch("order", "by")) { - nextMode = 1; - break; - - } else if (parser.isMatch("limit")) { - nextMode = 2; - break; - - } else { - sb.append(" ").append(parser.getWord()); - } - } - String whereClause = sb.toString().trim(); - if (whereClause.length() > 0) { - rawWhereClause = whereClause; - } - - if (nextMode == 1) { - readOrderBy(); - } else if (nextMode == 2) { - readLimit(); - } + if (props != null) { + props = props.substring(1, props.length() - 1); } + return new OrmQueryProperties(path, props); + } - private OrmQueryProperties readFindFetch() { - - boolean readAlias = false; - - String props = null; - String path = parser.nextWord(); - String token = null; - while ((token = parser.nextWord()) != null) { - if (!readAlias && parser.isMatch("as")) { - // next token is alias - parser.nextWord(); - readAlias = true; - - } else if ('(' == token.charAt(0)) { - props = token; - parser.nextWord(); - break; - - } else if (isFindFetchEnd()) { - break; - - } else if (!readAlias) { - readAlias = true; - - } else { - throw new PersistenceException("Expected (props) or new 'fetch' 'where' but got " + token); - } - } - if (props != null) { - props = props.substring(1, props.length() - 1); - } - return new OrmQueryProperties(path, props); + private boolean isFindFetchEnd() { + if (isFetch()) { + return true; } - - private boolean isFindFetchEnd() { - if (isFetch()) { - return true; - } - if (parser.isMatch("where")) { - return true; - } - if (parser.isMatch("order", "by")) { - return true; - } - return false; + if (parser.isMatch("where")) { + return true; } + if (parser.isMatch("order", "by")) { + return true; + } + return false; + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryLimitRequest.java b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryLimitRequest.java index e48817b3c..2f61869d1 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryLimitRequest.java +++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryLimitRequest.java @@ -6,40 +6,40 @@ import com.avaje.ebeaninternal.api.SpiQuery; public class OrmQueryLimitRequest implements SqlLimitRequest { - private final SpiQuery ormQuery; - + private final SpiQuery ormQuery; + private final DatabasePlatform dbPlatform; - private final String sql; - - private final String sqlOrderBy; - + private final String sql; + + private final String sqlOrderBy; + public OrmQueryLimitRequest(String sql, String sqlOrderBy, SpiQuery ormQuery, DatabasePlatform dbPlatform) { this.sql = sql; this.sqlOrderBy = sqlOrderBy; this.ormQuery = ormQuery; this.dbPlatform = dbPlatform; } - - public String getDbOrderBy() { - return sqlOrderBy; - } - public String getDbSql() { - return sql; - } + public String getDbOrderBy() { + return sqlOrderBy; + } - public int getFirstRow() { - return ormQuery.getFirstRow(); - } + public String getDbSql() { + return sql; + } - public int getMaxRows() { - return ormQuery.getMaxRows(); - } + public int getFirstRow() { + return ormQuery.getFirstRow(); + } - public boolean isDistinct() { - return ormQuery.isDistinctQuery(); - } + public int getMaxRows() { + return ormQuery.getMaxRows(); + } + + public boolean isDistinct() { + return ormQuery.isDistinctQuery(); + } public SpiQuery getOrmQuery() { return ormQuery; diff --git a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryProperties.java b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryProperties.java index 0bd24548a..cef728a61 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryProperties.java +++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryProperties.java @@ -29,586 +29,582 @@ import com.avaje.ebeaninternal.util.FilterExpressionList; */ public class OrmQueryProperties implements Serializable { - private static final long serialVersionUID = -8785582703966455658L; + private static final long serialVersionUID = -8785582703966455658L; - private String parentPath; - private String path; + private String parentPath; + private String path; - private String properties; + private String properties; - private String trimmedProperties; + private String trimmedProperties; - /** - * NB: -1 means no +query, 0 means use the default batch size. - */ - private int queryFetchBatch = -1; - private boolean queryFetchAll; + /** + * NB: -1 means no +query, 0 means use the default batch size. + */ + private int queryFetchBatch = -1; + private boolean queryFetchAll; - /** - * NB: -1 means no +lazy, 0 means use the default batch size. - */ - private int lazyFetchBatch = -1; + /** + * NB: -1 means no +lazy, 0 means use the default batch size. + */ + private int lazyFetchBatch = -1; - private FetchConfig fetchConfig; - - private boolean cache; + private FetchConfig fetchConfig; - private boolean readOnly; + private boolean cache; - /** - * Note this SHOULD be a LinkedHashSet to preserve order of the properties. - * This is to make using SqlSelect easier with predictable property/column - * ordering. - */ - private Set included; + private boolean readOnly; - /** - * Included bean joins. - */ - private Set includedBeanJoin; + /** + * Note this SHOULD be a LinkedHashSet to preserve order of the properties. This is to make using + * SqlSelect easier with predictable property/column ordering. + */ + private Set included; - /** - * Add these properties to the select so that the foreign key columns are - * included in the query. - */ - private Set secondaryQueryJoins; + /** + * Included bean joins. + */ + private Set includedBeanJoin; - private List secondaryChildren; + /** + * Add these properties to the select so that the foreign key columns are included in the query. + */ + private Set secondaryQueryJoins; - /** - * OrderBy properties that where on the main query - * but moved here as they relate to this (query join). - */ - @SuppressWarnings("rawtypes") - private OrderBy orderBy; - - /** - * A filter that can be applied to the fetch of this path in the object graph. - */ - @SuppressWarnings("rawtypes") - private SpiExpressionList filterMany; - - /** - * Construct with a given path (null == root path). - */ - public OrmQueryProperties(String path) { - this.path = path; - this.parentPath = SplitName.parent(path); + private List secondaryChildren; + + /** + * OrderBy properties that where on the main query but moved here as they relate to this (query + * join). + */ + @SuppressWarnings("rawtypes") + private OrderBy orderBy; + + /** + * A filter that can be applied to the fetch of this path in the object graph. + */ + @SuppressWarnings("rawtypes") + private SpiExpressionList filterMany; + + /** + * Construct with a given path (null == root path). + */ + public OrmQueryProperties(String path) { + this.path = path; + this.parentPath = SplitName.parent(path); + } + + public OrmQueryProperties() { + this(null); + } + + /** + * Used by query language parser. + */ + public OrmQueryProperties(String path, String properties) { + this(path); + setProperties(properties); + } + + /** + * Move a OrderBy.Property from the main query to this query join. + */ + @SuppressWarnings("rawtypes") + public void addSecJoinOrderProperty(OrderBy.Property orderProp) { + if (orderBy == null) { + orderBy = new OrderBy(); } + orderBy.add(orderProp); + } - public OrmQueryProperties() { - this(null); + /** + * Set the Fetch configuration options for this path. + */ + public void setFetchConfig(FetchConfig fetchConfig) { + if (fetchConfig != null) { + this.fetchConfig = fetchConfig; + lazyFetchBatch = fetchConfig.getLazyBatchSize(); + queryFetchBatch = fetchConfig.getQueryBatchSize(); + queryFetchAll = fetchConfig.isQueryAll(); } + } - /** - * Used by query language parser. - */ - public OrmQueryProperties(String path, String properties) { - this(path); - setProperties(properties); - } + public FetchConfig getFetchConfig() { + return fetchConfig; + } - /** - * Move a OrderBy.Property from the main query to this query join. - */ - @SuppressWarnings("rawtypes") - public void addSecJoinOrderProperty(OrderBy.Property orderProp) { - if (orderBy == null){ - orderBy = new OrderBy(); - } - orderBy.add(orderProp); - } - - /** - * Set the Fetch configuration options for this path. - */ - public void setFetchConfig(FetchConfig fetchConfig) { - if (fetchConfig != null) { - this.fetchConfig = fetchConfig; - lazyFetchBatch = fetchConfig.getLazyBatchSize(); - queryFetchBatch = fetchConfig.getQueryBatchSize(); - queryFetchAll = fetchConfig.isQueryAll(); - } - } - - public FetchConfig getFetchConfig() { - return fetchConfig; - } - - /** - * Set the comma delimited properties to fetch for this path. - *

- * This can include the +query and +lazy type hints. - *

- */ - public void setProperties(String properties) { - this.properties = properties; - this.trimmedProperties = properties; - parseProperties(); + /** + * Set the comma delimited properties to fetch for this path. + *

+ * This can include the +query and +lazy type hints. + *

+ */ + public void setProperties(String properties) { + this.properties = properties; + this.trimmedProperties = properties; + parseProperties(); - if (!isAllProperties()) { - Set parsed = parseIncluded(trimmedProperties); - if (parsed.contains("*")){ - this.included = null; - } else { - this.included = parsed; - } - } else { - this.included = null; - } - } - - private boolean isAllProperties() { - return (trimmedProperties == null) || (trimmedProperties.length() == 0) || "*".equals(trimmedProperties); - } - - /** - * Return the expressions used to filter on this path. - * This should be a many path to use this method. - */ - @SuppressWarnings({ "rawtypes", "unchecked" }) - public SpiExpressionList filterMany(Query rootQuery) { - if (filterMany == null){ - FilterExprPath exprPath = new FilterExprPath(path); - SpiExpressionFactory queryEf = (SpiExpressionFactory)rootQuery.getExpressionFactory(); - ExpressionFactory filterEf = queryEf.createExpressionFactory();//exprPath); - filterMany = new FilterExpressionList(exprPath, filterEf, rootQuery); - // by default we need to make this a 'query join' now - queryFetchAll = true; - queryFetchBatch = 100; - lazyFetchBatch = 100; - } - return filterMany; - } - - /** - * Return the filterMany expression list (can be null). - */ - public SpiExpressionList getFilterManyTrimPath(int trimPath) { - if (filterMany == null){ - return null; - } - return filterMany.trimPath(trimPath); - } - - /** - * Return the filterMany expression list (can be null). - */ - public SpiExpressionList getFilterMany() { - return filterMany; - } - - /** - * Set the filterMany expression list. - */ - public void setFilterMany(SpiExpressionList filterMany) { - this.filterMany = filterMany; - } - - /** - * Set the properties from deployment default FetchTypes. - */ - public void setDefaultProperties(String properties, Set included) { - this.properties = properties; - this.trimmedProperties = properties; - this.included = included; - } - - /** - * Set the properties from a matching autofetch tuned properties. - */ - public void setTunedProperties(OrmQueryProperties tunedProperties) { - this.properties = tunedProperties.properties; - this.trimmedProperties = tunedProperties.trimmedProperties; - this.included = tunedProperties.included; - } - - /** - * Define the select and joins for this query. - */ - @SuppressWarnings("unchecked") - public void configureBeanQuery(SpiQuery query) { - - if (trimmedProperties != null && trimmedProperties.length() > 0) { - query.select(trimmedProperties); - } - - if (filterMany != null){ - SpiExpressionList trimPath = filterMany.trimPath(path.length()+1); - List underlyingList = trimPath.getUnderlyingList(); - for (SpiExpression spiExpression : underlyingList) { - query.where().add(spiExpression); - } - } - - if (secondaryChildren != null) { - int trimPath = path.length() + 1; - for (int i = 0; i < secondaryChildren.size(); i++) { - OrmQueryProperties p = secondaryChildren.get(i); - String path = p.getPath(); - path = path.substring(trimPath); - query.fetch(path, p.getProperties(), p.getFetchConfig()); - query.setFilterMany(path, p.getFilterManyTrimPath(trimPath)); - } - } - - if (orderBy != null){ - query.setOrder(orderBy.copyWithTrim(path)); - } - } - - /** - * Creates a copy of the OrmQueryProperties. - */ - public OrmQueryProperties copy() { - OrmQueryProperties copy = new OrmQueryProperties(); - copy.parentPath = parentPath; - copy.path = path; - copy.properties = properties; - copy.cache = cache; - copy.readOnly = readOnly; - copy.queryFetchAll = queryFetchAll; - copy.queryFetchBatch = queryFetchBatch; - copy.lazyFetchBatch = lazyFetchBatch; - copy.filterMany = filterMany; - if (included != null) { - copy.included = new HashSet(included); - } - if (includedBeanJoin != null) { - copy.includedBeanJoin = new HashSet(includedBeanJoin); - } - return copy; - } - - public boolean hasSelectClause() { - if ("*".equals(trimmedProperties)) { - // explicitly selected all properties - return true; - } - // explicitly selected some properties - return included != null; - } - - public String toString() { - String s = ""; - if (path != null) { - s += path + " "; - } - if (properties != null) { - s += "(" + properties + ") "; - } else if (included != null) { - s += "(" + included + ") "; - } - return s; - } - - public boolean isChild(OrmQueryProperties possibleChild) { - return possibleChild.getPath().startsWith(path + "."); - } - - /** - * For secondary queries add a child element. - */ - public void add(OrmQueryProperties child) { - if (secondaryChildren == null) { - secondaryChildren = new ArrayList(); - } - secondaryChildren.add(child); - } - - public int autofetchPlanHash() { - - int hc = (path != null ? path.hashCode() : 1); - if (properties != null){ - hc = hc * 31 + properties.hashCode(); - } else { - hc = hc * 31 + (included != null ? included.hashCode() : 1); - } - - return hc; - } - - /** - * Calculate the query plan hash. - */ - @SuppressWarnings("unchecked") - public void queryPlanHash(BeanQueryRequest request, HashQueryPlanBuilder builder) { - - builder.add(path); - if (properties != null) { - builder.add(properties); + if (!isAllProperties()) { + Set parsed = parseIncluded(trimmedProperties); + if (parsed.contains("*")) { + this.included = null; } else { - builder.add(included); + this.included = parsed; } - builder.add(filterMany != null); - if (filterMany != null) { - filterMany.queryPlanHash(request, builder); + } else { + this.included = null; + } + } + + private boolean isAllProperties() { + return (trimmedProperties == null) || (trimmedProperties.length() == 0) || "*".equals(trimmedProperties); + } + + /** + * Return the expressions used to filter on this path. This should be a many path to use this + * method. + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + public SpiExpressionList filterMany(Query rootQuery) { + if (filterMany == null) { + FilterExprPath exprPath = new FilterExprPath(path); + SpiExpressionFactory queryEf = (SpiExpressionFactory) rootQuery.getExpressionFactory(); + ExpressionFactory filterEf = queryEf.createExpressionFactory();// exprPath); + filterMany = new FilterExpressionList(exprPath, filterEf, rootQuery); + // by default we need to make this a 'query join' now + queryFetchAll = true; + queryFetchBatch = 100; + lazyFetchBatch = 100; + } + return filterMany; + } + + /** + * Return the filterMany expression list (can be null). + */ + public SpiExpressionList getFilterManyTrimPath(int trimPath) { + if (filterMany == null) { + return null; + } + return filterMany.trimPath(trimPath); + } + + /** + * Return the filterMany expression list (can be null). + */ + public SpiExpressionList getFilterMany() { + return filterMany; + } + + /** + * Set the filterMany expression list. + */ + public void setFilterMany(SpiExpressionList filterMany) { + this.filterMany = filterMany; + } + + /** + * Set the properties from deployment default FetchTypes. + */ + public void setDefaultProperties(String properties, Set included) { + this.properties = properties; + this.trimmedProperties = properties; + this.included = included; + } + + /** + * Set the properties from a matching autofetch tuned properties. + */ + public void setTunedProperties(OrmQueryProperties tunedProperties) { + this.properties = tunedProperties.properties; + this.trimmedProperties = tunedProperties.trimmedProperties; + this.included = tunedProperties.included; + } + + /** + * Define the select and joins for this query. + */ + @SuppressWarnings("unchecked") + public void configureBeanQuery(SpiQuery query) { + + if (trimmedProperties != null && trimmedProperties.length() > 0) { + query.select(trimmedProperties); + } + + if (filterMany != null) { + SpiExpressionList trimPath = filterMany.trimPath(path.length() + 1); + List underlyingList = trimPath.getUnderlyingList(); + for (SpiExpression spiExpression : underlyingList) { + query.where().add(spiExpression); } } - public String getProperties() { - return properties; + if (secondaryChildren != null) { + int trimPath = path.length() + 1; + for (int i = 0; i < secondaryChildren.size(); i++) { + OrmQueryProperties p = secondaryChildren.get(i); + String path = p.getPath(); + path = path.substring(trimPath); + query.fetch(path, p.getProperties(), p.getFetchConfig()); + query.setFilterMany(path, p.getFilterManyTrimPath(trimPath)); + } } - /** - * Return true if this has properties. - */ - public boolean hasProperties() { - return properties != null || included != null; + if (orderBy != null) { + query.setOrder(orderBy.copyWithTrim(path)); + } + } + + /** + * Creates a copy of the OrmQueryProperties. + */ + public OrmQueryProperties copy() { + OrmQueryProperties copy = new OrmQueryProperties(); + copy.parentPath = parentPath; + copy.path = path; + copy.properties = properties; + copy.cache = cache; + copy.readOnly = readOnly; + copy.queryFetchAll = queryFetchAll; + copy.queryFetchBatch = queryFetchBatch; + copy.lazyFetchBatch = lazyFetchBatch; + copy.filterMany = filterMany; + if (included != null) { + copy.included = new HashSet(included); + } + if (includedBeanJoin != null) { + copy.includedBeanJoin = new HashSet(includedBeanJoin); + } + return copy; + } + + public boolean hasSelectClause() { + if ("*".equals(trimmedProperties)) { + // explicitly selected all properties + return true; + } + // explicitly selected some properties + return included != null; + } + + public String toString() { + String s = ""; + if (path != null) { + s += path + " "; + } + if (properties != null) { + s += "(" + properties + ") "; + } else if (included != null) { + s += "(" + included + ") "; + } + return s; + } + + public boolean isChild(OrmQueryProperties possibleChild) { + return possibleChild.getPath().startsWith(path + "."); + } + + /** + * For secondary queries add a child element. + */ + public void add(OrmQueryProperties child) { + if (secondaryChildren == null) { + secondaryChildren = new ArrayList(); + } + secondaryChildren.add(child); + } + + public int autofetchPlanHash() { + + int hc = (path != null ? path.hashCode() : 1); + if (properties != null) { + hc = hc * 31 + properties.hashCode(); + } else { + hc = hc * 31 + (included != null ? included.hashCode() : 1); } - /** - * Return true if this property is included as a bean join. - *

- * If a property is included as a bean join then it should not be included - * as a reference/proxy to avoid duplication. - *

- */ - public boolean isIncludedBeanJoin(String propertyName) { - if (includedBeanJoin == null) { - return false; - } else { - return includedBeanJoin.contains(propertyName); + return hc; + } + + /** + * Calculate the query plan hash. + */ + @SuppressWarnings("unchecked") + public void queryPlanHash(BeanQueryRequest request, HashQueryPlanBuilder builder) { + + builder.add(path); + if (properties != null) { + builder.add(properties); + } else { + builder.add(included); + } + builder.add(filterMany != null); + if (filterMany != null) { + filterMany.queryPlanHash(request, builder); + } + } + + public String getProperties() { + return properties; + } + + /** + * Return true if this has properties. + */ + public boolean hasProperties() { + return properties != null || included != null; + } + + /** + * Return true if this property is included as a bean join. + *

+ * If a property is included as a bean join then it should not be included as a reference/proxy to + * avoid duplication. + *

+ */ + public boolean isIncludedBeanJoin(String propertyName) { + if (includedBeanJoin == null) { + return false; + } else { + return includedBeanJoin.contains(propertyName); + } + } + + /** + * Add a bean join property. + */ + public void includeBeanJoin(String propertyName) { + if (includedBeanJoin == null) { + includedBeanJoin = new HashSet(); + } + includedBeanJoin.add(propertyName); + } + + public boolean allProperties() { + return included == null; + } + + /** + * This excludes the bean joined properties. + *

+ * This is because bean joins will have there own node in the SqlTree. + *

+ */ + public Iterator getSelectProperties() { + + if (secondaryQueryJoins == null) { + return included.iterator(); + } + + LinkedHashSet temp = new LinkedHashSet(secondaryQueryJoins.size() + included.size()); + temp.addAll(included); + temp.addAll(secondaryQueryJoins); + return temp.iterator(); + } + + public void addSecondaryQueryJoin(String property) { + if (secondaryQueryJoins == null) { + secondaryQueryJoins = new HashSet(4); + } + secondaryQueryJoins.add(property); + } + + /** + * Return all the properties including the bean joins. This is the set that will be used by + * EntityBeanIntercept to determine if a property needs to be lazy loaded. + */ + public Set getAllIncludedProperties() { + + if (included == null) { + return null; + } + + if (includedBeanJoin == null && secondaryQueryJoins == null) { + return new LinkedHashSet(included); + } + + LinkedHashSet s = new LinkedHashSet(2 * (included.size() + 5)); + if (included != null) { + s.addAll(included); + } + if (includedBeanJoin != null) { + s.addAll(includedBeanJoin); + } + if (secondaryQueryJoins != null) { + s.addAll(secondaryQueryJoins); + } + return s; + } + + public boolean isIncluded(String propName) { + + if (includedBeanJoin != null && includedBeanJoin.contains(propName)) { + return false; + } + if (included == null) { + // all properties included + return true; + } + return included.contains(propName); + } + + /** + * Used to convert this join to a query join. + * + * @param queryJoinBatch + * where -1 means not a query join and 0 means use the default batch size. + */ + public OrmQueryProperties setQueryFetchBatch(int queryFetchBatch) { + this.queryFetchBatch = queryFetchBatch; + return this; + } + + public OrmQueryProperties setQueryFetchAll(boolean queryFetchAll) { + this.queryFetchAll = queryFetchAll; + return this; + } + + public OrmQueryProperties setQueryFetch(int batch, boolean queryFetchAll) { + this.queryFetchBatch = batch; + this.queryFetchAll = queryFetchAll; + return this; + } + + /** + * Set the lazy loading batch size. + */ + public OrmQueryProperties setLazyFetchBatch(int lazyFetchBatch) { + this.lazyFetchBatch = lazyFetchBatch; + return this; + } + + public boolean isFetchJoin() { + return !isQueryFetch() && !isLazyFetch(); + } + + public boolean isQueryFetch() { + return queryFetchBatch > -1; + } + + public int getQueryFetchBatch() { + return queryFetchBatch; + } + + public boolean isQueryFetchAll() { + return queryFetchAll; + } + + public boolean isLazyFetch() { + return lazyFetchBatch > -1; + } + + public int getLazyFetchBatch() { + return lazyFetchBatch; + } + + public boolean isReadOnly() { + return readOnly; + } + + public boolean isCache() { + return cache; + } + + public String getParentPath() { + return parentPath; + } + + public String getPath() { + return path; + } + + private void parseProperties() { + if (trimmedProperties == null) { + return; + } + int pos = trimmedProperties.indexOf("+readonly"); + if (pos > -1) { + trimmedProperties = StringHelper.replaceString(trimmedProperties, "+readonly", ""); + this.readOnly = true; + } + pos = trimmedProperties.indexOf("+cache"); + if (pos > -1) { + trimmedProperties = StringHelper.replaceString(trimmedProperties, "+cache", ""); + this.cache = true; + } + pos = trimmedProperties.indexOf("+query"); + if (pos > -1) { + queryFetchBatch = parseBatchHint(pos, "+query"); + } + pos = trimmedProperties.indexOf("+lazy"); + if (pos > -1) { + lazyFetchBatch = parseBatchHint(pos, "+lazy"); + } + + trimmedProperties = trimmedProperties.trim(); + while (trimmedProperties.startsWith(",")) { + trimmedProperties = trimmedProperties.substring(1).trim(); + } + } + + private int parseBatchHint(int pos, String option) { + + int startPos = pos + option.length(); + + int endPos = findEndPos(startPos, trimmedProperties); + if (endPos == -1) { + trimmedProperties = StringHelper.replaceString(trimmedProperties, option, ""); + return 0; + + } else { + + String batchParam = trimmedProperties.substring(startPos + 1, endPos); + + if (endPos + 1 >= trimmedProperties.length()) { + trimmedProperties = trimmedProperties.substring(0, pos); + } else { + trimmedProperties = trimmedProperties.substring(0, pos) + trimmedProperties.substring(endPos + 1); + } + return Integer.parseInt(batchParam); + } + } + + private int findEndPos(int pos, String props) { + + if (pos < props.length()) { + if (props.charAt(pos) == '(') { + int endPara = props.indexOf(')', pos + 1); + if (endPara == -1) { + String m = "Error could not find ')' in " + props + " after position " + pos; + throw new RuntimeException(m); } + return endPara; + } + } + return -1; + } + + /** + * Parse the include separating by comma or semicolon. + */ + private static Set parseIncluded(String rawList) { + + String[] res = rawList.split(","); + + LinkedHashSet set = new LinkedHashSet(res.length + 3); + + String temp = null; + for (int i = 0; i < res.length; i++) { + temp = res[i].trim(); + if (temp.length() > 0) { + set.add(temp); + } } - /** - * Add a bean join property. - */ - public void includeBeanJoin(String propertyName) { - if (includedBeanJoin == null) { - includedBeanJoin = new HashSet(); - } - includedBeanJoin.add(propertyName); + if (set.isEmpty()) { + return null; } - public boolean allProperties() { - return included == null; - } - - /** - * This excludes the bean joined properties. - *

- * This is because bean joins will have there own node in the SqlTree. - *

- */ - public Iterator getSelectProperties() { - - if (secondaryQueryJoins == null) { - return included.iterator(); - } - - LinkedHashSet temp = new LinkedHashSet(secondaryQueryJoins.size() + included.size()); - temp.addAll(included); - temp.addAll(secondaryQueryJoins); - return temp.iterator(); - } - - public void addSecondaryQueryJoin(String property) { - if (secondaryQueryJoins == null) { - secondaryQueryJoins = new HashSet(4); - } - secondaryQueryJoins.add(property); - } - - /** - * Return all the properties including the bean joins. This is the set that - * will be used by EntityBeanIntercept to determine if a property needs to - * be lazy loaded. - */ - public Set getAllIncludedProperties() { - - if (included == null) { - return null; - } - - if (includedBeanJoin == null && secondaryQueryJoins == null) { - return new LinkedHashSet(included); - } - - LinkedHashSet s = new LinkedHashSet(2 * (included.size() + 5)); - if (included != null) { - s.addAll(included); - } - if (includedBeanJoin != null) { - s.addAll(includedBeanJoin); - } - if (secondaryQueryJoins != null) { - s.addAll(secondaryQueryJoins); - } - return s; - } - - public boolean isIncluded(String propName) { - - if (includedBeanJoin != null && includedBeanJoin.contains(propName)) { - return false; - } - if (included == null) { - // all properties included - return true; - } - return included.contains(propName); - } - - /** - * Used to convert this join to a query join. - * - * @param queryJoinBatch - * where -1 means not a query join and 0 means use the default - * batch size. - */ - public OrmQueryProperties setQueryFetchBatch(int queryFetchBatch) { - this.queryFetchBatch = queryFetchBatch; - return this; - } - - public OrmQueryProperties setQueryFetchAll(boolean queryFetchAll) { - this.queryFetchAll = queryFetchAll; - return this; - } - - public OrmQueryProperties setQueryFetch(int batch, boolean queryFetchAll) { - this.queryFetchBatch = batch; - this.queryFetchAll = queryFetchAll; - return this; - } - - /** - * Set the lazy loading batch size. - */ - public OrmQueryProperties setLazyFetchBatch(int lazyFetchBatch) { - this.lazyFetchBatch = lazyFetchBatch; - return this; - } - - public boolean isFetchJoin() { - return !isQueryFetch() && !isLazyFetch(); - } - - public boolean isQueryFetch() { - return queryFetchBatch > -1; - } - - public int getQueryFetchBatch() { - return queryFetchBatch; - } - - public boolean isQueryFetchAll() { - return queryFetchAll; - } - - public boolean isLazyFetch() { - return lazyFetchBatch > -1; - } - - public int getLazyFetchBatch() { - return lazyFetchBatch; - } - - public boolean isReadOnly() { - return readOnly; - } - - public boolean isCache() { - return cache; - } - - public String getParentPath() { - return parentPath; - } - - public String getPath() { - return path; - } - - private void parseProperties() { - if (trimmedProperties == null) { - return; - } - int pos = trimmedProperties.indexOf("+readonly"); - if (pos > -1) { - trimmedProperties = StringHelper.replaceString(trimmedProperties, "+readonly", ""); - this.readOnly = true; - } - pos = trimmedProperties.indexOf("+cache"); - if (pos > -1) { - trimmedProperties = StringHelper.replaceString(trimmedProperties, "+cache", ""); - this.cache = true; - } - pos = trimmedProperties.indexOf("+query"); - if (pos > -1) { - queryFetchBatch = parseBatchHint(pos, "+query"); - } - pos = trimmedProperties.indexOf("+lazy"); - if (pos > -1) { - lazyFetchBatch = parseBatchHint(pos, "+lazy"); - } - - trimmedProperties = trimmedProperties.trim(); - while (trimmedProperties.startsWith(",")) { - trimmedProperties = trimmedProperties.substring(1).trim(); - } - } - - private int parseBatchHint(int pos, String option) { - - int startPos = pos + option.length(); - - int endPos = findEndPos(startPos, trimmedProperties); - if (endPos == -1) { - trimmedProperties = StringHelper.replaceString(trimmedProperties, option, ""); - return 0; - - } else { - - String batchParam = trimmedProperties.substring(startPos + 1, endPos); - - if (endPos + 1 >= trimmedProperties.length()) { - trimmedProperties = trimmedProperties.substring(0, pos); - } else { - trimmedProperties = trimmedProperties.substring(0, pos) + trimmedProperties.substring(endPos + 1); - } - return Integer.parseInt(batchParam); - } - } - - private int findEndPos(int pos, String props) { - - if (pos < props.length()) { - if (props.charAt(pos) == '(') { - int endPara = props.indexOf(')', pos + 1); - if (endPara == -1) { - String m = "Error could not find ')' in " + props + " after position " + pos; - throw new RuntimeException(m); - } - return endPara; - } - } - return -1; - } - - /** - * Parse the include separating by comma or semicolon. - */ - private static Set parseIncluded(String rawList) { - - String[] res = rawList.split(","); - - LinkedHashSet set = new LinkedHashSet(res.length + 3); - - String temp = null; - for (int i = 0; i < res.length; i++) { - temp = res[i].trim(); - if (temp.length() > 0) { - set.add(temp); - } - } - - if (set.isEmpty()){ - return null; - } - - return Collections.unmodifiableSet(set); - } + return Collections.unmodifiableSet(set); + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/querydefn/SimpleTextParser.java b/src/main/java/com/avaje/ebeaninternal/server/querydefn/SimpleTextParser.java index dd1bffaea..e60f0683e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/SimpleTextParser.java +++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/SimpleTextParser.java @@ -2,176 +2,176 @@ package com.avaje.ebeaninternal.server.querydefn; public class SimpleTextParser { - private final String oql; - private final char[] chars; - private final int eof; + private final String oql; + private final char[] chars; + private final int eof; - private int pos; - private String word; - private String lowerWord; + private int pos; + private String word; + private String lowerWord; - private int openParenthesisCount; + private int openParenthesisCount; - public SimpleTextParser(String oql) { - this.oql = oql; - this.chars = oql.toCharArray(); - this.eof = oql.length(); - } + public SimpleTextParser(String oql) { + this.oql = oql; + this.chars = oql.toCharArray(); + this.eof = oql.length(); + } - public int getPos() { + public int getPos() { return pos; } public String getOql() { - return oql; - } + return oql; + } - public String getWord() { - return word; - } + public String getWord() { + return word; + } - public String peekNextWord() { - int origPos = pos; - String nw = nextWordInternal(); - pos = origPos; - return nw; - } + public String peekNextWord() { + int origPos = pos; + String nw = nextWordInternal(); + pos = origPos; + return nw; + } - /** - * Match the current and the next word. - */ - public boolean isMatch(String lowerMatch, String nextWordMatch) { + /** + * Match the current and the next word. + */ + public boolean isMatch(String lowerMatch, String nextWordMatch) { - if (isMatch(lowerMatch)) { - String nw = peekNextWord(); - if (nw != null) { - nw = nw.toLowerCase(); - return nw.equals(nextWordMatch); - } - } - return false; - } + if (isMatch(lowerMatch)) { + String nw = peekNextWord(); + if (nw != null) { + nw = nw.toLowerCase(); + return nw.equals(nextWordMatch); + } + } + return false; + } - public boolean isFinished() { - return word == null; - } + public boolean isFinished() { + return word == null; + } - public int findWordLower(String lowerMatch, int afterPos) { - this.pos = afterPos; - return findWordLower(lowerMatch); - } + public int findWordLower(String lowerMatch, int afterPos) { + this.pos = afterPos; + return findWordLower(lowerMatch); + } - public int findWordLower(String lowerMatch) { - do { - if (nextWord() == null) { - return -1; - } - if (lowerMatch.equals(lowerWord)) { - return pos - lowerWord.length(); - } - } while (true); - } + public int findWordLower(String lowerMatch) { + do { + if (nextWord() == null) { + return -1; + } + if (lowerMatch.equals(lowerWord)) { + return pos - lowerWord.length(); + } + } while (true); + } - /** - * Match the current word. - */ - public boolean isMatch(String lowerMatch) { - return lowerMatch.equals(lowerWord); - } + /** + * Match the current word. + */ + public boolean isMatch(String lowerMatch) { + return lowerMatch.equals(lowerWord); + } - public String nextWord() { - word = nextWordInternal(); - if (word != null) { - lowerWord = word.toLowerCase(); - } - return word; - } + public String nextWord() { + word = nextWordInternal(); + if (word != null) { + lowerWord = word.toLowerCase(); + } + return word; + } - private String nextWordInternal() { - trimLeadingWhitespace(); - if (pos >= eof) { - return null; - } - int start = pos; - if (chars[pos] == '(') { - moveToClose(); - } else { - moveToEndOfWord(); - } - return oql.substring(start, pos); - } + private String nextWordInternal() { + trimLeadingWhitespace(); + if (pos >= eof) { + return null; + } + int start = pos; + if (chars[pos] == '(') { + moveToClose(); + } else { + moveToEndOfWord(); + } + return oql.substring(start, pos); + } - private void moveToClose() { + private void moveToClose() { - pos++; - openParenthesisCount = 0; + pos++; + openParenthesisCount = 0; - for (; pos < eof; pos++) { - char c = chars[pos]; - if (c == '(') { - // count nested parenthesis - openParenthesisCount++; + for (; pos < eof; pos++) { + char c = chars[pos]; + if (c == '(') { + // count nested parenthesis + openParenthesisCount++; - } else if (c == ')') { - if (openParenthesisCount > 0) { - // still in nested parenthesis - --openParenthesisCount; - } else { - // we have found the end - pos++; - return; - } - } - } - } + } else if (c == ')') { + if (openParenthesisCount > 0) { + // still in nested parenthesis + --openParenthesisCount; + } else { + // we have found the end + pos++; + return; + } + } + } + } - private void moveToEndOfWord() { - char c = chars[pos]; - boolean isOperator = isOperator(c); - for (; pos < eof; pos++) { - c = chars[pos]; - if (isWordTerminator(c, isOperator)) { - return; - } - } - } + private void moveToEndOfWord() { + char c = chars[pos]; + boolean isOperator = isOperator(c); + for (; pos < eof; pos++) { + c = chars[pos]; + if (isWordTerminator(c, isOperator)) { + return; + } + } + } - private boolean isWordTerminator(char c, boolean isOperator) { - if (Character.isWhitespace(c)) { - return true; - } - if (isOperator(c)) { - return !isOperator; - } - if (c == '('){ - return true; - } + private boolean isWordTerminator(char c, boolean isOperator) { + if (Character.isWhitespace(c)) { + return true; + } + if (isOperator(c)) { + return !isOperator; + } + if (c == '(') { + return true; + } - return isOperator; - } + return isOperator; + } - private boolean isOperator(char c) { - switch (c) { - case '<': - return true; - case '>': - return true; - case '=': - return true; - case '!': - return true; + private boolean isOperator(char c) { + switch (c) { + case '<': + return true; + case '>': + return true; + case '=': + return true; + case '!': + return true; - default: - return false; - } - } + default: + return false; + } + } - private void trimLeadingWhitespace() { - for (; pos < eof; pos++) { - char c = chars[pos]; - if (!Character.isWhitespace(c)) { - break; - } - } - } + private void trimLeadingWhitespace() { + for (; pos < eof; pos++) { + char c = chars[pos]; + if (!Character.isWhitespace(c)) { + break; + } + } + } } \ No newline at end of file