/* * 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.loader.custom.sql; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import org.hibernate.HibernateException; import org.hibernate.MappingException; import org.hibernate.engine.query.spi.sql.NativeSQLQueryCollectionReturn; import org.hibernate.engine.query.spi.sql.NativeSQLQueryConstructorReturn; import org.hibernate.engine.query.spi.sql.NativeSQLQueryJoinReturn; import org.hibernate.engine.query.spi.sql.NativeSQLQueryNonScalarReturn; import org.hibernate.engine.query.spi.sql.NativeSQLQueryReturn; import org.hibernate.engine.query.spi.sql.NativeSQLQueryRootReturn; import org.hibernate.engine.query.spi.sql.NativeSQLQueryScalarReturn; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.loader.BasicLoader; import org.hibernate.loader.CollectionAliases; import org.hibernate.loader.ColumnEntityAliases; import org.hibernate.loader.DefaultEntityAliases; import org.hibernate.loader.EntityAliases; import org.hibernate.loader.GeneratedCollectionAliases; import org.hibernate.loader.custom.CollectionFetchReturn; import org.hibernate.loader.custom.CollectionReturn; import org.hibernate.loader.custom.ColumnCollectionAliases; import org.hibernate.loader.custom.ConstructorReturn; import org.hibernate.loader.custom.EntityFetchReturn; import org.hibernate.loader.custom.FetchReturn; import org.hibernate.loader.custom.NonScalarReturn; import org.hibernate.loader.custom.Return; import org.hibernate.loader.custom.RootReturn; import org.hibernate.loader.custom.ScalarReturn; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.SQLLoadableCollection; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.Joinable; import org.hibernate.persister.entity.SQLLoadable; import org.hibernate.type.EntityType; import org.hibernate.type.Type; /** * Responsible for processing the series of {@link org.hibernate.engine.query.spi.sql.NativeSQLQueryReturn returns} * defined by a {@link org.hibernate.engine.query.spi.sql.NativeSQLQuerySpecification} and * breaking them down into a series of {@link Return returns} for use within the * {@link org.hibernate.loader.custom.CustomLoader}. * * @author Gavin King * @author Max Andersen * @author Steve Ebersole */ public class SQLQueryReturnProcessor { private static final CoreMessageLogger LOG = CoreLogging.messageLogger( SQLQueryReturnProcessor.class ); private static final NativeSQLQueryReturn[] NO_RETURNS = new NativeSQLQueryReturn[0]; private NativeSQLQueryReturn[] queryReturns; // private final List persisters = new ArrayList(); private final Map alias2Return = new HashMap(); private final Map alias2OwnerAlias = new HashMap(); private final Map<String,EntityPersister> alias2Persister = new HashMap<String,EntityPersister>(); private final Map alias2Suffix = new HashMap(); private final Map<String,CollectionPersister> alias2CollectionPersister = new HashMap<String,CollectionPersister>(); private final Map alias2CollectionSuffix = new HashMap(); private final Map entityPropertyResultMaps = new HashMap(); private final Map collectionPropertyResultMaps = new HashMap(); // private final List scalarTypes = new ArrayList(); // private final List scalarColumnAliases = new ArrayList(); private final SessionFactoryImplementor factory; // private List collectionOwnerAliases = new ArrayList(); // private List collectionAliases = new ArrayList(); // private List collectionPersisters = new ArrayList(); // private List collectionResults = new ArrayList(); private int entitySuffixSeed; private int collectionSuffixSeed; public SQLQueryReturnProcessor(NativeSQLQueryReturn[] queryReturns, SessionFactoryImplementor factory) { this.queryReturns = queryReturns == null ? NO_RETURNS : queryReturns; this.factory = factory; } public class ResultAliasContext { public SQLLoadable getEntityPersister(String alias) { return (SQLLoadable) alias2Persister.get( alias ); } public SQLLoadableCollection getCollectionPersister(String alias) { return (SQLLoadableCollection) alias2CollectionPersister.get( alias ); } public String getEntitySuffix(String alias) { return (String) alias2Suffix.get( alias ); } public String getCollectionSuffix(String alias) { return (String) alias2CollectionSuffix.get( alias ); } public String getOwnerAlias(String alias) { return (String) alias2OwnerAlias.get( alias ); } public Map getPropertyResultsMap(String alias) { return internalGetPropertyResultsMap( alias ); } public String[] collectQuerySpaces() { final HashSet<String> spaces = new HashSet<String>(); collectQuerySpaces( spaces ); return spaces.toArray( new String[ spaces.size() ] ); } public void collectQuerySpaces(Collection<String> spaces) { for ( EntityPersister persister : alias2Persister.values() ) { Collections.addAll( spaces, (String[]) persister.getQuerySpaces() ); } for ( CollectionPersister persister : alias2CollectionPersister.values() ) { final Type elementType = persister.getElementType(); if ( elementType.isEntityType() && ! elementType.isAnyType() ) { final Joinable joinable = ( (EntityType) elementType ).getAssociatedJoinable( factory ); Collections.addAll( spaces, (String[]) ( (EntityPersister) joinable ).getQuerySpaces() ); } } } } private Map internalGetPropertyResultsMap(String alias) { NativeSQLQueryReturn rtn = ( NativeSQLQueryReturn ) alias2Return.get( alias ); if ( rtn instanceof NativeSQLQueryNonScalarReturn ) { return ( ( NativeSQLQueryNonScalarReturn ) rtn ).getPropertyResultsMap(); } else { return null; } } private boolean hasPropertyResultMap(String alias) { Map propertyMaps = internalGetPropertyResultsMap( alias ); return propertyMaps != null && ! propertyMaps.isEmpty(); } public ResultAliasContext process() { // first, break down the returns into maps keyed by alias // so that role returns can be more easily resolved to their owners for ( NativeSQLQueryReturn queryReturn : queryReturns ) { if ( queryReturn instanceof NativeSQLQueryNonScalarReturn ) { NativeSQLQueryNonScalarReturn rtn = (NativeSQLQueryNonScalarReturn) queryReturn; alias2Return.put( rtn.getAlias(), rtn ); if ( rtn instanceof NativeSQLQueryJoinReturn ) { NativeSQLQueryJoinReturn fetchReturn = (NativeSQLQueryJoinReturn) rtn; alias2OwnerAlias.put( fetchReturn.getAlias(), fetchReturn.getOwnerAlias() ); } } } // Now, process the returns for ( NativeSQLQueryReturn queryReturn : queryReturns ) { processReturn( queryReturn ); } return new ResultAliasContext(); } public List<Return> generateCustomReturns(boolean queryHadAliases) { List<Return> customReturns = new ArrayList<Return>(); Map<String,Return> customReturnsByAlias = new HashMap<String,Return>(); for ( NativeSQLQueryReturn queryReturn : queryReturns ) { if ( queryReturn instanceof NativeSQLQueryScalarReturn ) { NativeSQLQueryScalarReturn rtn = (NativeSQLQueryScalarReturn) queryReturn; customReturns.add( new ScalarReturn( rtn.getType(), rtn.getColumnAlias() ) ); } else if ( queryReturn instanceof NativeSQLQueryRootReturn ) { NativeSQLQueryRootReturn rtn = (NativeSQLQueryRootReturn) queryReturn; String alias = rtn.getAlias(); EntityAliases entityAliases; if ( queryHadAliases || hasPropertyResultMap( alias ) ) { entityAliases = new DefaultEntityAliases( (Map) entityPropertyResultMaps.get( alias ), (SQLLoadable) alias2Persister.get( alias ), (String) alias2Suffix.get( alias ) ); } else { entityAliases = new ColumnEntityAliases( (Map) entityPropertyResultMaps.get( alias ), (SQLLoadable) alias2Persister.get( alias ), (String) alias2Suffix.get( alias ) ); } RootReturn customReturn = new RootReturn( alias, rtn.getReturnEntityName(), entityAliases, rtn.getLockMode() ); customReturns.add( customReturn ); customReturnsByAlias.put( rtn.getAlias(), customReturn ); } else if ( queryReturn instanceof NativeSQLQueryCollectionReturn ) { NativeSQLQueryCollectionReturn rtn = (NativeSQLQueryCollectionReturn) queryReturn; String alias = rtn.getAlias(); SQLLoadableCollection persister = (SQLLoadableCollection) alias2CollectionPersister.get( alias ); boolean isEntityElements = persister.getElementType().isEntityType(); CollectionAliases collectionAliases; EntityAliases elementEntityAliases = null; if ( queryHadAliases || hasPropertyResultMap( alias ) ) { collectionAliases = new GeneratedCollectionAliases( (Map) collectionPropertyResultMaps.get( alias ), (SQLLoadableCollection) alias2CollectionPersister.get( alias ), (String) alias2CollectionSuffix.get( alias ) ); if ( isEntityElements ) { elementEntityAliases = new DefaultEntityAliases( (Map) entityPropertyResultMaps.get( alias ), (SQLLoadable) alias2Persister.get( alias ), (String) alias2Suffix.get( alias ) ); } } else { collectionAliases = new ColumnCollectionAliases( (Map) collectionPropertyResultMaps.get( alias ), (SQLLoadableCollection) alias2CollectionPersister.get( alias ) ); if ( isEntityElements ) { elementEntityAliases = new ColumnEntityAliases( (Map) entityPropertyResultMaps.get( alias ), (SQLLoadable) alias2Persister.get( alias ), (String) alias2Suffix.get( alias ) ); } } CollectionReturn customReturn = new CollectionReturn( alias, rtn.getOwnerEntityName(), rtn.getOwnerProperty(), collectionAliases, elementEntityAliases, rtn.getLockMode() ); customReturns.add( customReturn ); customReturnsByAlias.put( rtn.getAlias(), customReturn ); } else if ( queryReturn instanceof NativeSQLQueryJoinReturn ) { NativeSQLQueryJoinReturn rtn = (NativeSQLQueryJoinReturn) queryReturn; String alias = rtn.getAlias(); FetchReturn customReturn; NonScalarReturn ownerCustomReturn = (NonScalarReturn) customReturnsByAlias.get( rtn.getOwnerAlias() ); if ( alias2CollectionPersister.containsKey( alias ) ) { SQLLoadableCollection persister = (SQLLoadableCollection) alias2CollectionPersister.get( alias ); boolean isEntityElements = persister.getElementType().isEntityType(); CollectionAliases collectionAliases; EntityAliases elementEntityAliases = null; if ( queryHadAliases || hasPropertyResultMap( alias ) ) { collectionAliases = new GeneratedCollectionAliases( (Map) collectionPropertyResultMaps.get( alias ), persister, (String) alias2CollectionSuffix.get( alias ) ); if ( isEntityElements ) { elementEntityAliases = new DefaultEntityAliases( (Map) entityPropertyResultMaps.get( alias ), (SQLLoadable) alias2Persister.get( alias ), (String) alias2Suffix.get( alias ) ); } } else { collectionAliases = new ColumnCollectionAliases( (Map) collectionPropertyResultMaps.get( alias ), persister ); if ( isEntityElements ) { elementEntityAliases = new ColumnEntityAliases( (Map) entityPropertyResultMaps.get( alias ), (SQLLoadable) alias2Persister.get( alias ), (String) alias2Suffix.get( alias ) ); } } customReturn = new CollectionFetchReturn( alias, ownerCustomReturn, rtn.getOwnerProperty(), collectionAliases, elementEntityAliases, rtn.getLockMode() ); } else { EntityAliases entityAliases; if ( queryHadAliases || hasPropertyResultMap( alias ) ) { entityAliases = new DefaultEntityAliases( (Map) entityPropertyResultMaps.get( alias ), (SQLLoadable) alias2Persister.get( alias ), (String) alias2Suffix.get( alias ) ); } else { entityAliases = new ColumnEntityAliases( (Map) entityPropertyResultMaps.get( alias ), (SQLLoadable) alias2Persister.get( alias ), (String) alias2Suffix.get( alias ) ); } customReturn = new EntityFetchReturn( alias, entityAliases, ownerCustomReturn, rtn.getOwnerProperty(), rtn.getLockMode() ); } customReturns.add( customReturn ); customReturnsByAlias.put( alias, customReturn ); } else if ( NativeSQLQueryConstructorReturn.class.isInstance( queryReturn ) ) { final NativeSQLQueryConstructorReturn constructorReturn = (NativeSQLQueryConstructorReturn) queryReturn; final ScalarReturn[] scalars = new ScalarReturn[ constructorReturn.getColumnReturns().length ]; int i = 0; for ( NativeSQLQueryScalarReturn scalarReturn : constructorReturn.getColumnReturns() ) { scalars[i++] = new ScalarReturn( scalarReturn.getType(), scalarReturn.getColumnAlias() ); } customReturns.add( new ConstructorReturn( constructorReturn.getTargetClass(), scalars ) ); } else { throw new IllegalStateException( "Unrecognized NativeSQLQueryReturn concrete type : " + queryReturn ); } } return customReturns; } private SQLLoadable getSQLLoadable(String entityName) throws MappingException { EntityPersister persister = factory.getEntityPersister( entityName ); if ( !(persister instanceof SQLLoadable) ) { throw new MappingException( "class persister is not SQLLoadable: " + entityName ); } return (SQLLoadable) persister; } private String generateEntitySuffix() { return BasicLoader.generateSuffixes( entitySuffixSeed++, 1 )[0]; } private String generateCollectionSuffix() { return collectionSuffixSeed++ + "__"; } private void processReturn(NativeSQLQueryReturn rtn) { if ( rtn instanceof NativeSQLQueryScalarReturn ) { processScalarReturn( ( NativeSQLQueryScalarReturn ) rtn ); } else if ( rtn instanceof NativeSQLQueryRootReturn ) { processRootReturn( ( NativeSQLQueryRootReturn ) rtn ); } else if ( rtn instanceof NativeSQLQueryCollectionReturn ) { processCollectionReturn( (NativeSQLQueryCollectionReturn) rtn ); } else if ( NativeSQLQueryJoinReturn.class.isInstance( rtn ) ) { processJoinReturn( ( NativeSQLQueryJoinReturn ) rtn ); } else if ( NativeSQLQueryConstructorReturn.class.isInstance( rtn ) ) { processConstructorReturn( (NativeSQLQueryConstructorReturn) rtn ); } else { throw new IllegalStateException( "Unrecognized NativeSQLQueryReturn concrete type encountered : " + rtn ); } } private void processConstructorReturn(NativeSQLQueryConstructorReturn rtn) { //To change body of created methods use File | Settings | File Templates. } private void processScalarReturn(NativeSQLQueryScalarReturn typeReturn) { // scalarColumnAliases.add( typeReturn.getColumnAlias() ); // scalarTypes.add( typeReturn.getType() ); } private void processRootReturn(NativeSQLQueryRootReturn rootReturn) { if ( alias2Persister.containsKey( rootReturn.getAlias() ) ) { // already been processed... return; } SQLLoadable persister = getSQLLoadable( rootReturn.getReturnEntityName() ); addPersister( rootReturn.getAlias(), rootReturn.getPropertyResultsMap(), persister ); } private void addPersister(String alias, Map propertyResult, SQLLoadable persister) { alias2Persister.put( alias, persister ); String suffix = generateEntitySuffix(); LOG.tracev( "Mapping alias [{0}] to entity-suffix [{1}]", alias, suffix ); alias2Suffix.put( alias, suffix ); entityPropertyResultMaps.put( alias, propertyResult ); } private void addCollection(String role, String alias, Map propertyResults) { SQLLoadableCollection collectionPersister = ( SQLLoadableCollection ) factory.getCollectionPersister( role ); alias2CollectionPersister.put( alias, collectionPersister ); String suffix = generateCollectionSuffix(); LOG.tracev( "Mapping alias [{0}] to collection-suffix [{1}]", alias, suffix ); alias2CollectionSuffix.put( alias, suffix ); collectionPropertyResultMaps.put( alias, propertyResults ); if ( collectionPersister.isOneToMany() || collectionPersister.isManyToMany() ) { SQLLoadable persister = ( SQLLoadable ) collectionPersister.getElementPersister(); addPersister( alias, filter( propertyResults ), persister ); } } private Map filter(Map propertyResults) { Map result = new HashMap( propertyResults.size() ); String keyPrefix = "element."; Iterator iter = propertyResults.entrySet().iterator(); while ( iter.hasNext() ) { Map.Entry element = ( Map.Entry ) iter.next(); String path = ( String ) element.getKey(); if ( path.startsWith( keyPrefix ) ) { result.put( path.substring( keyPrefix.length() ), element.getValue() ); } } return result; } private void processCollectionReturn(NativeSQLQueryCollectionReturn collectionReturn) { // we are initializing an owned collection //collectionOwners.add( new Integer(-1) ); // collectionOwnerAliases.add( null ); String role = collectionReturn.getOwnerEntityName() + '.' + collectionReturn.getOwnerProperty(); addCollection( role, collectionReturn.getAlias(), collectionReturn.getPropertyResultsMap() ); } private void processJoinReturn(NativeSQLQueryJoinReturn fetchReturn) { String alias = fetchReturn.getAlias(); // if ( alias2Persister.containsKey( alias ) || collectionAliases.contains( alias ) ) { if ( alias2Persister.containsKey( alias ) || alias2CollectionPersister.containsKey( alias ) ) { // already been processed... return; } String ownerAlias = fetchReturn.getOwnerAlias(); // Make sure the owner alias is known... if ( !alias2Return.containsKey( ownerAlias ) ) { throw new HibernateException( "Owner alias [" + ownerAlias + "] is unknown for alias [" + alias + "]" ); } // If this return's alias has not been processed yet, do so b4 further processing of this return if ( !alias2Persister.containsKey( ownerAlias ) ) { NativeSQLQueryNonScalarReturn ownerReturn = ( NativeSQLQueryNonScalarReturn ) alias2Return.get(ownerAlias); processReturn( ownerReturn ); } SQLLoadable ownerPersister = ( SQLLoadable ) alias2Persister.get( ownerAlias ); Type returnType = ownerPersister.getPropertyType( fetchReturn.getOwnerProperty() ); if ( returnType.isCollectionType() ) { String role = ownerPersister.getEntityName() + '.' + fetchReturn.getOwnerProperty(); addCollection( role, alias, fetchReturn.getPropertyResultsMap() ); // collectionOwnerAliases.add( ownerAlias ); } else if ( returnType.isEntityType() ) { EntityType eType = ( EntityType ) returnType; String returnEntityName = eType.getAssociatedEntityName(); SQLLoadable persister = getSQLLoadable( returnEntityName ); addPersister( alias, fetchReturn.getPropertyResultsMap(), persister ); } } // public List getCollectionAliases() { // return collectionAliases; // } // // /*public List getCollectionOwners() { // return collectionOwners; // }*/ // // public List getCollectionOwnerAliases() { // return collectionOwnerAliases; // } // // public List getCollectionPersisters() { // return collectionPersisters; // } // // public Map getAlias2Persister() { // return alias2Persister; // } // // /*public boolean isCollectionInitializer() { // return isCollectionInitializer; // }*/ // //// public List getPersisters() { //// return persisters; //// } // // public Map getAlias2OwnerAlias() { // return alias2OwnerAlias; // } // // public List getScalarTypes() { // return scalarTypes; // } // public List getScalarColumnAliases() { // return scalarColumnAliases; // } // // public List getPropertyResults() { // return propertyResults; // } // // public List getCollectionPropertyResults() { // return collectionResults; // } // // // public Map getAlias2Return() { // return alias2Return; // } }