org.hibernate.loader.custom.sql.SQLQueryReturnProcessor 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.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 alias2Persister = new HashMap();
private final Map alias2Suffix = new HashMap();
private final Map alias2CollectionPersister = new HashMap();
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 spaces = new HashSet();
collectQuerySpaces( spaces );
return spaces.toArray( new String[ spaces.size() ] );
}
public void collectQuerySpaces(Collection 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();
}
private interface QueryReturnVisitor {
void visitScalarReturn(NativeSQLQueryScalarReturn rtn);
void visitRootReturn(NativeSQLQueryRootReturn rtn);
void visitCollectionReturn(NativeSQLQueryCollectionReturn rtn);
void visitFetch(NativeSQLQueryJoinReturn rtn);
void visitDynamicInstantiation(NativeSQLQueryConstructorReturn rtn);
}
public void visitReturns(QueryReturnVisitor visitor) {
for ( NativeSQLQueryReturn queryReturn : queryReturns ) {
if ( NativeSQLQueryScalarReturn.class.isInstance( queryReturn ) ) {
visitor.visitScalarReturn( (NativeSQLQueryScalarReturn) queryReturn );
}
else if ( NativeSQLQueryRootReturn.class.isInstance( queryReturn ) ) {
visitor.visitRootReturn( (NativeSQLQueryRootReturn) queryReturn );
}
else if ( NativeSQLQueryCollectionReturn.class.isInstance( queryReturn ) ) {
visitor.visitCollectionReturn( (NativeSQLQueryCollectionReturn) queryReturn );
}
else if ( NativeSQLQueryJoinReturn.class.isInstance( queryReturn ) ) {
visitor.visitFetch( (NativeSQLQueryJoinReturn) queryReturn );
}
else if ( NativeSQLQueryConstructorReturn.class.isInstance( queryReturn ) ) {
visitor.visitDynamicInstantiation( (NativeSQLQueryConstructorReturn) queryReturn );
}
else {
throw new IllegalStateException(
"Unrecognized NativeSQLQueryReturn concrete type : " + queryReturn
);
}
}
}
public List generateCustomReturns(boolean queryHadAliases) {
List customReturns = new ArrayList();
Map customReturnsByAlias = new HashMap();
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;
}
public List generateCallableReturns() {
final List customReturns = new ArrayList<>();
visitReturns(
new QueryReturnVisitor() {
@Override
public void visitScalarReturn(NativeSQLQueryScalarReturn rtn) {
customReturns.add( new ScalarReturn( rtn.getType(), rtn.getColumnAlias() ) );
}
@Override
public void visitRootReturn(NativeSQLQueryRootReturn rtn) {
customReturns.add(
new RootReturn(
rtn.getAlias(),
rtn.getReturnEntityName(),
new ColumnEntityAliases(
(Map) entityPropertyResultMaps.get( rtn.getAlias() ),
(SQLLoadable) alias2Persister.get( rtn.getAlias() ),
(String) alias2Suffix.get( rtn.getAlias() )
),
rtn.getLockMode()
)
);
}
@Override
public void visitCollectionReturn(NativeSQLQueryCollectionReturn rtn) {
throw new UnsupportedOperationException( "Collection returns not supported for stored procedure mapping" );
}
@Override
public void visitFetch(NativeSQLQueryJoinReturn rtn) {
throw new UnsupportedOperationException( "Collection returns not supported for stored procedure mapping" );
}
@Override
public void visitDynamicInstantiation(NativeSQLQueryConstructorReturn rtn) {
final ScalarReturn[] scalars = new ScalarReturn[ rtn.getColumnReturns().length ];
int i = 0;
for ( NativeSQLQueryScalarReturn scalarReturn : rtn.getColumnReturns() ) {
scalars[i++] = new ScalarReturn( scalarReturn.getType(), scalarReturn.getColumnAlias() );
}
customReturns.add( new ConstructorReturn( rtn.getTargetClass(), scalars ) );
}
}
);
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;
// }
}