/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
 * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
 */
package org.hibernate.internal;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.hibernate.CacheMode;
import org.hibernate.Criteria;
import org.hibernate.FetchMode;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.NaturalIdentifier;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projection;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.query.internal.AbstractProducedQuery;
import org.hibernate.sql.JoinType;
import org.hibernate.transform.ResultTransformer;

Implementation of the Criteria interface
Author:Gavin King
/** * Implementation of the <tt>Criteria</tt> interface * @author Gavin King */
public class CriteriaImpl implements Criteria, Serializable { private final String entityOrClassName; private transient SharedSessionContractImplementor session; private final String rootAlias; private List<CriterionEntry> criterionEntries = new ArrayList<>(); private List<OrderEntry> orderEntries = new ArrayList<>(); private Projection projection; private Criteria projectionCriteria; private List<Subcriteria> subcriteriaList = new ArrayList<>(); private Map<String, FetchMode> fetchModes = new HashMap<>(); private Map<String, LockMode> lockModes = new HashMap<>(); private Integer maxResults; private Integer firstResult; private Integer timeout; private Integer fetchSize; private boolean cacheable; private String cacheRegion; private String comment; private final List<String> queryHints = new ArrayList<String>(); private FlushMode flushMode; private CacheMode cacheMode; private FlushMode sessionFlushMode; private CacheMode sessionCacheMode; private Boolean readOnly; private ResultTransformer resultTransformer = Criteria.ROOT_ENTITY; // Constructors ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public CriteriaImpl(String entityOrClassName, SharedSessionContractImplementor session) { this(entityOrClassName, ROOT_ALIAS, session); } public CriteriaImpl(String entityOrClassName, String alias, SharedSessionContractImplementor session) { this.session = session; this.entityOrClassName = entityOrClassName; this.cacheable = false; this.rootAlias = alias; } @Override public String toString() { return "CriteriaImpl(" + entityOrClassName + ":" + (rootAlias==null ? "" : rootAlias) + subcriteriaList.toString() + criterionEntries.toString() + ( projection==null ? "" : projection.toString() ) + ')'; } // State ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public SharedSessionContractImplementor getSession() { return session; } public void setSession(SharedSessionContractImplementor session) { this.session = session; } public String getEntityOrClassName() { return entityOrClassName; } public Map<String, LockMode> getLockModes() { return lockModes; } public Criteria getProjectionCriteria() { return projectionCriteria; } public Iterator<Subcriteria> iterateSubcriteria() { return subcriteriaList.iterator(); } public Iterator<CriterionEntry> iterateExpressionEntries() { return criterionEntries.iterator(); } public Iterator<OrderEntry> iterateOrderings() { return orderEntries.iterator(); } public Criteria add(Criteria criteriaInst, Criterion expression) { criterionEntries.add( new CriterionEntry(expression, criteriaInst) ); return this; } // Criteria impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @Override public String getAlias() { return rootAlias; } public Projection getProjection() { return projection; } @Override public Criteria setProjection(Projection projection) { this.projection = projection; this.projectionCriteria = this; setResultTransformer( PROJECTION ); return this; } @Override public Criteria add(Criterion expression) { add( this, expression ); return this; } @Override public Criteria addOrder(Order ordering) { orderEntries.add( new OrderEntry( ordering, this ) ); return this; } public FetchMode getFetchMode(String path) { return fetchModes.get(path); } @Override public Criteria setFetchMode(String associationPath, FetchMode mode) { String rootAliasPathPrefix = rootAlias + "."; if (rootAlias != null && !associationPath.startsWith(rootAliasPathPrefix)) { associationPath = rootAliasPathPrefix + associationPath; } fetchModes.put( associationPath, mode ); return this; } @Override public Criteria setLockMode(LockMode lockMode) { return setLockMode( getAlias(), lockMode ); } @Override public Criteria setLockMode(String alias, LockMode lockMode) { lockModes.put( alias, lockMode ); return this; } @Override public Criteria createAlias(String associationPath, String alias) { return createAlias( associationPath, alias, JoinType.INNER_JOIN ); } @Override public Criteria createAlias(String associationPath, String alias, JoinType joinType) { new Subcriteria( this, associationPath, alias, joinType ); return this; } @Override public Criteria createAlias(String associationPath, String alias, int joinType) throws HibernateException { return createAlias( associationPath, alias, JoinType.parse( joinType ) ); } @Override public Criteria createAlias(String associationPath, String alias, JoinType joinType, Criterion withClause) { new Subcriteria( this, associationPath, alias, joinType, withClause ); return this; } @Override public Criteria createAlias(String associationPath, String alias, int joinType, Criterion withClause) throws HibernateException { return createAlias( associationPath, alias, JoinType.parse( joinType ), withClause ); } @Override public Criteria createCriteria(String associationPath) { return createCriteria( associationPath, JoinType.INNER_JOIN ); } @Override public Criteria createCriteria(String associationPath, JoinType joinType) { return new Subcriteria( this, associationPath, joinType ); } @Override public Criteria createCriteria(String associationPath, int joinType) throws HibernateException { return createCriteria(associationPath, JoinType.parse( joinType )); } @Override public Criteria createCriteria(String associationPath, String alias) { return createCriteria( associationPath, alias, JoinType.INNER_JOIN ); } @Override public Criteria createCriteria(String associationPath, String alias, JoinType joinType) { return new Subcriteria( this, associationPath, alias, joinType ); } @Override public Criteria createCriteria(String associationPath, String alias, int joinType) throws HibernateException { return createCriteria( associationPath, alias, JoinType.parse( joinType ) ); } @Override public Criteria createCriteria(String associationPath, String alias, JoinType joinType, Criterion withClause) { return new Subcriteria( this, associationPath, alias, joinType, withClause ); } @Override public Criteria createCriteria(String associationPath, String alias, int joinType, Criterion withClause) throws HibernateException { return createCriteria( associationPath, alias, JoinType.parse( joinType ), withClause ); } public ResultTransformer getResultTransformer() { return resultTransformer; } @Override public Criteria setResultTransformer(ResultTransformer tupleMapper) { this.resultTransformer = tupleMapper; return this; } public Integer getMaxResults() { return maxResults; } @Override public Criteria setMaxResults(int maxResults) { this.maxResults = maxResults; return this; } public Integer getFirstResult() { return firstResult; } @Override public Criteria setFirstResult(int firstResult) { this.firstResult = firstResult; return this; } public Integer getFetchSize() { return fetchSize; } @Override public Criteria setFetchSize(int fetchSize) { this.fetchSize = fetchSize; return this; } public Integer getTimeout() { return timeout; } @Override public Criteria setTimeout(int timeout) { this.timeout = timeout; return this; } @Override public boolean isReadOnlyInitialized() { return readOnly != null; } @Override public boolean isReadOnly() { if ( ! isReadOnlyInitialized() && getSession() == null ) { throw new IllegalStateException( "cannot determine readOnly/modifiable setting when it is not initialized and is not initialized and getSession() == null" ); } return ( isReadOnlyInitialized() ? readOnly : getSession().getPersistenceContext().isDefaultReadOnly() ); } @Override public Criteria setReadOnly(boolean readOnly) { this.readOnly = readOnly; return this; } public boolean getCacheable() { return this.cacheable; } @Override public Criteria setCacheable(boolean cacheable) { this.cacheable = cacheable; return this; } public String getCacheRegion() { return this.cacheRegion; } @Override public Criteria setCacheRegion(String cacheRegion) { this.cacheRegion = cacheRegion.trim(); return this; } public String getComment() { return comment; } @Override public Criteria setComment(String comment) { this.comment = comment; return this; } public List<String> getQueryHints() { return queryHints; } @Override public Criteria addQueryHint(String queryHint) { queryHints.add( queryHint ); return this; } @Override public Criteria setFlushMode(FlushMode flushMode) { this.flushMode = flushMode; return this; } @Override public Criteria setCacheMode(CacheMode cacheMode) { this.cacheMode = cacheMode; return this; } @Override public List list() throws HibernateException { before(); try { return session.list( this ); } finally { after(); } } @Override public ScrollableResults scroll() { return scroll( session.getFactory().getDialect().defaultScrollMode() ); } @Override public ScrollableResults scroll(ScrollMode scrollMode) { before(); try { return session.scroll(this, scrollMode); } finally { after(); } } @Override public Object uniqueResult() throws HibernateException { return AbstractProducedQuery.uniqueElement( list() ); } protected void before() { if ( flushMode != null ) { sessionFlushMode = getSession().getHibernateFlushMode(); getSession().setHibernateFlushMode( flushMode ); } if ( cacheMode != null ) { sessionCacheMode = getSession().getCacheMode(); getSession().setCacheMode( cacheMode ); } } protected void after() { if ( sessionFlushMode != null ) { getSession().setHibernateFlushMode( sessionFlushMode ); sessionFlushMode = null; } if ( sessionCacheMode != null ) { getSession().setCacheMode( sessionCacheMode ); sessionCacheMode = null; } } public boolean isLookupByNaturalKey() { if ( projection != null ) { return false; } if ( subcriteriaList.size() > 0 ) { return false; } if ( criterionEntries.size() != 1 ) { return false; } CriterionEntry ce = criterionEntries.get(0); return ce.getCriterion() instanceof NaturalIdentifier; } // Inner classes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public final class Subcriteria implements Criteria, Serializable { private String alias; private String path; private Criteria parent; private LockMode lockMode; private JoinType joinType = JoinType.INNER_JOIN; private Criterion withClause; private boolean hasRestriction; // Constructors ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ private Subcriteria(Criteria parent, String path, String alias, JoinType joinType, Criterion withClause) { this.alias = alias; this.path = path; this.parent = parent; this.joinType = joinType; this.withClause = withClause; this.hasRestriction = withClause != null; CriteriaImpl.this.subcriteriaList.add( this ); } private Subcriteria(Criteria parent, String path, String alias, JoinType joinType) { this( parent, path, alias, joinType, null ); } private Subcriteria(Criteria parent, String path, JoinType joinType) { this( parent, path, null, joinType ); } @Override public String toString() { return "Subcriteria(" + path + ":" + (alias==null ? "" : alias) + ')'; } // State ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @Override public String getAlias() { return alias; } public void setAlias(String alias) { this.alias = alias; } public String getPath() { return path; } public Criteria getParent() { return parent; } public LockMode getLockMode() { return lockMode; } @Override public Criteria setLockMode(LockMode lockMode) { this.lockMode = lockMode; return this; } public JoinType getJoinType() { return joinType; } public Criterion getWithClause() { return this.withClause; } public boolean hasRestriction() { return hasRestriction; } // Criteria impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @Override public Criteria add(Criterion expression) { hasRestriction = true; CriteriaImpl.this.add(this, expression); return this; } @Override public Criteria addOrder(Order order) { CriteriaImpl.this.orderEntries.add( new OrderEntry(order, this) ); return this; } @Override public Criteria createAlias(String associationPath, String alias) { return createAlias( associationPath, alias, JoinType.INNER_JOIN ); } @Override public Criteria createAlias(String associationPath, String alias, JoinType joinType) throws HibernateException { new Subcriteria( this, associationPath, alias, joinType ); return this; } @Override public Criteria createAlias(String associationPath, String alias, int joinType) throws HibernateException { return createAlias( associationPath, alias, JoinType.parse( joinType ) ); } @Override public Criteria createAlias(String associationPath, String alias, JoinType joinType, Criterion withClause) throws HibernateException { new Subcriteria( this, associationPath, alias, joinType, withClause ); return this; } @Override public Criteria createAlias(String associationPath, String alias, int joinType, Criterion withClause) throws HibernateException { return createAlias( associationPath, alias, JoinType.parse( joinType ), withClause ); } @Override public Criteria createCriteria(String associationPath) { return createCriteria( associationPath, JoinType.INNER_JOIN ); } @Override public Criteria createCriteria(String associationPath, JoinType joinType) throws HibernateException { return new Subcriteria( Subcriteria.this, associationPath, joinType ); } @Override public Criteria createCriteria(String associationPath, int joinType) throws HibernateException { return createCriteria( associationPath, JoinType.parse( joinType ) ); } @Override public Criteria createCriteria(String associationPath, String alias) { return createCriteria( associationPath, alias, JoinType.INNER_JOIN ); } @Override public Criteria createCriteria(String associationPath, String alias, JoinType joinType) throws HibernateException { return new Subcriteria( Subcriteria.this, associationPath, alias, joinType ); } @Override public Criteria createCriteria(String associationPath, String alias, int joinType) throws HibernateException { return createCriteria( associationPath, alias, JoinType.parse( joinType ) ); } @Override public Criteria createCriteria(String associationPath, String alias, JoinType joinType, Criterion withClause) throws HibernateException { return new Subcriteria( this, associationPath, alias, joinType, withClause ); } @Override public Criteria createCriteria(String associationPath, String alias, int joinType, Criterion withClause) throws HibernateException { return createCriteria( associationPath, alias, JoinType.parse( joinType ), withClause ); } @Override public boolean isReadOnly() { return CriteriaImpl.this.isReadOnly(); } @Override public boolean isReadOnlyInitialized() { return CriteriaImpl.this.isReadOnlyInitialized(); } @Override public Criteria setReadOnly(boolean readOnly) { CriteriaImpl.this.setReadOnly( readOnly ); return this; } @Override public Criteria setCacheable(boolean cacheable) { CriteriaImpl.this.setCacheable(cacheable); return this; } @Override public Criteria setCacheRegion(String cacheRegion) { CriteriaImpl.this.setCacheRegion(cacheRegion); return this; } @Override public List list() throws HibernateException { return CriteriaImpl.this.list(); } @Override public ScrollableResults scroll() throws HibernateException { return CriteriaImpl.this.scroll(); } @Override public ScrollableResults scroll(ScrollMode scrollMode) throws HibernateException { return CriteriaImpl.this.scroll(scrollMode); } @Override public Object uniqueResult() throws HibernateException { return CriteriaImpl.this.uniqueResult(); } @Override public Criteria setFetchMode(String associationPath, FetchMode mode) { CriteriaImpl.this.setFetchMode( StringHelper.qualify(path, associationPath), mode); return this; } @Override public Criteria setFlushMode(FlushMode flushMode) { CriteriaImpl.this.setFlushMode(flushMode); return this; } @Override public Criteria setCacheMode(CacheMode cacheMode) { CriteriaImpl.this.setCacheMode(cacheMode); return this; } @Override public Criteria setFirstResult(int firstResult) { CriteriaImpl.this.setFirstResult(firstResult); return this; } @Override public Criteria setMaxResults(int maxResults) { CriteriaImpl.this.setMaxResults(maxResults); return this; } @Override public Criteria setTimeout(int timeout) { CriteriaImpl.this.setTimeout(timeout); return this; } @Override public Criteria setFetchSize(int fetchSize) { CriteriaImpl.this.setFetchSize(fetchSize); return this; } @Override public Criteria setLockMode(String alias, LockMode lockMode) { CriteriaImpl.this.setLockMode(alias, lockMode); return this; } @Override public Criteria setResultTransformer(ResultTransformer resultProcessor) { CriteriaImpl.this.setResultTransformer(resultProcessor); return this; } @Override public Criteria setComment(String comment) { CriteriaImpl.this.setComment(comment); return this; } @Override public Criteria addQueryHint(String queryHint) { CriteriaImpl.this.addQueryHint( queryHint ); return this; } @Override public Criteria setProjection(Projection projection) { CriteriaImpl.this.projection = projection; CriteriaImpl.this.projectionCriteria = this; setResultTransformer(PROJECTION); return this; } } public static final class CriterionEntry implements Serializable { private final Criterion criterion; private final Criteria criteria; private CriterionEntry(Criterion criterion, Criteria criteria) { this.criteria = criteria; this.criterion = criterion; } public Criterion getCriterion() { return criterion; } public Criteria getCriteria() { return criteria; } @Override public String toString() { return criterion.toString(); } } public static final class OrderEntry implements Serializable { private final Order order; private final Criteria criteria; private OrderEntry(Order order, Criteria criteria) { this.criteria = criteria; this.order = order; } public Order getOrder() { return order; } public Criteria getCriteria() { return criteria; } @Override public String toString() { return order.toString(); } } }