package com.avaje.ebeaninternal.server.querydefn; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import javax.persistence.PersistenceException; import com.avaje.ebean.EbeanServer; import com.avaje.ebean.Expression; import com.avaje.ebean.ExpressionFactory; import com.avaje.ebean.ExpressionList; import com.avaje.ebean.FetchConfig; import com.avaje.ebean.FutureIds; import com.avaje.ebean.FutureList; import com.avaje.ebean.FutureRowCount; import com.avaje.ebean.OrderBy; import com.avaje.ebean.OrderBy.Property; import com.avaje.ebean.PagingList; import com.avaje.ebean.Query; import com.avaje.ebean.QueryIterator; import com.avaje.ebean.QueryListener; import com.avaje.ebean.QueryResultVisitor; import com.avaje.ebean.RawSql; import com.avaje.ebean.bean.BeanCollectionTouched; import com.avaje.ebean.bean.CallStack; import com.avaje.ebean.bean.EntityBean; import com.avaje.ebean.bean.ObjectGraphNode; import com.avaje.ebean.bean.ObjectGraphOrigin; import com.avaje.ebean.bean.PersistenceContext; import com.avaje.ebean.event.BeanQueryRequest; import com.avaje.ebean.meta.MetaAutoFetchStatistic; import com.avaje.ebeaninternal.api.BindParams; import com.avaje.ebeaninternal.api.ManyWhereJoins; import com.avaje.ebeaninternal.api.SpiExpression; import com.avaje.ebeaninternal.api.SpiExpressionList; import com.avaje.ebeaninternal.api.SpiQuery; import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager; import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; import com.avaje.ebeaninternal.server.deploy.DRawSqlSelect; import com.avaje.ebeaninternal.server.deploy.DeployNamedQuery; import com.avaje.ebeaninternal.server.deploy.TableJoin; import com.avaje.ebeaninternal.server.expression.SimpleExpression; import com.avaje.ebeaninternal.server.query.CancelableQuery; import com.avaje.ebeaninternal.util.DefaultExpressionList; /** * Default implementation of an Object Relational query. */ public class DefaultOrmQuery implements SpiQuery { 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; private transient QueryListener queryListener; /** * 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 transient AutoFetchManager autoFetchManager; private transient BeanDescriptor beanDescriptor; private boolean cancelled; private transient CancelableQuery cancelableQuery; /** * The name of the query. */ private String name; private UseIndex useIndex; 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 lazyLoadManyPath; /** * Set to true if you want a DISTINCT query. */ private boolean distinct; /** * Set to true if this is a future fetch using background threads. */ private boolean futureFetch; private List partialIds; /** * The rows after which the fetch continues in a bg thread. */ private int backgroundFetchAfter; private int timeout = -1; /** * The property used to get the key value for a Map. */ private String mapKey; /** * Used for find by id type query. */ private Object id; /** * Bind parameters when using the query language. */ private BindParams bindParams; private DefaultExpressionList whereExpressions; 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; /** * 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; /** * Hash of final query after AutoFetch tuning. */ private int 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 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 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; } public MetaAutoFetchStatistic getMetaAutoFetchStatistic() { if (parentNode != null && server != null){ ObjectGraphOrigin origin = parentNode.getOriginQueryPoint(); return server.find(MetaAutoFetchStatistic.class, origin.getKey()); } return null; } /** * 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); } } } } } 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; } } 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; copy.query = query; copy.additionalWhere = additionalWhere; copy.additionalHaving = additionalHaving; copy.distinct = distinct; copy.backgroundFetchAfter = backgroundFetchAfter; 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.forUpdate = forUpdate; copy.rawSql = rawSql; copy.rawWhereClause = rawWhereClause; return copy; } public Type getType() { return type; } public void setType(Type type) { this.type = type; } public UseIndex getUseIndex() { return useIndex; } public DefaultOrmQuery setUseIndex(UseIndex useIndex) { this.useIndex = useIndex; return this; } public String getLoadDescription() { return loadDescription; } public String getLoadMode() { return loadMode; } 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; } /** * 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; } /** * Return true if the query detail has neither select or joins specified. */ public boolean isDetailEmpty() { return detail.isEmpty(); } public boolean isAutofetchTuned() { return autoFetchTuned; } public void setAutoFetchTuned(boolean autoFetchTuned) { this.autoFetchTuned = autoFetchTuned; } 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 setForUpdate(boolean forUpdate) { this.forUpdate = forUpdate; return this; } public AutoFetchManager getAutoFetchManager() { return autoFetchManager; } public void setAutoFetchManager(AutoFetchManager autoFetchManager) { this.autoFetchManager = autoFetchManager; } public Mode getMode() { return mode; } public void setMode(Mode mode) { this.mode = mode; } public boolean isUsageProfiling() { return usageProfiling; } public void setUsageProfiling(boolean usageProfiling) { this.usageProfiling = usageProfiling; } public void setParentNode(ObjectGraphNode parentNode) { this.parentNode = parentNode; } public ObjectGraphNode getParentNode() { return parentNode; } 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; } /** * 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 int calculateHash(BeanQueryRequest request) { // exclude bind values and things unrelated to // the sql being generated // must use String name of class as actual class hashCode // can change between JVM restarts. int hc = beanType.getName().hashCode(); hc = hc * 31 + (type == null ? 0 : type.ordinal()); hc = hc * 31 + (useIndex == null ? 0 : useIndex.hashCode()); hc = hc * 31 + (rawSql == null ? 0 : rawSql.queryHash()); hc = hc * 31 + (autoFetchTuned ? 31 : 0); hc = hc * 31 + (distinct ? 31 : 0); hc = hc * 31 + (query == null ? 0 : query.hashCode()); hc = hc * 31 + detail.queryPlanHash(request); hc = hc * 31 + (firstRow == 0 ? 0 : firstRow); hc = hc * 31 + (maxRows == 0 ? 0 : maxRows); hc = hc * 31 + (orderBy == null ? 0 : orderBy.hash()); hc = hc * 31 + (rawWhereClause == null ? 0 : rawWhereClause.hashCode()); hc = hc * 31 + (additionalWhere == null ? 0 : additionalWhere.hashCode()); hc = hc * 31 + (additionalHaving == null ? 0 : additionalHaving.hashCode()); hc = hc * 31 + (mapKey == null ? 0 : mapKey.hashCode()); hc = hc * 31 + (id == null ? 0 : 1); if (bindParams != null) { hc = hc * 31 + bindParams.getQueryPlanHash(); } if (request == null) { // for AutoFetch... hc = hc * 31 + (whereExpressions == null ? 0 : whereExpressions.queryAutoFetchHash()); hc = hc * 31 + (havingExpressions == null ? 0 : havingExpressions.queryAutoFetchHash()); } else { // for query plan... hc = hc * 31 + (whereExpressions == null ? 0 : whereExpressions.queryPlanHash(request)); hc = hc * 31 + (havingExpressions == null ? 0 : havingExpressions.queryPlanHash(request)); } hc = hc * 31 + (forUpdate ? 31 : 0); return hc; } /** * 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 int queryAutofetchHash() { return calculateHash(null); } /** * 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 int queryPlanHash(BeanQueryRequest request) { queryPlanHash = calculateHash(request); return queryPlanHash; } /** * 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 hc; } /** * 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 int queryHash() { // calculateQueryPlanHash is called just after potential AutoFetch tuning // so queryPlanHash is calculated well before this method is called int hc = queryPlanHash; hc = hc * 31 + queryBindHash(); return hc; } /** * Return the query name. */ public String getName() { return name; } 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; } public DefaultOrmQuery setReadOnly(boolean readOnly) { this.readOnly = readOnly; return this; } public Boolean isUseBeanCache() { return useBeanCache; } 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 boolean isLoadBeanCache() { return loadBeanCache; } public DefaultOrmQuery setLoadBeanCache(boolean loadBeanCache) { this.loadBeanCache = loadBeanCache; return this; } public DefaultOrmQuery setTimeout(int secs) { this.timeout = secs; return this; } public DefaultOrmQuery setQuery(String queryString) throws PersistenceException { this.query = queryString; 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 setProperties(String columns) { return select(columns); } public void setDefaultSelectClause(){ detail.setDefaultSelectClause(beanDescriptor); } public DefaultOrmQuery select(String columns) { detail.select(columns); return this; } public DefaultOrmQuery fetch(String property) { return fetch(property, null, null); } public DefaultOrmQuery fetch(String property, FetchConfig joinConfig) { return fetch(property, null, joinConfig); } 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); } 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); } /** * 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; } /** * Return the order by clause. */ public String getRawWhereClause() { return rawWhereClause; } public OrderBy orderBy() { return order(); } public OrderBy order() { if (orderBy == null){ orderBy = new OrderBy(this, null); } return orderBy; } 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 this query uses DISTINCT. */ public boolean isDistinct() { return distinct; } /** * Set whether this query uses DISTINCT. */ public DefaultOrmQuery setDistinct(boolean isDistinct) { this.distinct = isDistinct; return this; } /** * Return the findListener is one has been set. */ public QueryListener getListener() { return queryListener; } /** * Set a FindListener. This is designed for large fetches where lots are * rows are to be processed and instead of returning all the rows they are * processed one at a time. *

* Note that the returning List Set or Map will be empty. *

*/ public DefaultOrmQuery setListener(QueryListener queryListener) { this.queryListener = queryListener; return this; } public Class getBeanType() { return beanType; } public void setDetail(OrmQueryDetail detail) { this.detail = detail; } public boolean tuneFetchProperties(OrmQueryDetail tunedDetail) { return detail.tuneFetchProperties(tunedDetail); } public OrmQueryDetail getDetail() { return detail; } /** * 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); } public String toString() { return "Query [" + whereExpressions + "]"; } public TableJoin getIncludeTableJoin() { return includeTableJoin; } public void setIncludeTableJoin(TableJoin includeTableJoin) { this.includeTableJoin = includeTableJoin; } public int getFirstRow() { return firstRow; } public DefaultOrmQuery setFirstRow(int firstRow) { this.firstRow = firstRow; return this; } public int getMaxRows() { return maxRows; } public DefaultOrmQuery setMaxRows(int maxRows) { this.maxRows = maxRows; return this; } public String getMapKey() { return mapKey; } public DefaultOrmQuery setMapKey(String mapKey) { this.mapKey = mapKey; return this; } public int getBackgroundFetchAfter() { return backgroundFetchAfter; } public DefaultOrmQuery setBackgroundFetchAfter(int backgroundFetchAfter) { this.backgroundFetchAfter = backgroundFetchAfter; return this; } 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 BindParams getBindParams() { return bindParams; } public String getQuery() { return query; } public DefaultOrmQuery addWhere(String addToWhereClause) { return where(addToWhereClause); } public DefaultOrmQuery addWhere(Expression expression) { return where(expression); } public ExpressionList addWhere() { return where(); } 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 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 void setFilterMany(String prop, ExpressionList filterMany){ if (filterMany != null){ OrmQueryProperties chunk = detail.getChunk(prop, true); chunk.setFilterMany((SpiExpressionList)filterMany); } } public DefaultOrmQuery addHaving(String addToHavingClause) { return having(addToHavingClause); } public DefaultOrmQuery addHaving(Expression expression) { return having(expression); } public ExpressionList addHaving() { return having(); } public DefaultOrmQuery having(String addToHavingClause) { if (additionalHaving == null) { additionalHaving = addToHavingClause; } else { additionalHaving += " " + addToHavingClause; } return this; } public DefaultOrmQuery having(Expression expression) { if (havingExpressions == null) { havingExpressions = new DefaultExpressionList(this, null); } havingExpressions.add(expression); return this; } public ExpressionList having() { if (havingExpressions == null) { havingExpressions = new DefaultExpressionList(this, null); } return havingExpressions; } public SpiExpressionList getHavingExpressions() { return havingExpressions; } public SpiExpressionList getWhereExpressions() { return whereExpressions; } /** * Return true if using background fetching or a queryListener. */ public boolean createOwnTransaction() { if (futureFetch){ // the future fetches have already created // their own transaction return false; } if (backgroundFetchAfter > 0 || queryListener != null) { // run in own transaction as we can't know how long // the background fetching will continue etc return true; } return false; } public String getGeneratedSql() { return generatedSql; } public void setGeneratedSql(String generatedSql) { this.generatedSql = generatedSql; } public Query setBufferFetchSizeHint(int bufferFetchSizeHint){ this.bufferFetchSizeHint = bufferFetchSizeHint; return this; } public int getBufferFetchSizeHint() { return bufferFetchSizeHint; } public void setBeanCollectionTouched(BeanCollectionTouched notify) { this.beanCollectionTouched = notify; } public BeanCollectionTouched getBeanCollectionTouched() { return beanCollectionTouched; } public List getIdList() { return partialIds; } public void setIdList(List partialIds) { this.partialIds = partialIds; } 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; } } }