org.hibernate.loader.criteria.CriteriaQueryTranslator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hibernate-core Show documentation
Show all versions of hibernate-core Show documentation
Hibernate's core ORM functionality
/*
* 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 .
*/
package org.hibernate.loader.criteria;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.MappingException;
import org.hibernate.QueryException;
import org.hibernate.criterion.CriteriaQuery;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.EnhancedProjection;
import org.hibernate.criterion.ParameterInfoCollector;
import org.hibernate.criterion.Projection;
import org.hibernate.engine.query.spi.OrdinalParameterDescriptor;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.RowSelection;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.TypedValue;
import org.hibernate.hql.internal.ast.util.SessionFactoryHelper;
import org.hibernate.internal.CriteriaImpl;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.Loadable;
import org.hibernate.persister.entity.PropertyMapping;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.sql.JoinType;
import org.hibernate.type.AssociationType;
import org.hibernate.type.CollectionType;
import org.hibernate.type.StringRepresentableType;
import org.hibernate.type.Type;
/**
* @author Gavin King
*/
public class CriteriaQueryTranslator implements CriteriaQuery {
public static final String ROOT_SQL_ALIAS = Criteria.ROOT_ALIAS + '_';
private CriteriaQuery outerQueryTranslator;
private final CriteriaImpl rootCriteria;
private final String rootEntityName;
private final String rootSQLAlias;
private final Map criteriaInfoMap = new LinkedHashMap();
private final Map nameCriteriaInfoMap = new LinkedHashMap();
private final Map criteriaSQLAliasMap = new HashMap();
private final Map aliasCriteriaMap = new HashMap();
private final Map associationPathCriteriaMap = new LinkedHashMap();
private final Map associationPathJoinTypesMap = new LinkedHashMap();
private final Map withClauseMap = new HashMap();
private Set associations;
private final SessionFactoryImplementor sessionFactory;
private final SessionFactoryHelper helper;
public CriteriaQueryTranslator(
final SessionFactoryImplementor factory,
final CriteriaImpl criteria,
final String rootEntityName,
final String rootSQLAlias,
CriteriaQuery outerQuery) throws HibernateException {
this( factory, criteria, rootEntityName, rootSQLAlias );
outerQueryTranslator = outerQuery;
}
public CriteriaQueryTranslator(
final SessionFactoryImplementor factory,
final CriteriaImpl criteria,
final String rootEntityName,
final String rootSQLAlias) throws HibernateException {
this.rootCriteria = criteria;
this.rootEntityName = rootEntityName;
this.sessionFactory = factory;
this.rootSQLAlias = rootSQLAlias;
this.helper = new SessionFactoryHelper(factory);
createAliasCriteriaMap();
createAssociationPathCriteriaMap();
createCriteriaEntityNameMap();
createCriteriaSQLAliasMap();
}
public void setAssociations(Set associations) {
this.associations = associations;
}
@Override
public String generateSQLAlias() {
int aliasCount = 0;
return StringHelper.generateAlias( Criteria.ROOT_ALIAS, aliasCount ) + '_';
}
public String getRootSQLALias() {
return rootSQLAlias;
}
private Criteria getAliasedCriteria(String alias) {
return aliasCriteriaMap.get( alias );
}
public boolean isJoin(String path) {
return associationPathCriteriaMap.containsKey( path );
}
public JoinType getJoinType(String path) {
JoinType result = associationPathJoinTypesMap.get( path );
return ( result == null ? JoinType.INNER_JOIN : result );
}
public Criteria getCriteria(String path) {
return associationPathCriteriaMap.get( path );
}
public Set getQuerySpaces() {
Set result = new HashSet<>();
for ( CriteriaInfoProvider info : criteriaInfoMap.values() ) {
result.addAll( Arrays.asList( info.getSpaces() ) );
}
for ( final Map.Entry entry : associationPathCriteriaMap.entrySet() ) {
String path = entry.getKey();
CriteriaImpl.Subcriteria crit = (CriteriaImpl.Subcriteria) entry.getValue();
int index = path.lastIndexOf( '.' );
if ( index > 0 ) {
path = path.substring( index + 1, path.length() );
}
CriteriaInfoProvider info = criteriaInfoMap.get( crit.getParent() );
CollectionPersister persister = getFactory().getMetamodel().collectionPersisters().get( info.getName() + "." + path );
if ( persister != null ) {
result.addAll( Arrays.asList( persister.getCollectionSpaces() ) );
}
}
return result;
}
private void createAliasCriteriaMap() {
aliasCriteriaMap.put( rootCriteria.getAlias(), rootCriteria );
Iterator iter = rootCriteria.iterateSubcriteria();
while ( iter.hasNext() ) {
Criteria subcriteria = iter.next();
if ( subcriteria.getAlias() != null ) {
Object old = aliasCriteriaMap.put( subcriteria.getAlias(), subcriteria );
if ( old != null ) {
throw new QueryException( "duplicate alias: " + subcriteria.getAlias() );
}
}
}
}
private void createAssociationPathCriteriaMap() {
final Iterator iter = rootCriteria.iterateSubcriteria();
while ( iter.hasNext() ) {
CriteriaImpl.Subcriteria crit = iter.next();
String wholeAssociationPath = getWholeAssociationPath( crit );
Object old = associationPathCriteriaMap.put( wholeAssociationPath, crit );
if ( old != null ) {
throw new QueryException( "duplicate association path: " + wholeAssociationPath );
}
JoinType joinType = crit.getJoinType();
old = associationPathJoinTypesMap.put( wholeAssociationPath, joinType );
if ( old != null ) {
// TODO : not so sure this is needed...
throw new QueryException( "duplicate association path: " + wholeAssociationPath );
}
if ( crit.getWithClause() != null ) {
this.withClauseMap.put( wholeAssociationPath, crit.getWithClause() );
}
}
}
private String getWholeAssociationPath(CriteriaImpl.Subcriteria subcriteria) {
String path = subcriteria.getPath();
// some messy, complex stuff here, since createCriteria() can take an
// aliased path, or a path rooted at the creating criteria instance
Criteria parent = null;
if ( path.indexOf( '.' ) > 0 ) {
// if it is a compound path
String testAlias = StringHelper.root( path );
if ( !testAlias.equals( subcriteria.getAlias() ) ) {
// and the qualifier is not the alias of this criteria
// -> check to see if we belong to some criteria other
// than the one that created us
parent = aliasCriteriaMap.get( testAlias );
}
}
if ( parent == null ) {
// otherwise assume the parent is the the criteria that created us
parent = subcriteria.getParent();
}
else {
path = StringHelper.unroot( path );
}
if ( parent.equals( rootCriteria ) ) {
// if its the root criteria, we are done
return path;
}
else {
// otherwise, recurse
return getWholeAssociationPath( ( CriteriaImpl.Subcriteria ) parent ) + '.' + path;
}
}
private void createCriteriaEntityNameMap() {
// initialize the rootProvider first
final CriteriaInfoProvider rootProvider = new EntityCriteriaInfoProvider(
(Queryable) sessionFactory.getEntityPersister( rootEntityName )
);
criteriaInfoMap.put( rootCriteria, rootProvider);
nameCriteriaInfoMap.put( rootProvider.getName(), rootProvider );
for ( Map.Entry entry : associationPathCriteriaMap.entrySet() ) {
final String key = entry.getKey();
final Criteria value = entry.getValue();
final CriteriaInfoProvider info = getPathInfo( key );
criteriaInfoMap.put( value, info );
nameCriteriaInfoMap.put( info.getName(), info );
}
}
private CriteriaInfoProvider getPathInfo(String path) {
StringTokenizer tokens = new StringTokenizer( path, "." );
String componentPath = "";
// start with the 'rootProvider'
CriteriaInfoProvider provider = nameCriteriaInfoMap.get( rootEntityName );
while ( tokens.hasMoreTokens() ) {
componentPath += tokens.nextToken();
final Type type = provider.getType( componentPath );
if ( type.isAssociationType() ) {
// CollectionTypes are always also AssociationTypes - but there's not always an associated entity...
final AssociationType atype = (AssociationType) type;
final CollectionType ctype = type.isCollectionType() ? (CollectionType)type : null;
final Type elementType = (ctype != null) ? ctype.getElementType( sessionFactory ) : null;
// is the association a collection of components or value-types? (i.e a colloction of valued types?)
if ( ctype != null && elementType.isComponentType() ) {
provider = new ComponentCollectionCriteriaInfoProvider( helper.getCollectionPersister(ctype.getRole()) );
}
else if ( ctype != null && !elementType.isEntityType() ) {
provider = new ScalarCollectionCriteriaInfoProvider( helper, ctype.getRole() );
}
else {
provider = new EntityCriteriaInfoProvider(
(Queryable) sessionFactory.getEntityPersister( atype.getAssociatedEntityName( sessionFactory ) )
);
}
componentPath = "";
}
else if ( type.isComponentType() ) {
if (!tokens.hasMoreTokens()) {
throw new QueryException(
"Criteria objects cannot be created directly on components. Create a criteria on " +
"owning entity and use a dotted property to access component property: " + path
);
}
else {
componentPath += '.';
}
}
else {
throw new QueryException( "not an association: " + componentPath );
}
}
return provider;
}
public int getSQLAliasCount() {
return criteriaSQLAliasMap.size();
}
private void createCriteriaSQLAliasMap() {
int i = 0;
for ( Map.Entry entry : criteriaInfoMap.entrySet() ) {
final Criteria crit = entry.getKey();
final CriteriaInfoProvider value = entry.getValue();
String alias = crit.getAlias();
if ( alias == null ) {
// the entity name
alias = value.getName();
}
criteriaSQLAliasMap.put( crit, StringHelper.generateAlias( alias, i++ ) );
}
criteriaSQLAliasMap.put( rootCriteria, rootSQLAlias );
}
public CriteriaImpl getRootCriteria() {
return rootCriteria;
}
public QueryParameters getQueryParameters() {
final RowSelection selection = new RowSelection();
selection.setFirstRow( rootCriteria.getFirstResult() );
selection.setMaxRows( rootCriteria.getMaxResults() );
selection.setTimeout( rootCriteria.getTimeout() );
selection.setFetchSize( rootCriteria.getFetchSize() );
final LockOptions lockOptions = new LockOptions();
final Map lockModeMap = rootCriteria.getLockModes();
for ( Map.Entry entry : lockModeMap.entrySet() ) {
final String key = entry.getKey();
final LockMode value = entry.getValue();
final Criteria subcriteria = getAliasedCriteria( key );
lockOptions.setAliasSpecificLockMode( getSQLAlias( subcriteria ), value );
}
final List