HHH-8276 - Integrate LoadPlans into UniqueEntityLoader (PoC)

This commit is contained in:
Steve Ebersole 2013-06-20 16:21:51 -05:00
parent 456d61bd4e
commit dc7cdf9d88
144 changed files with 7418 additions and 2925 deletions

View File

@ -71,9 +71,13 @@ public class PropertyAccessException extends HibernateException {
return propertyName; return propertyName;
} }
protected String originalMessage() {
return super.getMessage();
}
@Override @Override
public String getMessage() { public String getMessage() {
return super.getMessage() return originalMessage()
+ ( wasSetter ? " setter of " : " getter of " ) + ( wasSetter ? " setter of " : " getter of " )
+ StringHelper.qualify( persistentClass.getName(), propertyName ); + StringHelper.qualify( persistentClass.getName(), propertyName );
} }

View File

@ -0,0 +1,68 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate;
/**
* @author Steve Ebersole
*/
public class PropertySetterAccessException extends PropertyAccessException {
/**
* Constructs a PropertyAccessException using the specified information.
*
* @param cause The underlying cause
* @param persistentClass The class which is supposed to contain the property in question
* @param propertyName The name of the property.
* @param expectedType The expected property type
* @param target The target, which should be of type 'persistentClass'
* @param value The property value we are trying to set
*/
public PropertySetterAccessException(
Throwable cause,
Class persistentClass,
String propertyName,
Class expectedType,
Object target,
Object value) {
super(
cause,
String.format(
"IllegalArgumentException occurred while calling setter for property [%s.%s (expected type = %s)]; " +
"target = [%s], property value = [%s]",
persistentClass.getName(),
propertyName,
expectedType.getName(),
target,
value
),
true,
persistentClass,
propertyName
);
}
@Override
public String toString() {
return super.originalMessage();
}
}

View File

@ -57,6 +57,8 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
* *
* @author Emmanuel Bernard * @author Emmanuel Bernard
* @author Steve Ebersole * @author Steve Ebersole
*
* @see AnyMetaDef
*/ */
@java.lang.annotation.Target({METHOD, FIELD}) @java.lang.annotation.Target({METHOD, FIELD})
@Retention(RUNTIME) @Retention(RUNTIME)

View File

@ -28,6 +28,7 @@ import java.util.Properties;
import java.util.Set; import java.util.Set;
import org.hibernate.CustomEntityDirtinessStrategy; import org.hibernate.CustomEntityDirtinessStrategy;
import org.hibernate.EntityNameResolver;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.Interceptor; import org.hibernate.Interceptor;
import org.hibernate.MappingException; import org.hibernate.MappingException;
@ -290,4 +291,6 @@ public interface SessionFactoryImplementor extends Mapping, SessionFactory {
* @return * @return
*/ */
public NamedQueryRepository getNamedQueryRepository(); public NamedQueryRepository getNamedQueryRepository();
Iterable<EntityNameResolver> iterateEntityNameResolvers();
} }

View File

@ -52,12 +52,22 @@ public final class NameGenerator {
} }
public static String scalarName(int x, int y) { public static String scalarName(int x, int y) {
return new StringBuilder() return scalarName( "col_" + x, y );
.append( "col_" ) }
.append( x )
.append( '_' ) public static String scalarName(String base, int num) {
.append( y ) return base + '_' + num + '_';
.append( '_' ) }
.toString();
public static String[] scalarNames(String base, int count) {
final String[] names = new String[count];
for ( int j = 0; j < count; j++ ) {
names[j] = scalarName( base, j );
}
return names;
}
public static String[] scalarNames(int uniqueness, int count) {
return scalarNames( "col_" + uniqueness, count );
} }
} }

View File

@ -378,6 +378,11 @@ public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, Par
final FromElement fromElement; final FromElement fromElement;
if ( dot.getDataType() != null && dot.getDataType().isComponentType() ) { if ( dot.getDataType() != null && dot.getDataType().isComponentType() ) {
if ( dot.getDataType().isAnyType() ) {
throw new SemanticException( "An AnyType attribute cannot be join fetched" );
// ^^ because the discriminator (aka, the "meta columns") must be known to the SQL in
// a non-parameterized way.
}
FromElementFactory factory = new FromElementFactory( FromElementFactory factory = new FromElementFactory(
getCurrentFromClause(), getCurrentFromClause(),
dot.getLhs().getFromElement(), dot.getLhs().getFromElement(),

View File

@ -35,7 +35,6 @@ import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.Set; import java.util.Set;
@ -268,7 +267,6 @@ public final class SessionFactoryImpl
this.jdbcServices = this.serviceRegistry.getService( JdbcServices.class ); this.jdbcServices = this.serviceRegistry.getService( JdbcServices.class );
this.dialect = this.jdbcServices.getDialect(); this.dialect = this.jdbcServices.getDialect();
this.cacheAccess = this.serviceRegistry.getService( CacheImplementor.class ); this.cacheAccess = this.serviceRegistry.getService( CacheImplementor.class );
final RegionFactory regionFactory = cacheAccess.getRegionFactory();
this.sqlFunctionRegistry = new SQLFunctionRegistry( getDialect(), cfg.getSqlFunctions() ); this.sqlFunctionRegistry = new SQLFunctionRegistry( getDialect(), cfg.getSqlFunctions() );
if ( observer != null ) { if ( observer != null ) {
this.observer.addObserver( observer ); this.observer.addObserver( observer );
@ -329,15 +327,22 @@ public final class SessionFactoryImpl
} }
} }
imports = new HashMap<String,String>( cfg.getImports() );
/////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////
// Prepare persisters and link them up with their cache // Prepare persisters and link them up with their cache
// region/access-strategy // region/access-strategy
final RegionFactory regionFactory = cacheAccess.getRegionFactory();
final String cacheRegionPrefix = settings.getCacheRegionPrefix() == null ? "" : settings.getCacheRegionPrefix() + "."; final String cacheRegionPrefix = settings.getCacheRegionPrefix() == null ? "" : settings.getCacheRegionPrefix() + ".";
final PersisterFactory persisterFactory = serviceRegistry.getService( PersisterFactory.class ); final PersisterFactory persisterFactory = serviceRegistry.getService( PersisterFactory.class );
// todo : consider removing this silliness and just have EntityPersister directly implement ClassMetadata
// EntityPersister.getClassMetadata() for the internal impls simply "return this";
// collapsing those would allow us to remove this "extra" Map
//
// todo : similar for CollectionPersister/CollectionMetadata
entityPersisters = new HashMap(); entityPersisters = new HashMap();
Map entityAccessStrategies = new HashMap(); Map entityAccessStrategies = new HashMap();
Map<String,ClassMetadata> classMeta = new HashMap<String,ClassMetadata>(); Map<String,ClassMetadata> classMeta = new HashMap<String,ClassMetadata>();
@ -462,19 +467,13 @@ public final class SessionFactoryImpl
cfg.getSqlResultSetMappings().values(), cfg.getSqlResultSetMappings().values(),
toProcedureCallMementos( cfg.getNamedProcedureCallMap(), cfg.getSqlResultSetMappings() ) toProcedureCallMementos( cfg.getNamedProcedureCallMap(), cfg.getSqlResultSetMappings() )
); );
imports = new HashMap<String,String>( cfg.getImports() );
// after *all* persisters and named queries are registered // after *all* persisters and named queries are registered
Iterator iter = entityPersisters.values().iterator(); for ( EntityPersister persister : entityPersisters.values() ) {
while ( iter.hasNext() ) {
final EntityPersister persister = ( ( EntityPersister ) iter.next() );
persister.postInstantiate(); persister.postInstantiate();
registerEntityNameResolvers( persister ); registerEntityNameResolvers( persister );
} }
iter = collectionPersisters.values().iterator(); for ( CollectionPersister persister : collectionPersisters.values() ) {
while ( iter.hasNext() ) {
final CollectionPersister persister = ( ( CollectionPersister ) iter.next() );
persister.postInstantiate(); persister.postInstantiate();
} }
@ -1070,6 +1069,7 @@ public final class SessionFactoryImpl
entityNameResolvers.put( resolver, ENTITY_NAME_RESOLVER_MAP_VALUE ); entityNameResolvers.put( resolver, ENTITY_NAME_RESOLVER_MAP_VALUE );
} }
@Override
public Iterable<EntityNameResolver> iterateEntityNameResolvers() { public Iterable<EntityNameResolver> iterateEntityNameResolvers() {
return entityNameResolvers.keySet(); return entityNameResolvers.keySet();
} }

View File

@ -51,20 +51,22 @@ public class GeneratedCollectionAliases implements CollectionAliases {
this.keyAliases = getUserProvidedAliases( this.keyAliases = getUserProvidedAliases(
"key", "key",
persister.getKeyColumnAliases( suffix ) persister.getKeyColumnAliases( suffix )
); );
this.indexAliases = getUserProvidedAliases( this.indexAliases = getUserProvidedAliases(
"index", "index",
persister.getIndexColumnAliases( suffix ) persister.getIndexColumnAliases( suffix )
); );
this.elementAliases = getUserProvidedAliases( "element", this.elementAliases = getUserProvidedAliases(
"element",
persister.getElementColumnAliases( suffix ) persister.getElementColumnAliases( suffix )
); );
this.identifierAlias = getUserProvidedAlias( "id", this.identifierAlias = getUserProvidedAlias(
"id",
persister.getIdentifierColumnAlias( suffix ) persister.getIdentifierColumnAlias( suffix )
); );
} }
public GeneratedCollectionAliases(CollectionPersister persister, String string) { public GeneratedCollectionAliases(CollectionPersister persister, String string) {

View File

@ -24,6 +24,7 @@
package org.hibernate.loader; package org.hibernate.loader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@ -91,6 +92,9 @@ public class JoinWalker {
} }
public List getAssociations() {
return Collections.unmodifiableList( associations );
}
public String[] getCollectionSuffixes() { public String[] getCollectionSuffixes() {
return collectionSuffixes; return collectionSuffixes;

View File

@ -72,11 +72,19 @@ public abstract class BatchingEntityLoaderBuilder {
LoadQueryInfluencers influencers) { LoadQueryInfluencers influencers) {
if ( batchSize <= 1 ) { if ( batchSize <= 1 ) {
// no batching // no batching
return new EntityLoader( persister, lockMode, factory, influencers ); return buildNonBatchingLoader( persister, lockMode, factory, influencers );
} }
return buildBatchingLoader( persister, batchSize, lockMode, factory, influencers ); return buildBatchingLoader( persister, batchSize, lockMode, factory, influencers );
} }
protected UniqueEntityLoader buildNonBatchingLoader(
OuterJoinLoadable persister,
LockMode lockMode,
SessionFactoryImplementor factory,
LoadQueryInfluencers influencers) {
return new EntityLoader( persister, lockMode, factory, influencers );
}
protected abstract UniqueEntityLoader buildBatchingLoader( protected abstract UniqueEntityLoader buildBatchingLoader(
OuterJoinLoadable persister, OuterJoinLoadable persister,
int batchSize, int batchSize,
@ -103,11 +111,19 @@ public abstract class BatchingEntityLoaderBuilder {
LoadQueryInfluencers influencers) { LoadQueryInfluencers influencers) {
if ( batchSize <= 1 ) { if ( batchSize <= 1 ) {
// no batching // no batching
return new EntityLoader( persister, lockOptions, factory, influencers ); return buildNonBatchingLoader( persister, lockOptions, factory, influencers );
} }
return buildBatchingLoader( persister, batchSize, lockOptions, factory, influencers ); return buildBatchingLoader( persister, batchSize, lockOptions, factory, influencers );
} }
protected UniqueEntityLoader buildNonBatchingLoader(
OuterJoinLoadable persister,
LockOptions lockOptions,
SessionFactoryImplementor factory,
LoadQueryInfluencers influencers) {
return new EntityLoader( persister, lockOptions, factory, influencers );
}
protected abstract UniqueEntityLoader buildBatchingLoader( protected abstract UniqueEntityLoader buildBatchingLoader(
OuterJoinLoadable persister, OuterJoinLoadable persister,
int batchSize, int batchSize,

View File

@ -0,0 +1,58 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.entity.plan;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.entity.BatchingEntityLoaderBuilder;
import org.hibernate.loader.entity.UniqueEntityLoader;
import org.hibernate.persister.entity.OuterJoinLoadable;
/**
* Base class for LoadPlan-based BatchingEntityLoaderBuilder implementations. Mainly we handle the common
* "no batching" case here to use the LoadPlan-based EntityLoader
*
* @author Steve Ebersole
*/
public abstract class AbstractBatchingEntityLoaderBuilder extends BatchingEntityLoaderBuilder {
@Override
protected UniqueEntityLoader buildNonBatchingLoader(
OuterJoinLoadable persister,
LockMode lockMode,
SessionFactoryImplementor factory,
LoadQueryInfluencers influencers) {
return EntityLoader.forEntity( persister ).withLockMode( lockMode ).withInfluencers( influencers ).byPrimaryKey();
}
@Override
protected UniqueEntityLoader buildNonBatchingLoader(
OuterJoinLoadable persister,
LockOptions lockOptions,
SessionFactoryImplementor factory,
LoadQueryInfluencers influencers) {
return EntityLoader.forEntity( persister ).withLockOptions( lockOptions ).withInfluencers( influencers ).byPrimaryKey();
}
}

View File

@ -8,7 +8,6 @@ import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -22,7 +21,6 @@ import org.hibernate.dialect.pagination.LimitHandler;
import org.hibernate.dialect.pagination.LimitHelper; import org.hibernate.dialect.pagination.LimitHelper;
import org.hibernate.dialect.pagination.NoopLimitHandler; import org.hibernate.dialect.pagination.NoopLimitHandler;
import org.hibernate.engine.jdbc.ColumnNameCache; import org.hibernate.engine.jdbc.ColumnNameCache;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.QueryParameters; import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.RowSelection; import org.hibernate.engine.spi.RowSelection;
@ -32,17 +30,14 @@ import org.hibernate.engine.spi.TypedValue;
import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.loader.entity.UniqueEntityLoader; import org.hibernate.loader.entity.UniqueEntityLoader;
import org.hibernate.loader.internal.EntityLoadQueryBuilderImpl; import org.hibernate.loader.plan.exec.query.spi.NamedParameterContext;
import org.hibernate.loader.internal.LoadQueryAliasResolutionContextImpl; import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters;
import org.hibernate.loader.internal.ResultSetProcessorImpl; import org.hibernate.loader.plan.exec.spi.LoadQueryDetails;
import org.hibernate.loader.plan.internal.SingleRootReturnLoadPlanBuilderStrategy; import org.hibernate.loader.plan.internal.SingleRootReturnLoadPlanBuilderStrategy;
import org.hibernate.loader.plan.spi.LoadPlan; import org.hibernate.loader.plan.spi.LoadPlan;
import org.hibernate.loader.plan.spi.build.LoadPlanBuilder; import org.hibernate.loader.plan.spi.build.LoadPlanBuilder;
import org.hibernate.loader.spi.AfterLoadAction; import org.hibernate.loader.spi.AfterLoadAction;
import org.hibernate.loader.spi.LoadQueryAliasResolutionContext;
import org.hibernate.loader.spi.NamedParameterContext;
import org.hibernate.loader.spi.NoOpLoadPlanAdvisor; import org.hibernate.loader.spi.NoOpLoadPlanAdvisor;
import org.hibernate.loader.spi.ResultSetProcessor;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.OuterJoinLoadable; import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.pretty.MessageHelper; import org.hibernate.pretty.MessageHelper;
@ -63,63 +58,41 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
private final String entityName; private final String entityName;
private final LoadPlan plan; private final LoadPlan plan;
private final String staticSql; private final LoadQueryDetails staticLoadQuery;
private final LoadQueryAliasResolutionContext staticAliasResolutionContext;
private final ResultSetProcessor staticResultSetProcessor;
private ColumnNameCache columnNameCache; private ColumnNameCache columnNameCache;
public AbstractLoadPlanBasedEntityLoader( public AbstractLoadPlanBasedEntityLoader(
OuterJoinLoadable entityPersister, OuterJoinLoadable entityPersister,
Type uniqueKeyType,
SessionFactoryImplementor factory, SessionFactoryImplementor factory,
LoadQueryInfluencers loadQueryInfluencers) { String[] uniqueKeyColumnNames,
Type uniqueKeyType,
QueryBuildingParameters buildingParameters) {
this.entityPersister = entityPersister;
this.factory = factory; this.factory = factory;
this.uniqueKeyType = uniqueKeyType; this.uniqueKeyType = uniqueKeyType;
this.entityName = entityPersister.getEntityName(); this.entityName = entityPersister.getEntityName();
this.entityPersister = entityPersister;
final SingleRootReturnLoadPlanBuilderStrategy strategy = new SingleRootReturnLoadPlanBuilderStrategy( final SingleRootReturnLoadPlanBuilderStrategy strategy = new SingleRootReturnLoadPlanBuilderStrategy(
factory, factory,
loadQueryInfluencers buildingParameters.getQueryInfluencers()
); );
this.plan = LoadPlanBuilder.buildRootEntityLoadPlan( strategy, entityPersister ); this.plan = LoadPlanBuilder.buildRootEntityLoadPlan( strategy, entityPersister );
this.staticAliasResolutionContext = buildAliasResolutionContext( plan, factory ); this.staticLoadQuery = LoadQueryDetails.makeForBatching(
this.staticSql = generateStaticSql( plan, staticAliasResolutionContext, factory, loadQueryInfluencers ); uniqueKeyColumnNames,
this.staticResultSetProcessor = generateStaticResultSetProcessor( plan ); plan,
}
protected LoadQueryAliasResolutionContext buildAliasResolutionContext(LoadPlan plan, SessionFactoryImplementor factory) {
return new LoadQueryAliasResolutionContextImpl(
factory, factory,
0, buildingParameters
Collections.singletonMap( plan.getReturns().get( 0 ), new String[] {"abc"} )
); );
} }
protected String generateStaticSql(
LoadPlan plan,
LoadQueryAliasResolutionContext aliasResolutionContext,
SessionFactoryImplementor factory,
LoadQueryInfluencers loadQueryInfluencers) {
return new EntityLoadQueryBuilderImpl( loadQueryInfluencers, plan ).generateSql(
1,
factory,
aliasResolutionContext
);
}
protected ResultSetProcessor generateStaticResultSetProcessor(LoadPlan plan) {
return new ResultSetProcessorImpl( plan );
}
protected SessionFactoryImplementor getFactory() { protected SessionFactoryImplementor getFactory() {
return factory; return factory;
} }
protected String getSqlStatement() { protected LoadQueryDetails getStaticLoadQuery() {
return staticSql; return staticLoadQuery;
} }
protected String getEntityName() { protected String getEntityName() {
@ -152,17 +125,12 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
final QueryParameters qp = new QueryParameters(); final QueryParameters qp = new QueryParameters();
qp.setPositionalParameterTypes( types ); qp.setPositionalParameterTypes( types );
qp.setPositionalParameterValues( ids ); qp.setPositionalParameterValues( ids );
qp.setOptionalObject( optionalObject );
qp.setOptionalEntityName( optionalEntityName );
qp.setOptionalId( optionalId );
qp.setLockOptions( lockOptions ); qp.setLockOptions( lockOptions );
result = executeLoad( result = executeLoad(
session, session,
qp, qp,
staticSql, staticLoadQuery,
staticResultSetProcessor,
staticAliasResolutionContext,
false, false,
null null
); );
@ -171,7 +139,7 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
throw factory.getSQLExceptionHelper().convert( throw factory.getSQLExceptionHelper().convert(
sqle, sqle,
"could not load an entity batch: " + MessageHelper.infoString( entityPersister, ids, getFactory() ), "could not load an entity batch: " + MessageHelper.infoString( entityPersister, ids, getFactory() ),
getSqlStatement() staticLoadQuery.getSqlStatement()
); );
} }
@ -203,9 +171,7 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
final List results = executeLoad( final List results = executeLoad(
session, session,
qp, qp,
staticSql, staticLoadQuery,
staticResultSetProcessor,
staticAliasResolutionContext,
false, false,
null null
); );
@ -220,7 +186,7 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
entityPersister.getIdentifierType(), entityPersister.getIdentifierType(),
factory factory
), ),
getSqlStatement() staticLoadQuery.getSqlStatement()
); );
} }
@ -231,18 +197,14 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
protected List executeLoad( protected List executeLoad(
SessionImplementor session, SessionImplementor session,
QueryParameters queryParameters, QueryParameters queryParameters,
String sql, LoadQueryDetails loadQueryDetails,
ResultSetProcessor resultSetProcessor,
LoadQueryAliasResolutionContext aliasResolutionContext,
boolean returnProxies, boolean returnProxies,
ResultTransformer forcedResultTransformer) throws SQLException { ResultTransformer forcedResultTransformer) throws SQLException {
final List<AfterLoadAction> afterLoadActions = new ArrayList<AfterLoadAction>(); final List<AfterLoadAction> afterLoadActions = new ArrayList<AfterLoadAction>();
return executeLoad( return executeLoad(
session, session,
queryParameters, queryParameters,
sql, loadQueryDetails,
resultSetProcessor,
aliasResolutionContext,
returnProxies, returnProxies,
forcedResultTransformer, forcedResultTransformer,
afterLoadActions afterLoadActions
@ -252,9 +214,7 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
protected List executeLoad( protected List executeLoad(
SessionImplementor session, SessionImplementor session,
QueryParameters queryParameters, QueryParameters queryParameters,
String sql, LoadQueryDetails loadQueryDetails,
ResultSetProcessor resultSetProcessor,
LoadQueryAliasResolutionContext aliasResolutionContext,
boolean returnProxies, boolean returnProxies,
ResultTransformer forcedResultTransformer, ResultTransformer forcedResultTransformer,
List<AfterLoadAction> afterLoadActions) throws SQLException { List<AfterLoadAction> afterLoadActions) throws SQLException {
@ -273,9 +233,10 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
persistenceContext.beforeLoad(); persistenceContext.beforeLoad();
try { try {
List results; List results;
final String sql = loadQueryDetails.getSqlStatement();
try { try {
final SqlStatementWrapper wrapper = executeQueryStatement( sql, queryParameters, false, afterLoadActions, session ); final SqlStatementWrapper wrapper = executeQueryStatement( sql, queryParameters, false, afterLoadActions, session );
results = resultSetProcessor.extractResults( results = loadQueryDetails.getResultSetProcessor().extractResults(
// todo : hook in the JPA 2.1 entity graph advisor // todo : hook in the JPA 2.1 entity graph advisor
NoOpLoadPlanAdvisor.INSTANCE, NoOpLoadPlanAdvisor.INSTANCE,
wrapper.getResultSet(), wrapper.getResultSet(),
@ -287,7 +248,7 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
return AbstractLoadPlanBasedEntityLoader.this.getNamedParameterLocs( name ); return AbstractLoadPlanBasedEntityLoader.this.getNamedParameterLocs( name );
} }
}, },
aliasResolutionContext, loadQueryDetails.getAliasResolutionContext(),
returnProxies, returnProxies,
queryParameters.isReadOnly(), queryParameters.isReadOnly(),
forcedResultTransformer, forcedResultTransformer,
@ -334,9 +295,7 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
protected Object doQueryAndLoadEntity( protected Object doQueryAndLoadEntity(
SessionImplementor session, SessionImplementor session,
QueryParameters queryParameters, QueryParameters queryParameters,
String sql, LoadQueryDetails loadQueryDetails,
ResultSetProcessor resultSetProcessor,
LoadQueryAliasResolutionContext aliasResolutionContext,
boolean returnProxies, boolean returnProxies,
ResultTransformer forcedResultTransformer) throws SQLException { ResultTransformer forcedResultTransformer) throws SQLException {
@ -345,7 +304,7 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
final SqlStatementWrapper wrapper = executeQueryStatement( queryParameters, false, afterLoadActions, session ); final SqlStatementWrapper wrapper = executeQueryStatement( queryParameters, false, afterLoadActions, session );
try { try {
final List results = resultSetProcessor.extractResults( final List results = loadQueryDetails.getResultSetProcessor().extractResults(
NoOpLoadPlanAdvisor.INSTANCE, NoOpLoadPlanAdvisor.INSTANCE,
wrapper.getResultSet(), wrapper.getResultSet(),
session, session,
@ -356,7 +315,7 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
return AbstractLoadPlanBasedEntityLoader.this.getNamedParameterLocs( name ); return AbstractLoadPlanBasedEntityLoader.this.getNamedParameterLocs( name );
} }
}, },
staticAliasResolutionContext, loadQueryDetails.getAliasResolutionContext(),
returnProxies, returnProxies,
queryParameters.isReadOnly(), queryParameters.isReadOnly(),
forcedResultTransformer, forcedResultTransformer,
@ -398,7 +357,7 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
final boolean scroll, final boolean scroll,
List<AfterLoadAction> afterLoadActions, List<AfterLoadAction> afterLoadActions,
final SessionImplementor session) throws SQLException { final SessionImplementor session) throws SQLException {
return executeQueryStatement( getSqlStatement(), queryParameters, scroll, afterLoadActions, session ); return executeQueryStatement( staticLoadQuery.getSqlStatement(), queryParameters, scroll, afterLoadActions, session );
} }
protected SqlStatementWrapper executeQueryStatement( protected SqlStatementWrapper executeQueryStatement(

View File

@ -40,8 +40,10 @@ import org.hibernate.pretty.MessageHelper;
import org.hibernate.type.Type; import org.hibernate.type.Type;
/** /**
* The base contract for loaders capable of performing batch-fetch loading of entities using multiple primary key * The base contract for UniqueEntityLoader implementations capable of performing batch-fetch loading of entities
* values in the SQL <tt>WHERE</tt> clause. * using multiple primary key values in the SQL <tt>WHERE</tt> clause.
* <p/>
* Typically these are
* *
* @author Gavin King * @author Gavin King
* @author Steve Ebersole * @author Steve Ebersole

View File

@ -32,148 +32,125 @@ import org.hibernate.MappingException;
import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreLogging;
import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters;
import org.hibernate.persister.entity.OuterJoinLoadable; import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.type.Type; import org.hibernate.type.Type;
/** /**
* UniqueEntityLoader implementation that is the main functionality for LoadPlan-based Entity loading.
* <p/>
* Can handle batch-loading as well as non-pk, unique-key loading,
* <p/>
* Much is ultimately delegated to its superclass, AbstractLoadPlanBasedEntityLoader. However:
* todo How much of AbstractLoadPlanBasedEntityLoader is actually needed?
*
* Loads an entity instance using outerjoin fetching to fetch associated entities. * Loads an entity instance using outerjoin fetching to fetch associated entities.
* <br> * <br>
* The <tt>EntityPersister</tt> must implement <tt>Loadable</tt>. For other entities, * The <tt>EntityPersister</tt> must implement <tt>Loadable</tt>. For other entities,
* create a customized subclass of <tt>Loader</tt>. * create a customized subclass of <tt>Loader</tt>.
* *
* @author Gavin King * @author Gavin King
* @author Steve Ebersole
* @author Gail Badner
*/ */
public class EntityLoader extends AbstractLoadPlanBasedEntityLoader { public class EntityLoader extends AbstractLoadPlanBasedEntityLoader {
private static final Logger log = CoreLogging.logger( EntityLoader.class ); private static final Logger log = CoreLogging.logger( EntityLoader.class );
// private final boolean batchLoader; public static Builder forEntity(OuterJoinLoadable persister) {
// private final int[][] compositeKeyManyToOneTargetIndices; return new Builder( persister );
//
// public EntityLoader(
// OuterJoinLoadable persister,
// LockMode lockMode,
// SessionFactoryImplementor factory,
// LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
// this( persister, 1, lockMode, factory, loadQueryInfluencers );
// }
//
// public EntityLoader(
// OuterJoinLoadable persister,
// LockOptions lockOptions,
// SessionFactoryImplementor factory,
// LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
// this( persister, 1, lockOptions, factory, loadQueryInfluencers );
// }
public EntityLoader(
OuterJoinLoadable persister,
int batchSize,
LockMode lockMode,
SessionFactoryImplementor factory,
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
this(
persister,
persister.getIdentifierColumnNames(),
persister.getIdentifierType(),
batchSize,
lockMode,
factory,
loadQueryInfluencers
);
} }
public EntityLoader( public static class Builder {
OuterJoinLoadable persister, private final OuterJoinLoadable persister;
int batchSize, private int batchSize = 1;
LockOptions lockOptions, private LoadQueryInfluencers influencers = LoadQueryInfluencers.NONE;
SessionFactoryImplementor factory, private LockMode lockMode = LockMode.NONE;
LoadQueryInfluencers loadQueryInfluencers) throws MappingException { private LockOptions lockOptions;
this(
persister,
persister.getIdentifierColumnNames(),
persister.getIdentifierType(),
batchSize,
lockOptions,
factory,
loadQueryInfluencers
);
}
public EntityLoader( public Builder(OuterJoinLoadable persister) {
OuterJoinLoadable persister, this.persister = persister;
String[] uniqueKey,
Type uniqueKeyType,
int batchSize,
LockMode lockMode,
SessionFactoryImplementor factory,
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
super( persister, uniqueKeyType, factory, loadQueryInfluencers );
// EntityJoinWalker walker = new EntityJoinWalker(
// persister,
// uniqueKey,
// batchSize,
// lockMode,
// factory,
// loadQueryInfluencers
// );
// initFromWalker( walker );
// this.compositeKeyManyToOneTargetIndices = walker.getCompositeKeyManyToOneTargetIndices();
// postInstantiate();
//
// batchLoader = batchSize > 1;
//
if ( log.isDebugEnabled() ) {
log.debugf( "Static select for entity %s [%s]: %s", getEntityName(), lockMode, getSqlStatement() );
} }
}
public EntityLoader( public Builder withBatchSize(int batchSize) {
OuterJoinLoadable persister, this.batchSize = batchSize;
String[] uniqueKey, return this;
Type uniqueKeyType, }
int batchSize,
LockOptions lockOptions, public Builder withInfluencers(LoadQueryInfluencers influencers) {
SessionFactoryImplementor factory, this.influencers = influencers;
LoadQueryInfluencers loadQueryInfluencers) throws MappingException { return this;
super( persister, uniqueKeyType, factory, loadQueryInfluencers ); }
//
// EntityJoinWalker walker = new EntityJoinWalker( public Builder withLockMode(LockMode lockMode) {
// persister, this.lockMode = lockMode;
// uniqueKey, return this;
// batchSize, }
// lockOptions,
// factory, public Builder withLockOptions(LockOptions lockOptions) {
// loadQueryInfluencers this.lockOptions = lockOptions;
// ); return this;
// initFromWalker( walker ); }
// this.compositeKeyManyToOneTargetIndices = walker.getCompositeKeyManyToOneTargetIndices();
// postInstantiate(); public EntityLoader byPrimaryKey() {
// return byUniqueKey( persister.getIdentifierColumnNames(), persister.getIdentifierType() );
// batchLoader = batchSize > 1; }
//
if ( log.isDebugEnabled() ) { public EntityLoader byUniqueKey(String[] keyColumnNames, Type keyType) {
log.debugf( return new EntityLoader(
"Static select for entity %s [%s:%s]: %s", persister.getFactory(),
getEntityName(), persister,
lockOptions.getLockMode(), keyColumnNames,
lockOptions.getTimeOut(), keyType,
getSqlStatement() new QueryBuildingParameters() {
@Override
public LoadQueryInfluencers getQueryInfluencers() {
return influencers;
}
@Override
public int getBatchSize() {
return batchSize;
}
@Override
public LockMode getLockMode() {
return lockMode;
}
@Override
public LockOptions getLockOptions() {
return lockOptions;
}
}
); );
} }
} }
// public Object loadByUniqueKey(SessionImplementor session,Object key) { private EntityLoader(
// return load( session, key, null, null, LockOptions.NONE ); SessionFactoryImplementor factory,
// } OuterJoinLoadable persister,
// String[] uniqueKeyColumnNames,
// @Override Type uniqueKeyType,
// protected boolean isSingleRowLoader() { QueryBuildingParameters buildingParameters) throws MappingException {
// return !batchLoader; super( persister, factory, uniqueKeyColumnNames, uniqueKeyType, buildingParameters );
// } if ( log.isDebugEnabled() ) {
// if ( buildingParameters.getLockOptions() != null ) {
// @Override log.debugf(
// public int[][] getCompositeKeyManyToOneTargetIndices() { "Static select for entity %s [%s:%s]: %s",
// return compositeKeyManyToOneTargetIndices; getEntityName(),
// } buildingParameters.getLockOptions().getLockMode(),
buildingParameters.getLockOptions().getTimeOut(),
getStaticLoadQuery().getSqlStatement()
);
}
else if ( buildingParameters.getLockMode() != null ) {
log.debugf(
"Static select for entity %s [%s]: %s",
getEntityName(),
buildingParameters.getLockMode(),
getStaticLoadQuery().getSqlStatement()
);
}
}
}
} }

View File

@ -32,16 +32,15 @@ import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.loader.Loader;
import org.hibernate.loader.entity.BatchingEntityLoader;
import org.hibernate.loader.entity.BatchingEntityLoaderBuilder;
import org.hibernate.loader.entity.UniqueEntityLoader; import org.hibernate.loader.entity.UniqueEntityLoader;
import org.hibernate.persister.entity.OuterJoinLoadable; import org.hibernate.persister.entity.OuterJoinLoadable;
/** /**
* LoadPlan-based implementation of the the legacy batch loading strategy
*
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class LegacyBatchingEntityLoaderBuilder extends BatchingEntityLoaderBuilder { public class LegacyBatchingEntityLoaderBuilder extends AbstractBatchingEntityLoaderBuilder {
public static final LegacyBatchingEntityLoaderBuilder INSTANCE = new LegacyBatchingEntityLoaderBuilder(); public static final LegacyBatchingEntityLoaderBuilder INSTANCE = new LegacyBatchingEntityLoaderBuilder();
@Override @Override
@ -77,8 +76,11 @@ public class LegacyBatchingEntityLoaderBuilder extends BatchingEntityLoaderBuild
super( persister ); super( persister );
this.batchSizes = ArrayHelper.getBatchSizes( maxBatchSize ); this.batchSizes = ArrayHelper.getBatchSizes( maxBatchSize );
this.loaders = new EntityLoader[ batchSizes.length ]; this.loaders = new EntityLoader[ batchSizes.length ];
final EntityLoader.Builder entityLoaderBuilder = EntityLoader.forEntity( persister )
.withInfluencers( loadQueryInfluencers )
.withLockMode( lockMode );
for ( int i = 0; i < batchSizes.length; i++ ) { for ( int i = 0; i < batchSizes.length; i++ ) {
this.loaders[i] = new EntityLoader( persister, batchSizes[i], lockMode, factory, loadQueryInfluencers); this.loaders[i] = entityLoaderBuilder.withBatchSize( batchSizes[i] ).byPrimaryKey();
} }
} }
@ -91,8 +93,11 @@ public class LegacyBatchingEntityLoaderBuilder extends BatchingEntityLoaderBuild
super( persister ); super( persister );
this.batchSizes = ArrayHelper.getBatchSizes( maxBatchSize ); this.batchSizes = ArrayHelper.getBatchSizes( maxBatchSize );
this.loaders = new EntityLoader[ batchSizes.length ]; this.loaders = new EntityLoader[ batchSizes.length ];
final EntityLoader.Builder entityLoaderBuilder = EntityLoader.forEntity( persister )
.withInfluencers( loadQueryInfluencers )
.withLockOptions( lockOptions );
for ( int i = 0; i < batchSizes.length; i++ ) { for ( int i = 0; i < batchSizes.length; i++ ) {
this.loaders[i] = new EntityLoader( persister, batchSizes[i], lockOptions, factory, loadQueryInfluencers); this.loaders[i] = entityLoaderBuilder.withBatchSize( batchSizes[i] ).byPrimaryKey();
} }
} }

View File

@ -1,128 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.internal;
import java.util.List;
import org.hibernate.LockOptions;
import org.hibernate.MappingException;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.plan.spi.EntityReturn;
import org.hibernate.loader.spi.JoinableAssociation;
import org.hibernate.loader.spi.LoadQueryAliasResolutionContext;
import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.sql.JoinFragment;
import org.hibernate.sql.Select;
/**
* Represents an entity load query for criteria
* queries and entity loaders, used for generating SQL.
*
* This code is based on the SQL generation code originally in
* org.hibernate.loader.AbstractEntityJoinWalker.
*
* @author Gavin King
* @author Gail Badner
*/
public abstract class AbstractEntityLoadQueryImpl extends AbstractLoadQueryImpl {
private final EntityReturn entityReturn;
public AbstractEntityLoadQueryImpl(EntityReturn entityReturn, List<JoinableAssociation> associations) {
super( associations );
this.entityReturn = entityReturn;
}
protected final String generateSql(
final String whereString,
final String orderByString,
final LockOptions lockOptions,
final SessionFactoryImplementor factory,
final LoadQueryAliasResolutionContext aliasResolutionContext) throws MappingException {
return generateSql( null, whereString, orderByString, "", lockOptions, factory, aliasResolutionContext );
}
private String generateSql(
final String projection,
final String condition,
final String orderBy,
final String groupBy,
final LockOptions lockOptions,
final SessionFactoryImplementor factory,
final LoadQueryAliasResolutionContext aliasResolutionContext) throws MappingException {
JoinFragment ojf = mergeOuterJoins( factory, aliasResolutionContext );
// If no projection, then the last suffix should be for the entity return.
// TODO: simplify how suffixes are generated/processed.
final String entityReturnAlias = resolveEntityReturnAlias( aliasResolutionContext );
Select select = new Select( factory.getDialect() )
.setLockOptions( lockOptions )
.setSelectClause(
projection == null ?
getPersister().selectFragment(
entityReturnAlias,
aliasResolutionContext.resolveEntityColumnAliases( entityReturn ).getSuffix()
) + associationSelectString( aliasResolutionContext ) :
projection
)
.setFromClause(
factory.getDialect().appendLockHint(
lockOptions,
getPersister().fromTableFragment( entityReturnAlias )
) + getPersister().fromJoinFragment( entityReturnAlias, true, true )
)
.setWhereClause( condition )
.setOuterJoins(
ojf.toFromFragmentString(),
ojf.toWhereFragmentString() + getWhereFragment( aliasResolutionContext )
)
.setOrderByClause( orderBy( orderBy, aliasResolutionContext ) )
.setGroupByClause( groupBy );
if ( factory.getSettings().isCommentsEnabled() ) {
select.setComment( getComment() );
}
return select.toStatementString();
}
protected String getWhereFragment(LoadQueryAliasResolutionContext aliasResolutionContext) throws MappingException {
// here we do not bother with the discriminator.
return getPersister().whereJoinFragment( resolveEntityReturnAlias( aliasResolutionContext ), true, true );
}
protected abstract String getComment();
protected final OuterJoinLoadable getPersister() {
return (OuterJoinLoadable) entityReturn.getEntityPersister();
}
protected final String resolveEntityReturnAlias(LoadQueryAliasResolutionContext aliasResolutionContext) {
return aliasResolutionContext.resolveEntityTableAlias( entityReturn );
}
public String toString() {
return getClass().getName() + '(' + getPersister().getEntityName() + ')';
}
}

View File

@ -30,10 +30,12 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.loader.CollectionAliases; import org.hibernate.loader.CollectionAliases;
import org.hibernate.loader.EntityAliases; import org.hibernate.loader.EntityAliases;
import org.hibernate.loader.plan.exec.spi.AliasResolutionContext;
import org.hibernate.loader.plan.spi.Fetch;
import org.hibernate.loader.spi.JoinableAssociation; import org.hibernate.loader.spi.JoinableAssociation;
import org.hibernate.loader.spi.LoadQueryAliasResolutionContext;
import org.hibernate.persister.collection.QueryableCollection; import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.Joinable; import org.hibernate.persister.entity.Joinable;
import org.hibernate.persister.walking.internal.FetchStrategyHelper;
import org.hibernate.sql.ConditionFragment; import org.hibernate.sql.ConditionFragment;
import org.hibernate.sql.DisjunctionFragment; import org.hibernate.sql.DisjunctionFragment;
import org.hibernate.sql.InFragment; import org.hibernate.sql.InFragment;
@ -58,7 +60,7 @@ public abstract class AbstractLoadQueryImpl {
this.associations = associations; this.associations = associations;
} }
protected String orderBy(final String orderBy, LoadQueryAliasResolutionContext aliasResolutionContext) { protected String orderBy(final String orderBy, AliasResolutionContext aliasResolutionContext) {
return mergeOrderings( orderBy( associations, aliasResolutionContext ), orderBy ); return mergeOrderings( orderBy( associations, aliasResolutionContext ), orderBy );
} }
@ -77,7 +79,7 @@ public abstract class AbstractLoadQueryImpl {
/** /**
* Generate a sequence of <tt>LEFT OUTER JOIN</tt> clauses for the given associations. * Generate a sequence of <tt>LEFT OUTER JOIN</tt> clauses for the given associations.
*/ */
protected final JoinFragment mergeOuterJoins(SessionFactoryImplementor factory, LoadQueryAliasResolutionContext aliasResolutionContext) protected final JoinFragment mergeOuterJoins(SessionFactoryImplementor factory, AliasResolutionContext aliasResolutionContext)
throws MappingException { throws MappingException {
JoinFragment joinFragment = factory.getDialect().createOuterJoinFragment(); JoinFragment joinFragment = factory.getDialect().createOuterJoinFragment();
JoinableAssociation previous = null; JoinableAssociation previous = null;
@ -120,7 +122,7 @@ public abstract class AbstractLoadQueryImpl {
// TODO: why is this static? // TODO: why is this static?
protected static String orderBy( protected static String orderBy(
List<JoinableAssociation> associations, List<JoinableAssociation> associations,
LoadQueryAliasResolutionContext aliasResolutionContext) AliasResolutionContext aliasResolutionContext)
throws MappingException { throws MappingException {
StringBuilder buf = new StringBuilder(); StringBuilder buf = new StringBuilder();
JoinableAssociation previous = null; JoinableAssociation previous = null;
@ -197,7 +199,7 @@ public abstract class AbstractLoadQueryImpl {
/** /**
* Generate a select list of columns containing all properties of the entity classes * Generate a select list of columns containing all properties of the entity classes
*/ */
protected final String associationSelectString(LoadQueryAliasResolutionContext aliasResolutionContext) protected final String associationSelectString(AliasResolutionContext aliasResolutionContext)
throws MappingException { throws MappingException {
if ( associations.size() == 0 ) { if ( associations.size() == 0 ) {
@ -210,15 +212,19 @@ public abstract class AbstractLoadQueryImpl {
JoinableAssociation next = ( i == associations.size() - 1 ) JoinableAssociation next = ( i == associations.size() - 1 )
? null ? null
: associations.get( i + 1 ); : associations.get( i + 1 );
if ( !shouldAddToSql( association.getCurrentFetch() ) ) {
continue;
}
final Joinable joinable = association.getJoinable(); final Joinable joinable = association.getJoinable();
final EntityAliases currentEntityAliases = final EntityAliases currentEntityAliases =
association.getCurrentEntityReference() == null ? association.getCurrentEntityReference() == null ?
null : null :
aliasResolutionContext.resolveEntityColumnAliases( association.getCurrentEntityReference() ); aliasResolutionContext.resolveAliases( association.getCurrentEntityReference() ).getColumnAliases();
final CollectionAliases currentCollectionAliases = final CollectionAliases currentCollectionAliases =
association.getCurrentCollectionReference() == null ? association.getCurrentCollectionReference() == null ?
null : null :
aliasResolutionContext.resolveCollectionColumnAliases( association.getCurrentCollectionReference() ); aliasResolutionContext.resolveAliases( association.getCurrentCollectionReference() ).getCollectionColumnAliases();
final String selectFragment = joinable.selectFragment( final String selectFragment = joinable.selectFragment(
next == null ? null : next.getJoinable(), next == null ? null : next.getJoinable(),
next == null ? null : aliasResolutionContext.resolveAssociationRhsTableAlias( next ), next == null ? null : aliasResolutionContext.resolveAssociationRhsTableAlias( next ),
@ -236,6 +242,10 @@ public abstract class AbstractLoadQueryImpl {
} }
} }
private boolean shouldAddToSql(Fetch fetch) {
return FetchStrategyHelper.isJoinFetched( fetch.getFetchStrategy() );
}
private void addJoins( private void addJoins(
JoinFragment joinFragment, JoinFragment joinFragment,
JoinableAssociation association, JoinableAssociation association,
@ -260,7 +270,7 @@ public abstract class AbstractLoadQueryImpl {
private String resolveOnCondition( private String resolveOnCondition(
SessionFactoryImplementor factory, SessionFactoryImplementor factory,
JoinableAssociation joinableAssociation, JoinableAssociation joinableAssociation,
LoadQueryAliasResolutionContext aliasResolutionContext) { AliasResolutionContext aliasResolutionContext) {
final String withClause = StringHelper.isEmpty( joinableAssociation.getWithClause() ) ? final String withClause = StringHelper.isEmpty( joinableAssociation.getWithClause() ) ?
"" : "" :
" and ( " + joinableAssociation.getWithClause() + " )"; " and ( " + joinableAssociation.getWithClause() + " )";

View File

@ -58,7 +58,7 @@ public class EntityJoinableAssociationImpl extends AbstractJoinableAssociationIm
hasRestriction, hasRestriction,
enabledFilters enabledFilters
); );
this.joinableType = entityFetch.getEntityType(); this.joinableType = entityFetch.getFetchedType();
this.joinable = (Joinable) entityFetch.getEntityPersister(); this.joinable = (Joinable) entityFetch.getEntityPersister();
} }

View File

@ -1,227 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.internal;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.plan.spi.CollectionFetch;
import org.hibernate.loader.plan.spi.CollectionReference;
import org.hibernate.loader.plan.spi.CompositeFetch;
import org.hibernate.loader.plan.spi.EntityFetch;
import org.hibernate.loader.plan.spi.EntityReference;
import org.hibernate.loader.plan.spi.EntityReturn;
import org.hibernate.loader.plan.spi.LoadPlan;
import org.hibernate.loader.plan.spi.visit.LoadPlanVisitationStrategyAdapter;
import org.hibernate.loader.plan.spi.visit.LoadPlanVisitor;
import org.hibernate.loader.plan.spi.Return;
import org.hibernate.loader.spi.JoinableAssociation;
import org.hibernate.loader.spi.LoadQueryAliasResolutionContext;
import org.hibernate.loader.spi.LoadQueryBuilder;
import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.persister.walking.spi.WalkingException;
/**
* @author Gail Badner
*/
public class EntityLoadQueryBuilderImpl implements LoadQueryBuilder {
private final LoadQueryInfluencers loadQueryInfluencers;
private final LoadPlan loadPlan;
private final List<JoinableAssociation> associations;
public EntityLoadQueryBuilderImpl(
LoadQueryInfluencers loadQueryInfluencers,
LoadPlan loadPlan) {
this.loadQueryInfluencers = loadQueryInfluencers;
this.loadPlan = loadPlan;
// TODO: the whole point of the following is to build associations.
// this could be done while building loadPlan (and be a part of the LoadPlan).
// Should it be?
LocalVisitationStrategy strategy = new LocalVisitationStrategy();
LoadPlanVisitor.visit( loadPlan, strategy );
this.associations = strategy.associations;
}
@Override
public String generateSql(
int batchSize,
SessionFactoryImplementor sessionFactory,
LoadQueryAliasResolutionContext aliasResolutionContext) {
return generateSql(
batchSize,
getOuterJoinLoadable().getKeyColumnNames(),
sessionFactory,
aliasResolutionContext
);
}
public String generateSql(
int batchSize,
String[] uniqueKey,
SessionFactoryImplementor sessionFactory,
LoadQueryAliasResolutionContext aliasResolutionContext) {
final EntityLoadQueryImpl loadQuery = new EntityLoadQueryImpl(
getRootEntityReturn(),
associations
);
return loadQuery.generateSql(
uniqueKey,
batchSize,
getRootEntityReturn().getLockMode(),
sessionFactory,
aliasResolutionContext );
}
private EntityReturn getRootEntityReturn() {
return (EntityReturn) loadPlan.getReturns().get( 0 );
}
private OuterJoinLoadable getOuterJoinLoadable() {
return (OuterJoinLoadable) getRootEntityReturn().getEntityPersister();
}
private class LocalVisitationStrategy extends LoadPlanVisitationStrategyAdapter {
private final List<JoinableAssociation> associations = new ArrayList<JoinableAssociation>();
private Deque<EntityReference> entityReferenceStack = new ArrayDeque<EntityReference>();
private Deque<CollectionReference> collectionReferenceStack = new ArrayDeque<CollectionReference>();
private EntityReturn entityRootReturn;
@Override
public void handleEntityReturn(EntityReturn rootEntityReturn) {
this.entityRootReturn = rootEntityReturn;
}
@Override
public void startingRootReturn(Return rootReturn) {
if ( !EntityReturn.class.isInstance( rootReturn ) ) {
throw new WalkingException(
String.format(
"Unexpected type of return; expected [%s]; instead it was [%s]",
EntityReturn.class.getName(),
rootReturn.getClass().getName()
)
);
}
this.entityRootReturn = (EntityReturn) rootReturn;
pushToStack( entityReferenceStack, entityRootReturn );
}
@Override
public void finishingRootReturn(Return rootReturn) {
if ( !EntityReturn.class.isInstance( rootReturn ) ) {
throw new WalkingException(
String.format(
"Unexpected type of return; expected [%s]; instead it was [%s]",
EntityReturn.class.getName(),
rootReturn.getClass().getName()
)
);
}
popFromStack( entityReferenceStack, entityRootReturn );
}
@Override
public void startingEntityFetch(EntityFetch entityFetch) {
EntityJoinableAssociationImpl assoc = new EntityJoinableAssociationImpl(
entityFetch,
getCurrentCollectionReference(),
"", // getWithClause( entityFetch.getPropertyPath() )
false, // hasRestriction( entityFetch.getPropertyPath() )
loadQueryInfluencers.getEnabledFilters()
);
associations.add( assoc );
pushToStack( entityReferenceStack, entityFetch );
}
@Override
public void finishingEntityFetch(EntityFetch entityFetch) {
popFromStack( entityReferenceStack, entityFetch );
}
@Override
public void startingCollectionFetch(CollectionFetch collectionFetch) {
CollectionJoinableAssociationImpl assoc = new CollectionJoinableAssociationImpl(
collectionFetch,
getCurrentEntityReference(),
"", // getWithClause( entityFetch.getPropertyPath() )
false, // hasRestriction( entityFetch.getPropertyPath() )
loadQueryInfluencers.getEnabledFilters()
);
associations.add( assoc );
pushToStack( collectionReferenceStack, collectionFetch );
}
@Override
public void finishingCollectionFetch(CollectionFetch collectionFetch) {
popFromStack( collectionReferenceStack, collectionFetch );
}
@Override
public void startingCompositeFetch(CompositeFetch fetch) {
//To change body of implemented methods use File | Settings | File Templates.
}
@Override
public void finishingCompositeFetch(CompositeFetch fetch) {
//To change body of implemented methods use File | Settings | File Templates.
}
@Override
public void finish(LoadPlan loadPlan) {
entityReferenceStack.clear();
collectionReferenceStack.clear();
}
private EntityReference getCurrentEntityReference() {
return entityReferenceStack.peekFirst() == null ? null : entityReferenceStack.peekFirst();
}
private CollectionReference getCurrentCollectionReference() {
return collectionReferenceStack.peekFirst() == null ? null : collectionReferenceStack.peekFirst();
}
private <T> void pushToStack(Deque<T> stack, T value) {
stack.push( value );
}
private <T> void popFromStack(Deque<T> stack, T expectedValue) {
T poppedValue = stack.pop();
if ( poppedValue != expectedValue ) {
throw new WalkingException(
String.format(
"Unexpected value from stack. Expected=[%s]; instead it was [%s].",
expectedValue,
poppedValue
)
);
}
}
}
}

View File

@ -1,68 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.internal;
import java.util.Collections;
import java.util.List;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.MappingException;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.plan.spi.EntityReturn;
import org.hibernate.loader.spi.JoinableAssociation;
import org.hibernate.loader.spi.LoadQueryAliasResolutionContext;
/**
* Represents an load query for fetching an entity, used for generating SQL.
*
* This code is based on the SQL generation code originally in
* org.hibernate.loader.EntityJoinWalker.
*
* @author Gavin King
* @author Gail Badner
*/
public class EntityLoadQueryImpl extends AbstractEntityLoadQueryImpl {
public EntityLoadQueryImpl(
EntityReturn entityReturn,
List<JoinableAssociation> associations) throws MappingException {
super( entityReturn, associations );
}
public String generateSql(
String[] uniqueKey,
int batchSize,
LockMode lockMode,
SessionFactoryImplementor factory,
LoadQueryAliasResolutionContext aliasResolutionContext) {
StringBuilder whereCondition = whereString( resolveEntityReturnAlias( aliasResolutionContext ), uniqueKey, batchSize )
//include the discriminator and class-level where, but not filters
.append( getPersister().filterFragment( resolveEntityReturnAlias( aliasResolutionContext ), Collections.EMPTY_MAP ) );
return generateSql( whereCondition.toString(), "", new LockOptions().setLockMode( lockMode ), factory, aliasResolutionContext );
}
protected String getComment() {
return "load " + getPersister().getEntityName();
}
}

View File

@ -1,223 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.internal;
import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.jboss.logging.Logger;
import org.hibernate.cfg.NotYetImplementedException;
import org.hibernate.dialect.pagination.LimitHelper;
import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.RowSelection;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.loader.plan.spi.CollectionFetch;
import org.hibernate.loader.plan.spi.CollectionReturn;
import org.hibernate.loader.plan.spi.EntityFetch;
import org.hibernate.loader.plan.spi.LoadPlan;
import org.hibernate.loader.plan.spi.visit.LoadPlanVisitationStrategyAdapter;
import org.hibernate.loader.plan.spi.visit.LoadPlanVisitor;
import org.hibernate.loader.plan.spi.Return;
import org.hibernate.loader.spi.AfterLoadAction;
import org.hibernate.loader.spi.LoadPlanAdvisor;
import org.hibernate.loader.spi.LoadQueryAliasResolutionContext;
import org.hibernate.loader.spi.NamedParameterContext;
import org.hibernate.loader.spi.ScrollableResultSetProcessor;
import org.hibernate.loader.spi.ResultSetProcessor;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.pretty.MessageHelper;
import org.hibernate.transform.ResultTransformer;
/**
* @author Steve Ebersole
*/
public class ResultSetProcessorImpl implements ResultSetProcessor {
private static final Logger LOG = Logger.getLogger( ResultSetProcessorImpl.class );
private final LoadPlan baseLoadPlan;
private final boolean hadSubselectFetches;
public ResultSetProcessorImpl(LoadPlan loadPlan) {
this.baseLoadPlan = loadPlan;
LocalVisitationStrategy strategy = new LocalVisitationStrategy();
LoadPlanVisitor.visit( loadPlan, strategy );
this.hadSubselectFetches = strategy.hadSubselectFetches;
}
@Override
public ScrollableResultSetProcessor toOnDemandForm() {
// todo : implement
throw new NotYetImplementedException();
}
@Override
public List extractResults(
LoadPlanAdvisor loadPlanAdvisor,
ResultSet resultSet,
final SessionImplementor session,
QueryParameters queryParameters,
NamedParameterContext namedParameterContext,
LoadQueryAliasResolutionContext aliasResolutionContext,
boolean returnProxies,
boolean readOnly,
ResultTransformer forcedResultTransformer,
List<AfterLoadAction> afterLoadActionList) throws SQLException {
final LoadPlan loadPlan = loadPlanAdvisor.advise( this.baseLoadPlan );
if ( loadPlan == null ) {
throw new IllegalStateException( "LoadPlanAdvisor returned null" );
}
handlePotentiallyEmptyCollectionRootReturns( loadPlan, queryParameters.getCollectionKeys(), resultSet, session );
final int maxRows;
final RowSelection selection = queryParameters.getRowSelection();
if ( LimitHelper.hasMaxRows( selection ) ) {
maxRows = selection.getMaxRows();
LOG.tracef( "Limiting ResultSet processing to just %s rows", maxRows );
}
else {
maxRows = Integer.MAX_VALUE;
}
final ResultSetProcessingContextImpl context = new ResultSetProcessingContextImpl(
resultSet,
session,
loadPlan,
readOnly,
// true, // use optional entity key? for now, always say yes
false, // use optional entity key? actually for now always say no since in the simple test cases true causes failures because there is no optional key
queryParameters,
namedParameterContext,
aliasResolutionContext,
hadSubselectFetches
);
final List loadResults = new ArrayList();
final int rootReturnCount = loadPlan.getReturns().size();
LOG.trace( "Processing result set" );
int count;
for ( count = 0; count < maxRows && resultSet.next(); count++ ) {
LOG.debugf( "Starting ResultSet row #%s", count );
Object logicalRow;
if ( rootReturnCount == 1 ) {
loadPlan.getReturns().get( 0 ).hydrate( resultSet, context );
loadPlan.getReturns().get( 0 ).resolve( resultSet, context );
logicalRow = loadPlan.getReturns().get( 0 ).read( resultSet, context );
context.readCollectionElements( new Object[] { logicalRow } );
}
else {
for ( Return rootReturn : loadPlan.getReturns() ) {
rootReturn.hydrate( resultSet, context );
}
for ( Return rootReturn : loadPlan.getReturns() ) {
rootReturn.resolve( resultSet, context );
}
logicalRow = new Object[ rootReturnCount ];
int pos = 0;
for ( Return rootReturn : loadPlan.getReturns() ) {
( (Object[]) logicalRow )[pos] = rootReturn.read( resultSet, context );
pos++;
}
context.readCollectionElements( (Object[]) logicalRow );
}
// todo : apply transformers here?
loadResults.add( logicalRow );
context.finishUpRow();
}
LOG.tracev( "Done processing result set ({0} rows)", count );
context.finishUp( afterLoadActionList );
session.getPersistenceContext().initializeNonLazyCollections();
return loadResults;
}
private void handlePotentiallyEmptyCollectionRootReturns(
LoadPlan loadPlan,
Serializable[] collectionKeys,
ResultSet resultSet,
SessionImplementor session) {
if ( collectionKeys == null ) {
// this is not a collection initializer (and empty collections will be detected by looking for
// the owner's identifier in the result set)
return;
}
// this is a collection initializer, so we must create a collection
// for each of the passed-in keys, to account for the possibility
// that the collection is empty and has no rows in the result set
//
// todo : move this inside CollectionReturn ?
CollectionPersister persister = ( (CollectionReturn) loadPlan.getReturns().get( 0 ) ).getCollectionPersister();
for ( Serializable key : collectionKeys ) {
if ( LOG.isDebugEnabled() ) {
LOG.debugf(
"Preparing collection intializer : %s",
MessageHelper.collectionInfoString( persister, key, session.getFactory() )
);
session.getPersistenceContext()
.getLoadContexts()
.getCollectionLoadContext( resultSet )
.getLoadingCollection( persister, key );
}
}
}
private class LocalVisitationStrategy extends LoadPlanVisitationStrategyAdapter {
private boolean hadSubselectFetches = false;
@Override
public void startingEntityFetch(EntityFetch entityFetch) {
// only collections are currently supported for subselect fetching.
// hadSubselectFetches = hadSubselectFetches
// | entityFetch.getFetchStrategy().getStyle() == FetchStyle.SUBSELECT;
}
@Override
public void startingCollectionFetch(CollectionFetch collectionFetch) {
hadSubselectFetches = hadSubselectFetches
| collectionFetch.getFetchStrategy().getStyle() == FetchStyle.SUBSELECT;
}
}
}

View File

@ -21,140 +21,179 @@
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.loader.internal; package org.hibernate.loader.plan.exec.internal;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.hibernate.cfg.NotYetImplementedException; import org.hibernate.cfg.NotYetImplementedException;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.hql.internal.NameGenerator;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.loader.CollectionAliases; import org.hibernate.loader.CollectionAliases;
import org.hibernate.loader.DefaultEntityAliases; import org.hibernate.loader.DefaultEntityAliases;
import org.hibernate.loader.EntityAliases; import org.hibernate.loader.EntityAliases;
import org.hibernate.loader.GeneratedCollectionAliases; import org.hibernate.loader.GeneratedCollectionAliases;
import org.hibernate.loader.plan.spi.BidirectionalEntityFetch;
import org.hibernate.loader.plan.exec.spi.AliasResolutionContext;
import org.hibernate.loader.plan.exec.spi.CollectionReferenceAliases;
import org.hibernate.loader.plan.exec.spi.EntityReferenceAliases;
import org.hibernate.loader.plan.spi.AnyFetch;
import org.hibernate.loader.plan.spi.CollectionReference; import org.hibernate.loader.plan.spi.CollectionReference;
import org.hibernate.loader.plan.spi.CollectionReturn;
import org.hibernate.loader.plan.spi.CompositeElementGraph; import org.hibernate.loader.plan.spi.CompositeElementGraph;
import org.hibernate.loader.plan.spi.CompositeFetch;
import org.hibernate.loader.plan.spi.CompositeIndexGraph; import org.hibernate.loader.plan.spi.CompositeIndexGraph;
import org.hibernate.loader.plan.spi.EntityReference; import org.hibernate.loader.plan.spi.EntityReference;
import org.hibernate.loader.plan.spi.EntityReturn;
import org.hibernate.loader.plan.spi.Fetch; import org.hibernate.loader.plan.spi.Fetch;
import org.hibernate.loader.plan.spi.FetchOwner;
import org.hibernate.loader.plan.spi.Return; import org.hibernate.loader.plan.spi.Return;
import org.hibernate.loader.plan.spi.ScalarReturn; import org.hibernate.loader.plan.spi.ScalarReturn;
import org.hibernate.loader.plan.spi.SourceQualifiable;
import org.hibernate.loader.spi.JoinableAssociation; import org.hibernate.loader.spi.JoinableAssociation;
import org.hibernate.loader.spi.LoadQueryAliasResolutionContext;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Loadable; import org.hibernate.persister.entity.Loadable;
import org.hibernate.persister.walking.spi.WalkingException;
import org.hibernate.type.EntityType; import org.hibernate.type.EntityType;
/** /**
* Provides aliases that are used by load queries and ResultSet processors. * Provides aliases that are used by load queries and ResultSet processors.
* *
* @author Gail Badner * @author Gail Badner
* @author Steve Ebersole
*/ */
public class LoadQueryAliasResolutionContextImpl implements LoadQueryAliasResolutionContext { public class AliasResolutionContextImpl implements AliasResolutionContext {
private final Map<Return,String[]> aliasesByReturn; private final SessionFactoryImplementor sessionFactory;
private final Map<EntityReference,LoadQueryEntityAliasesImpl> aliasesByEntityReference =
new HashMap<EntityReference,LoadQueryEntityAliasesImpl>(); private final Map<Return,String> sourceAliasByReturnMap;
private final Map<SourceQualifiable,String> sourceQualifiersByReturnMap;
private final Map<EntityReference,EntityReferenceAliasesImpl> aliasesByEntityReference =
new HashMap<EntityReference,EntityReferenceAliasesImpl>();
private final Map<CollectionReference,LoadQueryCollectionAliasesImpl> aliasesByCollectionReference = private final Map<CollectionReference,LoadQueryCollectionAliasesImpl> aliasesByCollectionReference =
new HashMap<CollectionReference,LoadQueryCollectionAliasesImpl>(); new HashMap<CollectionReference,LoadQueryCollectionAliasesImpl>();
private final Map<JoinableAssociation,JoinableAssociationAliasesImpl> aliasesByJoinableAssociation = private final Map<JoinableAssociation,JoinableAssociationAliasesImpl> aliasesByJoinableAssociation =
new HashMap<JoinableAssociation, JoinableAssociationAliasesImpl>(); new HashMap<JoinableAssociation, JoinableAssociationAliasesImpl>();
private final SessionFactoryImplementor sessionFactory;
private int currentAliasSuffix = 0; private int currentAliasSuffix;
private int currentTableAliasUniqueness;
public LoadQueryAliasResolutionContextImpl( /**
* Constructs a AliasResolutionContextImpl without any source aliases. This form is used in
* non-query (HQL, criteria, etc) contexts.
*
* @param sessionFactory The session factory
*/
public AliasResolutionContextImpl(SessionFactoryImplementor sessionFactory) {
this( sessionFactory, 0 );
}
/**
* Constructs a AliasResolutionContextImpl without any source aliases. This form is used in
* non-query (HQL, criteria, etc) contexts.
*
* @param sessionFactory The session factory
* @param suffixSeed The seed value to use for generating the suffix used when generating SQL aliases.
*/
public AliasResolutionContextImpl(SessionFactoryImplementor sessionFactory, int suffixSeed) {
this(
sessionFactory,
suffixSeed,
Collections.<Return,String>emptyMap(),
Collections.<SourceQualifiable,String>emptyMap()
);
}
/**
* Constructs a AliasResolutionContextImpl with source aliases. See the notes on
* {@link org.hibernate.loader.plan.exec.spi.AliasResolutionContext#getSourceAlias(Return)} for discussion of "source aliases".
*
* @param sessionFactory The session factory
* @param suffixSeed The seed value to use for generating the suffix used when generating SQL aliases.
* @param sourceAliasByReturnMap Mapping of the source alias for each return (select-clause assigned alias).
* @param sourceQualifiersByReturnMap Mapping of source query qualifiers (from-clause assigned alias).
*/
public AliasResolutionContextImpl(
SessionFactoryImplementor sessionFactory, SessionFactoryImplementor sessionFactory,
int suffixSeed, int suffixSeed,
Map<Return,String[]> aliasesByReturn) { Map<Return, String> sourceAliasByReturnMap,
Map<SourceQualifiable, String> sourceQualifiersByReturnMap) {
this.sessionFactory = sessionFactory; this.sessionFactory = sessionFactory;
this.currentAliasSuffix = suffixSeed; this.currentAliasSuffix = suffixSeed;
this.sourceAliasByReturnMap = new HashMap<Return, String>( sourceAliasByReturnMap );
checkAliasesByReturn( aliasesByReturn ); this.sourceQualifiersByReturnMap = new HashMap<SourceQualifiable, String>( sourceQualifiersByReturnMap );
this.aliasesByReturn = new HashMap<Return, String[]>( aliasesByReturn );
} }
private static void checkAliasesByReturn(Map<Return, String[]> aliasesByReturn) { @Override
if ( aliasesByReturn == null || aliasesByReturn.size() == 0 ) { public String getSourceAlias(Return theReturn) {
throw new IllegalArgumentException( "No return aliases defined" ); return sourceAliasByReturnMap.get( theReturn );
}
@Override
public String[] resolveScalarColumnAliases(ScalarReturn scalarReturn) {
final int numberOfColumns = scalarReturn.getType().getColumnSpan( sessionFactory );
// if the scalar return was assigned an alias in the source query, use that as the basis for generating
// the SQL aliases
final String sourceAlias = getSourceAlias( scalarReturn );
if ( sourceAlias != null ) {
// generate one based on the source alias
// todo : to do this properly requires dialect involvement ++
// due to needing uniqueness even across identifier length based truncation; just truncating is
// *not* enough since truncated names might clash
//
// for now, don't even truncate...
return NameGenerator.scalarNames( sourceAlias, numberOfColumns );
} }
for ( Map.Entry<Return,String[]> entry : aliasesByReturn.entrySet() ) { else {
final Return aReturn = entry.getKey(); // generate one from scratch
final String[] aliases = entry.getValue(); return NameGenerator.scalarNames( currentAliasSuffix++, numberOfColumns );
if ( aReturn == null ) { }
throw new IllegalArgumentException( "null key found in aliasesByReturn" ); }
}
if ( aliases == null || aliases.length == 0 ) { @Override
throw new IllegalArgumentException( public EntityReferenceAliases resolveAliases(EntityReference entityReference) {
String.format( "No alias defined for [%s]", aReturn ) EntityReferenceAliasesImpl aliases = aliasesByEntityReference.get( entityReference );
if ( aliases == null ) {
if ( BidirectionalEntityFetch.class.isInstance( entityReference ) ) {
return resolveAliases(
( (BidirectionalEntityFetch) entityReference ).getTargetEntityReference()
); );
} }
if ( ( aliases.length > 1 ) && final EntityPersister entityPersister = entityReference.getEntityPersister();
( aReturn instanceof EntityReturn || aReturn instanceof CollectionReturn ) ) { aliases = new EntityReferenceAliasesImpl(
throw new IllegalArgumentException( String.format( "More than 1 alias defined for [%s]", aReturn ) ); createTableAlias( entityPersister ),
} createEntityAliases( entityPersister )
for ( String alias : aliases ) {
if ( StringHelper.isEmpty( alias ) ) {
throw new IllegalArgumentException( String.format( "An alias for [%s] is null or empty.", aReturn ) );
}
}
}
}
@Override
public String resolveEntityReturnAlias(EntityReturn entityReturn) {
return getAndCheckReturnAliasExists( entityReturn )[ 0 ];
}
@Override
public String resolveCollectionReturnAlias(CollectionReturn collectionReturn) {
return getAndCheckReturnAliasExists( collectionReturn )[ 0 ];
}
@Override
public String[] resolveScalarReturnAliases(ScalarReturn scalarReturn) {
throw new NotYetImplementedException( "Cannot resolve scalar column aliases yet." );
}
private String[] getAndCheckReturnAliasExists(Return aReturn) {
// There is already a check for the appropriate number of aliases stored in aliasesByReturn,
// so just check for existence here.
final String[] aliases = aliasesByReturn.get( aReturn );
if ( aliases == null ) {
throw new IllegalStateException(
String.format( "No alias is defined for [%s]", aReturn )
); );
aliasesByEntityReference.put( entityReference, aliases );
} }
return aliases; return aliases;
} }
@Override @Override
public String resolveEntityTableAlias(EntityReference entityReference) { public CollectionReferenceAliases resolveAliases(CollectionReference collectionReference) {
return getOrGenerateLoadQueryEntityAliases( entityReference ).tableAlias; LoadQueryCollectionAliasesImpl aliases = aliasesByCollectionReference.get( collectionReference );
if ( aliases == null ) {
final CollectionPersister collectionPersister = collectionReference.getCollectionPersister();
aliases = new LoadQueryCollectionAliasesImpl(
createTableAlias( collectionPersister.getRole() ),
collectionPersister.isManyToMany()
? createTableAlias( collectionPersister.getRole() )
: null,
createCollectionAliases( collectionPersister ),
createCollectionElementAliases( collectionPersister )
);
aliasesByCollectionReference.put( collectionReference, aliases );
}
return aliases;
} }
@Override
public EntityAliases resolveEntityColumnAliases(EntityReference entityReference) {
return getOrGenerateLoadQueryEntityAliases( entityReference ).columnAliases;
}
@Override
public String resolveCollectionTableAlias(CollectionReference collectionReference) {
return getOrGenerateLoadQueryCollectionAliases( collectionReference ).tableAlias;
}
@Override
public CollectionAliases resolveCollectionColumnAliases(CollectionReference collectionReference) {
return getOrGenerateLoadQueryCollectionAliases( collectionReference ).collectionAliases;
}
@Override
public EntityAliases resolveCollectionElementColumnAliases(CollectionReference collectionReference) {
return getOrGenerateLoadQueryCollectionAliases( collectionReference ).collectionElementAliases;
}
@Override @Override
public String resolveAssociationRhsTableAlias(JoinableAssociation joinableAssociation) { public String resolveAssociationRhsTableAlias(JoinableAssociation joinableAssociation) {
@ -179,48 +218,29 @@ public class LoadQueryAliasResolutionContextImpl implements LoadQueryAliasResolu
return Integer.toString( currentAliasSuffix++ ) + '_'; return Integer.toString( currentAliasSuffix++ ) + '_';
} }
private LoadQueryEntityAliasesImpl getOrGenerateLoadQueryEntityAliases(EntityReference entityReference) {
LoadQueryEntityAliasesImpl aliases = aliasesByEntityReference.get( entityReference );
if ( aliases == null ) {
final EntityPersister entityPersister = entityReference.getEntityPersister();
aliases = new LoadQueryEntityAliasesImpl(
createTableAlias( entityPersister ),
createEntityAliases( entityPersister )
);
aliasesByEntityReference.put( entityReference, aliases );
}
return aliases;
}
private LoadQueryCollectionAliasesImpl getOrGenerateLoadQueryCollectionAliases(CollectionReference collectionReference) {
LoadQueryCollectionAliasesImpl aliases = aliasesByCollectionReference.get( collectionReference );
if ( aliases == null ) {
final CollectionPersister collectionPersister = collectionReference.getCollectionPersister();
aliases = new LoadQueryCollectionAliasesImpl(
createTableAlias( collectionPersister.getRole() ),
createCollectionAliases( collectionPersister ),
createCollectionElementAliases( collectionPersister )
);
aliasesByCollectionReference.put( collectionReference, aliases );
}
return aliases;
}
private JoinableAssociationAliasesImpl getOrGenerateJoinAssocationAliases(JoinableAssociation joinableAssociation) { private JoinableAssociationAliasesImpl getOrGenerateJoinAssocationAliases(JoinableAssociation joinableAssociation) {
JoinableAssociationAliasesImpl aliases = aliasesByJoinableAssociation.get( joinableAssociation ); JoinableAssociationAliasesImpl aliases = aliasesByJoinableAssociation.get( joinableAssociation );
if ( aliases == null ) { if ( aliases == null ) {
final Fetch currentFetch = joinableAssociation.getCurrentFetch(); final Fetch currentFetch = joinableAssociation.getCurrentFetch();
final String lhsAlias; final String lhsAlias;
if ( EntityReference.class.isInstance( currentFetch.getOwner() ) ) { if ( AnyFetch.class.isInstance( currentFetch ) ) {
lhsAlias = resolveEntityTableAlias( (EntityReference) currentFetch.getOwner() ); throw new WalkingException( "Any type should never be joined!" );
}
else if ( EntityReference.class.isInstance( currentFetch.getOwner() ) ) {
lhsAlias = resolveAliases( (EntityReference) currentFetch.getOwner() ).getTableAlias();
}
else if ( CompositeFetch.class.isInstance( currentFetch.getOwner() ) ) {
lhsAlias = resolveAliases(
locateCompositeFetchEntityReferenceSource( (CompositeFetch) currentFetch.getOwner() )
).getTableAlias();
} }
else if ( CompositeElementGraph.class.isInstance( currentFetch.getOwner() ) ) { else if ( CompositeElementGraph.class.isInstance( currentFetch.getOwner() ) ) {
CompositeElementGraph compositeElementGraph = (CompositeElementGraph) currentFetch.getOwner(); CompositeElementGraph compositeElementGraph = (CompositeElementGraph) currentFetch.getOwner();
lhsAlias = resolveCollectionTableAlias( compositeElementGraph.getCollectionReference() ); lhsAlias = resolveAliases( compositeElementGraph.getCollectionReference() ).getElementTableAlias();
} }
else if ( CompositeIndexGraph.class.isInstance( currentFetch.getOwner() ) ) { else if ( CompositeIndexGraph.class.isInstance( currentFetch.getOwner() ) ) {
CompositeIndexGraph compositeIndexGraph = (CompositeIndexGraph) currentFetch.getOwner(); CompositeIndexGraph compositeIndexGraph = (CompositeIndexGraph) currentFetch.getOwner();
lhsAlias = resolveCollectionTableAlias( compositeIndexGraph.getCollectionReference() ); lhsAlias = resolveAliases( compositeIndexGraph.getCollectionReference() ).getElementTableAlias();
} }
else { else {
throw new NotYetImplementedException( "Cannot determine LHS alias for FetchOwner." ); throw new NotYetImplementedException( "Cannot determine LHS alias for FetchOwner." );
@ -229,16 +249,16 @@ public class LoadQueryAliasResolutionContextImpl implements LoadQueryAliasResolu
final String[] aliasedLhsColumnNames = currentFetch.toSqlSelectFragments( lhsAlias ); final String[] aliasedLhsColumnNames = currentFetch.toSqlSelectFragments( lhsAlias );
final String rhsAlias; final String rhsAlias;
if ( EntityReference.class.isInstance( currentFetch ) ) { if ( EntityReference.class.isInstance( currentFetch ) ) {
rhsAlias = resolveEntityTableAlias( (EntityReference) currentFetch ); rhsAlias = resolveAliases( (EntityReference) currentFetch ).getTableAlias();
} }
else if ( CollectionReference.class.isInstance( joinableAssociation.getCurrentFetch() ) ) { else if ( CollectionReference.class.isInstance( joinableAssociation.getCurrentFetch() ) ) {
rhsAlias = resolveCollectionTableAlias( (CollectionReference) currentFetch ); rhsAlias = resolveAliases( (CollectionReference) currentFetch ).getCollectionTableAlias();
} }
else { else {
throw new NotYetImplementedException( "Cannot determine RHS alis for a fetch that is not an EntityReference or CollectionReference." ); throw new NotYetImplementedException( "Cannot determine RHS alis for a fetch that is not an EntityReference or CollectionReference." );
} }
// TODO: can't this be found in CollectionAliases or EntityAliases? should be moved to LoadQueryAliasResolutionContextImpl // TODO: can't this be found in CollectionAliases or EntityAliases? should be moved to AliasResolutionContextImpl
aliases = new JoinableAssociationAliasesImpl( lhsAlias, aliasedLhsColumnNames, rhsAlias ); aliases = new JoinableAssociationAliasesImpl( lhsAlias, aliasedLhsColumnNames, rhsAlias );
aliasesByJoinableAssociation.put( joinableAssociation, aliases ); aliasesByJoinableAssociation.put( joinableAssociation, aliases );
@ -246,12 +266,24 @@ public class LoadQueryAliasResolutionContextImpl implements LoadQueryAliasResolu
return aliases; return aliases;
} }
private EntityReference locateCompositeFetchEntityReferenceSource(CompositeFetch composite) {
final FetchOwner owner = composite.getOwner();
if ( EntityReference.class.isInstance( owner ) ) {
return (EntityReference) owner;
}
if ( CompositeFetch.class.isInstance( owner ) ) {
return locateCompositeFetchEntityReferenceSource( (CompositeFetch) owner );
}
throw new WalkingException( "Cannot resolve entity source for a CompositeFetch" );
}
private String createTableAlias(EntityPersister entityPersister) { private String createTableAlias(EntityPersister entityPersister) {
return createTableAlias( StringHelper.unqualifyEntityName( entityPersister.getEntityName() ) ); return createTableAlias( StringHelper.unqualifyEntityName( entityPersister.getEntityName() ) );
} }
private String createTableAlias(String name) { private String createTableAlias(String name) {
return StringHelper.generateAlias( name ) + createSuffix(); return StringHelper.generateAlias( name, currentTableAliasUniqueness++ );
} }
private EntityAliases createEntityAliases(EntityPersister entityPersister) { private EntityAliases createEntityAliases(EntityPersister entityPersister) {
@ -272,28 +304,43 @@ public class LoadQueryAliasResolutionContextImpl implements LoadQueryAliasResolu
} }
} }
private static class LoadQueryEntityAliasesImpl { private static class LoadQueryCollectionAliasesImpl implements CollectionReferenceAliases {
private final String tableAlias;
private final EntityAliases columnAliases;
public LoadQueryEntityAliasesImpl(String tableAlias, EntityAliases columnAliases) {
this.tableAlias = tableAlias;
this.columnAliases = columnAliases;
}
}
private static class LoadQueryCollectionAliasesImpl {
private final String tableAlias; private final String tableAlias;
private final String manyToManyAssociationTableAlias;
private final CollectionAliases collectionAliases; private final CollectionAliases collectionAliases;
private final EntityAliases collectionElementAliases; private final EntityAliases entityElementAliases;
public LoadQueryCollectionAliasesImpl( public LoadQueryCollectionAliasesImpl(
String tableAlias, String tableAlias,
String manyToManyAssociationTableAlias,
CollectionAliases collectionAliases, CollectionAliases collectionAliases,
EntityAliases collectionElementAliases) { EntityAliases entityElementAliases) {
this.tableAlias = tableAlias; this.tableAlias = tableAlias;
this.manyToManyAssociationTableAlias = manyToManyAssociationTableAlias;
this.collectionAliases = collectionAliases; this.collectionAliases = collectionAliases;
this.collectionElementAliases = collectionElementAliases; this.entityElementAliases = entityElementAliases;
}
@Override
public String getCollectionTableAlias() {
return StringHelper.isNotEmpty( manyToManyAssociationTableAlias )
? manyToManyAssociationTableAlias
: tableAlias;
}
@Override
public String getElementTableAlias() {
return tableAlias;
}
@Override
public CollectionAliases getCollectionColumnAliases() {
return collectionAliases;
}
@Override
public EntityAliases getEntityElementColumnAliases() {
return entityElementAliases;
} }
} }

View File

@ -0,0 +1,51 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan.exec.internal;
import org.hibernate.loader.EntityAliases;
import org.hibernate.loader.plan.exec.spi.EntityReferenceAliases;
/**
* @author Gail Badner
* @author Steve Ebersole
*/
class EntityReferenceAliasesImpl implements EntityReferenceAliases {
private final String tableAlias;
private final EntityAliases columnAliases;
public EntityReferenceAliasesImpl(String tableAlias, EntityAliases columnAliases) {
this.tableAlias = tableAlias;
this.columnAliases = columnAliases;
}
@Override
public String getTableAlias() {
return tableAlias;
}
@Override
public EntityAliases getColumnAliases() {
return columnAliases;
}
}

View File

@ -0,0 +1,155 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan.exec.process.internal;
import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.jboss.logging.Logger;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.internal.CoreLogging;
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext;
import org.hibernate.loader.plan.exec.spi.CollectionReferenceAliases;
import org.hibernate.loader.plan.spi.CollectionReference;
import org.hibernate.pretty.MessageHelper;
/**
* @author Steve Ebersole
*/
public class CollectionReferenceReader {
private static final Logger log = CoreLogging.logger( CollectionReferenceReader.class );
private final CollectionReference collectionReference;
public CollectionReferenceReader(CollectionReference collectionReference) {
this.collectionReference = collectionReference;
}
public void finishUpRow(ResultSet resultSet, ResultSetProcessingContextImpl context) {
final CollectionReferenceAliases aliases = context.getAliasResolutionContext().resolveAliases(
collectionReference
);
try {
// read the collection key for this reference for the current row.
final PersistenceContext persistenceContext = context.getSession().getPersistenceContext();
final Serializable collectionRowKey = (Serializable) collectionReference.getCollectionPersister().readKey(
resultSet,
aliases.getCollectionColumnAliases().getSuffixedKeyAliases(),
context.getSession()
);
if ( collectionRowKey != null ) {
// we found a collection element in the result set
if ( log.isDebugEnabled() ) {
log.debugf(
"Found row of collection: %s",
MessageHelper.collectionInfoString(
collectionReference.getCollectionPersister(),
collectionRowKey,
context.getSession().getFactory()
)
);
}
Object collectionOwner = findCollectionOwner( collectionRowKey, resultSet, context );
PersistentCollection rowCollection = persistenceContext.getLoadContexts()
.getCollectionLoadContext( resultSet )
.getLoadingCollection( collectionReference.getCollectionPersister(), collectionRowKey );
if ( rowCollection != null ) {
rowCollection.readFrom(
resultSet,
collectionReference.getCollectionPersister(),
aliases.getCollectionColumnAliases(),
collectionOwner
);
}
}
else {
final Serializable optionalKey = findCollectionOwnerKey( context );
if ( optionalKey != null ) {
// we did not find a collection element in the result set, so we
// ensure that a collection is created with the owner's identifier,
// since what we have is an empty collection
if ( log.isDebugEnabled() ) {
log.debugf(
"Result set contains (possibly empty) collection: %s",
MessageHelper.collectionInfoString(
collectionReference.getCollectionPersister(),
optionalKey,
context.getSession().getFactory()
)
);
}
// handle empty collection
persistenceContext.getLoadContexts()
.getCollectionLoadContext( resultSet )
.getLoadingCollection( collectionReference.getCollectionPersister(), optionalKey );
}
}
// else no collection element, but also no owner
}
catch ( SQLException sqle ) {
// TODO: would be nice to have the SQL string that failed...
throw context.getSession().getFactory().getSQLExceptionHelper().convert(
sqle,
"could not read next row of results"
);
}
}
protected Object findCollectionOwner(
Serializable collectionRowKey,
ResultSet resultSet,
ResultSetProcessingContextImpl context) {
final Object collectionOwner = context.getSession().getPersistenceContext().getCollectionOwner(
collectionRowKey,
collectionReference.getCollectionPersister()
);
// todo : try org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext.getOwnerProcessingState() ??
// -- specifically to return its ResultSetProcessingContext.EntityReferenceProcessingState#getEntityInstance()
if ( collectionOwner == null ) {
//TODO: This is assertion is disabled because there is a bug that means the
// original owner of a transient, uninitialized collection is not known
// if the collection is re-referenced by a different object associated
// with the current Session
//throw new AssertionFailure("bug loading unowned collection");
}
return collectionOwner;
}
protected Serializable findCollectionOwnerKey(ResultSetProcessingContext context) {
return null;
}
}

View File

@ -0,0 +1,74 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan.exec.process.internal;
import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext;
import org.hibernate.loader.plan.exec.process.spi.ReturnReader;
import org.hibernate.loader.plan.spi.CollectionReturn;
/**
* @author Steve Ebersole
*/
public class CollectionReturnReader extends CollectionReferenceReader implements ReturnReader {
private final CollectionReturn collectionReturn;
public CollectionReturnReader(CollectionReturn collectionReturn) {
super( collectionReturn );
this.collectionReturn = collectionReturn;
}
@Override
protected Object findCollectionOwner(
Serializable collectionRowKey,
ResultSet resultSet,
ResultSetProcessingContextImpl context) {
if ( context.shouldUseOptionalEntityInformation() ) {
final Object optionalEntityInstance = context.getQueryParameters().getOptionalObject();
if ( optionalEntityInstance != null ) {
return optionalEntityInstance;
}
}
return super.findCollectionOwner( collectionRowKey, resultSet, context );
}
@Override
protected Serializable findCollectionOwnerKey(ResultSetProcessingContext context) {
final EntityKey entityKey = context.shouldUseOptionalEntityInformation()
? ResultSetProcessorHelper.getOptionalObjectKey( context.getQueryParameters(), context.getSession() )
: null;
return entityKey == null
? super.findCollectionOwnerKey( context )
: entityKey;
}
@Override
public Object read(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
}

View File

@ -0,0 +1,88 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan.exec.process.internal;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext;
/**
* Identifiers are read from the ResultSet in 2 distinct phases:
* <ol>
* <li>
* First we hydrate the identifier values (see {@link #hydrate}). During this "phase" 2 things happen:
* <ol>
* <li>
* Any "optional identifier" specified on QueryParameters is considered. If the "optional identifier"
* is to be used for this identifier read, it is used to build an EntityKey which is associated with
* the {@link ResultSetProcessingContext.EntityReferenceProcessingState} for the EntityReference under
* {@link ResultSetProcessingContext.EntityReferenceProcessingState#registerEntityKey}
* </li>
* <li>
* All other id values are hydrated from the ResultSet. Those hydrated values are then registered
* with the {@link ResultSetProcessingContext.EntityReferenceProcessingState} for the EntityReference
* under {@link ResultSetProcessingContext.EntityReferenceProcessingState#registerIdentifierHydratedForm}
* </li>
* </ol>
* </li>
* <li>
* Then we resolve the identifier. This is again a 2 step process:
* <ol>
* <li>
* For all fetches that "come from" an identifier (key-many-to-ones), we fully hydrate those entities
* </li>
* <li>
* We then call resolve on root identifier type, and use that to build an EntityKey,which is then
* registered with the {@link ResultSetProcessingContext.EntityReferenceProcessingState} for the
* EntityReference whose identifier we are reading under
* {@link ResultSetProcessingContext.EntityReferenceProcessingState#registerEntityKey}
* </li>
* </ol>
* </li>
* </ol>
*
* @author Steve Ebersole
*/
public interface EntityIdentifierReader {
/**
* Hydrate the entity identifier. Perform the first phase outlined above.
*
* @param resultSet The ResultSet
* @param context The processing context
*
* @throws java.sql.SQLException Problem accessing ResultSet
*/
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException;
/**
* Resolve the entity identifier. Perform the second phase outlined above.
*
* @param resultSet The ResultSet
* @param context The processing context
*
* @throws java.sql.SQLException Problem accessing ResultSet
*/
public void resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException;
}

View File

@ -0,0 +1,326 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan.exec.process.internal;
import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.jboss.logging.Logger;
import org.hibernate.HibernateException;
import org.hibernate.JDBCException;
import org.hibernate.cfg.NotYetImplementedException;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.internal.CoreLogging;
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext;
import org.hibernate.loader.plan.spi.CompositeFetch;
import org.hibernate.loader.plan.spi.EntityFetch;
import org.hibernate.loader.plan.spi.EntityReference;
import org.hibernate.loader.plan.spi.EntityReturn;
import org.hibernate.loader.plan.spi.Fetch;
import org.hibernate.loader.plan.spi.FetchOwner;
import org.hibernate.persister.spi.HydratedCompoundValueHandler;
import org.hibernate.persister.walking.internal.FetchStrategyHelper;
import org.hibernate.persister.walking.spi.WalkingException;
import org.hibernate.type.Type;
import static org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext.*;
/**
* Encapsulates the logic for reading a single entity identifier from a JDBC ResultSet, including support for fetches
* that are part of the identifier.
*
* @author Steve Ebersole
*/
class EntityIdentifierReaderImpl implements EntityIdentifierReader {
private static final Logger log = CoreLogging.logger( EntityIdentifierReaderImpl.class );
private final EntityReference entityReference;
private List<EntityReferenceReader> identifierFetchReaders;
private final boolean isReturn;
private final Type identifierType;
/**
* Creates a delegate capable of performing the reading of an entity identifier
*
* @param entityReference The entity reference for which we will be reading the identifier.
*/
EntityIdentifierReaderImpl(EntityReference entityReference) {
this.entityReference = entityReference;
this.isReturn = EntityReturn.class.isInstance( entityReference );
this.identifierType = entityReference.getEntityPersister().getIdentifierType();
identifierFetchReaders = collectIdentifierFetchReaders();
}
private List<EntityReferenceReader> collectIdentifierFetchReaders() {
if ( ! identifierType.isComponentType() ) {
return Collections.emptyList();
}
final Fetch[] fetches = entityReference.getIdentifierDescription().getFetches();
if ( fetches == null || fetches.length == 0 ) {
return Collections.emptyList();
}
final List<EntityReferenceReader> readers = new ArrayList<EntityReferenceReader>();
for ( Fetch fetch : fetches ) {
collectIdentifierFetchReaders( readers, fetch );
}
return readers;
}
private void collectIdentifierFetchReaders(List<EntityReferenceReader> readers, Fetch fetch) {
if ( CompositeFetch.class.isInstance( fetch ) ) {
for ( Fetch subFetch : ( (CompositeFetch) fetch).getFetches() ) {
collectIdentifierFetchReaders( readers, subFetch );
}
}
else if ( ! EntityFetch.class.isInstance( fetch ) ) {
throw new IllegalStateException(
String.format(
"non-entity (and non-composite) fetch [%s] was found as part of entity identifier : %s",
fetch,
entityReference.getEntityPersister().getEntityName()
)
);
}
else {
final EntityReference fetchedEntityReference = (EntityReference) fetch;
readers.add( new EntityReferenceReader( fetchedEntityReference ) );
}
}
@Override
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
final EntityReferenceProcessingState processingState = context.getProcessingState( entityReference );
// if the entity reference we are hydrating is a Return, it is possible that its EntityKey is
// supplied by the QueryParameter optional entity information
if ( context.shouldUseOptionalEntityInformation() ) {
if ( isReturn ) {
final EntityKey entityKey = ResultSetProcessorHelper.getOptionalObjectKey(
context.getQueryParameters(),
context.getSession()
);
if ( entityKey != null ) {
processingState.registerEntityKey( entityKey );
return;
}
}
}
// get any previously registered identifier hydrated-state
Object identifierHydratedForm = processingState.getIdentifierHydratedForm();
if ( identifierHydratedForm == null ) {
// if there is none, read it from the result set
identifierHydratedForm = readIdentifierHydratedState( resultSet, context );
// broadcast the fact that a hydrated identifier value just became associated with
// this entity reference
processingState.registerIdentifierHydratedForm( identifierHydratedForm );
// hydrateIdentifierFetchIdentifiers( resultSet, context, identifierHydratedForm );
for ( EntityReferenceReader reader : identifierFetchReaders ) {
reader.hydrateIdentifier( resultSet, context );
}
}
}
/**
* Read the identifier state for the entity reference for the currently processing row in the ResultSet
*
* @param resultSet The ResultSet being processed
* @param context The processing context
*
* @return The hydrated state
*
* @throws java.sql.SQLException Indicates a problem accessing the ResultSet
*/
private Object readIdentifierHydratedState(ResultSet resultSet, ResultSetProcessingContext context)
throws SQLException {
// if ( EntityReturn.class.isInstance( entityReference ) ) {
// // if there is a "optional entity key" associated with the context it would pertain to this
// // entity reference, because it is the root return.
// final EntityKey suppliedEntityKey = context.getSuppliedOptionalEntityKey();
// if ( suppliedEntityKey != null ) {
// return suppliedEntityKey.getIdentifier();
// }
// }
// Otherwise, read it from the ResultSet
final String[] columnNames;
if ( EntityFetch.class.isInstance( entityReference )
&& !FetchStrategyHelper.isJoinFetched( ((EntityFetch) entityReference).getFetchStrategy() ) ) {
final EntityFetch fetch = (EntityFetch) entityReference;
final FetchOwner fetchOwner = fetch.getOwner();
if ( EntityReference.class.isInstance( fetchOwner ) ) {
throw new NotYetImplementedException();
// final EntityReference ownerEntityReference = (EntityReference) fetchOwner;
// final EntityAliases ownerEntityAliases = context.getAliasResolutionContext()
// .resolveEntityColumnAliases( ownerEntityReference );
// final int propertyIndex = ownerEntityReference.getEntityPersister()
// .getEntityMetamodel()
// .getPropertyIndex( fetch.getOwnerPropertyName() );
// columnNames = ownerEntityAliases.getSuffixedPropertyAliases()[ propertyIndex ];
}
else {
// todo : better message here...
throw new WalkingException( "Cannot locate association column names" );
}
}
else {
columnNames = context.getAliasResolutionContext()
.resolveAliases( entityReference )
.getColumnAliases()
.getSuffixedKeyAliases();
}
try {
return entityReference.getEntityPersister().getIdentifierType().hydrate(
resultSet,
columnNames,
context.getSession(),
null
);
}
catch (Exception e) {
throw new HibernateException(
"Encountered problem trying to hydrate identifier for entity ["
+ entityReference.getEntityPersister() + "]",
e
);
}
}
// /**
// * Hydrate the identifiers of all fetches that are part of this entity reference's identifier (key-many-to-one).
// *
// * @param resultSet The ResultSet
// * @param context The processing context
// * @param hydratedIdentifierState The hydrated identifier state of the entity reference. We can extract the
// * fetch identifier's hydrated state from there if available, without having to read the Result (which can
// * be a performance problem on some drivers).
// */
// private void hydrateIdentifierFetchIdentifiers(
// ResultSet resultSet,
// ResultSetProcessingContext context,
// Object hydratedIdentifierState) throws SQLException {
// // for all fetches that are part of our identifier...
// for ( Fetch fetch : entityReference.getIdentifierDescription().getFetches() ) {
// hydrateIdentifierFetchIdentifier( resultSet, context, fetch, hydratedIdentifierState );
// }
// }
//
// private void hydrateIdentifierFetchIdentifier(
// ResultSet resultSet,
// ResultSetProcessingContext context,
// Fetch fetch,
// Object hydratedIdentifierState) throws SQLException {
// if ( CompositeFetch.class.isInstance( fetch ) ) {
// for ( Fetch subFetch : ( (CompositeFetch) fetch).getFetches() ) {
// hydrateIdentifierFetchIdentifier( resultSet, context, subFetch, hydratedIdentifierState );
// }
// }
// else if ( ! EntityFetch.class.isInstance( fetch ) ) {
// throw new NotYetImplementedException( "Cannot hydrate identifier Fetch that is not an EntityFetch" );
// }
// else {
// final EntityFetch entityFetch = (EntityFetch) fetch;
// final EntityReferenceProcessingState fetchProcessingState = context.getProcessingState( entityFetch );
//
// // if the identifier for the fetch was already hydrated, nothing to do
// if ( fetchProcessingState.getIdentifierHydratedForm() != null ) {
// return;
// }
//
// // we can either hydrate the fetch's identifier from the incoming 'hydratedIdentifierState' (by
// // extracting the relevant portion using HydratedCompoundValueHandler) or we can
// // read it from the ResultSet
// if ( hydratedIdentifierState != null ) {
// final HydratedCompoundValueHandler hydratedStateHandler = entityReference.getIdentifierDescription().getHydratedStateHandler( fetch );
// if ( hydratedStateHandler != null ) {
// final Serializable extracted = (Serializable) hydratedStateHandler.extract( hydratedIdentifierState );
// fetchProcessingState.registerIdentifierHydratedForm( extracted );
// }
// }
// else {
// // Use a reader to hydrate the fetched entity.
// //
// // todo : Ideally these should be kept around
// new EntityReferenceReader( entityFetch ).hydrateIdentifier( resultSet, context );
// }
// }
// }
public void resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
// resolve fetched state from the identifier first
for ( EntityReferenceReader reader : identifierFetchReaders ) {
reader.resolveEntityKey( resultSet, context );
}
for ( EntityReferenceReader reader : identifierFetchReaders ) {
reader.hydrateEntityState( resultSet, context );
}
final EntityReferenceProcessingState processingState = context.getProcessingState( entityReference );
// see if we already have an EntityKey associated with this EntityReference in the processing state.
// if we do, this should have come from the optional entity identifier...
final EntityKey entityKey = processingState.getEntityKey();
if ( entityKey != null ) {
log.debugf(
"On call to EntityIdentifierReaderImpl#resolve [for %s], EntityKey was already known; " +
"should only happen on root returns with an optional identifier specified"
);
return;
}
// Look for the hydrated form
final Object identifierHydratedForm = processingState.getIdentifierHydratedForm();
if ( identifierHydratedForm == null ) {
// we need to register the missing identifier, but that happens later after all readers have had a chance
// to resolve its EntityKey
return;
}
final Type identifierType = entityReference.getEntityPersister().getIdentifierType();
final Serializable resolvedId = (Serializable) identifierType.resolve(
identifierHydratedForm,
context.getSession(),
null
);
if ( resolvedId != null ) {
processingState.registerEntityKey(
context.getSession().generateEntityKey( resolvedId, entityReference.getEntityPersister() )
);
}
}
}

View File

@ -0,0 +1,436 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan.exec.process.internal;
import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.jboss.logging.Logger;
import org.hibernate.LockMode;
import org.hibernate.StaleObjectStateException;
import org.hibernate.WrongClassException;
import org.hibernate.engine.internal.TwoPhaseLoad;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.EntityUniqueKey;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.internal.CoreLogging;
import org.hibernate.loader.EntityAliases;
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext;
import org.hibernate.loader.plan.exec.spi.EntityReferenceAliases;
import org.hibernate.loader.plan.spi.EntityFetch;
import org.hibernate.loader.plan.spi.EntityReference;
import org.hibernate.loader.plan.spi.EntityReturn;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Loadable;
import org.hibernate.persister.entity.UniqueKeyLoadable;
import org.hibernate.pretty.MessageHelper;
import org.hibernate.type.CompositeType;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;
import org.hibernate.type.VersionType;
import static org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext.EntityReferenceProcessingState;
/**
* @author Steve Ebersole
*/
public class EntityReferenceReader {
private static final Logger log = CoreLogging.logger( EntityReferenceReader.class );
private final EntityReference entityReference;
private final EntityIdentifierReader identifierReader;
private final boolean isReturn;
protected EntityReferenceReader(EntityReference entityReference, EntityIdentifierReader identifierReader) {
this.entityReference = entityReference;
this.identifierReader = identifierReader;
this.isReturn = EntityReturn.class.isInstance( entityReference );
}
public EntityReferenceReader(EntityReference entityReference) {
this( entityReference, new EntityIdentifierReaderImpl( entityReference ) );
}
public EntityReference getEntityReference() {
return entityReference;
}
public void hydrateIdentifier(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
identifierReader.hydrate( resultSet, context );
}
public void resolveEntityKey(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
identifierReader.resolve( resultSet, context );
}
public void hydrateEntityState(ResultSet resultSet, ResultSetProcessingContext context) {
// hydrate the entity reference. at this point it is expected that
final EntityReferenceProcessingState processingState = context.getProcessingState( entityReference );
// If there is no identifier for this entity reference for this row, nothing to do
if ( processingState.isMissingIdentifier() ) {
handleMissingIdentifier( context );
return;
}
// make sure we have the EntityKey
final EntityKey entityKey = processingState.getEntityKey();
if ( entityKey == null ) {
handleMissingIdentifier( context );
return;
}
// Have we already hydrated this entity's state?
if ( processingState.getEntityInstance() != null ) {
return;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// In getting here, we know that:
// 1) We need to hydrate the entity state
// 2) We have a valid EntityKey for the entity
// see if we have an existing entry in the session for this EntityKey
final Object existing = context.getSession().getEntityUsingInterceptor( entityKey );
if ( existing != null ) {
// It is previously associated with the Session, perform some checks
if ( ! entityReference.getEntityPersister().isInstance( existing ) ) {
throw new WrongClassException(
"loaded object was of wrong class " + existing.getClass(),
entityKey.getIdentifier(),
entityReference.getEntityPersister().getEntityName()
);
}
checkVersion( resultSet, context, entityKey, existing );
// use the existing association as the hydrated state
processingState.registerEntityInstance( existing );
return;
}
// Otherwise, we need to load it from the ResultSet...
// determine which entity instance to use. Either the supplied one, or instantiate one
Object optionalEntityInstance = null;
if ( isReturn && context.shouldUseOptionalEntityInformation() ) {
final EntityKey optionalEntityKey = ResultSetProcessorHelper.getOptionalObjectKey(
context.getQueryParameters(),
context.getSession()
);
if ( optionalEntityKey != null ) {
if ( optionalEntityKey.equals( entityKey ) ) {
optionalEntityInstance = context.getQueryParameters().getOptionalObject();
}
}
}
final String concreteEntityTypeName = getConcreteEntityTypeName( resultSet, context, entityKey );
final Object entityInstance = optionalEntityInstance != null
? optionalEntityInstance
: context.getSession().instantiate( concreteEntityTypeName, entityKey.getIdentifier() );
processingState.registerEntityInstance( entityInstance );
// need to hydrate it.
// grab its state from the ResultSet and keep it in the Session
// (but don't yet initialize the object itself)
// note that we acquire LockMode.READ even if it was not requested
log.trace( "hydrating entity state" );
final LockMode requestedLockMode = context.resolveLockMode( entityReference );
final LockMode lockModeToAcquire = requestedLockMode == LockMode.NONE
? LockMode.READ
: requestedLockMode;
loadFromResultSet(
resultSet,
context,
entityInstance,
concreteEntityTypeName,
entityKey,
lockModeToAcquire
);
}
private void handleMissingIdentifier(ResultSetProcessingContext context) {
if ( EntityFetch.class.isInstance( entityReference ) ) {
final EntityFetch fetch = (EntityFetch) entityReference;
final EntityType fetchedType = fetch.getFetchedType();
if ( ! fetchedType.isOneToOne() ) {
return;
}
final EntityReferenceProcessingState fetchOwnerState = context.getOwnerProcessingState( fetch );
if ( fetchOwnerState == null ) {
throw new IllegalStateException( "Could not locate fetch owner state" );
}
final EntityKey ownerEntityKey = fetchOwnerState.getEntityKey();
if ( ownerEntityKey == null ) {
throw new IllegalStateException( "Could not locate fetch owner EntityKey" );
}
context.getSession().getPersistenceContext().addNullProperty(
ownerEntityKey,
fetchedType.getPropertyName()
);
}
}
private void loadFromResultSet(
ResultSet resultSet,
ResultSetProcessingContext context,
Object entityInstance,
String concreteEntityTypeName,
EntityKey entityKey,
LockMode lockModeToAcquire) {
final Serializable id = entityKey.getIdentifier();
// Get the persister for the _subclass_
final Loadable concreteEntityPersister = (Loadable) context.getSession().getFactory().getEntityPersister( concreteEntityTypeName );
if ( log.isTraceEnabled() ) {
log.tracev(
"Initializing object from ResultSet: {0}",
MessageHelper.infoString(
concreteEntityPersister,
id,
context.getSession().getFactory()
)
);
}
// add temp entry so that the next step is circular-reference
// safe - only needed because some types don't take proper
// advantage of two-phase-load (esp. components)
TwoPhaseLoad.addUninitializedEntity(
entityKey,
entityInstance,
concreteEntityPersister,
lockModeToAcquire,
!context.getLoadPlan().areLazyAttributesForceFetched(),
context.getSession()
);
final EntityPersister rootEntityPersister = context.getSession().getFactory().getEntityPersister(
concreteEntityPersister.getRootEntityName()
);
final EntityReferenceAliases aliases = context.getAliasResolutionContext().resolveAliases( entityReference );
final Object[] values;
try {
values = concreteEntityPersister.hydrate(
resultSet,
id,
entityInstance,
(Loadable) entityReference.getEntityPersister(),
concreteEntityPersister == rootEntityPersister
? aliases.getColumnAliases().getSuffixedPropertyAliases()
: aliases.getColumnAliases().getSuffixedPropertyAliases( concreteEntityPersister ),
context.getLoadPlan().areLazyAttributesForceFetched(),
context.getSession()
);
context.getProcessingState( entityReference ).registerHydratedState( values );
}
catch (SQLException e) {
throw context.getSession().getFactory().getJdbcServices().getSqlExceptionHelper().convert(
e,
"Could not read entity state from ResultSet : " + entityKey
);
}
final Object rowId;
try {
rowId = concreteEntityPersister.hasRowId() ? resultSet.getObject( aliases.getColumnAliases().getRowIdAlias() ) : null;
}
catch (SQLException e) {
throw context.getSession().getFactory().getJdbcServices().getSqlExceptionHelper().convert(
e,
"Could not read entity row-id from ResultSet : " + entityKey
);
}
final EntityType entityType = EntityFetch.class.isInstance( entityReference )
? ( (EntityFetch) entityReference ).getFetchedType()
: entityReference.getEntityPersister().getEntityMetamodel().getEntityType();
if ( entityType != null ) {
String ukName = entityType.getRHSUniqueKeyPropertyName();
if ( ukName != null ) {
final int index = ( (UniqueKeyLoadable) concreteEntityPersister ).getPropertyIndex( ukName );
final Type type = concreteEntityPersister.getPropertyTypes()[index];
// polymorphism not really handled completely correctly,
// perhaps...well, actually its ok, assuming that the
// entity name used in the lookup is the same as the
// the one used here, which it will be
EntityUniqueKey euk = new EntityUniqueKey(
entityReference.getEntityPersister().getEntityName(),
ukName,
type.semiResolve( values[index], context.getSession(), entityInstance ),
type,
concreteEntityPersister.getEntityMode(),
context.getSession().getFactory()
);
context.getSession().getPersistenceContext().addEntity( euk, entityInstance );
}
}
TwoPhaseLoad.postHydrate(
concreteEntityPersister,
id,
values,
rowId,
entityInstance,
lockModeToAcquire,
!context.getLoadPlan().areLazyAttributesForceFetched(),
context.getSession()
);
context.registerHydratedEntity( entityReference, entityKey, entityInstance );
}
private String getConcreteEntityTypeName(
ResultSet resultSet,
ResultSetProcessingContext context,
EntityKey entityKey) {
final Loadable loadable = (Loadable) entityReference.getEntityPersister();
if ( ! loadable.hasSubclasses() ) {
return entityReference.getEntityPersister().getEntityName();
}
final Object discriminatorValue;
try {
discriminatorValue = loadable.getDiscriminatorType().nullSafeGet(
resultSet,
context.getAliasResolutionContext().resolveAliases( entityReference ).getColumnAliases().getSuffixedDiscriminatorAlias(),
context.getSession(),
null
);
}
catch (SQLException e) {
throw context.getSession().getFactory().getJdbcServices().getSqlExceptionHelper().convert(
e,
"Could not read discriminator value from ResultSet"
);
}
final String result = loadable.getSubclassForDiscriminatorValue( discriminatorValue );
if ( result == null ) {
// whoops! we got an instance of another class hierarchy branch
throw new WrongClassException(
"Discriminator: " + discriminatorValue,
entityKey.getIdentifier(),
entityReference.getEntityPersister().getEntityName()
);
}
return result;
}
private void checkVersion(
ResultSet resultSet,
ResultSetProcessingContext context,
EntityKey entityKey,
Object existing) {
final LockMode requestedLockMode = context.resolveLockMode( entityReference );
if ( requestedLockMode != LockMode.NONE ) {
final LockMode currentLockMode = context.getSession().getPersistenceContext().getEntry( existing ).getLockMode();
final boolean isVersionCheckNeeded = entityReference.getEntityPersister().isVersioned()
&& currentLockMode.lessThan( requestedLockMode );
// we don't need to worry about existing version being uninitialized because this block isn't called
// by a re-entrant load (re-entrant loads *always* have lock mode NONE)
if ( isVersionCheckNeeded ) {
//we only check the version when *upgrading* lock modes
checkVersion(
context.getSession(),
resultSet,
entityReference.getEntityPersister(),
context.getAliasResolutionContext().resolveAliases( entityReference ).getColumnAliases(),
entityKey,
existing
);
//we need to upgrade the lock mode to the mode requested
context.getSession().getPersistenceContext().getEntry( existing ).setLockMode( requestedLockMode );
}
}
}
private void checkVersion(
SessionImplementor session,
ResultSet resultSet,
EntityPersister persister,
EntityAliases entityAliases,
EntityKey entityKey,
Object entityInstance) {
final Object version = session.getPersistenceContext().getEntry( entityInstance ).getVersion();
if ( version != null ) {
//null version means the object is in the process of being loaded somewhere else in the ResultSet
VersionType versionType = persister.getVersionType();
final Object currentVersion;
try {
currentVersion = versionType.nullSafeGet(
resultSet,
entityAliases.getSuffixedVersionAliases(),
session,
null
);
}
catch (SQLException e) {
throw session.getFactory().getJdbcServices().getSqlExceptionHelper().convert(
e,
"Could not read version value from result set"
);
}
if ( !versionType.isEqual( version, currentVersion ) ) {
if ( session.getFactory().getStatistics().isStatisticsEnabled() ) {
session.getFactory().getStatisticsImplementor().optimisticFailure( persister.getEntityName() );
}
throw new StaleObjectStateException( persister.getEntityName(), entityKey.getIdentifier() );
}
}
}
public void resolve(ResultSet resultSet, ResultSetProcessingContext context) {
//To change body of created methods use File | Settings | File Templates.
}
public void finishUpRow(ResultSet resultSet, ResultSetProcessingContextImpl context) {
//To change body of created methods use File | Settings | File Templates.
}
}

View File

@ -0,0 +1,123 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan.exec.process.internal;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.hibernate.AssertionFailure;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext;
import org.hibernate.loader.plan.exec.process.spi.ReturnReader;
import org.hibernate.loader.plan.spi.EntityReturn;
import org.hibernate.proxy.HibernateProxy;
import static org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext.EntityReferenceProcessingState;
/**
* @author Steve Ebersole
*/
public class EntityReturnReader extends EntityReferenceReader implements ReturnReader {
private final EntityReturn entityReturn;
public EntityReturnReader(EntityReturn entityReturn) {
super( entityReturn );
this.entityReturn = entityReturn;
}
// @Override
// public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
// final EntityKey entityKey = getEntityKeyFromContext( context );
// if ( entityKey != null ) {
// getIdentifierResolutionContext( context ).registerEntityKey( entityKey );
// return;
// }
//
// entityReturn.getIdentifierDescription().hydrate( resultSet, context );
//
// for ( Fetch fetch : entityReturn.getFetches() ) {
// if ( FetchStrategyHelper.isJoinFetched( fetch.getFetchStrategy() ) ) {
// fetch.hydrate( resultSet, context );
// }
// }
// }
private EntityReferenceProcessingState getIdentifierResolutionContext(ResultSetProcessingContext context) {
final ResultSetProcessingContext.EntityReferenceProcessingState entityReferenceProcessingState = context.getProcessingState(
entityReturn
);
if ( entityReferenceProcessingState == null ) {
throw new AssertionFailure(
String.format(
"Could not locate EntityReferenceProcessingState for root entity return [%s (%s)]",
entityReturn.getPropertyPath().getFullPath(),
entityReturn.getEntityPersister().getEntityName()
)
);
}
return entityReferenceProcessingState;
}
// @Override
// public void resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
// final EntityReferenceProcessingState entityReferenceProcessingState = getIdentifierResolutionContext( context );
// EntityKey entityKey = entityReferenceProcessingState.getEntityKey();
// if ( entityKey != null ) {
// return;
// }
//
// entityKey = entityReturn.getIdentifierDescription().resolve( resultSet, context );
// entityReferenceProcessingState.registerEntityKey( entityKey );
//
// for ( Fetch fetch : entityReturn.getFetches() ) {
// if ( FetchStrategyHelper.isJoinFetched( fetch.getFetchStrategy() ) ) {
// fetch.resolve( resultSet, context );
// }
// }
// }
@Override
public Object read(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
final EntityReferenceProcessingState processingState = getIdentifierResolutionContext( context );
final EntityKey entityKey = processingState.getEntityKey();
final Object entityInstance = context.getProcessingState( entityReturn ).getEntityInstance();
if ( context.shouldReturnProxies() ) {
final Object proxy = context.getSession().getPersistenceContext().proxyFor(
entityReturn.getEntityPersister(),
entityKey,
entityInstance
);
if ( proxy != entityInstance ) {
( (HibernateProxy) proxy ).getHibernateLazyInitializer().setImplementation( proxy );
return proxy;
}
}
return entityInstance;
}
}

View File

@ -0,0 +1,58 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan.exec.process.internal;
import org.hibernate.loader.plan.spi.CompositeFetch;
import org.hibernate.loader.plan.spi.EntityElementGraph;
import org.hibernate.loader.plan.spi.EntityReference;
import org.hibernate.loader.plan.spi.FetchOwner;
/**
* @author Steve Ebersole
*/
public class Helper {
/**
* Singleton access
*/
public static final Helper INSTANCE = new Helper();
private Helper() {
}
public EntityReference findOwnerEntityReference(FetchOwner owner) {
if ( EntityReference.class.isInstance( owner ) ) {
return (EntityReference) owner;
}
else if ( CompositeFetch.class.isInstance( owner ) ) {
return findOwnerEntityReference( ( (CompositeFetch) owner).getOwner() );
}
else if ( EntityElementGraph.class.isInstance( owner ) ) {
return ( (EntityElementGraph) owner ).getEntityReference();
}
throw new IllegalStateException(
"Could not locate owner's EntityReference : " + owner.getPropertyPath().getFullPath()
);
}
}

View File

@ -0,0 +1,46 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan.exec.process.internal;
import org.hibernate.loader.plan.spi.EntityFetch;
import org.hibernate.loader.plan.spi.EntityReference;
/**
* @author Steve Ebersole
*/
public class OneToOneFetchReader extends EntityReferenceReader {
private final EntityReference ownerEntityReference;
public OneToOneFetchReader(EntityFetch entityFetch, EntityReference ownerEntityReference) {
super( entityFetch, new OneToOneFetchIdentifierReader( entityFetch, ownerEntityReference ) );
this.ownerEntityReference = ownerEntityReference;
}
private static class OneToOneFetchIdentifierReader extends EntityIdentifierReaderImpl {
public OneToOneFetchIdentifierReader(EntityFetch oneToOne, EntityReference ownerEntityReference) {
super( oneToOne );
}
}
}

View File

@ -21,26 +21,26 @@
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.loader.internal; package org.hibernate.loader.plan.exec.process.internal;
import java.io.Serializable; import java.io.Serializable;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.hibernate.HibernateException;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.StaleObjectStateException; import org.hibernate.StaleObjectStateException;
import org.hibernate.WrongClassException; import org.hibernate.WrongClassException;
import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.internal.TwoPhaseLoad; import org.hibernate.engine.internal.TwoPhaseLoad;
import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.EntityUniqueKey; import org.hibernate.engine.spi.EntityUniqueKey;
@ -53,16 +53,21 @@ import org.hibernate.event.spi.PostLoadEvent;
import org.hibernate.event.spi.PreLoadEvent; import org.hibernate.event.spi.PreLoadEvent;
import org.hibernate.loader.CollectionAliases; import org.hibernate.loader.CollectionAliases;
import org.hibernate.loader.EntityAliases; import org.hibernate.loader.EntityAliases;
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext;
import org.hibernate.loader.plan.exec.query.spi.NamedParameterContext;
import org.hibernate.loader.plan.exec.spi.AliasResolutionContext;
import org.hibernate.loader.plan.exec.spi.LockModeResolver;
import org.hibernate.loader.plan.spi.CollectionFetch; import org.hibernate.loader.plan.spi.CollectionFetch;
import org.hibernate.loader.plan.spi.CollectionReturn; import org.hibernate.loader.plan.spi.CollectionReturn;
import org.hibernate.loader.plan.spi.CompositeFetch;
import org.hibernate.loader.plan.spi.EntityFetch;
import org.hibernate.loader.plan.spi.EntityReference; import org.hibernate.loader.plan.spi.EntityReference;
import org.hibernate.loader.plan.spi.Fetch;
import org.hibernate.loader.plan.spi.FetchOwner;
import org.hibernate.loader.plan.spi.LoadPlan; import org.hibernate.loader.plan.spi.LoadPlan;
import org.hibernate.loader.plan.spi.visit.LoadPlanVisitationStrategyAdapter; import org.hibernate.loader.plan.spi.visit.LoadPlanVisitationStrategyAdapter;
import org.hibernate.loader.plan.spi.visit.LoadPlanVisitor; import org.hibernate.loader.plan.spi.visit.LoadPlanVisitor;
import org.hibernate.loader.spi.AfterLoadAction; import org.hibernate.loader.spi.AfterLoadAction;
import org.hibernate.loader.spi.LoadQueryAliasResolutionContext;
import org.hibernate.loader.spi.NamedParameterContext;
import org.hibernate.loader.spi.ResultSetProcessingContext;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Loadable; import org.hibernate.persister.entity.Loadable;
@ -82,46 +87,75 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
private final SessionImplementor session; private final SessionImplementor session;
private final LoadPlan loadPlan; private final LoadPlan loadPlan;
private final boolean readOnly; private final boolean readOnly;
private final boolean shouldUseOptionalEntityInformation;
private final boolean forceFetchLazyAttributes;
private final boolean shouldReturnProxies;
private final QueryParameters queryParameters; private final QueryParameters queryParameters;
private final NamedParameterContext namedParameterContext; private final NamedParameterContext namedParameterContext;
private final LoadQueryAliasResolutionContext aliasResolutionContext; private final AliasResolutionContext aliasResolutionContext;
private final boolean hadSubselectFetches; private final boolean hadSubselectFetches;
private final EntityKey dictatedRootEntityKey;
private List<HydratedEntityRegistration> currentRowHydratedEntityRegistrationList; private List<HydratedEntityRegistration> currentRowHydratedEntityRegistrationList;
private Map<EntityPersister,Set<EntityKey>> subselectLoadableEntityKeyMap; private Map<EntityPersister,Set<EntityKey>> subselectLoadableEntityKeyMap;
private List<HydratedEntityRegistration> hydratedEntityRegistrationList; private List<HydratedEntityRegistration> hydratedEntityRegistrationList;
private LockModeResolver lockModeResolverDelegate = new LockModeResolver() {
@Override
public LockMode resolveLockMode(EntityReference entityReference) {
return LockMode.NONE;
}
};
/**
* Builds a ResultSetProcessingContextImpl
*
* @param resultSet
* @param session
* @param loadPlan
* @param readOnly
* @param shouldUseOptionalEntityInformation There are times when the "optional entity information" on
* QueryParameters should be used and times when they should not. Collection initializers, batch loaders, etc
* are times when it should NOT be used.
* @param forceFetchLazyAttributes
* @param shouldReturnProxies
* @param queryParameters
* @param namedParameterContext
* @param aliasResolutionContext
* @param hadSubselectFetches
*/
public ResultSetProcessingContextImpl( public ResultSetProcessingContextImpl(
ResultSet resultSet, ResultSet resultSet,
SessionImplementor session, SessionImplementor session,
LoadPlan loadPlan, LoadPlan loadPlan,
boolean readOnly, boolean readOnly,
boolean useOptionalEntityKey, boolean shouldUseOptionalEntityInformation,
boolean forceFetchLazyAttributes,
boolean shouldReturnProxies,
QueryParameters queryParameters, QueryParameters queryParameters,
NamedParameterContext namedParameterContext, NamedParameterContext namedParameterContext,
LoadQueryAliasResolutionContext aliasResolutionContext, AliasResolutionContext aliasResolutionContext,
boolean hadSubselectFetches) { boolean hadSubselectFetches) {
this.resultSet = resultSet; this.resultSet = resultSet;
this.session = session; this.session = session;
this.loadPlan = loadPlan; this.loadPlan = loadPlan;
this.readOnly = readOnly; this.readOnly = readOnly;
this.shouldUseOptionalEntityInformation = shouldUseOptionalEntityInformation;
this.forceFetchLazyAttributes = forceFetchLazyAttributes;
this.shouldReturnProxies = shouldReturnProxies;
this.queryParameters = queryParameters; this.queryParameters = queryParameters;
this.namedParameterContext = namedParameterContext; this.namedParameterContext = namedParameterContext;
this.aliasResolutionContext = aliasResolutionContext; this.aliasResolutionContext = aliasResolutionContext;
this.hadSubselectFetches = hadSubselectFetches; this.hadSubselectFetches = hadSubselectFetches;
if ( useOptionalEntityKey ) { if ( shouldUseOptionalEntityInformation ) {
this.dictatedRootEntityKey = ResultSetProcessorHelper.getOptionalObjectKey( queryParameters, session ); if ( queryParameters.getOptionalId() != null ) {
if ( this.dictatedRootEntityKey == null ) { // make sure we have only one return
throw new HibernateException( "Unable to resolve optional entity-key" ); if ( loadPlan.getReturns().size() > 1 ) {
throw new IllegalStateException( "Cannot specify 'optional entity' values with multi-return load plans" );
}
} }
} }
else {
this.dictatedRootEntityKey = null;
}
} }
@Override @Override
@ -129,28 +163,48 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
return session; return session;
} }
@Override
public boolean shouldUseOptionalEntityInformation() {
return shouldUseOptionalEntityInformation;
}
@Override @Override
public QueryParameters getQueryParameters() { public QueryParameters getQueryParameters() {
return queryParameters; return queryParameters;
} }
@Override @Override
public EntityKey getDictatedRootEntityKey() { public boolean shouldReturnProxies() {
return dictatedRootEntityKey; return shouldReturnProxies;
} }
private Map<EntityReference,IdentifierResolutionContext> identifierResolutionContextMap; @Override
public LoadPlan getLoadPlan() {
return loadPlan;
}
@Override @Override
public IdentifierResolutionContext getIdentifierResolutionContext(final EntityReference entityReference) { public LockMode resolveLockMode(EntityReference entityReference) {
final LockMode lockMode = lockModeResolverDelegate.resolveLockMode( entityReference );
return LockMode.NONE == lockMode ? LockMode.NONE : lockMode;
}
private Map<EntityReference,EntityReferenceProcessingState> identifierResolutionContextMap;
@Override
public EntityReferenceProcessingState getProcessingState(final EntityReference entityReference) {
if ( identifierResolutionContextMap == null ) { if ( identifierResolutionContextMap == null ) {
identifierResolutionContextMap = new HashMap<EntityReference, IdentifierResolutionContext>(); identifierResolutionContextMap = new IdentityHashMap<EntityReference, EntityReferenceProcessingState>();
} }
IdentifierResolutionContext context = identifierResolutionContextMap.get( entityReference );
EntityReferenceProcessingState context = identifierResolutionContextMap.get( entityReference );
if ( context == null ) { if ( context == null ) {
context = new IdentifierResolutionContext() { context = new EntityReferenceProcessingState() {
private Object hydratedForm; private boolean wasMissingIdentifier;
private Object identifierHydratedForm;
private EntityKey entityKey; private EntityKey entityKey;
private Object[] hydratedState;
private Object entityInstance;
@Override @Override
public EntityReference getEntityReference() { public EntityReference getEntityReference() {
@ -158,23 +212,31 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
} }
@Override @Override
public void registerHydratedForm(Object hydratedForm) { public void registerMissingIdentifier() {
if ( this.hydratedForm != null ) { if ( !EntityFetch.class.isInstance( entityReference ) ) {
// this could be bad... throw new IllegalStateException( "Missing return row identifier" );
} }
this.hydratedForm = hydratedForm; ResultSetProcessingContextImpl.this.registerNonExists( (EntityFetch) entityReference );
wasMissingIdentifier = true;
} }
@Override @Override
public Object getHydratedForm() { public boolean isMissingIdentifier() {
return hydratedForm; return wasMissingIdentifier;
}
@Override
public void registerIdentifierHydratedForm(Object identifierHydratedForm) {
this.identifierHydratedForm = identifierHydratedForm;
}
@Override
public Object getIdentifierHydratedForm() {
return identifierHydratedForm;
} }
@Override @Override
public void registerEntityKey(EntityKey entityKey) { public void registerEntityKey(EntityKey entityKey) {
if ( this.entityKey != null ) {
// again, could be trouble...
}
this.entityKey = entityKey; this.entityKey = entityKey;
} }
@ -182,6 +244,26 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
public EntityKey getEntityKey() { public EntityKey getEntityKey() {
return entityKey; return entityKey;
} }
@Override
public void registerHydratedState(Object[] hydratedState) {
this.hydratedState = hydratedState;
}
@Override
public Object[] getHydratedState() {
return hydratedState;
}
@Override
public void registerEntityInstance(Object entityInstance) {
this.entityInstance = entityInstance;
}
@Override
public Object getEntityInstance() {
return entityInstance;
}
}; };
identifierResolutionContextMap.put( entityReference, context ); identifierResolutionContextMap.put( entityReference, context );
} }
@ -189,15 +271,55 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
return context; return context;
} }
@Override private void registerNonExists(EntityFetch fetch) {
public Set<IdentifierResolutionContext> getIdentifierResolutionContexts() { final EntityType fetchedType = fetch.getFetchedType();
return Collections.unmodifiableSet( if ( ! fetchedType.isOneToOne() ) {
new HashSet<IdentifierResolutionContext>( identifierResolutionContextMap.values() ) return;
}
final EntityReferenceProcessingState fetchOwnerState = getOwnerProcessingState( fetch );
if ( fetchOwnerState == null ) {
throw new IllegalStateException( "Could not locate fetch owner state" );
}
final EntityKey ownerEntityKey = fetchOwnerState.getEntityKey();
if ( ownerEntityKey == null ) {
throw new IllegalStateException( "Could not locate fetch owner EntityKey" );
}
session.getPersistenceContext().addNullProperty(
ownerEntityKey,
fetchedType.getPropertyName()
); );
} }
@Override @Override
public LoadQueryAliasResolutionContext getLoadQueryAliasResolutionContext() { public EntityReferenceProcessingState getOwnerProcessingState(Fetch fetch) {
return getProcessingState( resolveFetchOwnerEntityReference( fetch ) );
}
private EntityReference resolveFetchOwnerEntityReference(Fetch fetch) {
final FetchOwner fetchOwner = fetch.getOwner();
if ( EntityReference.class.isInstance( fetchOwner ) ) {
return (EntityReference) fetchOwner;
}
else if ( CompositeFetch.class.isInstance( fetchOwner ) ) {
return resolveFetchOwnerEntityReference( (CompositeFetch) fetchOwner );
}
throw new IllegalStateException(
String.format(
"Cannot resolve FetchOwner [%s] of Fetch [%s (%s)] to an EntityReference",
fetchOwner,
fetch,
fetch.getPropertyPath()
)
);
}
@Override
public AliasResolutionContext getAliasResolutionContext() {
return aliasResolutionContext; return aliasResolutionContext;
} }
@ -309,7 +431,7 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
checkVersion( checkVersion(
resultSet, resultSet,
entityKeyContext.getEntityPersister(), entityKeyContext.getEntityPersister(),
aliasResolutionContext.resolveEntityColumnAliases( entityKeyContext.getEntityReference() ), aliasResolutionContext.resolveAliases( entityKeyContext.getEntityReference() ).getColumnAliases(),
entityKey, entityKey,
existing existing
); );
@ -324,14 +446,26 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
final String concreteEntityTypeName = getConcreteEntityTypeName( final String concreteEntityTypeName = getConcreteEntityTypeName(
resultSet, resultSet,
entityKeyContext.getEntityPersister(), entityKeyContext.getEntityPersister(),
aliasResolutionContext.resolveEntityColumnAliases( entityKeyContext.getEntityReference() ), aliasResolutionContext.resolveAliases( entityKeyContext.getEntityReference() ).getColumnAliases(),
entityKey entityKey
); );
final Object entityInstance = getSession().instantiate( final Object entityInstance;
concreteEntityTypeName, // if ( suppliedOptionalEntityKey != null && entityKey.equals( suppliedOptionalEntityKey ) ) {
entityKey.getIdentifier() // // its the given optional object
); // entityInstance = queryParameters.getOptionalObject();
// }
// else {
// instantiate a new instance
entityInstance = session.instantiate( concreteEntityTypeName, entityKey.getIdentifier() );
// }
FetchStrategy fetchStrategy = null;
final EntityReference entityReference = entityKeyContext.getEntityReference();
if ( EntityFetch.class.isInstance( entityReference ) ) {
final EntityFetch fetch = (EntityFetch) entityReference;
fetchStrategy = fetch.getFetchStrategy();
}
//need to hydrate it. //need to hydrate it.
@ -350,15 +484,16 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
entityInstance, entityInstance,
concreteEntityTypeName, concreteEntityTypeName,
entityKey, entityKey,
aliasResolutionContext.resolveEntityColumnAliases( entityKeyContext.getEntityReference() ), aliasResolutionContext.resolveAliases( entityKeyContext.getEntityReference() ).getColumnAliases(),
acquiredLockMode, acquiredLockMode,
entityKeyContext.getEntityPersister(), entityKeyContext.getEntityPersister(),
fetchStrategy,
true, true,
entityKeyContext.getEntityPersister().getEntityMetamodel().getEntityType() entityKeyContext.getEntityPersister().getEntityMetamodel().getEntityType()
); );
// materialize associations (and initialize the object) later // materialize associations (and initialize the object) later
registerHydratedEntity( entityKeyContext.getEntityPersister(), entityKey, entityInstance ); registerHydratedEntity( entityKeyContext.getEntityReference(), entityKey, entityInstance );
return entityInstance; return entityInstance;
} }
@ -373,6 +508,7 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
EntityAliases entityAliases, EntityAliases entityAliases,
LockMode acquiredLockMode, LockMode acquiredLockMode,
EntityPersister rootPersister, EntityPersister rootPersister,
FetchStrategy fetchStrategy,
boolean eagerFetch, boolean eagerFetch,
EntityType associationType) { EntityType associationType) {
@ -400,7 +536,7 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
entityInstance, entityInstance,
persister, persister,
acquiredLockMode, acquiredLockMode,
!eagerFetch, !forceFetchLazyAttributes,
session session
); );
@ -417,7 +553,7 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
entityInstance, entityInstance,
(Loadable) rootPersister, (Loadable) rootPersister,
cols, cols,
eagerFetch, loadPlan.areLazyAttributesForceFetched(),
session session
); );
} }
@ -469,7 +605,7 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
rowId, rowId,
entityInstance, entityInstance,
acquiredLockMode, acquiredLockMode,
!eagerFetch, !loadPlan.areLazyAttributesForceFetched(),
session session
); );
@ -485,7 +621,7 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
null, null,
null, null,
rootCollectionReturn.getCollectionPersister(), rootCollectionReturn.getCollectionPersister(),
aliasResolutionContext.resolveCollectionColumnAliases( rootCollectionReturn ), aliasResolutionContext.resolveAliases( rootCollectionReturn ).getCollectionColumnAliases(),
resultSet, resultSet,
session session
); );
@ -499,7 +635,7 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
owner, owner,
collectionFetch.getCollectionPersister().getCollectionType().getKeyOfOwner( owner, session ), collectionFetch.getCollectionPersister().getCollectionType().getKeyOfOwner( owner, session ),
collectionFetch.getCollectionPersister(), collectionFetch.getCollectionPersister(),
aliasResolutionContext.resolveCollectionColumnAliases( collectionFetch ), aliasResolutionContext.resolveAliases( collectionFetch ).getCollectionColumnAliases(),
resultSet, resultSet,
session session
); );
@ -583,11 +719,17 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
} }
@Override @Override
public void registerHydratedEntity(EntityPersister persister, EntityKey entityKey, Object entityInstance) { public void registerHydratedEntity(EntityReference entityReference, EntityKey entityKey, Object entityInstance) {
if ( currentRowHydratedEntityRegistrationList == null ) { if ( currentRowHydratedEntityRegistrationList == null ) {
currentRowHydratedEntityRegistrationList = new ArrayList<HydratedEntityRegistration>(); currentRowHydratedEntityRegistrationList = new ArrayList<HydratedEntityRegistration>();
} }
currentRowHydratedEntityRegistrationList.add( new HydratedEntityRegistration( persister, entityKey, entityInstance ) ); currentRowHydratedEntityRegistrationList.add(
new HydratedEntityRegistration(
entityReference,
entityKey,
entityInstance
)
);
} }
/** /**
@ -612,10 +754,10 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
subselectLoadableEntityKeyMap = new HashMap<EntityPersister, Set<EntityKey>>(); subselectLoadableEntityKeyMap = new HashMap<EntityPersister, Set<EntityKey>>();
} }
for ( HydratedEntityRegistration registration : currentRowHydratedEntityRegistrationList ) { for ( HydratedEntityRegistration registration : currentRowHydratedEntityRegistrationList ) {
Set<EntityKey> entityKeys = subselectLoadableEntityKeyMap.get( registration.persister ); Set<EntityKey> entityKeys = subselectLoadableEntityKeyMap.get( registration.entityReference.getEntityPersister() );
if ( entityKeys == null ) { if ( entityKeys == null ) {
entityKeys = new HashSet<EntityKey>(); entityKeys = new HashSet<EntityKey>();
subselectLoadableEntityKeyMap.put( registration.persister, entityKeys ); subselectLoadableEntityKeyMap.put( registration.entityReference.getEntityPersister(), entityKeys );
} }
entityKeys.add( registration.key ); entityKeys.add( registration.key );
} }
@ -623,6 +765,8 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
// release the currentRowHydratedEntityRegistrationList entries // release the currentRowHydratedEntityRegistrationList entries
currentRowHydratedEntityRegistrationList.clear(); currentRowHydratedEntityRegistrationList.clear();
identifierResolutionContextMap.clear();
} }
/** /**
@ -753,7 +897,7 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
TwoPhaseLoad.postLoad( registration.instance, session, postLoadEvent ); TwoPhaseLoad.postLoad( registration.instance, session, postLoadEvent );
if ( afterLoadActionList != null ) { if ( afterLoadActionList != null ) {
for ( AfterLoadAction afterLoadAction : afterLoadActionList ) { for ( AfterLoadAction afterLoadAction : afterLoadActionList ) {
afterLoadAction.afterLoad( session, registration.instance, (Loadable) registration.persister ); afterLoadAction.afterLoad( session, registration.instance, (Loadable) registration.entityReference.getEntityPersister() );
} }
} }
} }
@ -790,12 +934,12 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
} }
private static class HydratedEntityRegistration { private static class HydratedEntityRegistration {
private final EntityPersister persister; private final EntityReference entityReference;
private final EntityKey key; private final EntityKey key;
private final Object instance; private Object instance;
private HydratedEntityRegistration(EntityPersister persister, EntityKey key, Object instance) { private HydratedEntityRegistration(EntityReference entityReference, EntityKey key, Object instance) {
this.persister = persister; this.entityReference = entityReference;
this.key = key; this.key = key;
this.instance = instance; this.instance = instance;
} }

View File

@ -21,37 +21,57 @@
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.loader.internal; package org.hibernate.loader.plan.exec.process.internal;
import java.io.Serializable; import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.QueryParameters; import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.loader.EntityAliases; import org.hibernate.loader.plan.exec.query.spi.NamedParameterContext;
import org.hibernate.loader.plan.spi.EntityReference;
import org.hibernate.loader.plan.spi.Fetch;
import org.hibernate.loader.spi.NamedParameterContext;
import org.hibernate.loader.spi.ResultSetProcessingContext;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.type.CompositeType; import org.hibernate.type.CompositeType;
import org.hibernate.type.Type;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class ResultSetProcessorHelper { public class ResultSetProcessorHelper {
/**
* Singleton access
*/
public static final ResultSetProcessorHelper INSTANCE = new ResultSetProcessorHelper();
public static EntityKey getOptionalObjectKey(QueryParameters queryParameters, SessionImplementor session) { public static EntityKey getOptionalObjectKey(QueryParameters queryParameters, SessionImplementor session) {
final Object optionalObject = queryParameters.getOptionalObject(); final Object optionalObject = queryParameters.getOptionalObject();
final Serializable optionalId = queryParameters.getOptionalId(); final Serializable optionalId = queryParameters.getOptionalId();
final String optionalEntityName = queryParameters.getOptionalEntityName(); final String optionalEntityName = queryParameters.getOptionalEntityName();
if ( optionalObject != null && optionalEntityName != null ) { return INSTANCE.interpretEntityKey( session, optionalEntityName, optionalId, optionalObject );
return session.generateEntityKey( optionalId, session.getEntityPersister( optionalEntityName, optionalObject ) ); }
public EntityKey interpretEntityKey(
SessionImplementor session,
String optionalEntityName,
Serializable optionalId,
Object optionalObject) {
if ( optionalEntityName != null ) {
final EntityPersister entityPersister;
if ( optionalObject != null ) {
entityPersister = session.getEntityPersister( optionalEntityName, optionalObject );
}
else {
entityPersister = session.getFactory().getEntityPersister( optionalEntityName );
}
if ( entityPersister.isInstance( optionalId ) ) {
// embedded (non-encapsulated) composite identifier
final Serializable identifierState = ( (CompositeType) entityPersister.getIdentifierType() ).getPropertyValues( optionalId, session );
return session.generateEntityKey( identifierState, entityPersister );
}
else {
return session.generateEntityKey( optionalId, entityPersister );
}
} }
else { else {
return null; return null;
@ -74,4 +94,5 @@ public class ResultSetProcessorHelper {
} }
return namedParameterLocMap; return namedParameterLocMap;
} }
} }

View File

@ -0,0 +1,543 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan.exec.process.internal;
import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.jboss.logging.Logger;
import org.hibernate.cfg.NotYetImplementedException;
import org.hibernate.dialect.pagination.LimitHelper;
import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.RowSelection;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.loader.plan.exec.process.spi.ReturnReader;
import org.hibernate.loader.plan.spi.CollectionFetch;
import org.hibernate.loader.plan.spi.CollectionReference;
import org.hibernate.loader.plan.spi.CollectionReturn;
import org.hibernate.loader.plan.spi.CompositeFetch;
import org.hibernate.loader.plan.spi.EntityFetch;
import org.hibernate.loader.plan.spi.EntityReference;
import org.hibernate.loader.plan.spi.EntityReturn;
import org.hibernate.loader.plan.spi.Fetch;
import org.hibernate.loader.plan.spi.FetchOwner;
import org.hibernate.loader.plan.spi.LoadPlan;
import org.hibernate.loader.plan.spi.ScalarReturn;
import org.hibernate.loader.plan.spi.visit.LoadPlanVisitationStrategyAdapter;
import org.hibernate.loader.plan.spi.visit.LoadPlanVisitor;
import org.hibernate.loader.plan.spi.Return;
import org.hibernate.loader.spi.AfterLoadAction;
import org.hibernate.loader.spi.LoadPlanAdvisor;
import org.hibernate.loader.plan.exec.spi.AliasResolutionContext;
import org.hibernate.loader.plan.exec.query.spi.NamedParameterContext;
import org.hibernate.loader.plan.exec.process.spi.ScrollableResultSetProcessor;
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessor;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.pretty.MessageHelper;
import org.hibernate.transform.ResultTransformer;
/**
* @author Steve Ebersole
*/
public class ResultSetProcessorImpl implements ResultSetProcessor {
private static final Logger LOG = Logger.getLogger( ResultSetProcessorImpl.class );
private final LoadPlan baseLoadPlan;
private final RowReader rowReader;
private final boolean shouldUseOptionalEntityInstance;
private final boolean hadSubselectFetches;
public ResultSetProcessorImpl(
LoadPlan loadPlan,
boolean shouldUseOptionalEntityInstance) {
this.baseLoadPlan = loadPlan;
this.rowReader = buildRowReader( loadPlan );
this.shouldUseOptionalEntityInstance = shouldUseOptionalEntityInstance;
LocalVisitationStrategy strategy = new LocalVisitationStrategy();
LoadPlanVisitor.visit( loadPlan, strategy );
this.hadSubselectFetches = strategy.hadSubselectFetches;
}
private RowReader buildRowReader(LoadPlan loadPlan) {
switch ( loadPlan.getDisposition() ) {
case MIXED: {
return new MixedReturnRowReader( loadPlan );
}
case ENTITY_LOADER: {
return new EntityLoaderRowReader( loadPlan );
}
case COLLECTION_INITIALIZER: {
return new CollectionInitializerRowReader( loadPlan );
}
default: {
throw new IllegalStateException( "Unrecognized LoadPlan Return dispostion : " + loadPlan.getDisposition() );
}
}
}
@Override
public ScrollableResultSetProcessor toOnDemandForm() {
// todo : implement
throw new NotYetImplementedException();
}
@Override
public List extractResults(
LoadPlanAdvisor loadPlanAdvisor,
ResultSet resultSet,
final SessionImplementor session,
QueryParameters queryParameters,
NamedParameterContext namedParameterContext,
AliasResolutionContext aliasResolutionContext,
boolean returnProxies,
boolean readOnly,
ResultTransformer forcedResultTransformer,
List<AfterLoadAction> afterLoadActionList) throws SQLException {
final LoadPlan loadPlan = loadPlanAdvisor.advise( this.baseLoadPlan );
if ( loadPlan == null ) {
throw new IllegalStateException( "LoadPlanAdvisor returned null" );
}
handlePotentiallyEmptyCollectionRootReturns( loadPlan, queryParameters.getCollectionKeys(), resultSet, session );
final int maxRows;
final RowSelection selection = queryParameters.getRowSelection();
if ( LimitHelper.hasMaxRows( selection ) ) {
maxRows = selection.getMaxRows();
LOG.tracef( "Limiting ResultSet processing to just %s rows", maxRows );
}
else {
maxRows = Integer.MAX_VALUE;
}
// There are times when the "optional entity information" on QueryParameters should be used and
// times when they should be ignored. Loader uses its isSingleRowLoader method to allow
// subclasses to override that. Collection initializers, batch loaders, e.g. override that
// it to be false. The 'shouldUseOptionalEntityInstance' setting is meant to fill that same role.
final boolean shouldUseOptionalEntityInstance = true;
// Handles the "FETCH ALL PROPERTIES" directive in HQL
final boolean forceFetchLazyAttributes = false;
final ResultSetProcessingContextImpl context = new ResultSetProcessingContextImpl(
resultSet,
session,
loadPlan,
readOnly,
shouldUseOptionalEntityInstance,
forceFetchLazyAttributes,
returnProxies,
queryParameters,
namedParameterContext,
aliasResolutionContext,
hadSubselectFetches
);
final List loadResults = new ArrayList();
LOG.trace( "Processing result set" );
int count;
for ( count = 0; count < maxRows && resultSet.next(); count++ ) {
LOG.debugf( "Starting ResultSet row #%s", count );
Object logicalRow = rowReader.readRow( resultSet, context );
// todo : apply transformers here?
loadResults.add( logicalRow );
context.finishUpRow();
}
LOG.tracev( "Done processing result set ({0} rows)", count );
context.finishUp( afterLoadActionList );
session.getPersistenceContext().initializeNonLazyCollections();
return loadResults;
}
private void handlePotentiallyEmptyCollectionRootReturns(
LoadPlan loadPlan,
Serializable[] collectionKeys,
ResultSet resultSet,
SessionImplementor session) {
if ( collectionKeys == null ) {
// this is not a collection initializer (and empty collections will be detected by looking for
// the owner's identifier in the result set)
return;
}
// this is a collection initializer, so we must create a collection
// for each of the passed-in keys, to account for the possibility
// that the collection is empty and has no rows in the result set
//
// todo : move this inside CollectionReturn ?
CollectionPersister persister = ( (CollectionReturn) loadPlan.getReturns().get( 0 ) ).getCollectionPersister();
for ( Serializable key : collectionKeys ) {
if ( LOG.isDebugEnabled() ) {
LOG.debugf(
"Preparing collection intializer : %s",
MessageHelper.collectionInfoString( persister, key, session.getFactory() )
);
session.getPersistenceContext()
.getLoadContexts()
.getCollectionLoadContext( resultSet )
.getLoadingCollection( persister, key );
}
}
}
private class LocalVisitationStrategy extends LoadPlanVisitationStrategyAdapter {
private boolean hadSubselectFetches = false;
@Override
public void startingEntityFetch(EntityFetch entityFetch) {
// only collections are currently supported for subselect fetching.
// hadSubselectFetches = hadSubselectFetches
// || entityFetch.getFetchStrategy().getStyle() == FetchStyle.SUBSELECT;
}
@Override
public void startingCollectionFetch(CollectionFetch collectionFetch) {
hadSubselectFetches = hadSubselectFetches
|| collectionFetch.getFetchStrategy().getStyle() == FetchStyle.SUBSELECT;
}
}
private static interface RowReader {
Object readRow(ResultSet resultSet, ResultSetProcessingContextImpl context) throws SQLException;
}
private static abstract class AbstractRowReader implements RowReader {
@Override
public Object readRow(ResultSet resultSet, ResultSetProcessingContextImpl context) throws SQLException {
final List<EntityReferenceReader> entityReferenceReaders = getEntityReferenceReaders();
final List<CollectionReferenceReader> collectionReferenceReaders = getCollectionReferenceReaders();
final boolean hasEntityReferenceReaders = entityReferenceReaders != null && entityReferenceReaders.size() > 0;
final boolean hasCollectionReferenceReaders = collectionReferenceReaders != null && collectionReferenceReaders.size() > 0;
if ( hasEntityReferenceReaders ) {
// 1) allow entity references to resolve identifiers (in 2 steps)
for ( EntityReferenceReader entityReferenceReader : entityReferenceReaders ) {
entityReferenceReader.hydrateIdentifier( resultSet, context );
}
for ( EntityReferenceReader entityReferenceReader : entityReferenceReaders ) {
entityReferenceReader.resolveEntityKey( resultSet, context );
}
// 2) allow entity references to resolve their hydrated state and entity instance
for ( EntityReferenceReader entityReferenceReader : entityReferenceReaders ) {
entityReferenceReader.hydrateEntityState( resultSet, context );
}
}
// 3) read the logical row
Object logicalRow = readLogicalRow( resultSet, context );
// 4) allow entities and collection to read their elements
if ( hasEntityReferenceReaders ) {
for ( EntityReferenceReader entityReferenceReader : entityReferenceReaders ) {
entityReferenceReader.finishUpRow( resultSet, context );
}
}
if ( hasCollectionReferenceReaders ) {
for ( CollectionReferenceReader collectionReferenceReader : collectionReferenceReaders ) {
collectionReferenceReader.finishUpRow( resultSet, context );
}
}
return logicalRow;
}
protected abstract List<EntityReferenceReader> getEntityReferenceReaders();
protected abstract List<CollectionReferenceReader> getCollectionReferenceReaders();
protected abstract Object readLogicalRow(ResultSet resultSet, ResultSetProcessingContextImpl context)
throws SQLException;
}
private class MixedReturnRowReader extends AbstractRowReader implements RowReader {
private final List<ReturnReader> returnReaders;
private List<EntityReferenceReader> entityReferenceReaders = new ArrayList<EntityReferenceReader>();
private List<CollectionReferenceReader> collectionReferenceReaders = new ArrayList<CollectionReferenceReader>();
private final int numberOfReturns;
public MixedReturnRowReader(LoadPlan loadPlan) {
LoadPlanVisitor.visit(
loadPlan,
new LoadPlanVisitationStrategyAdapter() {
@Override
public void startingEntityFetch(EntityFetch entityFetch) {
entityReferenceReaders.add( new EntityReferenceReader( entityFetch ) );
}
@Override
public void startingCollectionFetch(CollectionFetch collectionFetch) {
collectionReferenceReaders.add( new CollectionReferenceReader( collectionFetch ) );
}
}
);
final List<ReturnReader> readers = new ArrayList<ReturnReader>();
for ( Return rtn : loadPlan.getReturns() ) {
final ReturnReader returnReader = buildReturnReader( rtn );
if ( EntityReferenceReader.class.isInstance( returnReader ) ) {
entityReferenceReaders.add( (EntityReferenceReader) returnReader );
}
readers.add( returnReader );
}
this.returnReaders = readers;
this.numberOfReturns = readers.size();
}
@Override
protected List<EntityReferenceReader> getEntityReferenceReaders() {
return entityReferenceReaders;
}
@Override
protected List<CollectionReferenceReader> getCollectionReferenceReaders() {
return collectionReferenceReaders;
}
@Override
protected Object readLogicalRow(ResultSet resultSet, ResultSetProcessingContextImpl context) throws SQLException {
Object[] logicalRow = new Object[ numberOfReturns ];
int pos = 0;
for ( ReturnReader reader : returnReaders ) {
logicalRow[pos] = reader.read( resultSet, context );
pos++;
}
return logicalRow;
}
}
private static ReturnReader buildReturnReader(Return rtn) {
if ( ScalarReturn.class.isInstance( rtn ) ) {
return new ScalarReturnReader( (ScalarReturn) rtn );
}
else if ( EntityReturn.class.isInstance( rtn ) ) {
return new EntityReturnReader( (EntityReturn) rtn );
}
else if ( CollectionReturn.class.isInstance( rtn ) ) {
return new CollectionReturnReader( (CollectionReturn) rtn );
}
else {
throw new IllegalStateException( "Unknown Return type : " + rtn );
}
}
private static interface EntityReferenceReaderListBuildingAccess {
public void add(EntityReferenceReader reader);
}
private static interface CollectionReferenceReaderListBuildingAccess {
public void add(CollectionReferenceReader reader);
}
private class EntityLoaderRowReader extends AbstractRowReader implements RowReader {
private final EntityReturnReader returnReader;
private final List<EntityReferenceReader> entityReferenceReaders = new ArrayList<EntityReferenceReader>();
private List<CollectionReferenceReader> collectionReferenceReaders = null;
public EntityLoaderRowReader(LoadPlan loadPlan) {
final EntityReturn entityReturn = (EntityReturn) loadPlan.getReturns().get( 0 );
this.returnReader = (EntityReturnReader) buildReturnReader( entityReturn );
// final EntityReferenceReaderListBuildingAccess entityReaders = new EntityReferenceReaderListBuildingAccess() {
// @Override
// public void add(EntityReferenceReader reader) {
// entityReferenceReaders.add( reader );
// }
// };
//
// final CollectionReferenceReaderListBuildingAccess collectionReaders = new CollectionReferenceReaderListBuildingAccess() {
// @Override
// public void add(CollectionReferenceReader reader) {
// if ( collectionReferenceReaders == null ) {
// collectionReferenceReaders = new ArrayList<CollectionReferenceReader>();
// }
// collectionReferenceReaders.add( reader );
// }
// };
//
// buildFetchReaders( entityReaders, collectionReaders, entityReturn, returnReader );
LoadPlanVisitor.visit(
loadPlan,
new LoadPlanVisitationStrategyAdapter() {
@Override
public void startingEntityFetch(EntityFetch entityFetch) {
entityReferenceReaders.add( new EntityReferenceReader( entityFetch ) );
}
@Override
public void startingCollectionFetch(CollectionFetch collectionFetch) {
if ( collectionReferenceReaders == null ) {
collectionReferenceReaders = new ArrayList<CollectionReferenceReader>();
}
collectionReferenceReaders.add( new CollectionReferenceReader( collectionFetch ) );
}
}
);
entityReferenceReaders.add( returnReader );
}
// private void buildFetchReaders(
// EntityReferenceReaderListBuildingAccess entityReaders,
// CollectionReferenceReaderListBuildingAccess collectionReaders,
// FetchOwner fetchOwner,
// EntityReferenceReader entityReferenceReader) {
// for ( Fetch fetch : fetchOwner.getFetches() ) {
// if ( CollectionFetch.class.isInstance( fetch ) ) {
// final CollectionFetch collectionFetch = (CollectionFetch) fetch;
// buildFetchReaders(
// entityReaders,
// collectionReaders,
// collectionFetch.getIndexGraph(),
// null
// );
// buildFetchReaders(
// entityReaders,
// collectionReaders,
// collectionFetch.getElementGraph(),
// null
// );
// collectionReaders.add( new CollectionReferenceReader( collectionFetch ) );
// }
// else if ( CompositeFetch.class.isInstance( fetch ) ) {
// buildFetchReaders(
// entityReaders,
// collectionReaders,
// (CompositeFetch) fetch,
// entityReferenceReader
// );
// }
// else {
// final EntityFetch entityFetch = (EntityFetch) fetch;
// if ( entityFetch.getFetchedType().isOneToOne() ) {
// // entityReferenceReader should reference the owner still...
// if ( entityReferenceReader == null ) {
// throw new IllegalStateException( "Entity reader for one-to-one fetch owner not known" );
// }
// final EntityReferenceReader fetchReader = new OneToOneFetchReader(
// entityFetch,
// entityReferenceReader.getEntityReference()
// );
// }
// else {
//
// }
// }
// }
// //To change body of created methods use File | Settings | File Templates.
// }
@Override
protected List<EntityReferenceReader> getEntityReferenceReaders() {
return entityReferenceReaders;
}
@Override
protected List<CollectionReferenceReader> getCollectionReferenceReaders() {
return collectionReferenceReaders;
}
@Override
protected Object readLogicalRow(ResultSet resultSet, ResultSetProcessingContextImpl context) throws SQLException {
return returnReader.read( resultSet, context );
}
}
private class CollectionInitializerRowReader extends AbstractRowReader implements RowReader {
private final CollectionReturnReader returnReader;
private List<EntityReferenceReader> entityReferenceReaders = null;
private final List<CollectionReferenceReader> collectionReferenceReaders = new ArrayList<CollectionReferenceReader>();
public CollectionInitializerRowReader(LoadPlan loadPlan) {
returnReader = (CollectionReturnReader) buildReturnReader( loadPlan.getReturns().get( 0 ) );
LoadPlanVisitor.visit(
loadPlan,
new LoadPlanVisitationStrategyAdapter() {
@Override
public void startingEntityFetch(EntityFetch entityFetch) {
if ( entityReferenceReaders == null ) {
entityReferenceReaders = new ArrayList<EntityReferenceReader>();
}
entityReferenceReaders.add( new EntityReferenceReader( entityFetch ) );
}
@Override
public void startingCollectionFetch(CollectionFetch collectionFetch) {
collectionReferenceReaders.add( new CollectionReferenceReader( collectionFetch ) );
}
}
);
collectionReferenceReaders.add( returnReader );
}
@Override
protected List<EntityReferenceReader> getEntityReferenceReaders() {
return entityReferenceReaders;
}
@Override
protected List<CollectionReferenceReader> getCollectionReferenceReaders() {
return collectionReferenceReaders;
}
@Override
protected Object readLogicalRow(ResultSet resultSet, ResultSetProcessingContextImpl context) throws SQLException {
return returnReader.read( resultSet, context );
}
}
}

View File

@ -0,0 +1,52 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan.exec.process.internal;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext;
import org.hibernate.loader.plan.exec.process.spi.ReturnReader;
import org.hibernate.loader.plan.spi.ScalarReturn;
/**
* @author Steve Ebersole
*/
public class ScalarReturnReader implements ReturnReader {
private final ScalarReturn scalarReturn;
public ScalarReturnReader(ScalarReturn scalarReturn) {
this.scalarReturn = scalarReturn;
}
@Override
public Object read(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
return scalarReturn.getType().nullSafeGet(
resultSet,
context.getAliasResolutionContext().resolveScalarColumnAliases( scalarReturn ),
context.getSession(),
null
);
}
}

View File

@ -21,13 +21,13 @@
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.loader.internal; package org.hibernate.loader.plan.exec.process.internal;
import java.sql.ResultSet; import java.sql.ResultSet;
import org.hibernate.engine.spi.QueryParameters; import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.loader.spi.ScrollableResultSetProcessor; import org.hibernate.loader.plan.exec.process.spi.ScrollableResultSetProcessor;
/** /**
* @author Steve Ebersole * @author Steve Ebersole

View File

@ -0,0 +1,4 @@
/**
* Defines support for processing ResultSet values as defined by a LoadPlan
*/
package org.hibernate.loader.plan.exec.process;

View File

@ -0,0 +1,171 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan.exec.process.spi;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.hibernate.LockMode;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.loader.EntityAliases;
import org.hibernate.loader.plan.exec.spi.AliasResolutionContext;
import org.hibernate.loader.plan.exec.spi.LockModeResolver;
import org.hibernate.loader.plan.spi.EntityReference;
import org.hibernate.loader.plan.spi.Fetch;
import org.hibernate.loader.plan.spi.LoadPlan;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.type.EntityType;
/**
* @author Steve Ebersole
*/
public interface ResultSetProcessingContext extends LockModeResolver {
public SessionImplementor getSession();
public QueryParameters getQueryParameters();
public boolean shouldUseOptionalEntityInformation();
public boolean shouldReturnProxies();
public LoadPlan getLoadPlan();
/**
* Holds all pieces of information known about an entity reference in relation to each row as we process the
* result set. Caches these values and makes it easy for access while processing Fetches.
*/
public static interface EntityReferenceProcessingState {
/**
* The EntityReference for which this is collecting process state
*
* @return The EntityReference
*/
public EntityReference getEntityReference();
/**
* Register the fact that no identifier was found on attempt to hydrate it from ResultSet
*/
public void registerMissingIdentifier();
/**
*
* @return
*/
public boolean isMissingIdentifier();
/**
* Register the hydrated form (raw Type-read ResultSet values) of the entity's identifier for the row
* currently being processed.
*
* @param hydratedForm The entity identifier hydrated state
*/
public void registerIdentifierHydratedForm(Object hydratedForm);
/**
* Obtain the hydrated form (the raw Type-read ResultSet values) of the entity's identifier
*
* @return The entity identifier hydrated state
*/
public Object getIdentifierHydratedForm();
/**
* Register the processed EntityKey for this Entity for the row currently being processed.
*
* @param entityKey The processed EntityKey for this EntityReference
*/
public void registerEntityKey(EntityKey entityKey);
/**
* Obtain the registered EntityKey for this EntityReference for the row currently being processed.
*
* @return The registered EntityKey for this EntityReference
*/
public EntityKey getEntityKey();
public void registerHydratedState(Object[] hydratedState);
public Object[] getHydratedState();
// usually uninitialized at this point
public void registerEntityInstance(Object instance);
// may be uninitialized
public Object getEntityInstance();
}
public EntityReferenceProcessingState getProcessingState(EntityReference entityReference);
/**
* Find the EntityReferenceProcessingState for the FetchOwner of the given Fetch.
*
* @param fetch The Fetch for which to find the EntityReferenceProcessingState of its FetchOwner.
*
* @return The FetchOwner's EntityReferenceProcessingState
*/
public EntityReferenceProcessingState getOwnerProcessingState(Fetch fetch);
public AliasResolutionContext getAliasResolutionContext();
public void registerHydratedEntity(EntityReference entityReference, EntityKey entityKey, Object entityInstance);
public static interface EntityKeyResolutionContext {
public EntityPersister getEntityPersister();
public LockMode getLockMode();
public EntityReference getEntityReference();
}
public Object resolveEntityKey(EntityKey entityKey, EntityKeyResolutionContext entityKeyContext);
// should be able to get rid of the methods below here from the interface ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public void checkVersion(
ResultSet resultSet,
EntityPersister persister,
EntityAliases entityAliases,
EntityKey entityKey,
Object entityInstance) throws SQLException;
public String getConcreteEntityTypeName(
ResultSet resultSet,
EntityPersister persister,
EntityAliases entityAliases,
EntityKey entityKey) throws SQLException;
public void loadFromResultSet(
ResultSet resultSet,
Object entityInstance,
String concreteEntityTypeName,
EntityKey entityKey,
EntityAliases entityAliases,
LockMode acquiredLockMode,
EntityPersister persister,
FetchStrategy fetchStrategy,
boolean eagerFetch,
EntityType associationType) throws SQLException;
}

View File

@ -21,7 +21,7 @@
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.loader.spi; package org.hibernate.loader.plan.exec.process.spi;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
@ -29,6 +29,10 @@ import java.util.List;
import org.hibernate.engine.spi.QueryParameters; import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.loader.plan.exec.query.spi.NamedParameterContext;
import org.hibernate.loader.plan.exec.spi.AliasResolutionContext;
import org.hibernate.loader.spi.AfterLoadAction;
import org.hibernate.loader.spi.LoadPlanAdvisor;
import org.hibernate.transform.ResultTransformer; import org.hibernate.transform.ResultTransformer;
/** /**
@ -66,7 +70,7 @@ public interface ResultSetProcessor {
SessionImplementor session, SessionImplementor session,
QueryParameters queryParameters, QueryParameters queryParameters,
NamedParameterContext namedParameterContext, NamedParameterContext namedParameterContext,
LoadQueryAliasResolutionContext aliasResolutionContext, AliasResolutionContext aliasResolutionContext,
boolean returnProxies, boolean returnProxies,
boolean readOnly, boolean readOnly,
ResultTransformer forcedResultTransformer, ResultTransformer forcedResultTransformer,

View File

@ -0,0 +1,46 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan.exec.process.spi;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* Handles reading results from a JDBC ResultSet relating to a single Return object.
*
* @author Steve Ebersole
*/
public interface ReturnReader {
/**
* Essentially performs the second phase of two-phase loading.
*
* @param resultSet The result set being processed
* @param context The context for the processing
*
* @return The read object
*
* @throws SQLException Indicates a problem access the JDBC result set
*/
public Object read(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException;
}

View File

@ -21,7 +21,7 @@
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.loader.spi; package org.hibernate.loader.plan.exec.process.spi;
import java.sql.ResultSet; import java.sql.ResultSet;

View File

@ -0,0 +1,213 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan.exec.query.internal;
import org.hibernate.LockOptions;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.plan.exec.query.spi.EntityLoadQueryBuilder;
import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters;
import org.hibernate.loader.plan.exec.spi.AliasResolutionContext;
import org.hibernate.loader.plan.spi.EntityReturn;
import org.hibernate.loader.plan.spi.LoadPlan;
import org.hibernate.loader.plan.spi.Return;
import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.sql.ConditionFragment;
import org.hibernate.sql.DisjunctionFragment;
import org.hibernate.sql.InFragment;
/**
* @author Steve Ebersole
*/
public class EntityLoadQueryBuilderImpl implements EntityLoadQueryBuilder {
/**
* Singleton access
*/
public static final EntityLoadQueryBuilderImpl INSTANCE = new EntityLoadQueryBuilderImpl();
@Override
public String generateSql(
LoadPlan loadPlan,
SessionFactoryImplementor factory,
QueryBuildingParameters buildingParameters,
AliasResolutionContext aliasResolutionContext) {
final EntityReturn rootReturn = extractRootReturn( loadPlan );
return generateSql(
( (Queryable) rootReturn.getEntityPersister() ).getKeyColumnNames(),
rootReturn,
factory,
buildingParameters,
aliasResolutionContext
);
}
private static EntityReturn extractRootReturn(LoadPlan loadPlan) {
if ( loadPlan.getReturns().size() == 0 ) {
throw new IllegalStateException( "LoadPlan contained no root returns" );
}
else if ( loadPlan.getReturns().size() > 1 ) {
throw new IllegalStateException( "LoadPlan contained more than one root returns" );
}
final Return rootReturn = loadPlan.getReturns().get( 0 );
if ( !EntityReturn.class.isInstance( rootReturn ) ) {
throw new IllegalStateException(
String.format(
"Unexpected LoadPlan root return; expecting %s, but found %s",
EntityReturn.class.getName(),
rootReturn.getClass().getName()
)
);
}
return (EntityReturn) rootReturn;
}
@Override
public String generateSql(
String[] keyColumnNames,
LoadPlan loadPlan,
SessionFactoryImplementor factory,
QueryBuildingParameters buildingParameters,
AliasResolutionContext aliasResolutionContext) {
final EntityReturn rootReturn = extractRootReturn( loadPlan );
return generateSql(
keyColumnNames,
rootReturn,
factory,
buildingParameters,
aliasResolutionContext
);
}
protected String generateSql(
String[] keyColumnNames,
EntityReturn rootReturn,
SessionFactoryImplementor factory,
QueryBuildingParameters buildingParameters,
AliasResolutionContext aliasResolutionContext) {
final SelectStatementBuilder select = new SelectStatementBuilder( factory.getDialect() );
// apply root entity return specifics
applyRootReturnSpecifics( select, keyColumnNames, rootReturn, factory, buildingParameters, aliasResolutionContext );
LoadQueryBuilderHelper.applyJoinFetches(
select,
factory,
rootReturn,
buildingParameters,
aliasResolutionContext
);
return select.toStatementString();
}
protected void applyRootReturnSpecifics(
SelectStatementBuilder select,
String[] keyColumnNames,
EntityReturn rootReturn,
SessionFactoryImplementor factory,
QueryBuildingParameters buildingParameters,
AliasResolutionContext aliasResolutionContext) {
final String rootAlias = aliasResolutionContext.resolveAliases( rootReturn ).getTableAlias();
final OuterJoinLoadable rootLoadable = (OuterJoinLoadable) rootReturn.getEntityPersister();
final Queryable rootQueryable = (Queryable) rootReturn.getEntityPersister();
applyKeyRestriction( select, rootAlias, keyColumnNames, buildingParameters.getBatchSize() );
select.appendRestrictions(
rootQueryable.filterFragment(
rootAlias,
buildingParameters.getQueryInfluencers().getEnabledFilters()
)
);
select.appendRestrictions( rootLoadable.whereJoinFragment( rootAlias, true, true ) );
select.appendSelectClauseFragment(
rootLoadable.selectFragment(
rootAlias,
aliasResolutionContext.resolveAliases( rootReturn ).getColumnAliases().getSuffix()
)
);
final String fromTableFragment;
if ( buildingParameters.getLockOptions() != null ) {
fromTableFragment = factory.getDialect().appendLockHint(
buildingParameters.getLockOptions(),
rootLoadable.fromTableFragment( rootAlias )
);
select.setLockOptions( buildingParameters.getLockOptions() );
}
else if ( buildingParameters.getLockMode() != null ) {
fromTableFragment = factory.getDialect().appendLockHint(
buildingParameters.getLockMode(),
rootLoadable.fromTableFragment( rootAlias )
);
select.setLockMode( buildingParameters.getLockMode() );
}
else {
fromTableFragment = rootLoadable.fromTableFragment( rootAlias );
}
select.appendFromClauseFragment( fromTableFragment + rootLoadable.fromJoinFragment( rootAlias, true, true ) );
}
private void applyKeyRestriction(SelectStatementBuilder select, String alias, String[] keyColumnNames, int batchSize) {
if ( keyColumnNames.length==1 ) {
// NOT A COMPOSITE KEY
// for batching, use "foo in (?, ?, ?)" for batching
// for no batching, use "foo = ?"
// (that distinction is handled inside InFragment)
final InFragment in = new InFragment().setColumn( alias, keyColumnNames[0] );
for ( int i = 0; i < batchSize; i++ ) {
in.addValue( "?" );
}
select.appendRestrictions( in.toFragmentString() );
}
else {
// A COMPOSITE KEY...
final ConditionFragment keyRestrictionBuilder = new ConditionFragment()
.setTableAlias( alias )
.setCondition( keyColumnNames, "?" );
final String keyRestrictionFragment = keyRestrictionBuilder.toFragmentString();
StringBuilder restrictions = new StringBuilder();
if ( batchSize==1 ) {
// for no batching, use "foo = ? and bar = ?"
restrictions.append( keyRestrictionFragment );
}
else {
// for batching, use "( (foo = ? and bar = ?) or (foo = ? and bar = ?) )"
restrictions.append( '(' );
DisjunctionFragment df = new DisjunctionFragment();
for ( int i=0; i<batchSize; i++ ) {
df.addCondition( keyRestrictionFragment );
}
restrictions.append( df.toFragmentString() );
restrictions.append( ')' );
}
select.appendRestrictions( restrictions.toString() );
}
}
}

View File

@ -0,0 +1,512 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan.exec.query.internal;
import org.hibernate.cfg.NotYetImplementedException;
import org.hibernate.engine.internal.JoinHelper;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters;
import org.hibernate.loader.plan.exec.spi.AliasResolutionContext;
import org.hibernate.loader.plan.exec.spi.CollectionReferenceAliases;
import org.hibernate.loader.plan.spi.AnyFetch;
import org.hibernate.loader.plan.spi.CollectionFetch;
import org.hibernate.loader.plan.spi.CompositeElementGraph;
import org.hibernate.loader.plan.spi.CompositeFetch;
import org.hibernate.loader.plan.spi.CompositeIndexGraph;
import org.hibernate.loader.plan.spi.EntityElementGraph;
import org.hibernate.loader.plan.spi.EntityFetch;
import org.hibernate.loader.plan.spi.EntityReference;
import org.hibernate.loader.plan.spi.Fetch;
import org.hibernate.loader.plan.spi.FetchOwner;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Joinable;
import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.persister.walking.internal.FetchStrategyHelper;
import org.hibernate.persister.walking.spi.WalkingException;
import org.hibernate.sql.JoinFragment;
import org.hibernate.sql.JoinType;
import org.hibernate.type.AssociationType;
/**
* Helper for implementors of entity and collection based query building based on LoadPlans providing common
* functionality
*
* @author Steve Ebersole
*/
public class LoadQueryBuilderHelper {
private LoadQueryBuilderHelper() {
}
public static void applyJoinFetches(
SelectStatementBuilder selectStatementBuilder,
SessionFactoryImplementor factory,
FetchOwner fetchOwner,
QueryBuildingParameters buildingParameters,
AliasResolutionContext aliasResolutionContext) {
final JoinFragment joinFragment = factory.getDialect().createOuterJoinFragment();
processJoinFetches(
selectStatementBuilder,
factory,
joinFragment,
fetchOwner,
buildingParameters,
aliasResolutionContext
);
selectStatementBuilder.setOuterJoins(
joinFragment.toFromFragmentString(),
joinFragment.toWhereFragmentString()
);
}
private static void processJoinFetches(
SelectStatementBuilder selectStatementBuilder,
SessionFactoryImplementor factory,
JoinFragment joinFragment,
FetchOwner fetchOwner,
QueryBuildingParameters buildingParameters,
AliasResolutionContext aliasResolutionContext) {
for ( Fetch fetch : fetchOwner.getFetches() ) {
processFetch(
selectStatementBuilder,
factory,
joinFragment,
fetchOwner,
fetch,
buildingParameters,
aliasResolutionContext
);
}
}
private static void processFetch(
SelectStatementBuilder selectStatementBuilder,
SessionFactoryImplementor factory,
JoinFragment joinFragment,
FetchOwner fetchOwner,
Fetch fetch,
QueryBuildingParameters buildingParameters,
AliasResolutionContext aliasResolutionContext) {
if ( ! FetchStrategyHelper.isJoinFetched( fetch.getFetchStrategy() ) ) {
return;
}
if ( EntityFetch.class.isInstance( fetch ) ) {
processEntityFetch(
selectStatementBuilder,
factory,
joinFragment,
fetchOwner,
(EntityFetch) fetch,
buildingParameters,
aliasResolutionContext
);
processJoinFetches(
selectStatementBuilder,
factory,
joinFragment,
(EntityFetch) fetch,
buildingParameters,
aliasResolutionContext
);
}
else if ( CollectionFetch.class.isInstance( fetch ) ) {
processCollectionFetch(
selectStatementBuilder,
factory,
joinFragment,
fetchOwner,
(CollectionFetch) fetch,
buildingParameters,
aliasResolutionContext
);
final CollectionFetch collectionFetch = (CollectionFetch) fetch;
if ( collectionFetch.getIndexGraph() != null ) {
processJoinFetches(
selectStatementBuilder,
factory,
joinFragment,
collectionFetch.getIndexGraph(),
buildingParameters,
aliasResolutionContext
);
}
if ( collectionFetch.getElementGraph() != null ) {
processJoinFetches(
selectStatementBuilder,
factory,
joinFragment,
collectionFetch.getElementGraph(),
buildingParameters,
aliasResolutionContext
);
}
}
else {
// could also be a CompositeFetch, we ignore those here
// but do still need to visit their fetches...
if ( FetchOwner.class.isInstance( fetch ) ) {
processJoinFetches(
selectStatementBuilder,
factory,
joinFragment,
(FetchOwner) fetch,
buildingParameters,
aliasResolutionContext
);
}
}
}
private static void processEntityFetch(
SelectStatementBuilder selectStatementBuilder,
SessionFactoryImplementor factory,
JoinFragment joinFragment,
FetchOwner fetchOwner,
EntityFetch fetch,
QueryBuildingParameters buildingParameters,
AliasResolutionContext aliasResolutionContext) {
final String rhsAlias = aliasResolutionContext.resolveAliases( fetch ).getTableAlias();
final String[] rhsColumnNames = JoinHelper.getRHSColumnNames( fetch.getFetchedType(), factory );
final String lhsTableAlias = resolveLhsTableAlias( fetchOwner, fetch, aliasResolutionContext );
// todo : this is not exactly correct. it assumes the join refers to the LHS PK
final String[] aliasedLhsColumnNames = fetch.toSqlSelectFragments( lhsTableAlias );
final String additionalJoinConditions = resolveAdditionalJoinCondition(
factory,
rhsAlias,
fetchOwner,
fetch,
buildingParameters.getQueryInfluencers(),
aliasResolutionContext
);
final Joinable joinable = (Joinable) fetch.getEntityPersister();
addJoins(
joinFragment,
joinable,
fetch.isNullable() ? JoinType.LEFT_OUTER_JOIN : JoinType.INNER_JOIN,
rhsAlias,
rhsColumnNames,
aliasedLhsColumnNames,
additionalJoinConditions
);
// the null arguments here relate to many-to-many fetches
selectStatementBuilder.appendSelectClauseFragment(
joinable.selectFragment(
null,
null,
rhsAlias,
aliasResolutionContext.resolveAliases( fetch ).getColumnAliases().getSuffix(),
null,
true
)
);
}
private static String[] resolveAliasedLhsJoinColumns(
FetchOwner fetchOwner,
Fetch fetch,
AliasResolutionContext aliasResolutionContext) {
// IMPL NOTE : the fetch-owner is the LHS; the fetch is the RHS
final String lhsTableAlias = resolveLhsTableAlias( fetchOwner, fetch, aliasResolutionContext );
return fetch.toSqlSelectFragments( lhsTableAlias );
}
private static String resolveLhsTableAlias(
FetchOwner fetchOwner,
Fetch fetch,
AliasResolutionContext aliasResolutionContext) {
// IMPL NOTE : the fetch-owner is the LHS; the fetch is the RHS
if ( AnyFetch.class.isInstance( fetchOwner ) ) {
throw new WalkingException( "Any type should never be joined!" );
}
else if ( EntityReference.class.isInstance( fetchOwner ) ) {
return aliasResolutionContext.resolveAliases( (EntityReference) fetchOwner ).getTableAlias();
}
else if ( CompositeFetch.class.isInstance( fetchOwner ) ) {
return aliasResolutionContext.resolveAliases(
locateCompositeFetchEntityReferenceSource( (CompositeFetch) fetchOwner )
).getTableAlias();
}
else if ( CompositeElementGraph.class.isInstance( fetchOwner ) ) {
final CompositeElementGraph compositeElementGraph = (CompositeElementGraph) fetchOwner;
return aliasResolutionContext.resolveAliases( compositeElementGraph.getCollectionReference() ).getCollectionTableAlias();
}
else if ( CompositeIndexGraph.class.isInstance( fetchOwner ) ) {
final CompositeIndexGraph compositeIndexGraph = (CompositeIndexGraph) fetchOwner;
return aliasResolutionContext.resolveAliases( compositeIndexGraph.getCollectionReference() ).getCollectionTableAlias();
}
else {
throw new NotYetImplementedException( "Cannot determine LHS alias for FetchOwner." );
}
}
private static EntityReference locateCompositeFetchEntityReferenceSource(CompositeFetch composite) {
final FetchOwner owner = composite.getOwner();
if ( EntityReference.class.isInstance( owner ) ) {
return (EntityReference) owner;
}
if ( CompositeFetch.class.isInstance( owner ) ) {
return locateCompositeFetchEntityReferenceSource( (CompositeFetch) owner );
}
throw new WalkingException( "Cannot resolve entity source for a CompositeFetch" );
}
private static String resolveAdditionalJoinCondition(
SessionFactoryImplementor factory,
String rhsTableAlias,
FetchOwner fetchOwner,
Fetch fetch,
LoadQueryInfluencers influencers,
AliasResolutionContext aliasResolutionContext) {
final String withClause = StringHelper.isEmpty( fetch.getAdditionalJoinConditions() )
? ""
: " and ( " + fetch.getAdditionalJoinConditions() + " )";
return ( (AssociationType) fetch.getFetchedType() ).getOnCondition(
rhsTableAlias,
factory,
influencers.getEnabledFilters()
) + withClause;
}
private static void addJoins(
JoinFragment joinFragment,
Joinable joinable,
JoinType joinType,
String rhsAlias,
String[] rhsColumnNames,
String[] aliasedLhsColumnNames,
String additionalJoinConditions) {
joinFragment.addJoin(
joinable.getTableName(),
rhsAlias,
aliasedLhsColumnNames,
rhsColumnNames,
joinType,
additionalJoinConditions
);
joinFragment.addJoins(
joinable.fromJoinFragment( rhsAlias, false, true ),
joinable.whereJoinFragment( rhsAlias, false, true )
);
}
private static void processCollectionFetch(
SelectStatementBuilder selectStatementBuilder,
SessionFactoryImplementor factory,
JoinFragment joinFragment,
FetchOwner fetchOwner,
CollectionFetch fetch,
QueryBuildingParameters buildingParameters,
AliasResolutionContext aliasResolutionContext) {
final QueryableCollection queryableCollection = (QueryableCollection) fetch.getCollectionPersister();
final Joinable joinableCollection = (Joinable) fetch.getCollectionPersister();
if ( fetch.getCollectionPersister().isManyToMany() ) {
// for many-to-many we have 3 table aliases. By way of example, consider a normal m-n: User<->Role
// where User is the FetchOwner and Role (User.roles) is the Fetch. We'd have:
// 1) the owner's table : user
final String ownerTableAlias = resolveLhsTableAlias( fetchOwner, fetch, aliasResolutionContext );
// 2) the m-n table : user_role
final String collectionTableAlias = aliasResolutionContext.resolveAliases( fetch ).getCollectionTableAlias();
// 3) the element table : role
final String elementTableAlias = aliasResolutionContext.resolveAliases( fetch ).getElementTableAlias();
{
// add join fragments from the owner table -> collection table ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
final String filterFragment = ( (Joinable) fetch.getCollectionPersister() ).filterFragment(
collectionTableAlias,
buildingParameters.getQueryInfluencers().getEnabledFilters()
);
joinFragment.addJoin(
joinableCollection.getTableName(),
collectionTableAlias,
StringHelper.qualify( ownerTableAlias, extractJoinable( fetchOwner ).getKeyColumnNames() ),
queryableCollection.getKeyColumnNames(),
fetch.isNullable() ? JoinType.LEFT_OUTER_JOIN : JoinType.INNER_JOIN,
filterFragment
);
joinFragment.addJoins(
joinableCollection.fromJoinFragment( collectionTableAlias, false, true ),
joinableCollection.whereJoinFragment( collectionTableAlias, false, true )
);
// add select fragments from the collection table ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
selectStatementBuilder.appendSelectClauseFragment(
joinableCollection.selectFragment(
(Joinable) queryableCollection.getElementPersister(),
ownerTableAlias,
collectionTableAlias,
aliasResolutionContext.resolveAliases( fetch ).getEntityElementColumnAliases().getSuffix(),
aliasResolutionContext.resolveAliases( fetch ).getCollectionColumnAliases().getSuffix(),
true
)
);
}
{
// add join fragments from the collection table -> element entity table ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
final String additionalJoinConditions = resolveAdditionalJoinCondition(
factory,
elementTableAlias,
fetchOwner,
fetch,
buildingParameters.getQueryInfluencers(),
aliasResolutionContext
);
final String manyToManyFilter = fetch.getCollectionPersister().getManyToManyFilterFragment(
collectionTableAlias,
buildingParameters.getQueryInfluencers().getEnabledFilters()
);
final String condition;
if ( "".equals( manyToManyFilter ) ) {
condition = additionalJoinConditions;
}
else if ( "".equals( additionalJoinConditions ) ) {
condition = manyToManyFilter;
}
else {
condition = additionalJoinConditions + " and " + manyToManyFilter;
}
final OuterJoinLoadable elementPersister = (OuterJoinLoadable) queryableCollection.getElementPersister();
addJoins(
joinFragment,
elementPersister,
// JoinType.INNER_JOIN,
JoinType.LEFT_OUTER_JOIN,
elementTableAlias,
elementPersister.getIdentifierColumnNames(),
StringHelper.qualify( collectionTableAlias, queryableCollection.getElementColumnNames() ),
condition
);
// add select fragments from the element entity table ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
final CollectionReferenceAliases aliases = aliasResolutionContext.resolveAliases( fetch );
selectStatementBuilder.appendSelectClauseFragment(
elementPersister.selectFragment(
aliases.getElementTableAlias(),
aliases.getEntityElementColumnAliases().getSuffix()
)
);
}
final String manyToManyOrdering = queryableCollection.getManyToManyOrderByString( collectionTableAlias );
if ( StringHelper.isNotEmpty( manyToManyOrdering ) ) {
selectStatementBuilder.appendOrderByFragment( manyToManyOrdering );
}
final String ordering = queryableCollection.getSQLOrderByString( collectionTableAlias );
if ( StringHelper.isNotEmpty( ordering ) ) {
selectStatementBuilder.appendOrderByFragment( ordering );
}
}
else {
final String rhsTableAlias = aliasResolutionContext.resolveAliases( fetch ).getElementTableAlias();
final String[] rhsColumnNames = JoinHelper.getRHSColumnNames( fetch.getFetchedType(), factory );
final String lhsTableAlias = resolveLhsTableAlias( fetchOwner, fetch, aliasResolutionContext );
// todo : this is not exactly correct. it assumes the join refers to the LHS PK
final String[] aliasedLhsColumnNames = fetch.toSqlSelectFragments( lhsTableAlias );
final String on = resolveAdditionalJoinCondition(
factory,
rhsTableAlias,
fetchOwner,
fetch,
buildingParameters.getQueryInfluencers(),
aliasResolutionContext
);
addJoins(
joinFragment,
joinableCollection,
fetch.isNullable() ? JoinType.LEFT_OUTER_JOIN : JoinType.INNER_JOIN,
rhsTableAlias,
rhsColumnNames,
aliasedLhsColumnNames,
on
);
// select the "collection columns"
selectStatementBuilder.appendSelectClauseFragment(
queryableCollection.selectFragment(
rhsTableAlias,
aliasResolutionContext.resolveAliases( fetch ).getCollectionColumnAliases().getSuffix()
)
);
if ( fetch.getCollectionPersister().isOneToMany() ) {
// if the collection elements are entities, select the entity columns as well
final CollectionReferenceAliases aliases = aliasResolutionContext.resolveAliases( fetch );
final OuterJoinLoadable elementPersister = (OuterJoinLoadable) queryableCollection.getElementPersister();
selectStatementBuilder.appendSelectClauseFragment(
elementPersister.selectFragment(
aliases.getElementTableAlias(),
aliases.getEntityElementColumnAliases().getSuffix()
)
);
}
final String ordering = queryableCollection.getSQLOrderByString( rhsTableAlias );
if ( StringHelper.isNotEmpty( ordering ) ) {
selectStatementBuilder.appendOrderByFragment( ordering );
}
}
}
private static Joinable extractJoinable(FetchOwner fetchOwner) {
// this is used for collection fetches. At the end of the day, a fetched collection must be owned by
// an entity. Find that entity's persister and return it
if ( EntityReference.class.isInstance( fetchOwner ) ) {
return (Joinable) ( (EntityReference) fetchOwner ).getEntityPersister();
}
else if ( CompositeFetch.class.isInstance( fetchOwner ) ) {
return (Joinable) locateCompositeFetchEntityReferenceSource( (CompositeFetch) fetchOwner ).getEntityPersister();
}
else if ( EntityElementGraph.class.isInstance( fetchOwner ) ) {
return (Joinable) ( (EntityElementGraph) fetchOwner ).getEntityPersister();
}
throw new IllegalStateException( "Uncertain how to extract Joinable from given FetchOwner : " + fetchOwner );
}
}

View File

@ -0,0 +1,232 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan.exec.query.internal;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.dialect.Dialect;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.sql.SelectFragment;
/**
* Largely a copy of the {@link org.hibernate.sql.Select} class, but changed up slightly to better meet needs
* of building a SQL SELECT statement from a LoadPlan
*
* @author Steve Ebersole
* @author Gavin King
*/
public class SelectStatementBuilder {
public final Dialect dialect;
private StringBuilder selectClause = new StringBuilder();
private StringBuilder fromClause = new StringBuilder();
// private StringBuilder outerJoinsAfterFrom;
private String outerJoinsAfterFrom;
private StringBuilder whereClause;
// private StringBuilder outerJoinsAfterWhere;
private String outerJoinsAfterWhere;
private StringBuilder orderByClause;
private String comment;
private LockOptions lockOptions = new LockOptions();
private int guesstimatedBufferSize = 20;
public SelectStatementBuilder(Dialect dialect) {
this.dialect = dialect;
}
/**
* Appends a select clause fragment
*
* @param selection The selection fragment
*/
public void appendSelectClauseFragment(String selection) {
if ( this.selectClause.length() > 0 ) {
this.selectClause.append( ", " );
this.guesstimatedBufferSize += 2;
}
this.selectClause.append( selection );
this.guesstimatedBufferSize += selection.length();
}
public void appendSelectClauseFragment(SelectFragment selectFragment) {
appendSelectClauseFragment( selectFragment.toFragmentString().substring( 2 ) );
}
public void appendFromClauseFragment(String fragment) {
if ( this.fromClause.length() > 0 ) {
this.fromClause.append( ", " );
this.guesstimatedBufferSize += 2;
}
this.fromClause.append( fragment );
this.guesstimatedBufferSize += fragment.length();
}
public void appendFromClauseFragment(String tableName, String alias) {
appendFromClauseFragment( tableName + ' ' + alias );
}
public void appendRestrictions(String restrictions) {
final String cleaned = cleanRestrictions( restrictions );
if ( StringHelper.isEmpty( cleaned ) ) {
return;
}
this.guesstimatedBufferSize += cleaned.length();
if ( whereClause == null ) {
whereClause = new StringBuilder( cleaned );
}
else {
whereClause.append( " and " ).append( cleaned );
this.guesstimatedBufferSize += 5;
}
}
private String cleanRestrictions(String restrictions) {
restrictions = restrictions.trim();
if ( restrictions.startsWith( "and" ) ) {
restrictions = restrictions.substring( 4 );
}
if ( restrictions.endsWith( "and" ) ) {
restrictions = restrictions.substring( 0, restrictions.length()-4 );
}
return restrictions;
}
// public void appendOuterJoins(String outerJoinsAfterFrom, String outerJoinsAfterWhere) {
// appendOuterJoinsAfterFrom( outerJoinsAfterFrom );
// appendOuterJoinsAfterWhere( outerJoinsAfterWhere );
// }
//
// private void appendOuterJoinsAfterFrom(String outerJoinsAfterFrom) {
// if ( this.outerJoinsAfterFrom == null ) {
// this.outerJoinsAfterFrom = new StringBuilder( outerJoinsAfterFrom );
// }
// else {
// this.outerJoinsAfterFrom.append( ' ' ).append( outerJoinsAfterFrom );
// }
// }
//
// private void appendOuterJoinsAfterWhere(String outerJoinsAfterWhere) {
// final String cleaned = cleanRestrictions( outerJoinsAfterWhere );
//
// if ( this.outerJoinsAfterWhere == null ) {
// this.outerJoinsAfterWhere = new StringBuilder( cleaned );
// }
// else {
// this.outerJoinsAfterWhere.append( " and " ).append( cleaned );
// this.guesstimatedBufferSize += 5;
// }
//
// this.guesstimatedBufferSize += cleaned.length();
// }
public void setOuterJoins(String outerJoinsAfterFrom, String outerJoinsAfterWhere) {
this.outerJoinsAfterFrom = outerJoinsAfterFrom;
final String cleanRestrictions = cleanRestrictions( outerJoinsAfterWhere );
this.outerJoinsAfterWhere = cleanRestrictions;
this.guesstimatedBufferSize += outerJoinsAfterFrom.length() + cleanRestrictions.length();
}
public void appendOrderByFragment(String ordering) {
if ( this.orderByClause == null ) {
this.orderByClause = new StringBuilder();
}
else {
this.orderByClause.append( ", " );
this.guesstimatedBufferSize += 2;
}
this.orderByClause.append( ordering );
}
public void setComment(String comment) {
this.comment = comment;
this.guesstimatedBufferSize += comment.length();
}
public void setLockMode(LockMode lockMode) {
this.lockOptions.setLockMode( lockMode );
}
public void setLockOptions(LockOptions lockOptions) {
LockOptions.copy( lockOptions, this.lockOptions );
}
/**
* Construct an SQL <tt>SELECT</tt> statement from the given clauses
*/
public String toStatementString() {
final StringBuilder buf = new StringBuilder( guesstimatedBufferSize );
if ( StringHelper.isNotEmpty( comment ) ) {
buf.append( "/* " ).append( comment ).append( " */ " );
}
buf.append( "select " )
.append( selectClause )
.append( " from " )
.append( fromClause );
if ( StringHelper.isNotEmpty( outerJoinsAfterFrom ) ) {
buf.append( outerJoinsAfterFrom );
}
if ( isNotEmpty( whereClause ) || isNotEmpty( outerJoinsAfterWhere ) ) {
buf.append( " where " );
// the outerJoinsAfterWhere needs to come before where clause to properly
// handle dynamic filters
if ( StringHelper.isNotEmpty( outerJoinsAfterWhere ) ) {
buf.append( outerJoinsAfterWhere );
if ( isNotEmpty( whereClause ) ) {
buf.append( " and " );
}
}
if ( isNotEmpty( whereClause ) ) {
buf.append( whereClause );
}
}
if ( orderByClause != null ) {
buf.append( " order by " ).append( orderByClause );
}
if ( lockOptions.getLockMode() != LockMode.NONE ) {
buf.append( dialect.getForUpdateString( lockOptions ) );
}
return dialect.transformSelectString( buf.toString() );
}
private boolean isNotEmpty(String string) {
return StringHelper.isNotEmpty( string );
}
private boolean isNotEmpty(StringBuilder builder) {
return builder != null && builder.length() > 0;
}
}

View File

@ -0,0 +1,4 @@
/**
* Defines support for build a query (SQL string specifically for now) based on a LoadPlan.
*/
package org.hibernate.loader.plan.exec.query;

View File

@ -0,0 +1,73 @@
/*
* jDocBook, processing of DocBook sources
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan.exec.query.spi;
import org.hibernate.LockOptions;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.plan.exec.spi.AliasResolutionContext;
import org.hibernate.loader.plan.spi.LoadPlan;
/**
* Contract for generating the query (currently the SQL string specifically) based on a LoadPlan with a
* single root EntityReturn
*
* @author Steve Ebersole
* @author Gail Badner
*/
public interface EntityLoadQueryBuilder {
/**
* Generates the query for the performing load.
*
* @param loadPlan The load
* @param factory The session factory.
* @param buildingParameters Parameters influencing the building of the query
* @param aliasResolutionContext The alias resolution context.
*
* @return the SQL string for performing the load
*/
String generateSql(
LoadPlan loadPlan,
SessionFactoryImplementor factory,
QueryBuildingParameters buildingParameters,
AliasResolutionContext aliasResolutionContext);
/**
* Generates the query for the performing load, based on the specified key column(s).
*
* @param keyColumnNames The names of the key columns to use
* @param loadPlan The load
* @param factory The session factory.
* @param buildingParameters Parameters influencing the building of the query
* @param aliasResolutionContext The alias resolution context.
*
* @return the SQL string for performing the load
*/
String generateSql(
String[] keyColumnNames,
LoadPlan loadPlan,
SessionFactoryImplementor factory,
QueryBuildingParameters buildingParameters,
AliasResolutionContext aliasResolutionContext);
}

View File

@ -21,7 +21,7 @@
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.loader.spi; package org.hibernate.loader.plan.exec.query.spi;
/** /**
* The context for named parameters. * The context for named parameters.

View File

@ -0,0 +1,40 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan.exec.query.spi;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.engine.spi.LoadQueryInfluencers;
/**
* @author Steve Ebersole
*/
public interface QueryBuildingParameters {
public LoadQueryInfluencers getQueryInfluencers();
public int getBatchSize();
// ultimately it would be better to have a way to resolve the LockMode for a given Return/Fetch...
public LockMode getLockMode();
public LockOptions getLockOptions();
}

View File

@ -21,91 +21,71 @@
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.loader.spi; package org.hibernate.loader.plan.exec.spi;
import org.hibernate.loader.CollectionAliases; import org.hibernate.loader.CollectionAliases;
import org.hibernate.loader.EntityAliases; import org.hibernate.loader.EntityAliases;
import org.hibernate.loader.plan.spi.CollectionReturn;
import org.hibernate.loader.plan.spi.CollectionReference; import org.hibernate.loader.plan.spi.CollectionReference;
import org.hibernate.loader.plan.spi.EntityReference; import org.hibernate.loader.plan.spi.EntityReference;
import org.hibernate.loader.plan.spi.EntityReturn; import org.hibernate.loader.plan.spi.EntityReturn;
import org.hibernate.loader.plan.spi.Return;
import org.hibernate.loader.plan.spi.ScalarReturn; import org.hibernate.loader.plan.spi.ScalarReturn;
import org.hibernate.loader.spi.JoinableAssociation;
/** /**
* Provides aliases that are used by load queries and ResultSet processors. * Provides aliases that are used by load queries and ResultSet processors.
* *
* @author Gail Badner * @author Gail Badner
* @author Steve Ebersole
*/ */
public interface LoadQueryAliasResolutionContext { public interface AliasResolutionContext {
/** /**
* Resolve the alias associated with the specified {@link EntityReturn}. * Resolve the source alias (select-clause assigned alias) associated with the specified Return. The source
* alias is the alias associated with the Return in the source query.
* <p/>
* The concept of a source alias only has meaning in the case of queries (HQL, Criteria, etc). Not sure we
* really need to keep these here. One argument for keeping them is that I always thought it would be nice to
* base the SQL aliases on the source aliases. Keeping the source aliases here would allow us to do that as
* we are generating those SQL aliases internally.
* <p/>
* Should also consider pushing the source "from clause aliases" here if we keep pushing the select aliases
* *
* @param entityReturn - the {@link EntityReturn}. * @param theReturn The Return to locate
* *
* @return the alias associated with the specified {@link EntityReturn}. * @return the alias associated with the specified {@link EntityReturn}.
*/ */
public String resolveEntityReturnAlias(EntityReturn entityReturn); public String getSourceAlias(Return theReturn);
/** /**
* Resolve the alias associated with the specified {@link CollectionReturn}. * Resolve the SQL column aliases associated with the specified {@link ScalarReturn}.
* *
* @param collectionReturn - the {@link CollectionReturn}. * @param scalarReturn The {@link ScalarReturn} for which we want SQL column aliases
* *
* @return the alias associated with {@link CollectionReturn}. * @return The SQL column aliases associated with {@link ScalarReturn}.
*/ */
public String resolveCollectionReturnAlias(CollectionReturn collectionReturn); public String[] resolveScalarColumnAliases(ScalarReturn scalarReturn);
/** /**
* Resolve the aliases associated with the specified {@link ScalarReturn}. * Resolve the alias information related to the given entity reference.
* *
* @param scalarReturn - the {@link ScalarReturn}. * @param entityReference The entity reference for which to obtain alias info
* *
* @return the alias associated with {@link ScalarReturn}. * @return The resolved alias info,
*/ */
String[] resolveScalarReturnAliases(ScalarReturn scalarReturn); public EntityReferenceAliases resolveAliases(EntityReference entityReference);
/** /**
* Resolve the SQL table alias for the specified {@link EntityReference}. * Resolve the alias information related to the given collection reference.
* *
* @param entityReference - the {@link EntityReference}. * @param collectionReference The collection reference for which to obtain alias info
* @return The SQL table alias for the specified {@link EntityReference}. *
* @return The resolved alias info,
*/ */
String resolveEntityTableAlias(EntityReference entityReference); public CollectionReferenceAliases resolveAliases(CollectionReference collectionReference);
/**
* Returns the description of the aliases in the JDBC ResultSet that identify values "belonging" to
* an entity.
*
* @param entityReference - the {@link EntityReference} for the entity.
*
* @return The ResultSet alias descriptor for the {@link EntityReference}
*/
EntityAliases resolveEntityColumnAliases(EntityReference entityReference);
/**
* Resolve the SQL table alias for the specified {@link CollectionReference}.
*
* @param collectionReference - the {@link CollectionReference}.
* @return The SQL table alias for the specified {@link CollectionReference}.
*/
String resolveCollectionTableAlias(CollectionReference collectionReference);
/**
* Returns the description of the aliases in the JDBC ResultSet that identify values "belonging" to
* the specified {@link CollectionReference}.
*
* @return The ResultSet alias descriptor for the {@link CollectionReference}
*/
CollectionAliases resolveCollectionColumnAliases(CollectionReference collectionReference);
/**
* If the elements of this collection are entities, this methods returns the JDBC ResultSet alias descriptions
* for that entity; {@code null} indicates a non-entity collection.
*
* @return The ResultSet alias descriptor for the collection's entity element, or {@code null}
*/
EntityAliases resolveCollectionElementColumnAliases(CollectionReference collectionReference);
/** /**
* Resolve the table alias on the right-hand-side of the specified association. * Resolve the table alias on the right-hand-side of the specified association.

View File

@ -0,0 +1,69 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan.exec.spi;
import org.hibernate.loader.CollectionAliases;
import org.hibernate.loader.EntityAliases;
/**
* @author Steve Ebersole
*/
public interface CollectionReferenceAliases {
/**
* Obtain the table alias used for the collection table of the CollectionReference.
*
* @return The collection table alias.
*/
public String getCollectionTableAlias();
/**
* Obtain the alias of the table that contains the collection element values.
* <p/>
* Unlike in the legacy Loader case, CollectionReferences in the LoadPlan code refer to both the
* collection and the elements *always*. In Loader the elements were handled by EntityPersister associations
* entries for one-to-many and many-to-many. In LoadPlan we need to describe the collection table/columns
* as well as the entity element table/columns. For "basic collections" and one-to-many collections, the
* "element table" and the "collection table" are actually the same. For the many-to-many case this will be
* different and we need to track it separately.
*
* @return The element table alias. Only different from {@link #getCollectionTableAlias()} in the case of
* many-to-many.
*/
public String getElementTableAlias();
/**
* Obtain the aliases for the columns related to the collection structure such as the FK, index/key, or identifier
* (idbag).
*
* @return The collection column aliases.
*/
public CollectionAliases getCollectionColumnAliases();
/**
* Obtain the column aliases for the element values when the element of the collection is an entity.
*
* @return The column aliases for the entity element; {@code null} if the collection element is not an entity.
*/
public EntityAliases getEntityElementColumnAliases();
}

View File

@ -0,0 +1,54 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan.exec.spi;
import org.hibernate.loader.EntityAliases;
/**
* Aggregates the alias/suffix information in relation to an {@link org.hibernate.loader.plan.spi.EntityReference}
*
* todo : add a contract (interface) that can be shared by entity and collection alias info objects as lhs/rhs of a join ?
*
* @author Steve Ebersole
*/
public interface EntityReferenceAliases {
/**
* Obtain the table alias used for referencing the table of the EntityReference.
* <p/>
* Note that this currently just returns the "root alias" whereas sometimes an entity reference covers
* multiple tables. todo : to help manage this, consider a solution like TableAliasRoot from the initial ANTLR re-work
* see http://anonsvn.jboss.org/repos/hibernate/core/branches/antlr3/src/main/java/org/hibernate/sql/ast/alias/TableAliasGenerator.java
*
* @return The (root) table alias for the described entity reference.
*/
public String getTableAlias();
/**
* Obtain the column aliases for the select fragment columns associated with the described entity reference. These
* are the column renames by which the values can be extracted from the SQL result set.
*
* @return The column aliases associated with the described entity reference.
*/
public EntityAliases getColumnAliases();
}

View File

@ -0,0 +1,111 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan.exec.spi;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.plan.exec.internal.AliasResolutionContextImpl;
import org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl;
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessor;
import org.hibernate.loader.plan.exec.query.internal.EntityLoadQueryBuilderImpl;
import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters;
import org.hibernate.loader.plan.spi.LoadPlan;
/**
* Wraps a LoadPlan (for an entity load) and exposes details about the query and its execution.
*
* @author Steve Ebersole
*/
public class LoadQueryDetails {
private final SessionFactoryImplementor factory;
private final LoadPlan loadPlan;
private final AliasResolutionContext aliasResolutionContext;
private final String sqlStatement;
private final ResultSetProcessor resultSetProcessor;
/**
* Constructs a LoadQueryDetails object from the given inputs.
*
*
* @param uniqueKeyColumnNames
* @param loadPlan The load plan
* @param factory The SessionFactory
* @param buildingParameters And influencers that would affect the generated SQL (mostly we are concerned with those
* that add additional joins here)
*
* @return The LoadQueryDetails
*/
public static LoadQueryDetails makeForBatching(
String[] uniqueKeyColumnNames,
LoadPlan loadPlan,
SessionFactoryImplementor factory,
QueryBuildingParameters buildingParameters) {
final AliasResolutionContext aliasResolutionContext = new AliasResolutionContextImpl( factory );
final ResultSetProcessor resultSetProcessor = new ResultSetProcessorImpl( loadPlan, false );
final String sqlStatement = EntityLoadQueryBuilderImpl.INSTANCE.generateSql(
uniqueKeyColumnNames,
loadPlan,
factory,
buildingParameters,
aliasResolutionContext
);
return new LoadQueryDetails( factory, loadPlan, aliasResolutionContext, resultSetProcessor, sqlStatement );
}
private LoadQueryDetails(
SessionFactoryImplementor factory,
LoadPlan loadPlan,
AliasResolutionContext aliasResolutionContext,
ResultSetProcessor resultSetProcessor,
String sqlStatement) {
this.factory = factory;
this.loadPlan = loadPlan;
this.aliasResolutionContext = aliasResolutionContext;
this.resultSetProcessor = resultSetProcessor;
this.sqlStatement = sqlStatement;
}
public SessionFactoryImplementor getFactory() {
return factory;
}
public LoadPlan getLoadPlan() {
return loadPlan;
}
public AliasResolutionContext getAliasResolutionContext() {
return aliasResolutionContext;
}
public String getSqlStatement() {
return sqlStatement;
}
public ResultSetProcessor getResultSetProcessor() {
return resultSetProcessor;
}
}

View File

@ -0,0 +1,34 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan.exec.spi;
import org.hibernate.LockMode;
import org.hibernate.loader.plan.spi.EntityReference;
/**
* @author Steve Ebersole
*/
public interface LockModeResolver {
public LockMode resolveLockMode(EntityReference entityReference);
}

View File

@ -25,12 +25,16 @@ package org.hibernate.loader.plan.internal;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchStrategy;
import org.hibernate.loader.plan.spi.AbstractFetchOwner;
import org.hibernate.loader.plan.spi.AnyFetch;
import org.hibernate.loader.plan.spi.CollectionFetch; import org.hibernate.loader.plan.spi.CollectionFetch;
import org.hibernate.loader.plan.spi.CompositeFetch; import org.hibernate.loader.plan.spi.CompositeFetch;
import org.hibernate.loader.plan.spi.EntityFetch; import org.hibernate.loader.plan.spi.EntityFetch;
import org.hibernate.loader.plan.spi.FetchOwner; import org.hibernate.loader.plan.spi.FetchOwner;
import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext; import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext;
import org.hibernate.persister.walking.spi.AnyMappingDefinition;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.persister.walking.spi.CompositionDefinition; import org.hibernate.persister.walking.spi.CompositionDefinition;
/** /**
@ -47,7 +51,7 @@ public class LoadPlanBuildingHelper {
LockMode.NONE, // todo : for now LockMode.NONE, // todo : for now
fetchOwner, fetchOwner,
fetchStrategy, fetchStrategy,
attributeDefinition.getName() attributeDefinition
); );
} }
@ -56,12 +60,11 @@ public class LoadPlanBuildingHelper {
AssociationAttributeDefinition attributeDefinition, AssociationAttributeDefinition attributeDefinition,
FetchStrategy fetchStrategy, FetchStrategy fetchStrategy,
LoadPlanBuildingContext loadPlanBuildingContext) { LoadPlanBuildingContext loadPlanBuildingContext) {
return new EntityFetch( return new EntityFetch(
loadPlanBuildingContext.getSessionFactory(), loadPlanBuildingContext.getSessionFactory(),
LockMode.NONE, // todo : for now LockMode.NONE, // todo : for now
fetchOwner, fetchOwner,
attributeDefinition.getName(), attributeDefinition,
fetchStrategy fetchStrategy
); );
} }
@ -73,7 +76,21 @@ public class LoadPlanBuildingHelper {
return new CompositeFetch( return new CompositeFetch(
loadPlanBuildingContext.getSessionFactory(), loadPlanBuildingContext.getSessionFactory(),
fetchOwner, fetchOwner,
attributeDefinition.getName() attributeDefinition
);
}
public static AnyFetch buildAnyFetch(
FetchOwner fetchOwner,
AttributeDefinition attribute,
AnyMappingDefinition anyDefinition,
FetchStrategy fetchStrategy, LoadPlanBuildingContext loadPlanBuildingContext) {
return new AnyFetch(
loadPlanBuildingContext.getSessionFactory(),
fetchOwner,
attribute,
anyDefinition,
fetchStrategy
); );
} }
} }

View File

@ -26,6 +26,7 @@ package org.hibernate.loader.plan.internal;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import org.hibernate.loader.plan.spi.CollectionReturn;
import org.hibernate.loader.plan.spi.EntityReturn; import org.hibernate.loader.plan.spi.EntityReturn;
import org.hibernate.loader.plan.spi.LoadPlan; import org.hibernate.loader.plan.spi.LoadPlan;
import org.hibernate.loader.plan.spi.Return; import org.hibernate.loader.plan.spi.Return;
@ -36,29 +37,45 @@ import org.hibernate.loader.plan.spi.Return;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class LoadPlanImpl implements LoadPlan { public class LoadPlanImpl implements LoadPlan {
private final boolean hasScalars; private final List<? extends Return> returns;
private final List<Return> returns; private final Disposition disposition;
private final boolean areLazyAttributesForceFetched;
public LoadPlanImpl(boolean hasScalars, List<Return> returns) { protected LoadPlanImpl(List<? extends Return> returns, Disposition disposition, boolean areLazyAttributesForceFetched) {
this.hasScalars = hasScalars;
this.returns = returns; this.returns = returns;
this.disposition = disposition;
this.areLazyAttributesForceFetched = areLazyAttributesForceFetched;
} }
public LoadPlanImpl(boolean hasScalars, Return rootReturn) { public LoadPlanImpl(EntityReturn rootReturn) {
this( hasScalars, Collections.singletonList( rootReturn ) ); this( Collections.singletonList( rootReturn ), Disposition.ENTITY_LOADER, false );
} }
public LoadPlanImpl(EntityReturn entityReturn) { public LoadPlanImpl(CollectionReturn rootReturn) {
this( false, entityReturn ); this( Collections.singletonList( rootReturn ), Disposition.ENTITY_LOADER, false );
}
public LoadPlanImpl(List<? extends Return> returns, boolean areLazyAttributesForceFetched) {
this( returns, Disposition.MIXED, areLazyAttributesForceFetched );
}
@Override
public List<? extends Return> getReturns() {
return returns;
}
@Override
public Disposition getDisposition() {
return disposition;
}
@Override
public boolean areLazyAttributesForceFetched() {
return areLazyAttributesForceFetched;
} }
@Override @Override
public boolean hasAnyScalarReturns() { public boolean hasAnyScalarReturns() {
return hasScalars; return disposition == Disposition.MIXED;
}
@Override
public List<Return> getReturns() {
return returns;
} }
} }

View File

@ -91,7 +91,15 @@ public class SingleRootReturnLoadPlanBuilderStrategy
@Override @Override
public LoadPlan buildLoadPlan() { public LoadPlan buildLoadPlan() {
return new LoadPlanImpl( false, rootReturn ); if ( EntityReturn.class.isInstance( rootReturn ) ) {
return new LoadPlanImpl( (EntityReturn) rootReturn );
}
else if ( CollectionReturn.class.isInstance( rootReturn ) ) {
return new LoadPlanImpl( (CollectionReturn) rootReturn );
}
else {
throw new IllegalStateException( "Unexpected root Return type : " + rootReturn );
}
} }
@Override @Override

View File

@ -27,6 +27,7 @@ import org.hibernate.LockMode;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.PropertyPath; import org.hibernate.loader.PropertyPath;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type; import org.hibernate.type.Type;
/** /**
@ -44,14 +45,15 @@ public abstract class AbstractCollectionReference extends AbstractPlanNode imple
SessionFactoryImplementor sessionFactory, SessionFactoryImplementor sessionFactory,
LockMode lockMode, LockMode lockMode,
CollectionPersister collectionPersister, CollectionPersister collectionPersister,
PropertyPath propertyPath) { PropertyPath propertyPath,
EntityReference ownerEntityReference) {
super( sessionFactory ); super( sessionFactory );
this.lockMode = lockMode; this.lockMode = lockMode;
this.collectionPersister = collectionPersister; this.collectionPersister = collectionPersister;
this.propertyPath = propertyPath; this.propertyPath = propertyPath;
this.indexGraph = buildIndexGraph( getCollectionPersister() ); this.indexGraph = buildIndexGraph( getCollectionPersister() );
this.elementGraph = buildElementGraph( getCollectionPersister() ); this.elementGraph = buildElementGraph( getCollectionPersister(), ownerEntityReference );
} }
private FetchableCollectionIndex buildIndexGraph(CollectionPersister persister) { private FetchableCollectionIndex buildIndexGraph(CollectionPersister persister) {
@ -70,10 +72,30 @@ public abstract class AbstractCollectionReference extends AbstractPlanNode imple
return null; return null;
} }
private FetchableCollectionElement buildElementGraph(CollectionPersister persister) { private FetchableCollectionElement buildElementGraph(
CollectionPersister persister,
EntityReference ownerEntityReference) {
final Type type = persister.getElementType(); final Type type = persister.getElementType();
if ( type.isAssociationType() ) { if ( type.isAssociationType() ) {
if ( type.isEntityType() ) { if ( type.isEntityType() ) {
final EntityType elementEntityType = (EntityType) type;
if ( ownerEntityReference != null ) {
// check for bi-directionality
final boolean sameType = elementEntityType.getAssociatedEntityName().equals(
ownerEntityReference.getEntityPersister().getEntityName()
);
if ( sameType ) {
// todo : check for columns too...
return new BidirectionalEntityElementGraph(
sessionFactory(),
this,
getPropertyPath(),
ownerEntityReference
);
}
}
return new EntityElementGraph( sessionFactory(), this, getPropertyPath() ); return new EntityElementGraph( sessionFactory(), this, getPropertyPath() );
} }
} }
@ -119,8 +141,22 @@ public abstract class AbstractCollectionReference extends AbstractPlanNode imple
return elementGraph; return elementGraph;
} }
@Override
public boolean hasEntityElements() { private class BidirectionalEntityElementGraph extends EntityElementGraph implements BidirectionalEntityFetch {
return getCollectionPersister().isOneToMany() || getCollectionPersister().isManyToMany(); private final EntityReference targetEntityReference;
private BidirectionalEntityElementGraph(
SessionFactoryImplementor sessionFactory,
CollectionReference collectionReference,
PropertyPath propertyPath,
EntityReference targetEntityReference) {
super( sessionFactory, collectionReference, propertyPath );
this.targetEntityReference = targetEntityReference;
}
@Override
public EntityReference getTargetEntityReference() {
return targetEntityReference;
}
} }
} }

View File

@ -31,7 +31,9 @@ import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.plan.internal.LoadPlanBuildingHelper; import org.hibernate.loader.plan.internal.LoadPlanBuildingHelper;
import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext; import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext;
import org.hibernate.persister.walking.spi.AnyMappingDefinition;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.persister.walking.spi.CompositionDefinition; import org.hibernate.persister.walking.spi.CompositionDefinition;
import org.hibernate.type.Type; import org.hibernate.type.Type;
@ -100,25 +102,34 @@ public abstract class AbstractFetchOwner extends AbstractPlanNode implements Fet
return fetches == null ? NO_FETCHES : fetches.toArray( new Fetch[ fetches.size() ] ); return fetches == null ? NO_FETCHES : fetches.toArray( new Fetch[ fetches.size() ] );
} }
/**
* Abstract method returning the delegate for obtaining details about an owned fetch.
* @return the delegate
*/
protected abstract FetchOwnerDelegate getFetchOwnerDelegate();
@Override @Override
public boolean isNullable(Fetch fetch) { public boolean isNullable(Fetch fetch) {
return getFetchOwnerDelegate().locateFetchMetadata( fetch ).isNullable(); return fetch.isNullable();
} }
@Override @Override
public Type getType(Fetch fetch) { public Type getType(Fetch fetch) {
return getFetchOwnerDelegate().locateFetchMetadata( fetch ).getType(); return fetch.getFetchedType();
} }
@Override @Override
public String[] toSqlSelectFragments(Fetch fetch, String alias) { public String[] toSqlSelectFragments(Fetch fetch, String alias) {
return getFetchOwnerDelegate().locateFetchMetadata( fetch ).toSqlSelectFragments( alias ); return fetch.toSqlSelectFragments( alias );
}
@Override
public AnyFetch buildAnyFetch(
AttributeDefinition attribute,
AnyMappingDefinition anyDefinition,
FetchStrategy fetchStrategy,
LoadPlanBuildingContext loadPlanBuildingContext) {
return LoadPlanBuildingHelper.buildAnyFetch(
this,
attribute,
anyDefinition,
fetchStrategy,
loadPlanBuildingContext
);
} }
@Override @Override

View File

@ -28,6 +28,8 @@ import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.PropertyPath; import org.hibernate.loader.PropertyPath;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.type.Type;
/** /**
* Represents a singular attribute that is both a {@link FetchOwner} and a {@link Fetch}. * Represents a singular attribute that is both a {@link FetchOwner} and a {@link Fetch}.
@ -37,7 +39,7 @@ import org.hibernate.loader.PropertyPath;
*/ */
public abstract class AbstractSingularAttributeFetch extends AbstractFetchOwner implements Fetch { public abstract class AbstractSingularAttributeFetch extends AbstractFetchOwner implements Fetch {
private final FetchOwner owner; private final FetchOwner owner;
private final String ownerProperty; private final AttributeDefinition fetchedAttribute;
private final FetchStrategy fetchStrategy; private final FetchStrategy fetchStrategy;
private final PropertyPath propertyPath; private final PropertyPath propertyPath;
@ -47,22 +49,22 @@ public abstract class AbstractSingularAttributeFetch extends AbstractFetchOwner
* *
* @param factory - the session factory. * @param factory - the session factory.
* @param owner - the fetch owner for this fetch. * @param owner - the fetch owner for this fetch.
* @param ownerProperty - the owner's property referring to this fetch. * @param fetchedAttribute - the attribute being fetched
* @param fetchStrategy - the fetch strategy for this fetch. * @param fetchStrategy - the fetch strategy for this fetch.
*/ */
public AbstractSingularAttributeFetch( public AbstractSingularAttributeFetch(
SessionFactoryImplementor factory, SessionFactoryImplementor factory,
FetchOwner owner, FetchOwner owner,
String ownerProperty, AttributeDefinition fetchedAttribute,
FetchStrategy fetchStrategy) { FetchStrategy fetchStrategy) {
super( factory ); super( factory );
this.owner = owner; this.owner = owner;
this.ownerProperty = ownerProperty; this.fetchedAttribute = fetchedAttribute;
this.fetchStrategy = fetchStrategy; this.fetchStrategy = fetchStrategy;
owner.addFetch( this ); owner.addFetch( this );
this.propertyPath = owner.getPropertyPath().append( ownerProperty ); this.propertyPath = owner.getPropertyPath().append( fetchedAttribute.getName() );
} }
public AbstractSingularAttributeFetch( public AbstractSingularAttributeFetch(
@ -71,7 +73,7 @@ public abstract class AbstractSingularAttributeFetch extends AbstractFetchOwner
FetchOwner fetchOwnerCopy) { FetchOwner fetchOwnerCopy) {
super( original, copyContext ); super( original, copyContext );
this.owner = fetchOwnerCopy; this.owner = fetchOwnerCopy;
this.ownerProperty = original.ownerProperty; this.fetchedAttribute = original.fetchedAttribute;
this.fetchStrategy = original.fetchStrategy; this.fetchStrategy = original.fetchStrategy;
this.propertyPath = original.propertyPath; this.propertyPath = original.propertyPath;
} }
@ -81,14 +83,19 @@ public abstract class AbstractSingularAttributeFetch extends AbstractFetchOwner
return owner; return owner;
} }
public AttributeDefinition getFetchedAttribute() {
return fetchedAttribute;
}
@Override @Override
public String getOwnerPropertyName() { public Type getFetchedType() {
return ownerProperty; return fetchedAttribute.getType();
} }
@Override @Override
public boolean isNullable() { public boolean isNullable() {
return owner.isNullable( this ); return fetchedAttribute.isNullable();
// return owner.isNullable( this );
} }
@Override @Override
@ -102,10 +109,23 @@ public abstract class AbstractSingularAttributeFetch extends AbstractFetchOwner
} }
@Override @Override
public void validateFetchPlan(FetchStrategy fetchStrategy) { public String getAdditionalJoinConditions() {
// only pertinent for HQL...
return null;
}
@Override
public void validateFetchPlan(FetchStrategy fetchStrategy, AttributeDefinition attributeDefinition) {
if ( fetchStrategy.getStyle() == FetchStyle.JOIN ) { if ( fetchStrategy.getStyle() == FetchStyle.JOIN ) {
if ( this.fetchStrategy.getStyle() != FetchStyle.JOIN ) { if ( this.fetchStrategy.getStyle() != FetchStyle.JOIN ) {
throw new HibernateException( "Cannot specify join fetch from owner that is a non-joined fetch" );
throw new HibernateException(
String.format(
"Cannot specify join fetch from owner [%s] that is a non-joined fetch : %s",
getPropertyPath().getFullPath(),
attributeDefinition.getName()
)
);
} }
} }
} }

View File

@ -0,0 +1,141 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan.spi;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.hibernate.cfg.NotYetImplementedException;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.PropertyPath;
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext;
import org.hibernate.persister.walking.spi.AnyMappingDefinition;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.type.AnyType;
/**
* @author Steve Ebersole
*/
public class AnyFetch extends AbstractPlanNode implements Fetch {
private final FetchOwner owner;
private final AttributeDefinition fetchedAttribute;
private final AnyMappingDefinition definition;
private final FetchStrategy fetchStrategy;
private final PropertyPath propertyPath;
public AnyFetch(
SessionFactoryImplementor sessionFactory,
FetchOwner owner,
AttributeDefinition ownerProperty,
AnyMappingDefinition definition,
FetchStrategy fetchStrategy) {
super( sessionFactory );
this.owner = owner;
this.fetchedAttribute = ownerProperty;
this.definition = definition;
this.fetchStrategy = fetchStrategy;
this.propertyPath = owner.getPropertyPath().append( ownerProperty.getName() );
owner.addFetch( this );
}
/**
* Copy constructor.
*
* @param original The original fetch
* @param copyContext Access to contextual needs for the copy operation
*/
protected AnyFetch(AnyFetch original, CopyContext copyContext, FetchOwner fetchOwnerCopy) {
super( original );
this.owner = fetchOwnerCopy;
this.fetchedAttribute = original.fetchedAttribute;
this.definition = original.definition;
this.fetchStrategy = original.fetchStrategy;
this.propertyPath = original.propertyPath;
}
@Override
public FetchOwner getOwner() {
return owner;
}
@Override
public AnyType getFetchedType() {
return (AnyType) fetchedAttribute.getType();
}
@Override
public boolean isNullable() {
return owner.isNullable( this );
}
@Override
public String[] toSqlSelectFragments(String alias) {
return owner.toSqlSelectFragments( this, alias );
}
@Override
public String getAdditionalJoinConditions() {
// only pertinent for HQL...
return null;
}
@Override
public FetchStrategy getFetchStrategy() {
return fetchStrategy;
}
@Override
public PropertyPath getPropertyPath() {
return propertyPath;
}
@Override
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
throw new NotYetImplementedException();
}
@Override
public Object resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
throw new NotYetImplementedException();
}
@Override
public void read(ResultSet resultSet, ResultSetProcessingContext context, Object owner) throws SQLException {
throw new NotYetImplementedException();
}
@Override
public AnyFetch makeCopy(CopyContext copyContext, FetchOwner fetchOwnerCopy) {
copyContext.getReturnGraphVisitationStrategy().startingAnyFetch( this );
final AnyFetch copy = new AnyFetch( this, copyContext, fetchOwnerCopy );
copyContext.getReturnGraphVisitationStrategy().startingAnyFetch( this );
return copy;
}
}

View File

@ -1,5 +1,5 @@
/* /*
* jDocBook, processing of DocBook sources * Hibernate, Relational Persistence for Idiomatic Java
* *
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as * Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution * indicated by the @author tags or express copyright attribution
@ -21,24 +21,15 @@
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.loader.spi; package org.hibernate.loader.plan.spi;
import org.hibernate.engine.spi.SessionFactoryImplementor;
/** /**
* Builds a load query for generating SQL. * Represents the circular side of a bi-directional entity fetch. Wraps a reference to an EntityReference
* as an EntityFetch. We can use the special type as a trigger in AliasResolutionContext, etc to lookup information
* based on the wrapped reference.
* *
* @author Gail Badner * @author Steve Ebersole
*/ */
public interface LoadQueryBuilder { public interface BidirectionalEntityFetch {
public EntityReference getTargetEntityReference();
/**
* Generates SQL for the performing the load.
* @param batchSize - the batch size.
* @param factory - the session factory.
* @param aliasResolutionContext - the alias resolution context.
*
* @return the SQL string for performing the load
*/
String generateSql(int batchSize, SessionFactoryImplementor factory, LoadQueryAliasResolutionContext aliasResolutionContext);
} }

View File

@ -23,19 +23,34 @@
*/ */
package org.hibernate.loader.plan.spi; package org.hibernate.loader.plan.spi;
import java.io.Serializable;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import org.jboss.logging.Logger;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.spi.ResultSetProcessingContext; import org.hibernate.internal.CoreLogging;
import org.hibernate.loader.CollectionAliases;
import org.hibernate.loader.plan.exec.process.internal.Helper;
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.pretty.MessageHelper;
import org.hibernate.type.CollectionType;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class CollectionFetch extends AbstractCollectionReference implements Fetch { public class CollectionFetch extends AbstractCollectionReference implements Fetch {
private static final Logger log = CoreLogging.logger( CollectionFetch.class );
private final FetchOwner fetchOwner; private final FetchOwner fetchOwner;
private final AttributeDefinition fetchedAttribute;
private final FetchStrategy fetchStrategy; private final FetchStrategy fetchStrategy;
public CollectionFetch( public CollectionFetch(
@ -43,16 +58,16 @@ public class CollectionFetch extends AbstractCollectionReference implements Fetc
LockMode lockMode, LockMode lockMode,
FetchOwner fetchOwner, FetchOwner fetchOwner,
FetchStrategy fetchStrategy, FetchStrategy fetchStrategy,
String ownerProperty) { AttributeDefinition fetchedAttribute) {
super( super(
sessionFactory, sessionFactory,
lockMode, lockMode,
sessionFactory.getCollectionPersister( sessionFactory.getCollectionPersister( ( (CollectionType) fetchedAttribute.getType() ).getRole() ),
fetchOwner.retrieveFetchSourcePersister().getEntityName() + '.' + ownerProperty fetchOwner.getPropertyPath().append( fetchedAttribute.getName() ),
), (EntityReference) fetchOwner
fetchOwner.getPropertyPath().append( ownerProperty )
); );
this.fetchOwner = fetchOwner; this.fetchOwner = fetchOwner;
this.fetchedAttribute = fetchedAttribute;
this.fetchStrategy = fetchStrategy; this.fetchStrategy = fetchStrategy;
fetchOwner.addFetch( this ); fetchOwner.addFetch( this );
} }
@ -60,6 +75,7 @@ public class CollectionFetch extends AbstractCollectionReference implements Fetc
protected CollectionFetch(CollectionFetch original, CopyContext copyContext, FetchOwner fetchOwnerCopy) { protected CollectionFetch(CollectionFetch original, CopyContext copyContext, FetchOwner fetchOwnerCopy) {
super( original, copyContext ); super( original, copyContext );
this.fetchOwner = fetchOwnerCopy; this.fetchOwner = fetchOwnerCopy;
this.fetchedAttribute = original.fetchedAttribute;
this.fetchStrategy = original.fetchStrategy; this.fetchStrategy = original.fetchStrategy;
} }
@ -69,8 +85,8 @@ public class CollectionFetch extends AbstractCollectionReference implements Fetc
} }
@Override @Override
public String getOwnerPropertyName() { public CollectionType getFetchedType() {
return getPropertyPath().getProperty(); return (CollectionType) fetchedAttribute.getType();
} }
@Override @Override
@ -78,9 +94,15 @@ public class CollectionFetch extends AbstractCollectionReference implements Fetc
return true; return true;
} }
@Override
public String getAdditionalJoinConditions() {
// only pertinent for HQL...
return null;
}
@Override @Override
public String[] toSqlSelectFragments(String alias) { public String[] toSqlSelectFragments(String alias) {
return getOwner().toSqlSelectFragments( this, alias ); return getOwner().toSqlSelectFragmentResolver().toSqlSelectFragments( alias, fetchedAttribute );
} }
@Override @Override
@ -95,7 +117,84 @@ public class CollectionFetch extends AbstractCollectionReference implements Fetc
@Override @Override
public Object resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException { public Object resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
return null; //To change body of implemented methods use File | Settings | File Templates. return null;
}
@Override
public void read(ResultSet resultSet, ResultSetProcessingContext context, Object owner) throws SQLException {
final Serializable collectionRowKey = (Serializable) getCollectionPersister().readKey(
resultSet,
context.getAliasResolutionContext().resolveAliases( this ).getCollectionColumnAliases().getSuffixedKeyAliases(),
context.getSession()
);
final PersistenceContext persistenceContext = context.getSession().getPersistenceContext();
if ( collectionRowKey == null ) {
// we did not find a collection element in the result set, so we
// ensure that a collection is created with the owner's identifier,
// since what we have is an empty collection
final EntityKey ownerEntityKey = findOwnerEntityKey( context );
if ( ownerEntityKey == null ) {
// should not happen
throw new IllegalStateException(
"Could not locate owner's EntityKey during attempt to read collection element fro JDBC row : " +
getPropertyPath().getFullPath()
);
}
if ( log.isDebugEnabled() ) {
log.debugf(
"Result set contains (possibly empty) collection: %s",
MessageHelper.collectionInfoString(
getCollectionPersister(),
ownerEntityKey,
context.getSession().getFactory()
)
);
}
persistenceContext.getLoadContexts()
.getCollectionLoadContext( resultSet )
.getLoadingCollection( getCollectionPersister(), ownerEntityKey );
}
else {
// we found a collection element in the result set
if ( log.isDebugEnabled() ) {
log.debugf(
"Found row of collection: %s",
MessageHelper.collectionInfoString(
getCollectionPersister(),
collectionRowKey,
context.getSession().getFactory()
)
);
}
PersistentCollection rowCollection = persistenceContext.getLoadContexts()
.getCollectionLoadContext( resultSet )
.getLoadingCollection( getCollectionPersister(), collectionRowKey );
final CollectionAliases descriptor = context.getAliasResolutionContext().resolveAliases( this ).getCollectionColumnAliases();
if ( rowCollection != null ) {
final Object element = rowCollection.readFrom( resultSet, getCollectionPersister(), descriptor, owner );
if ( getElementGraph() != null ) {
for ( Fetch fetch : getElementGraph().getFetches() ) {
fetch.read( resultSet, context, element );
}
}
}
}
}
private EntityKey findOwnerEntityKey(ResultSetProcessingContext context) {
return context.getProcessingState( findOwnerEntityReference( getOwner() ) ).getEntityKey();
}
private EntityReference findOwnerEntityReference(FetchOwner owner) {
return Helper.INSTANCE.findOwnerEntityReference( owner );
} }
@Override @Override

View File

@ -53,6 +53,4 @@ public interface CollectionReference {
public FetchOwner getElementGraph(); public FetchOwner getElementGraph();
public PropertyPath getPropertyPath(); public PropertyPath getPropertyPath();
public boolean hasEntityElements();
} }

View File

@ -23,13 +23,9 @@
*/ */
package org.hibernate.loader.plan.spi; package org.hibernate.loader.plan.spi;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.PropertyPath; import org.hibernate.loader.PropertyPath;
import org.hibernate.loader.spi.ResultSetProcessingContext;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
@ -47,7 +43,10 @@ public class CollectionReturn extends AbstractCollectionReference implements Ret
sessionFactory, sessionFactory,
lockMode, lockMode,
sessionFactory.getCollectionPersister( ownerEntityName + '.' + ownerProperty ), sessionFactory.getCollectionPersister( ownerEntityName + '.' + ownerProperty ),
new PropertyPath() // its a root // its a root
new PropertyPath(),
// no owner
null
); );
this.ownerEntityName = ownerEntityName; this.ownerEntityName = ownerEntityName;
this.ownerProperty = ownerProperty; this.ownerProperty = ownerProperty;
@ -77,21 +76,6 @@ public class CollectionReturn extends AbstractCollectionReference implements Ret
return ownerProperty; return ownerProperty;
} }
@Override
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
// todo : anything to do here?
}
@Override
public void resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
// todo : anything to do here?
}
@Override
public Object read(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
@Override @Override
public String toString() { public String toString() {
return "CollectionReturn(" + getCollectionPersister().getRole() + ")"; return "CollectionReturn(" + getCollectionPersister().getRole() + ")";

View File

@ -0,0 +1,88 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan.spi;
import java.util.Arrays;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.persister.walking.spi.WalkingException;
import org.hibernate.type.CompositeType;
import org.hibernate.type.Type;
/**
* @author Steve Ebersole
*/
public class CompositeBasedSqlSelectFragmentResolver implements SqlSelectFragmentResolver {
protected static interface BaseSqlSelectFragmentResolver {
public String[] toSqlSelectFragments(String alias);
}
public CompositeBasedSqlSelectFragmentResolver(
SessionFactoryImplementor sessionFactory, CompositeType compositeType,
BaseSqlSelectFragmentResolver baseResolver) {
this.sessionFactory = sessionFactory;
this.compositeType = compositeType;
this.baseResolver = baseResolver;
}
private final SessionFactoryImplementor sessionFactory;
private final CompositeType compositeType;
private final BaseSqlSelectFragmentResolver baseResolver;
@Override
public String[] toSqlSelectFragments(String alias, AttributeDefinition attributeDefinition) {
int subIndex = -1;
int selectFragmentRangeStart = 0;
int selectFragmentRangeEnd = -1;
for ( int i = 0; i < compositeType.getPropertyNames().length; i++ ) {
final Type type = compositeType.getSubtypes()[i];
final int typeColSpan = type.getColumnSpan( sessionFactory );
if ( compositeType.getPropertyNames()[ i ].equals( attributeDefinition.getName() ) ) {
// fount it!
subIndex = i;
selectFragmentRangeEnd = selectFragmentRangeStart + typeColSpan;
break;
}
selectFragmentRangeStart += typeColSpan;
}
if ( subIndex < 0 ) {
throw new WalkingException(
String.format(
"Owner property [%s] not found in composite properties [%s]",
attributeDefinition.getName(),
Arrays.asList( compositeType.getPropertyNames() )
)
);
}
return Arrays.copyOfRange(
baseResolver.toSqlSelectFragments( alias ),
selectFragmentRangeStart,
selectFragmentRangeEnd
);
}
}

View File

@ -9,6 +9,7 @@ import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.collection.QueryableCollection; import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.type.CompositeType; import org.hibernate.type.CompositeType;
/** /**
@ -21,7 +22,7 @@ public class CompositeElementGraph extends AbstractFetchOwner implements Fetchab
private final CollectionReference collectionReference; private final CollectionReference collectionReference;
private final PropertyPath propertyPath; private final PropertyPath propertyPath;
private final CollectionPersister collectionPersister; private final CollectionPersister collectionPersister;
private final FetchOwnerDelegate fetchOwnerDelegate; private final CompositeBasedSqlSelectFragmentResolver sqlSelectFragmentResolver;
/** /**
* Constructs a {@link CompositeElementGraph}. * Constructs a {@link CompositeElementGraph}.
@ -39,15 +40,16 @@ public class CompositeElementGraph extends AbstractFetchOwner implements Fetchab
this.collectionReference = collectionReference; this.collectionReference = collectionReference;
this.collectionPersister = collectionReference.getCollectionPersister(); this.collectionPersister = collectionReference.getCollectionPersister();
this.propertyPath = collectionPath.append( "<elements>" ); this.propertyPath = collectionPath.append( "<elements>" );
this.fetchOwnerDelegate = new CompositeFetchOwnerDelegate( this.sqlSelectFragmentResolver = new CompositeBasedSqlSelectFragmentResolver(
sessionFactory, sessionFactory,
(CompositeType) collectionPersister.getElementType(), (CompositeType) collectionPersister.getElementType(),
new CompositeFetchOwnerDelegate.PropertyMappingDelegate() { new CompositeBasedSqlSelectFragmentResolver.BaseSqlSelectFragmentResolver() {
@Override @Override
public String[] toSqlSelectFragments(String alias) { public String[] toSqlSelectFragments(String alias) {
return ( (QueryableCollection) collectionPersister ).getElementColumnNames( alias ); return ( (QueryableCollection) collectionPersister ).getElementColumnNames( alias );
} }
} }
); );
} }
@ -56,7 +58,7 @@ public class CompositeElementGraph extends AbstractFetchOwner implements Fetchab
this.collectionReference = original.collectionReference; this.collectionReference = original.collectionReference;
this.collectionPersister = original.collectionPersister; this.collectionPersister = original.collectionPersister;
this.propertyPath = original.propertyPath; this.propertyPath = original.propertyPath;
this.fetchOwnerDelegate = original.fetchOwnerDelegate; this.sqlSelectFragmentResolver = original.sqlSelectFragmentResolver;
} }
@Override @Override
@ -65,7 +67,7 @@ public class CompositeElementGraph extends AbstractFetchOwner implements Fetchab
} }
@Override @Override
public void validateFetchPlan(FetchStrategy fetchStrategy) { public void validateFetchPlan(FetchStrategy fetchStrategy, AttributeDefinition attributeDefinition) {
} }
@Override @Override
@ -83,11 +85,6 @@ public class CompositeElementGraph extends AbstractFetchOwner implements Fetchab
return new CompositeElementGraph( this, copyContext ); return new CompositeElementGraph( this, copyContext );
} }
@Override
protected FetchOwnerDelegate getFetchOwnerDelegate() {
return fetchOwnerDelegate;
}
@Override @Override
public CollectionFetch buildCollectionFetch( public CollectionFetch buildCollectionFetch(
AssociationAttributeDefinition attributeDefinition, AssociationAttributeDefinition attributeDefinition,
@ -95,4 +92,9 @@ public class CompositeElementGraph extends AbstractFetchOwner implements Fetchab
LoadPlanBuildingContext loadPlanBuildingContext) { LoadPlanBuildingContext loadPlanBuildingContext) {
throw new HibernateException( "Collection composite element cannot define collections" ); throw new HibernateException( "Collection composite element cannot define collections" );
} }
@Override
public SqlSelectFragmentResolver toSqlSelectFragmentResolver() {
return sqlSelectFragmentResolver;
}
} }

View File

@ -25,12 +25,19 @@ package org.hibernate.loader.plan.spi;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import org.hibernate.LockMode;
import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.FetchTiming; import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.spi.ResultSetProcessingContext; import org.hibernate.loader.plan.exec.process.internal.Helper;
import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext;
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.type.CompositeType; import org.hibernate.type.CompositeType;
/** /**
@ -43,27 +50,28 @@ import org.hibernate.type.CompositeType;
public class CompositeFetch extends AbstractSingularAttributeFetch { public class CompositeFetch extends AbstractSingularAttributeFetch {
private static final FetchStrategy FETCH_PLAN = new FetchStrategy( FetchTiming.IMMEDIATE, FetchStyle.JOIN ); private static final FetchStrategy FETCH_PLAN = new FetchStrategy( FetchTiming.IMMEDIATE, FetchStyle.JOIN );
private final FetchOwnerDelegate delegate; private final CompositeBasedSqlSelectFragmentResolver sqlSelectFragmentResolver;
/** /**
* Constructs a {@link CompositeFetch} object. * Constructs a {@link CompositeFetch} object.
* *
* @param sessionFactory - the session factory. * @param sessionFactory - the session factory.
* @param owner - the fetch owner for this fetch. * @param owner - the fetch owner for this fetch.
* @param ownerProperty - the owner's property referring to this fetch. * @param fetchedAttribute - the owner's property referring to this fetch.
*/ */
public CompositeFetch( public CompositeFetch(
SessionFactoryImplementor sessionFactory, SessionFactoryImplementor sessionFactory,
final FetchOwner owner, final FetchOwner owner,
String ownerProperty) { final AttributeDefinition fetchedAttribute) {
super( sessionFactory, owner, ownerProperty, FETCH_PLAN ); super( sessionFactory, owner, fetchedAttribute, FETCH_PLAN );
this.delegate = new CompositeFetchOwnerDelegate(
this.sqlSelectFragmentResolver = new CompositeBasedSqlSelectFragmentResolver(
sessionFactory, sessionFactory,
(CompositeType) getOwner().getType( this ), (CompositeType) fetchedAttribute.getType(),
new CompositeFetchOwnerDelegate.PropertyMappingDelegate() { new CompositeBasedSqlSelectFragmentResolver.BaseSqlSelectFragmentResolver() {
@Override @Override
public String[] toSqlSelectFragments(String alias) { public String[] toSqlSelectFragments(String alias) {
return owner.toSqlSelectFragments( CompositeFetch.this, alias ); return owner.toSqlSelectFragmentResolver().toSqlSelectFragments( alias, fetchedAttribute );
} }
} }
); );
@ -71,12 +79,12 @@ public class CompositeFetch extends AbstractSingularAttributeFetch {
public CompositeFetch(CompositeFetch original, CopyContext copyContext, FetchOwner fetchOwnerCopy) { public CompositeFetch(CompositeFetch original, CopyContext copyContext, FetchOwner fetchOwnerCopy) {
super( original, copyContext, fetchOwnerCopy ); super( original, copyContext, fetchOwnerCopy );
this.delegate = original.getFetchOwnerDelegate(); this.sqlSelectFragmentResolver = original.sqlSelectFragmentResolver;
} }
@Override @Override
protected FetchOwnerDelegate getFetchOwnerDelegate() { public SqlSelectFragmentResolver toSqlSelectFragmentResolver() {
return delegate; return sqlSelectFragmentResolver;
} }
@Override @Override
@ -86,12 +94,26 @@ public class CompositeFetch extends AbstractSingularAttributeFetch {
@Override @Override
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException { public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
//To change body of implemented methods use File | Settings | File Templates. // anything to do?
} }
@Override @Override
public Object resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException { public Object resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
return null; //To change body of implemented methods use File | Settings | File Templates. // anything to do?
return null;
}
@Override
public void read(ResultSet resultSet, ResultSetProcessingContext context, Object owner) throws SQLException {
final EntityReference ownerEntityReference = Helper.INSTANCE.findOwnerEntityReference( this );
final ResultSetProcessingContext.EntityReferenceProcessingState entityReferenceProcessingState = context.getProcessingState(
ownerEntityReference
);
final EntityKey entityKey = entityReferenceProcessingState.getEntityKey();
final Object entity = context.resolveEntityKey( entityKey, Helper.INSTANCE.findOwnerEntityReference( (FetchOwner) ownerEntityReference ) );
for ( Fetch fetch : getFetches() ) {
fetch.read( resultSet, context, entity );
}
} }
@Override @Override
@ -101,4 +123,18 @@ public class CompositeFetch extends AbstractSingularAttributeFetch {
copyContext.getReturnGraphVisitationStrategy().finishingCompositeFetch( this ); copyContext.getReturnGraphVisitationStrategy().finishingCompositeFetch( this );
return copy; return copy;
} }
@Override
public CollectionFetch buildCollectionFetch(
AssociationAttributeDefinition attributeDefinition,
FetchStrategy fetchStrategy,
LoadPlanBuildingContext loadPlanBuildingContext) {
return new CollectionFetch(
loadPlanBuildingContext.getSessionFactory(),
LockMode.NONE, // todo : for now
this,
fetchStrategy,
attributeDefinition
);
}
} }

View File

@ -1,143 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan.spi;
import java.util.Arrays;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.persister.walking.spi.WalkingException;
import org.hibernate.type.CompositeType;
import org.hibernate.type.Type;
/**
* A delegate for a composite fetch owner to obtain details about an
* owned sub-attribute fetch.
*
* @author Gail Badner
* @author Steve Ebersole
*/
public class CompositeFetchOwnerDelegate extends AbstractFetchOwnerDelegate implements FetchOwnerDelegate {
private final SessionFactoryImplementor sessionFactory;
private final CompositeType compositeType;
private final PropertyMappingDelegate propertyMappingDelegate;
/**
* Constructs a CompositeFetchOwnerDelegate
*
* @param sessionFactory - the session factory.
* @param compositeType - the composite type.
* @param propertyMappingDelegate - delegate for handling property mapping
*/
public CompositeFetchOwnerDelegate(
SessionFactoryImplementor sessionFactory,
CompositeType compositeType,
PropertyMappingDelegate propertyMappingDelegate) {
this.sessionFactory = sessionFactory;
this.compositeType = compositeType;
this.propertyMappingDelegate = propertyMappingDelegate;
}
public static interface PropertyMappingDelegate {
public String[] toSqlSelectFragments(String alias);
}
@Override
protected FetchMetadata buildFetchMetadata(Fetch fetch) {
int subIndex = -1;
int selectFragmentRangeStart = 0;
int selectFragmentRangeEnd = -1;
for ( int i = 0; i < compositeType.getPropertyNames().length; i++ ) {
final Type type = compositeType.getSubtypes()[i];
final int typeColSpan = type.getColumnSpan( sessionFactory );
if ( compositeType.getPropertyNames()[ i ].equals( fetch.getOwnerPropertyName() ) ) {
// fount it!
subIndex = i;
selectFragmentRangeEnd = selectFragmentRangeStart + typeColSpan;
break;
}
selectFragmentRangeStart += typeColSpan;
}
if ( subIndex < 0 ) {
throw new WalkingException(
String.format(
"Owner property [%s] not found in composite properties [%s]",
fetch.getOwnerPropertyName(),
Arrays.asList( compositeType.getPropertyNames() )
)
);
}
return new FetchMetadataImpl(
compositeType,
subIndex,
propertyMappingDelegate,
selectFragmentRangeStart,
selectFragmentRangeEnd
);
// todo : we really need a PropertyMapping delegate which can encapsulate both the PropertyMapping and the path
}
private static class FetchMetadataImpl implements FetchMetadata {
private final CompositeType compositeType;
private final int index;
private final PropertyMappingDelegate propertyMappingDelegate;
private final int selectFragmentRangeStart;
private final int selectFragmentRangeEnd;
public FetchMetadataImpl(
CompositeType compositeType,
int index,
PropertyMappingDelegate propertyMappingDelegate,
int selectFragmentRangeStart,
int selectFragmentRangeEnd) {
this.compositeType = compositeType;
this.index = index;
this.propertyMappingDelegate = propertyMappingDelegate;
this.selectFragmentRangeStart = selectFragmentRangeStart;
this.selectFragmentRangeEnd = selectFragmentRangeEnd;
}
@Override
public boolean isNullable() {
return compositeType.getPropertyNullability()[ index ];
}
@Override
public Type getType() {
return compositeType.getSubtypes()[ index ];
}
@Override
public String[] toSqlSelectFragments(String alias) {
return Arrays.copyOfRange(
propertyMappingDelegate.toSqlSelectFragments( alias ),
selectFragmentRangeStart,
selectFragmentRangeEnd
);
}
}
}

View File

@ -9,6 +9,7 @@ import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.collection.QueryableCollection; import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.type.CompositeType; import org.hibernate.type.CompositeType;
/** /**
@ -21,7 +22,7 @@ public class CompositeIndexGraph extends AbstractFetchOwner implements Fetchable
private final CollectionReference collectionReference; private final CollectionReference collectionReference;
private final PropertyPath propertyPath; private final PropertyPath propertyPath;
private final CollectionPersister collectionPersister; private final CollectionPersister collectionPersister;
private final FetchOwnerDelegate fetchOwnerDelegate; private final CompositeBasedSqlSelectFragmentResolver sqlSelectFragmentResolver;
/** /**
* Constructs a {@link CompositeElementGraph}. * Constructs a {@link CompositeElementGraph}.
@ -38,10 +39,10 @@ public class CompositeIndexGraph extends AbstractFetchOwner implements Fetchable
this.collectionReference = collectionReference; this.collectionReference = collectionReference;
this.collectionPersister = collectionReference.getCollectionPersister(); this.collectionPersister = collectionReference.getCollectionPersister();
this.propertyPath = collectionPath.append( "<index>" ); this.propertyPath = collectionPath.append( "<index>" );
this.fetchOwnerDelegate = new CompositeFetchOwnerDelegate( this.sqlSelectFragmentResolver = new CompositeBasedSqlSelectFragmentResolver(
sessionFactory, sessionFactory,
(CompositeType) collectionPersister.getIndexType(), (CompositeType) collectionPersister.getIndexType(),
new CompositeFetchOwnerDelegate.PropertyMappingDelegate() { new CompositeBasedSqlSelectFragmentResolver.BaseSqlSelectFragmentResolver() {
@Override @Override
public String[] toSqlSelectFragments(String alias) { public String[] toSqlSelectFragments(String alias) {
return ( (QueryableCollection) collectionPersister ).getIndexColumnNames( alias ); return ( (QueryableCollection) collectionPersister ).getIndexColumnNames( alias );
@ -55,11 +56,11 @@ public class CompositeIndexGraph extends AbstractFetchOwner implements Fetchable
this.collectionReference = original.collectionReference; this.collectionReference = original.collectionReference;
this.collectionPersister = original.collectionPersister; this.collectionPersister = original.collectionPersister;
this.propertyPath = original.propertyPath; this.propertyPath = original.propertyPath;
this.fetchOwnerDelegate = original.fetchOwnerDelegate; this.sqlSelectFragmentResolver = original.sqlSelectFragmentResolver;
} }
@Override @Override
public void validateFetchPlan(FetchStrategy fetchStrategy) { public void validateFetchPlan(FetchStrategy fetchStrategy, AttributeDefinition attributeDefinition) {
} }
@Override @Override
@ -82,11 +83,6 @@ public class CompositeIndexGraph extends AbstractFetchOwner implements Fetchable
return new CompositeIndexGraph( this, copyContext ); return new CompositeIndexGraph( this, copyContext );
} }
@Override
protected FetchOwnerDelegate getFetchOwnerDelegate() {
return fetchOwnerDelegate;
}
@Override @Override
public CollectionFetch buildCollectionFetch( public CollectionFetch buildCollectionFetch(
AssociationAttributeDefinition attributeDefinition, AssociationAttributeDefinition attributeDefinition,
@ -95,4 +91,9 @@ public class CompositeIndexGraph extends AbstractFetchOwner implements Fetchable
throw new HibernateException( "Composite index cannot define collections" ); throw new HibernateException( "Composite index cannot define collections" );
} }
@Override
public SqlSelectFragmentResolver toSqlSelectFragmentResolver() {
return sqlSelectFragmentResolver;
}
} }

View File

@ -4,9 +4,14 @@ import org.hibernate.LockMode;
import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.PropertyPath; import org.hibernate.loader.PropertyPath;
import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.type.AssociationType; import org.hibernate.type.AssociationType;
import org.hibernate.type.EntityType;
/** /**
* Represents the {@link FetchOwner} for a collection element that is * Represents the {@link FetchOwner} for a collection element that is
@ -20,7 +25,7 @@ public class EntityElementGraph extends AbstractFetchOwner implements FetchableC
private final AssociationType elementType; private final AssociationType elementType;
private final EntityPersister elementPersister; private final EntityPersister elementPersister;
private final PropertyPath propertyPath; private final PropertyPath propertyPath;
private final FetchOwnerDelegate fetchOwnerDelegate; private final EntityPersisterBasedSqlSelectFragmentResolver sqlSelectFragmentResolver;
private IdentifierDescription identifierDescription; private IdentifierDescription identifierDescription;
@ -42,7 +47,7 @@ public class EntityElementGraph extends AbstractFetchOwner implements FetchableC
this.elementType = (AssociationType) collectionPersister.getElementType(); this.elementType = (AssociationType) collectionPersister.getElementType();
this.elementPersister = (EntityPersister) this.elementType.getAssociatedJoinable( sessionFactory() ); this.elementPersister = (EntityPersister) this.elementType.getAssociatedJoinable( sessionFactory() );
this.propertyPath = collectionPath; this.propertyPath = collectionPath;
this.fetchOwnerDelegate = new EntityFetchOwnerDelegate( elementPersister ); this.sqlSelectFragmentResolver = new EntityPersisterBasedSqlSelectFragmentResolver( (Queryable) elementPersister );
} }
public EntityElementGraph(EntityElementGraph original, CopyContext copyContext) { public EntityElementGraph(EntityElementGraph original, CopyContext copyContext) {
@ -53,7 +58,7 @@ public class EntityElementGraph extends AbstractFetchOwner implements FetchableC
this.elementType = original.elementType; this.elementType = original.elementType;
this.elementPersister = original.elementPersister; this.elementPersister = original.elementPersister;
this.propertyPath = original.propertyPath; this.propertyPath = original.propertyPath;
this.fetchOwnerDelegate = original.fetchOwnerDelegate; this.sqlSelectFragmentResolver = original.sqlSelectFragmentResolver;
} }
@Override @Override
@ -77,7 +82,7 @@ public class EntityElementGraph extends AbstractFetchOwner implements FetchableC
} }
@Override @Override
public void validateFetchPlan(FetchStrategy fetchStrategy) { public void validateFetchPlan(FetchStrategy fetchStrategy, AttributeDefinition attributeDefinition) {
} }
@Override @Override
@ -111,7 +116,65 @@ public class EntityElementGraph extends AbstractFetchOwner implements FetchableC
} }
@Override @Override
protected FetchOwnerDelegate getFetchOwnerDelegate() { public SqlSelectFragmentResolver toSqlSelectFragmentResolver() {
return fetchOwnerDelegate; return sqlSelectFragmentResolver;
}
@Override
public EntityFetch buildEntityFetch(
AssociationAttributeDefinition attributeDefinition,
FetchStrategy fetchStrategy,
LoadPlanBuildingContext loadPlanBuildingContext) {
final EntityType attributeType = (EntityType) attributeDefinition.getType();
final FetchOwner collectionOwner = CollectionFetch.class.isInstance( collectionReference )
? ( (CollectionFetch) collectionReference ).getOwner()
: null;
if ( collectionOwner != null ) {
// check for bi-directionality
final boolean sameType = attributeType.getAssociatedEntityName().equals(
collectionOwner.retrieveFetchSourcePersister().getEntityName()
);
if ( sameType ) {
// todo : check for columns too...
return new BidirectionalEntityElementGraphFetch(
sessionFactory(),
LockMode.READ,
this,
attributeDefinition,
fetchStrategy,
collectionOwner
);
}
}
return super.buildEntityFetch(
attributeDefinition,
fetchStrategy,
loadPlanBuildingContext
);
}
private class BidirectionalEntityElementGraphFetch extends EntityFetch implements BidirectionalEntityFetch {
private final FetchOwner collectionOwner;
public BidirectionalEntityElementGraphFetch(
SessionFactoryImplementor sessionFactory,
LockMode lockMode,
FetchOwner owner,
AttributeDefinition fetchedAttribute,
FetchStrategy fetchStrategy,
FetchOwner collectionOwner) {
super( sessionFactory, lockMode, owner, fetchedAttribute, fetchStrategy );
this.collectionOwner = collectionOwner;
}
@Override
public EntityReference getTargetEntityReference() {
return (EntityReference) collectionOwner;
}
} }
} }

View File

@ -29,13 +29,17 @@ import java.sql.SQLException;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.WrongClassException; import org.hibernate.WrongClassException;
import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.spi.ResultSetProcessingContext; import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.persister.walking.spi.WalkingException;
import org.hibernate.type.EntityType; import org.hibernate.type.EntityType;
import static org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext.EntityReferenceProcessingState;
/** /**
* Represents a {@link Fetch} for an entity association attribute as well as a * Represents a {@link Fetch} for an entity association attribute as well as a
* {@link FetchOwner} of the entity association sub-attribute fetches. * {@link FetchOwner} of the entity association sub-attribute fetches.
@ -43,35 +47,44 @@ import org.hibernate.type.EntityType;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class EntityFetch extends AbstractSingularAttributeFetch implements EntityReference, Fetch { public class EntityFetch extends AbstractSingularAttributeFetch implements EntityReference, Fetch {
private final EntityPersister persister; private final EntityPersister persister;
private final LockMode lockMode; private final EntityPersisterBasedSqlSelectFragmentResolver sqlSelectFragmentResolver;
private final FetchOwnerDelegate fetchOwnerDelegate;
private IdentifierDescription identifierDescription; private IdentifierDescription identifierDescription;
// todo : remove these
private final LockMode lockMode;
/** /**
* Constructs an {@link EntityFetch} object. * Constructs an {@link EntityFetch} object.
* *
* @param sessionFactory - the session factory. * @param sessionFactory - the session factory.
* @param lockMode - the lock mode. * @param lockMode - the lock mode.
* @param owner - the fetch owner for this fetch. * @param owner - the fetch owner for this fetch.
* @param ownerProperty - the owner's property referring to this fetch. * @param fetchedAttribute - the attribute being fetched
* @param fetchStrategy - the fetch strategy for this fetch. * @param fetchStrategy - the fetch strategy for this fetch.
*/ */
public EntityFetch( public EntityFetch(
SessionFactoryImplementor sessionFactory, SessionFactoryImplementor sessionFactory,
LockMode lockMode, LockMode lockMode,
FetchOwner owner, FetchOwner owner,
String ownerProperty, AttributeDefinition fetchedAttribute,
FetchStrategy fetchStrategy) { FetchStrategy fetchStrategy) {
super( sessionFactory, owner, ownerProperty, fetchStrategy ); super( sessionFactory, owner, fetchedAttribute, fetchStrategy );
this.persister = sessionFactory.getEntityPersister( getFetchedType().getAssociatedEntityName() );
if ( persister == null ) {
throw new WalkingException(
String.format(
"Unable to locate EntityPersister [%s] for fetch [%s]",
getFetchedType().getAssociatedEntityName(),
fetchedAttribute.getName()
)
);
}
this.sqlSelectFragmentResolver = new EntityPersisterBasedSqlSelectFragmentResolver( (Queryable) persister );
this.persister = sessionFactory.getEntityPersister(
getEntityType().getAssociatedEntityName()
);
this.lockMode = lockMode; this.lockMode = lockMode;
this.fetchOwnerDelegate = new EntityFetchOwnerDelegate( persister );
} }
/** /**
@ -83,16 +96,19 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit
protected EntityFetch(EntityFetch original, CopyContext copyContext, FetchOwner fetchOwnerCopy) { protected EntityFetch(EntityFetch original, CopyContext copyContext, FetchOwner fetchOwnerCopy) {
super( original, copyContext, fetchOwnerCopy ); super( original, copyContext, fetchOwnerCopy );
this.persister = original.persister; this.persister = original.persister;
this.sqlSelectFragmentResolver = original.sqlSelectFragmentResolver;
this.lockMode = original.lockMode; this.lockMode = original.lockMode;
this.fetchOwnerDelegate = original.fetchOwnerDelegate;
} }
/** @Override
* Returns the entity type for this fetch. public EntityType getFetchedType() {
* @return the entity type for this fetch. return (EntityType) super.getFetchedType();
*/ }
public final EntityType getEntityType() {
return (EntityType) getOwner().getType( this ); @Override
public String[] toSqlSelectFragments(String alias) {
return getOwner().toSqlSelectFragmentResolver().toSqlSelectFragments( alias, getFetchedAttribute() );
} }
@Override @Override
@ -105,6 +121,11 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit
return persister; return persister;
} }
@Override
public SqlSelectFragmentResolver toSqlSelectFragmentResolver() {
return sqlSelectFragmentResolver;
}
@Override @Override
public IdentifierDescription getIdentifierDescription() { public IdentifierDescription getIdentifierDescription() {
return identifierDescription; return identifierDescription;
@ -127,12 +148,6 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit
@Override @Override
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException { public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
EntityKey entityKey = context.getDictatedRootEntityKey();
if ( entityKey != null ) {
context.getIdentifierResolutionContext( this ).registerEntityKey( entityKey );
return;
}
identifierDescription.hydrate( resultSet, context ); identifierDescription.hydrate( resultSet, context );
for ( Fetch fetch : getFetches() ) { for ( Fetch fetch : getFetches() ) {
@ -142,23 +157,25 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit
@Override @Override
public EntityKey resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException { public EntityKey resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
final ResultSetProcessingContext.IdentifierResolutionContext identifierResolutionContext = context.getIdentifierResolutionContext( this ); final ResultSetProcessingContext.EntityReferenceProcessingState entityReferenceProcessingState = context.getProcessingState(
EntityKey entityKey = identifierResolutionContext.getEntityKey(); this
);
EntityKey entityKey = entityReferenceProcessingState.getEntityKey();
if ( entityKey == null ) { if ( entityKey == null ) {
entityKey = identifierDescription.resolve( resultSet, context ); entityKey = identifierDescription.resolve( resultSet, context );
if ( entityKey == null ) { if ( entityKey == null ) {
// register the non-existence (though only for one-to-one associations) // register the non-existence (though only for one-to-one associations)
if ( getEntityType().isOneToOne() ) { if ( getFetchedType().isOneToOne() ) {
// first, find our owner's entity-key... // first, find our owner's entity-key...
final EntityKey ownersEntityKey = context.getIdentifierResolutionContext( (EntityReference) getOwner() ).getEntityKey(); final EntityKey ownersEntityKey = context.getProcessingState( (EntityReference) getOwner() ).getEntityKey();
if ( ownersEntityKey != null ) { if ( ownersEntityKey != null ) {
context.getSession().getPersistenceContext() context.getSession().getPersistenceContext()
.addNullProperty( ownersEntityKey, getEntityType().getPropertyName() ); .addNullProperty( ownersEntityKey, getFetchedType().getPropertyName() );
} }
} }
} }
identifierResolutionContext.registerEntityKey( entityKey ); entityReferenceProcessingState.registerEntityKey( entityKey );
for ( Fetch fetch : getFetches() ) { for ( Fetch fetch : getFetches() ) {
fetch.resolve( resultSet, context ); fetch.resolve( resultSet, context );
@ -168,6 +185,19 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit
return entityKey; return entityKey;
} }
@Override
public void read(ResultSet resultSet, ResultSetProcessingContext context, Object owner) throws SQLException {
final EntityReferenceProcessingState entityReferenceProcessingState = context.getProcessingState( this );
final EntityKey entityKey = entityReferenceProcessingState.getEntityKey();
if ( entityKey == null ) {
return;
}
final Object entity = context.resolveEntityKey( entityKey, this );
for ( Fetch fetch : getFetches() ) {
fetch.read( resultSet, context, entity );
}
}
/** /**
* Resolve any fetches required to resolve the identifier as well * Resolve any fetches required to resolve the identifier as well
* as the entity key for this fetch.. * as the entity key for this fetch..
@ -205,7 +235,7 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit
context.checkVersion( context.checkVersion(
resultSet, resultSet,
persister, persister,
context.getLoadQueryAliasResolutionContext().resolveEntityColumnAliases( this ), context.getAliasResolutionContext().resolveAliases( this ).getColumnAliases(),
entityKey, entityKey,
existing existing
); );
@ -218,7 +248,7 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit
final String concreteEntityTypeName = context.getConcreteEntityTypeName( final String concreteEntityTypeName = context.getConcreteEntityTypeName(
resultSet, resultSet,
persister, persister,
context.getLoadQueryAliasResolutionContext().resolveEntityColumnAliases( this ), context.getAliasResolutionContext().resolveAliases( this ).getColumnAliases(),
entityKey entityKey
); );
@ -239,15 +269,16 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit
entityInstance, entityInstance,
concreteEntityTypeName, concreteEntityTypeName,
entityKey, entityKey,
context.getLoadQueryAliasResolutionContext().resolveEntityColumnAliases( this ), context.getAliasResolutionContext().resolveAliases( this ).getColumnAliases(),
acquiredLockMode, acquiredLockMode,
persister, persister,
getFetchStrategy().getTiming() == FetchTiming.IMMEDIATE, getFetchStrategy(),
getEntityType() true,
getFetchedType()
); );
// materialize associations (and initialize the object) later // materialize associations (and initialize the object) later
context.registerHydratedEntity( persister, entityKey, entityInstance ); context.registerHydratedEntity( this, entityKey, entityInstance );
} }
return entityKey; return entityKey;
@ -265,9 +296,4 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit
copyContext.getReturnGraphVisitationStrategy().finishingEntityFetch( this ); copyContext.getReturnGraphVisitationStrategy().finishingEntityFetch( this );
return copy; return copy;
} }
@Override
protected FetchOwnerDelegate getFetchOwnerDelegate() {
return fetchOwnerDelegate;
}
} }

View File

@ -1,201 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan.spi;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.persister.walking.spi.WalkingException;
import org.hibernate.type.CompositeType;
import org.hibernate.type.Type;
/**
* A delegate for an entity fetch owner to obtain details about
* an owned attribute fetch.
*
* @author Gail Badner
* @author Steve Ebersole
*/
public class EntityFetchOwnerDelegate extends AbstractFetchOwnerDelegate implements FetchOwnerDelegate {
private final EntityPersister entityPersister;
/**
* Constructs an {@link EntityFetchOwnerDelegate}.
*
* @param entityPersister - the entity persister.
*/
public EntityFetchOwnerDelegate(EntityPersister entityPersister) {
this.entityPersister = entityPersister;
}
@Override
protected FetchMetadata buildFetchMetadata(Fetch fetch) {
final Integer propertyIndex = entityPersister.getEntityMetamodel().getPropertyIndexOrNull(
fetch.getOwnerPropertyName()
);
if ( propertyIndex == null ) {
// possibly it is part of the identifier; but that's only possible if the identifier is composite
final Type idType = entityPersister.getIdentifierType();
if ( CompositeType.class.isInstance( idType ) ) {
final CompositeType cidType = (CompositeType) idType;
if ( entityPersister.hasIdentifierProperty() ) {
// encapsulated composite id case; this *should have* been handled as part of building the fetch...
throw new WalkingException(
"Expecting different FetchOwnerDelegate type for encapsulated composite id case"
);
}
else {
// non-encapsulated composite id case...
return new NonEncapsulatedIdentifierAttributeFetchMetadata(
entityPersister,
cidType,
fetch.getOwnerPropertyName()
);
}
}
}
else {
return new NonIdentifierAttributeFetchMetadata(
entityPersister,
fetch.getOwnerPropertyName(),
propertyIndex.intValue()
);
}
throw new WalkingException(
"Could not locate metadata about given fetch [" + fetch + "] in its owning persister"
);
}
private class NonIdentifierAttributeFetchMetadata implements FetchMetadata {
private final EntityPersister entityPersister;
private final String attributeName;
private final int propertyIndex;
private Type attributeType;
public NonIdentifierAttributeFetchMetadata(
EntityPersister entityPersister,
String attributeName,
int propertyIndex) {
this.entityPersister = entityPersister;
this.attributeName = attributeName;
this.propertyIndex = propertyIndex;
}
@Override
public boolean isNullable() {
return entityPersister.getPropertyNullability()[ propertyIndex ];
}
@Override
public Type getType() {
if ( attributeType == null ) {
attributeType = entityPersister.getPropertyTypes()[ propertyIndex ];
}
return attributeType;
}
@Override
public String[] toSqlSelectFragments(String alias) {
// final Type type = getType();
// final OuterJoinLoadable outerJoinLoadable = (OuterJoinLoadable) entityPersister;
final Queryable queryable = (Queryable) entityPersister;
return queryable.toColumns( alias, attributeName );
// if ( type.isAssociationType() ) {
// return JoinHelper.getLHSColumnNames(
// (AssociationType) type,
// propertyIndex,
// outerJoinLoadable,
// outerJoinLoadable.getFactory()
// );
// }
// else {
// return outerJoinLoadable.getPropertyColumnNames( propertyIndex );
// }
}
}
private class NonEncapsulatedIdentifierAttributeFetchMetadata implements FetchMetadata {
private final EntityPersister entityPersister;
private final String attributeName;
// virtually final fields
private Type type;
private int selectFragmentRangeStart;
private int selectFragmentRangeEnd;
public NonEncapsulatedIdentifierAttributeFetchMetadata(
EntityPersister entityPersister,
CompositeType cidType,
String attributeName) {
this.entityPersister = entityPersister;
this.attributeName = attributeName;
this.selectFragmentRangeStart = 0;
Type subType;
boolean foundIt = false;
for ( int i = 0; i < cidType.getPropertyNames().length; i++ ) {
subType = cidType.getSubtypes()[i];
if ( cidType.getPropertyNames()[i].equals( attributeName ) ) {
// found it!
foundIt = true;
this.type = subType;
break;
}
selectFragmentRangeStart += subType.getColumnSpan( entityPersister.getFactory() );
}
if ( !foundIt ) {
throw new WalkingException( "Could not find " );
}
selectFragmentRangeEnd = selectFragmentRangeStart + type.getColumnSpan( entityPersister.getFactory() );
}
@Override
public boolean isNullable() {
return false;
}
@Override
public Type getType() {
return type;
}
@Override
public String[] toSqlSelectFragments(String alias) {
return Arrays.copyOfRange(
( (Queryable) entityPersister ).toColumns( alias, attributeName ),
selectFragmentRangeStart,
selectFragmentRangeEnd
);
}
}
}

View File

@ -29,6 +29,8 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.PropertyPath; import org.hibernate.loader.PropertyPath;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.type.AssociationType; import org.hibernate.type.AssociationType;
/** /**
@ -42,7 +44,7 @@ public class EntityIndexGraph extends AbstractFetchOwner implements FetchableCol
private final AssociationType indexType; private final AssociationType indexType;
private final EntityPersister indexPersister; private final EntityPersister indexPersister;
private final PropertyPath propertyPath; private final PropertyPath propertyPath;
private final FetchOwnerDelegate fetchOwnerDelegate; private final EntityPersisterBasedSqlSelectFragmentResolver sqlSelectFragmentResolver;
private IdentifierDescription identifierDescription; private IdentifierDescription identifierDescription;
@ -63,7 +65,7 @@ public class EntityIndexGraph extends AbstractFetchOwner implements FetchableCol
this.indexType = (AssociationType) collectionPersister.getIndexType(); this.indexType = (AssociationType) collectionPersister.getIndexType();
this.indexPersister = (EntityPersister) this.indexType.getAssociatedJoinable( sessionFactory() ); this.indexPersister = (EntityPersister) this.indexType.getAssociatedJoinable( sessionFactory() );
this.propertyPath = collectionPath.append( "<index>" ); // todo : do we want the <index> part? this.propertyPath = collectionPath.append( "<index>" ); // todo : do we want the <index> part?
this.fetchOwnerDelegate = new EntityFetchOwnerDelegate( indexPersister ); this.sqlSelectFragmentResolver = new EntityPersisterBasedSqlSelectFragmentResolver( (Queryable) indexPersister );
} }
public EntityIndexGraph(EntityIndexGraph original, CopyContext copyContext) { public EntityIndexGraph(EntityIndexGraph original, CopyContext copyContext) {
@ -73,7 +75,7 @@ public class EntityIndexGraph extends AbstractFetchOwner implements FetchableCol
this.indexType = original.indexType; this.indexType = original.indexType;
this.indexPersister = original.indexPersister; this.indexPersister = original.indexPersister;
this.propertyPath = original.propertyPath; this.propertyPath = original.propertyPath;
this.fetchOwnerDelegate = original.fetchOwnerDelegate; this.sqlSelectFragmentResolver = original.sqlSelectFragmentResolver;
} }
/** /**
@ -100,7 +102,7 @@ public class EntityIndexGraph extends AbstractFetchOwner implements FetchableCol
} }
@Override @Override
public void validateFetchPlan(FetchStrategy fetchStrategy) { public void validateFetchPlan(FetchStrategy fetchStrategy, AttributeDefinition attributeDefinition) {
} }
@Override @Override
@ -129,7 +131,7 @@ public class EntityIndexGraph extends AbstractFetchOwner implements FetchableCol
} }
@Override @Override
protected FetchOwnerDelegate getFetchOwnerDelegate() { public SqlSelectFragmentResolver toSqlSelectFragmentResolver() {
return fetchOwnerDelegate; return sqlSelectFragmentResolver;
} }
} }

View File

@ -23,29 +23,22 @@
*/ */
package org.hibernate.loader.plan.spi; package org.hibernate.loader.plan.spi;
import java.util.HashMap; import org.hibernate.persister.entity.Queryable;
import java.util.Map; import org.hibernate.persister.walking.spi.AttributeDefinition;
/** /**
* Base implementation of FetchOwnerDelegate providing caching of FetchMetadata.
*
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public abstract class AbstractFetchOwnerDelegate implements FetchOwnerDelegate { public class EntityPersisterBasedSqlSelectFragmentResolver implements SqlSelectFragmentResolver {
private Map<String,FetchMetadata> fetchMetadataMap; private final Queryable entityPersister;
@Override public EntityPersisterBasedSqlSelectFragmentResolver(Queryable entityPersister) {
public FetchMetadata locateFetchMetadata(Fetch fetch) { this.entityPersister = entityPersister;
FetchMetadata metadata = fetchMetadataMap == null ? null : fetchMetadataMap.get( fetch.getOwnerPropertyName() ); }
if ( metadata == null ) {
if ( fetchMetadataMap == null ) { @Override
fetchMetadataMap = new HashMap<String, FetchMetadata>(); public String[] toSqlSelectFragments(String alias, AttributeDefinition attributeDefinition) {
} return entityPersister.toColumns( alias, attributeDefinition.getName() );
metadata = buildFetchMetadata( fetch );
fetchMetadataMap.put( fetch.getOwnerPropertyName(), metadata );
}
return metadata;
} }
protected abstract FetchMetadata buildFetchMetadata(Fetch fetch);
} }

View File

@ -24,7 +24,7 @@
package org.hibernate.loader.plan.spi; package org.hibernate.loader.plan.spi;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.loader.spi.ResultSetProcessingContext; import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
/** /**

View File

@ -23,19 +23,13 @@
*/ */
package org.hibernate.loader.plan.spi; package org.hibernate.loader.plan.spi;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.hibernate.AssertionFailure;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.PropertyPath; import org.hibernate.loader.PropertyPath;
import org.hibernate.loader.spi.ResultSetProcessingContext;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Queryable;
import static org.hibernate.loader.spi.ResultSetProcessingContext.IdentifierResolutionContext; import org.hibernate.persister.walking.spi.AttributeDefinition;
/** /**
* Represents an entity return value in the query results. Not the same * Represents an entity return value in the query results. Not the same
@ -46,17 +40,14 @@ import static org.hibernate.loader.spi.ResultSetProcessingContext.IdentifierReso
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class EntityReturn extends AbstractFetchOwner implements Return, EntityReference, CopyableReturn { public class EntityReturn extends AbstractFetchOwner implements Return, EntityReference, CopyableReturn {
private final EntityPersister persister; private final EntityPersister persister;
private final EntityPersisterBasedSqlSelectFragmentResolver sqlSelectFragmentResolver;
private IdentifierDescription identifierDescription;
private final PropertyPath propertyPath = new PropertyPath(); // it's a root private final PropertyPath propertyPath;
private final LockMode lockMode; private final LockMode lockMode;
private final FetchOwnerDelegate fetchOwnerDelegate;
private IdentifierDescription identifierDescription;
/** /**
* Construct an {@link EntityReturn}. * Construct an {@link EntityReturn}.
* *
@ -70,15 +61,19 @@ public class EntityReturn extends AbstractFetchOwner implements Return, EntityRe
String entityName) { String entityName) {
super( sessionFactory ); super( sessionFactory );
this.persister = sessionFactory.getEntityPersister( entityName ); this.persister = sessionFactory.getEntityPersister( entityName );
this.propertyPath = new PropertyPath( entityName );
this.sqlSelectFragmentResolver = new EntityPersisterBasedSqlSelectFragmentResolver( (Queryable) persister );
this.lockMode = lockMode; this.lockMode = lockMode;
this.fetchOwnerDelegate = new EntityFetchOwnerDelegate( persister );
} }
protected EntityReturn(EntityReturn original, CopyContext copyContext) { protected EntityReturn(EntityReturn original, CopyContext copyContext) {
super( original, copyContext ); super( original, copyContext );
this.persister = original.persister; this.persister = original.persister;
this.propertyPath = original.propertyPath;
this.sqlSelectFragmentResolver = original.sqlSelectFragmentResolver;
this.lockMode = original.lockMode; this.lockMode = original.lockMode;
this.fetchOwnerDelegate = original.fetchOwnerDelegate;
} }
@Override @Override
@ -102,7 +97,7 @@ public class EntityReturn extends AbstractFetchOwner implements Return, EntityRe
} }
@Override @Override
public void validateFetchPlan(FetchStrategy fetchStrategy) { public void validateFetchPlan(FetchStrategy fetchStrategy, AttributeDefinition attributeDefinition) {
} }
@Override @Override
@ -115,67 +110,6 @@ public class EntityReturn extends AbstractFetchOwner implements Return, EntityRe
return propertyPath; return propertyPath;
} }
@Override
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
EntityKey entityKey = getEntityKeyFromContext( context );
if ( entityKey != null ) {
context.getIdentifierResolutionContext( this ).registerEntityKey( entityKey );
return;
}
identifierDescription.hydrate( resultSet, context );
for ( Fetch fetch : getFetches() ) {
fetch.hydrate( resultSet, context );
}
}
private EntityKey getEntityKeyFromContext(ResultSetProcessingContext context) {
if ( context.getDictatedRootEntityKey() != null ) {
return context.getDictatedRootEntityKey();
}
else if ( context.getQueryParameters().getOptionalId() != null ) {
return context.getSession().generateEntityKey(
context.getQueryParameters().getOptionalId(),
getEntityPersister()
);
}
return null;
}
@Override
public void resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
final IdentifierResolutionContext identifierResolutionContext = context.getIdentifierResolutionContext( this );
EntityKey entityKey = identifierResolutionContext.getEntityKey();
if ( entityKey != null ) {
return;
}
entityKey = identifierDescription.resolve( resultSet, context );
identifierResolutionContext.registerEntityKey( entityKey );
for ( Fetch fetch : getFetches() ) {
fetch.resolve( resultSet, context );
}
}
@Override
public Object read(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
Object objectForThisEntityReturn = null;
for ( IdentifierResolutionContext identifierResolutionContext : context.getIdentifierResolutionContexts() ) {
final EntityReference entityReference = identifierResolutionContext.getEntityReference();
final EntityKey entityKey = identifierResolutionContext.getEntityKey();
if ( entityKey == null ) {
throw new AssertionFailure( "Could not locate resolved EntityKey");
}
final Object object = context.resolveEntityKey( entityKey, entityReference );
if ( this == entityReference ) {
objectForThisEntityReturn = object;
}
}
return objectForThisEntityReturn;
}
@Override @Override
public void injectIdentifierDescription(IdentifierDescription identifierDescription) { public void injectIdentifierDescription(IdentifierDescription identifierDescription) {
this.identifierDescription = identifierDescription; this.identifierDescription = identifierDescription;
@ -192,7 +126,7 @@ public class EntityReturn extends AbstractFetchOwner implements Return, EntityRe
} }
@Override @Override
protected FetchOwnerDelegate getFetchOwnerDelegate() { public SqlSelectFragmentResolver toSqlSelectFragmentResolver() {
return fetchOwnerDelegate; return sqlSelectFragmentResolver;
} }
} }

View File

@ -28,7 +28,8 @@ import java.sql.SQLException;
import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchStrategy;
import org.hibernate.loader.PropertyPath; import org.hibernate.loader.PropertyPath;
import org.hibernate.loader.spi.ResultSetProcessingContext; import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext;
import org.hibernate.type.Type;
/** /**
* Contract for associations that are being fetched. * Contract for associations that are being fetched.
@ -46,25 +47,13 @@ public interface Fetch extends CopyableFetch {
public FetchOwner getOwner(); public FetchOwner getOwner();
/** /**
* Obtain the name of the property, relative to the owner, being fetched. * Get the property path to this fetch
* *
* @return The fetched property name. * @return The property path
*/ */
public String getOwnerPropertyName(); public PropertyPath getPropertyPath();
/** public Type getFetchedType();
* Is this fetch nullable?
*
* @return true, if this fetch is nullable; false, otherwise.
*/
public boolean isNullable();
/**
* Generates the SQL select fragments for this fetch. A select fragment is the column and formula references.
*
* @return the select fragments
*/
public String[] toSqlSelectFragments(String alias);
/** /**
* Gets the fetch strategy for this fetch. * Gets the fetch strategy for this fetch.
@ -74,16 +63,27 @@ public interface Fetch extends CopyableFetch {
public FetchStrategy getFetchStrategy(); public FetchStrategy getFetchStrategy();
/** /**
* Get the property path to this fetch * Is this fetch nullable?
* *
* @return The property path * @return true, if this fetch is nullable; false, otherwise.
*/ */
public PropertyPath getPropertyPath(); public boolean isNullable();
public String getAdditionalJoinConditions();
/**
* Generates the SQL select fragments for this fetch. A select fragment is the column and formula references.
*
* @return the select fragments
*/
public String[] toSqlSelectFragments(String alias);
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException; public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException;
public Object resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException; public Object resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException;
public void read(ResultSet resultSet, ResultSetProcessingContext context, Object owner) throws SQLException;
@Override @Override
public Fetch makeCopy(CopyContext copyContext, FetchOwner fetchOwnerCopy); public Fetch makeCopy(CopyContext copyContext, FetchOwner fetchOwnerCopy);
} }

View File

@ -25,9 +25,13 @@ package org.hibernate.loader.plan.spi;
import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchStrategy;
import org.hibernate.loader.PropertyPath; import org.hibernate.loader.PropertyPath;
import org.hibernate.loader.plan.spi.build.AbstractLoadPlanBuilderStrategy;
import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext; import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.PropertyMapping;
import org.hibernate.persister.walking.spi.AnyMappingDefinition;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.persister.walking.spi.CompositionDefinition; import org.hibernate.persister.walking.spi.CompositionDefinition;
import org.hibernate.type.Type; import org.hibernate.type.Type;
@ -89,9 +93,10 @@ public interface FetchOwner {
/** /**
* Is the asserted plan valid from this owner to a fetch? * Is the asserted plan valid from this owner to a fetch?
* *
* @param fetchStrategy The pla to validate * @param fetchStrategy The type of fetch to validate
* @param attributeDefinition The attribute to be fetched
*/ */
public void validateFetchPlan(FetchStrategy fetchStrategy); public void validateFetchPlan(FetchStrategy fetchStrategy, AttributeDefinition attributeDefinition);
/** /**
* Retrieve the EntityPersister that is the base for any property references in the fetches it owns. * Retrieve the EntityPersister that is the base for any property references in the fetches it owns.
@ -121,5 +126,11 @@ public interface FetchOwner {
CompositionDefinition attributeDefinition, CompositionDefinition attributeDefinition,
LoadPlanBuildingContext loadPlanBuildingContext); LoadPlanBuildingContext loadPlanBuildingContext);
public AnyFetch buildAnyFetch(
AttributeDefinition attribute,
AnyMappingDefinition anyDefinition,
FetchStrategy fetchStrategy,
LoadPlanBuildingContext loadPlanBuildingContext);
public SqlSelectFragmentResolver toSqlSelectFragmentResolver();
} }

View File

@ -1,69 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan.spi;
import org.hibernate.type.Type;
/**
* This interface provides a delegate for a fetch owner to obtain details about an owned fetch.
*
* @author Gail Badner
*/
public interface FetchOwnerDelegate {
public static interface FetchMetadata {
/**
* Is the fetch nullable?
*
* @return true, if the fetch is nullable; false, otherwise.
*/
public boolean isNullable();
/**
* Returns the type of the fetched attribute
*
* @return the type of the fetched attribute.
*/
public Type getType();
/**
* Generates the SQL select fragments for the specified fetch. A select fragment is the column and formula
* references.
*
* @param alias The table alias to apply to the fragments (used to qualify column references)
*
* @return the select fragments
*/
public String[] toSqlSelectFragments(String alias);
}
/**
* Locate the metadata for the specified Fetch. Allows easier caching of the resolved information.
*
* @param fetch The fetch for which to locate metadata
*
* @return The metadata; never {@code null}, rather an exception is thrown if the information for the fetch cannot
* be located.
*/
public FetchMetadata locateFetchMetadata(Fetch fetch);
}

View File

@ -27,7 +27,8 @@ import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.EntityKey;
import org.hibernate.loader.spi.ResultSetProcessingContext; import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext;
import org.hibernate.persister.spi.HydratedCompoundValueHandler;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
@ -38,4 +39,6 @@ public interface IdentifierDescription {
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException; public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException;
public EntityKey resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException; public EntityKey resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException;
HydratedCompoundValueHandler getHydratedStateHandler(Fetch fetch);
} }

View File

@ -0,0 +1,62 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan.spi;
import org.hibernate.LockMode;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.persister.walking.spi.AttributeDefinition;
/**
* We can use the special type as a trigger in AliasResolutionContext, etc to lookup information based on
* the wrapped reference. E.g.
*
* @author Steve Ebersole
*/
public class KeyManyToOneBidirectionalEntityFetch extends EntityFetch implements BidirectionalEntityFetch {
private final EntityReference targetEntityReference;
public KeyManyToOneBidirectionalEntityFetch(
SessionFactoryImplementor sessionFactory,
LockMode lockMode,
FetchOwner owner,
AttributeDefinition fetchedAttribute,
EntityReference targetEntityReference,
FetchStrategy fetchStrategy) {
super( sessionFactory, lockMode, owner, fetchedAttribute, fetchStrategy );
this.targetEntityReference = targetEntityReference;
}
public KeyManyToOneBidirectionalEntityFetch(
KeyManyToOneBidirectionalEntityFetch original,
CopyContext copyContext,
FetchOwner fetchOwnerCopy) {
super( original, copyContext, fetchOwnerCopy );
this.targetEntityReference = original.targetEntityReference;
}
public EntityReference getTargetEntityReference() {
return targetEntityReference;
}
}

View File

@ -46,6 +46,26 @@ import java.util.List;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface LoadPlan { public interface LoadPlan {
public List<? extends Return> getReturns();
public Disposition getDisposition();
/**
* Does this load plan indicate that lazy attributes are to be force fetched?
* <p/>
* Here we are talking about laziness in regards to the legacy bytecode enhancement which adds support for
* partial selects of an entity's state (e.g., skip loading a lob initially, wait until/if it is needed)
* <p/>
* This one would effect the SQL that needs to get generated as well as how the result set would be read.
* Therefore we make this part of the LoadPlan contract.
* <p/>
* NOTE that currently this is only relevant for HQL loaders when the HQL has specified the {@code FETCH ALL PROPERTIES}
* key-phrase. In all other cases, this returns false.
* @return Whether or not to
*/
public boolean areLazyAttributesForceFetched();
/** /**
* Convenient form of checking {@link #getReturns()} for scalar root returns. * Convenient form of checking {@link #getReturns()} for scalar root returns.
* *
@ -53,7 +73,26 @@ public interface LoadPlan {
*/ */
public boolean hasAnyScalarReturns(); public boolean hasAnyScalarReturns();
public List<Return> getReturns(); /**
* Enumerated possibilities for describing the disposition of this LoadPlan.
*/
public static enum Disposition {
/**
* This is an "entity loader" load plan, which describes a plan for loading one or more entity instances of
* the same entity type. There is a single return, which will be of type {@link EntityReturn}
*/
ENTITY_LOADER,
/**
* This is a "collection initializer" load plan, which describes a plan for loading one or more entity instances of
* the same collection type. There is a single return, which will be of type {@link CollectionReturn}
*/
COLLECTION_INITIALIZER,
/**
* We have a mixed load plan, which will have one or more returns of {@link EntityReturn} and {@link ScalarReturn}
* (NOT {@link CollectionReturn}).
*/
MIXED
}
// todo : would also like to see "call back" style access for handling "subsequent actions" such as: // todo : would also like to see "call back" style access for handling "subsequent actions" such as:
// 1) follow-on locking // 1) follow-on locking

View File

@ -23,11 +23,6 @@
*/ */
package org.hibernate.loader.plan.spi; package org.hibernate.loader.plan.spi;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.hibernate.loader.spi.ResultSetProcessingContext;
/** /**
* Represents a return value in the query results. Not the same as a result (column) in the JDBC ResultSet! * Represents a return value in the query results. Not the same as a result (column) in the JDBC ResultSet!
* <p/> * <p/>
@ -40,28 +35,4 @@ import org.hibernate.loader.spi.ResultSetProcessingContext;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface Return { public interface Return {
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException;
/**
* Effectively performs first phase of two-phase loading. For scalar results first/second phase is one. For
* entities, first phase is to resolve identifiers; second phase is to resolve the entity instances.
*
* @param resultSet The result set being processed
* @param context The context for the processing
*
* @throws SQLException Indicates a problem access the JDBC result set
*/
public void resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException;
/**
* Essentially performs the second phase of two-phase loading.
*
* @param resultSet The result set being processed
* @param context The context for the processing
*
* @return The read object
*
* @throws SQLException Indicates a problem access the JDBC result set
*/
public Object read(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException;
} }

View File

@ -23,16 +23,15 @@
*/ */
package org.hibernate.loader.plan.spi; package org.hibernate.loader.plan.spi;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.spi.ResultSetProcessingContext;
import org.hibernate.type.Type; import org.hibernate.type.Type;
/** /**
* Represent a simple scalar return within a query result. Generally this would be values of basic (String, Integer, * Represent a simple scalar return within a query result. Generally this would be values of basic (String, Integer,
* etc) or composite types. * etc) or composite types.
* <p/>
* todo : we should link the Returns back to their "source"
* aka the entity/collection/etc that defines the qualifier used to qualify this Return's columns
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
@ -47,23 +46,4 @@ public class ScalarReturn extends AbstractPlanNode implements Return {
public Type getType() { public Type getType() {
return type; return type;
} }
@Override
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) {
// nothing to do
}
@Override
public void resolve(ResultSet resultSet, ResultSetProcessingContext context) {
// nothing to do
}
@Override
public Object read(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
return type.nullSafeGet(
resultSet,
context.getLoadQueryAliasResolutionContext().resolveScalarReturnAliases( this ),
context.getSession(),
null );
}
} }

View File

@ -0,0 +1,32 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan.spi;
/**
* Marker interface for LoadPlan nodes that can be qualifiable in the source queries.
*
* @author Steve Ebersole
*/
public class SourceQualifiable {
}

View File

@ -0,0 +1,40 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan.spi;
import org.hibernate.persister.walking.spi.AttributeDefinition;
/**
* Delegate for resolving the "SQL select fragments" pertaining to an attribute.
* <p/>
* Most implementations will delegate to the {@link org.hibernate.persister.entity.PropertyMapping} portion
* of the EntityPersister contract via the "optional" {@link org.hibernate.persister.entity.Queryable} contract.
*
* @author Steve Ebersole
*
* @see org.hibernate.persister.entity.PropertyMapping
*/
public interface SqlSelectFragmentResolver {
public String[] toSqlSelectFragments(String alias, AttributeDefinition attributeDefinition);
}

View File

@ -37,31 +37,34 @@ import org.hibernate.HibernateException;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.cfg.NotYetImplementedException; import org.hibernate.cfg.NotYetImplementedException;
import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchTiming; import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.loader.PropertyPath; import org.hibernate.loader.PropertyPath;
import org.hibernate.loader.plan.spi.AbstractFetchOwner; import org.hibernate.loader.plan.spi.AbstractFetchOwner;
import org.hibernate.loader.plan.spi.AbstractFetchOwnerDelegate; import org.hibernate.loader.plan.spi.AnyFetch;
import org.hibernate.loader.plan.spi.CollectionFetch; import org.hibernate.loader.plan.spi.CollectionFetch;
import org.hibernate.loader.plan.spi.CollectionReference; import org.hibernate.loader.plan.spi.CollectionReference;
import org.hibernate.loader.plan.spi.CollectionReturn; import org.hibernate.loader.plan.spi.CollectionReturn;
import org.hibernate.loader.plan.spi.CompositeElementGraph; import org.hibernate.loader.plan.spi.CompositeElementGraph;
import org.hibernate.loader.plan.spi.CompositeFetch; import org.hibernate.loader.plan.spi.CompositeFetch;
import org.hibernate.loader.plan.spi.CompositeFetchOwnerDelegate;
import org.hibernate.loader.plan.spi.EntityFetch; import org.hibernate.loader.plan.spi.EntityFetch;
import org.hibernate.loader.plan.spi.EntityPersisterBasedSqlSelectFragmentResolver;
import org.hibernate.loader.plan.spi.EntityReference; import org.hibernate.loader.plan.spi.EntityReference;
import org.hibernate.loader.plan.spi.EntityReturn; import org.hibernate.loader.plan.spi.EntityReturn;
import org.hibernate.loader.plan.spi.Fetch; import org.hibernate.loader.plan.spi.Fetch;
import org.hibernate.loader.plan.spi.FetchOwner; import org.hibernate.loader.plan.spi.FetchOwner;
import org.hibernate.loader.plan.spi.FetchOwnerDelegate;
import org.hibernate.loader.plan.spi.IdentifierDescription; import org.hibernate.loader.plan.spi.IdentifierDescription;
import org.hibernate.loader.plan.spi.KeyManyToOneBidirectionalEntityFetch;
import org.hibernate.loader.plan.spi.Return; import org.hibernate.loader.plan.spi.Return;
import org.hibernate.loader.spi.ResultSetProcessingContext; import org.hibernate.loader.plan.spi.SqlSelectFragmentResolver;
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Loadable; import org.hibernate.persister.entity.Queryable;
import org.hibernate.persister.spi.HydratedCompoundValueHandler; import org.hibernate.persister.spi.HydratedCompoundValueHandler;
import org.hibernate.persister.walking.internal.FetchStrategyHelper;
import org.hibernate.persister.walking.spi.AnyMappingDefinition;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.AttributeDefinition; import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.persister.walking.spi.CollectionDefinition; import org.hibernate.persister.walking.spi.CollectionDefinition;
@ -72,11 +75,9 @@ import org.hibernate.persister.walking.spi.CompositionDefinition;
import org.hibernate.persister.walking.spi.EntityDefinition; import org.hibernate.persister.walking.spi.EntityDefinition;
import org.hibernate.persister.walking.spi.EntityIdentifierDefinition; import org.hibernate.persister.walking.spi.EntityIdentifierDefinition;
import org.hibernate.persister.walking.spi.WalkingException; import org.hibernate.persister.walking.spi.WalkingException;
import org.hibernate.type.CompositeType; import org.hibernate.type.EntityType;
import org.hibernate.type.Type; import org.hibernate.type.Type;
import static org.hibernate.loader.spi.ResultSetProcessingContext.IdentifierResolutionContext;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
*/ */
@ -313,12 +314,10 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
@Override @Override
public void startingCompositeCollectionElement(CompositeCollectionElementDefinition compositeElementDefinition) { public void startingCompositeCollectionElement(CompositeCollectionElementDefinition compositeElementDefinition) {
System.out.println( log.tracef(
String.format( "%s Starting composite collection element for (%s)",
"%s Starting composite collection element for (%s)", StringHelper.repeat( ">>", fetchOwnerStack.size() ),
StringHelper.repeat( ">>", fetchOwnerStack.size() ), compositeElementDefinition.getCollectionDefinition().getCollectionPersister().getRole()
compositeElementDefinition.getCollectionDefinition().getCollectionPersister().getRole()
)
); );
} }
@ -397,16 +396,17 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
final Type attributeType = attributeDefinition.getType(); final Type attributeType = attributeDefinition.getType();
final boolean isComponentType = attributeType.isComponentType(); final boolean isComponentType = attributeType.isComponentType();
final boolean isBasicType = ! ( isComponentType || attributeType.isAssociationType() ); final boolean isAssociationType = attributeType.isAssociationType();
final boolean isBasicType = ! ( isComponentType || isAssociationType );
if ( isBasicType ) { if ( isBasicType ) {
return true; return true;
} }
else if ( isComponentType ) { else if ( isAssociationType ) {
return handleCompositeAttribute( (CompositionDefinition) attributeDefinition ); return handleAssociationAttribute( (AssociationAttributeDefinition) attributeDefinition );
} }
else { else {
return handleAssociationAttribute( (AssociationAttributeDefinition) attributeDefinition ); return handleCompositeAttribute( (CompositionDefinition) attributeDefinition );
} }
} }
@ -419,6 +419,24 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
); );
} }
@Override
public void foundAny(AssociationAttributeDefinition attributeDefinition, AnyMappingDefinition anyDefinition) {
// for ANY mappings we need to build a Fetch:
// 1) fetch type is SELECT, timing might be IMMEDIATE or DELAYED depending on whether it was defined as lazy
// 2) (because the fetch cannot be a JOIN...) do not push it to the stack
final FetchStrategy fetchStrategy = determineFetchPlan( attributeDefinition );
final FetchOwner fetchOwner = currentFetchOwner();
fetchOwner.validateFetchPlan( fetchStrategy, attributeDefinition );
fetchOwner.buildAnyFetch(
attributeDefinition,
anyDefinition,
fetchStrategy,
this
);
}
protected boolean handleCompositeAttribute(CompositionDefinition attributeDefinition) { protected boolean handleCompositeAttribute(CompositionDefinition attributeDefinition) {
final FetchOwner fetchOwner = currentFetchOwner(); final FetchOwner fetchOwner = currentFetchOwner();
final CompositeFetch fetch = fetchOwner.buildCompositeFetch( attributeDefinition, this ); final CompositeFetch fetch = fetchOwner.buildCompositeFetch( attributeDefinition, this );
@ -427,26 +445,35 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
} }
protected boolean handleAssociationAttribute(AssociationAttributeDefinition attributeDefinition) { protected boolean handleAssociationAttribute(AssociationAttributeDefinition attributeDefinition) {
// todo : this seems to not be correct for one-to-one
final FetchStrategy fetchStrategy = determineFetchPlan( attributeDefinition ); final FetchStrategy fetchStrategy = determineFetchPlan( attributeDefinition );
if ( fetchStrategy.getTiming() != FetchTiming.IMMEDIATE ) { if ( fetchStrategy.getStyle() != FetchStyle.JOIN ) {
return false; return false;
} }
// if ( fetchStrategy.getTiming() != FetchTiming.IMMEDIATE ) {
// return false;
// }
final FetchOwner fetchOwner = currentFetchOwner(); final FetchOwner fetchOwner = currentFetchOwner();
fetchOwner.validateFetchPlan( fetchStrategy ); fetchOwner.validateFetchPlan( fetchStrategy, attributeDefinition );
final Fetch associationFetch; final Fetch associationFetch;
if ( attributeDefinition.isCollection() ) { final AssociationAttributeDefinition.AssociationNature nature = attributeDefinition.getAssociationNature();
associationFetch = fetchOwner.buildCollectionFetch( attributeDefinition, fetchStrategy, this ); if ( nature == AssociationAttributeDefinition.AssociationNature.ANY ) {
pushToCollectionStack( (CollectionReference) associationFetch ); return false;
// throw new NotYetImplementedException( "AnyType support still in progress" );
} }
else { else if ( nature == AssociationAttributeDefinition.AssociationNature.ENTITY ) {
associationFetch = fetchOwner.buildEntityFetch( associationFetch = fetchOwner.buildEntityFetch(
attributeDefinition, attributeDefinition,
fetchStrategy, fetchStrategy,
this this
); );
} }
else {
associationFetch = fetchOwner.buildCollectionFetch( attributeDefinition, fetchStrategy, this );
pushToCollectionStack( (CollectionReference) associationFetch );
}
if ( FetchOwner.class.isInstance( associationFetch) ) { if ( FetchOwner.class.isInstance( associationFetch) ) {
pushToStack( (FetchOwner) associationFetch ); pushToStack( (FetchOwner) associationFetch );
@ -521,12 +548,16 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
protected static abstract class AbstractIdentifierAttributeCollector extends AbstractFetchOwner protected static abstract class AbstractIdentifierAttributeCollector extends AbstractFetchOwner
implements FetchOwner, EntityReference, FetchStackAware { implements FetchOwner, EntityReference, FetchStackAware {
protected final EntityReference entityReference; protected final EntityReference entityReference;
private final EntityPersisterBasedSqlSelectFragmentResolver sqlSelectFragmentResolver;
protected final Map<Fetch,HydratedCompoundValueHandler> fetchToHydratedStateExtractorMap protected final Map<Fetch,HydratedCompoundValueHandler> fetchToHydratedStateExtractorMap
= new HashMap<Fetch, HydratedCompoundValueHandler>(); = new HashMap<Fetch, HydratedCompoundValueHandler>();
public AbstractIdentifierAttributeCollector(SessionFactoryImplementor sessionFactory, EntityReference entityReference) { public AbstractIdentifierAttributeCollector(SessionFactoryImplementor sessionFactory, EntityReference entityReference) {
super( sessionFactory ); super( sessionFactory );
this.entityReference = entityReference; this.entityReference = entityReference;
this.sqlSelectFragmentResolver = new EntityPersisterBasedSqlSelectFragmentResolver(
(Queryable) entityReference.getEntityPersister()
);
} }
@Override @Override
@ -557,6 +588,15 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
throw new WalkingException( "Entity identifier cannot contain persistent collections" ); throw new WalkingException( "Entity identifier cannot contain persistent collections" );
} }
@Override
public AnyFetch buildAnyFetch(
AttributeDefinition attribute,
AnyMappingDefinition anyDefinition,
FetchStrategy fetchStrategy,
LoadPlanBuildingContext loadPlanBuildingContext) {
throw new WalkingException( "Entity identifier cannot contain ANY type mappings" );
}
@Override @Override
public EntityFetch buildEntityFetch( public EntityFetch buildEntityFetch(
AssociationAttributeDefinition attributeDefinition, AssociationAttributeDefinition attributeDefinition,
@ -566,25 +606,75 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
// //
// IMPL NOTE: we pass ourselves as the FetchOwner which will route the fetch back through our #addFetch // IMPL NOTE: we pass ourselves as the FetchOwner which will route the fetch back through our #addFetch
// impl. We collect them there and later build the IdentifierDescription // impl. We collect them there and later build the IdentifierDescription
// if `this` is a fetch and its owner is "the same" (bi-directionality) as the attribute to be join fetched
// we should wrap our FetchOwner as an EntityFetch. That should solve everything except for the alias
// context lookups because of the different instances (because of wrapping). So somehow the consumer of this
// needs to be able to unwrap it to do the alias lookup, and would have to know to do that.
//
//
// we are processing the EntityReference(Address) identifier. we come across its key-many-to-one reference
// to Person. Now, if EntityReference(Address) is an instance of EntityFetch(Address) there is a strong
// likelihood that we have a bi-directionality and need to handle that specially.
//
// how to best (a) find the bi-directionality and (b) represent that?
if ( EntityFetch.class.isInstance( entityReference ) ) {
// we just confirmed that EntityReference(Address) is an instance of EntityFetch(Address),
final EntityFetch entityFetch = (EntityFetch) entityReference;
final FetchOwner entityFetchOwner = entityFetch.getOwner();
// so at this point we need to see if entityFetchOwner and attributeDefinition refer to the
// "same thing". "same thing" == "same type" && "same column(s)"?
//
// i make assumptions here that that the attribute type is the EntityType, is that always valid?
final EntityType attributeDefinitionTypeAsEntityType = (EntityType) attributeDefinition.getType();
final boolean sameType = attributeDefinitionTypeAsEntityType.getAssociatedEntityName().equals(
entityFetchOwner.retrieveFetchSourcePersister().getEntityName()
);
if ( sameType ) {
// check same columns as well?
return new KeyManyToOneBidirectionalEntityFetch(
sessionFactory(),
//ugh
LockMode.READ,
this,
attributeDefinition,
(EntityReference) entityFetchOwner,
fetchStrategy
);
}
}
final EntityFetch fetch = super.buildEntityFetch( attributeDefinition, fetchStrategy, loadPlanBuildingContext ); final EntityFetch fetch = super.buildEntityFetch( attributeDefinition, fetchStrategy, loadPlanBuildingContext );
// pretty sure this HydratedCompoundValueExtractor stuff is not needed...
fetchToHydratedStateExtractorMap.put( fetch, attributeDefinition.getHydratedCompoundValueExtractor() ); fetchToHydratedStateExtractorMap.put( fetch, attributeDefinition.getHydratedCompoundValueExtractor() );
return fetch; return fetch;
} }
@Override @Override
public Type getType(Fetch fetch) { public Type getType(Fetch fetch) {
return getFetchOwnerDelegate().locateFetchMetadata( fetch ).getType(); return fetch.getFetchedType();
} }
@Override @Override
public boolean isNullable(Fetch fetch) { public boolean isNullable(Fetch fetch) {
return getFetchOwnerDelegate().locateFetchMetadata( fetch ).isNullable(); return fetch.isNullable();
} }
@Override @Override
public String[] toSqlSelectFragments(Fetch fetch, String alias) { public String[] toSqlSelectFragments(Fetch fetch, String alias) {
return getFetchOwnerDelegate().locateFetchMetadata( fetch ).toSqlSelectFragments( alias ); return fetch.toSqlSelectFragments( alias );
}
@Override
public SqlSelectFragmentResolver toSqlSelectFragmentResolver() {
return sqlSelectFragmentResolver;
} }
@Override @Override
@ -596,8 +686,8 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
protected abstract IdentifierDescription buildIdentifierDescription(); protected abstract IdentifierDescription buildIdentifierDescription();
@Override @Override
public void validateFetchPlan(FetchStrategy fetchStrategy) { public void validateFetchPlan(FetchStrategy fetchStrategy, AttributeDefinition attributeDefinition) {
( (FetchOwner) entityReference ).validateFetchPlan( fetchStrategy ); ( (FetchOwner) entityReference ).validateFetchPlan( fetchStrategy, attributeDefinition );
} }
@Override @Override
@ -615,51 +705,12 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
protected static class EncapsulatedIdentifierAttributeCollector extends AbstractIdentifierAttributeCollector { protected static class EncapsulatedIdentifierAttributeCollector extends AbstractIdentifierAttributeCollector {
private final PropertyPath propertyPath; private final PropertyPath propertyPath;
private final FetchOwnerDelegate delegate;
public EncapsulatedIdentifierAttributeCollector( public EncapsulatedIdentifierAttributeCollector(
final SessionFactoryImplementor sessionFactory, final SessionFactoryImplementor sessionFactory,
final EntityReference entityReference) { final EntityReference entityReference) {
super( sessionFactory, entityReference ); super( sessionFactory, entityReference );
this.propertyPath = ( (FetchOwner) entityReference ).getPropertyPath(); this.propertyPath = ( (FetchOwner) entityReference ).getPropertyPath();
this.delegate = new AbstractFetchOwnerDelegate() {
final boolean isCompositeType = entityReference.getEntityPersister().getIdentifierType().isComponentType();
@Override
protected FetchMetadata buildFetchMetadata(Fetch fetch) {
if ( !isCompositeType ) {
throw new WalkingException( "Non-composite identifier cannot be a fetch owner" );
}
if ( !fetch.getOwnerPropertyName().equals( entityReference.getEntityPersister().getIdentifierPropertyName() ) ) {
throw new IllegalArgumentException(
String.format(
"Fetch owner property name [%s] is not the same as the identifier prop" +
fetch.getOwnerPropertyName(),
entityReference.getEntityPersister().getIdentifierPropertyName()
)
);
}
return new FetchMetadata() {
@Override
public boolean isNullable() {
return false;
}
@Override
public Type getType() {
return entityReference.getEntityPersister().getIdentifierType();
}
@Override
public String[] toSqlSelectFragments(String alias) {
// should not ever be called iiuc...
throw new WalkingException( "Should not be called" );
}
};
}
};
} }
@Override @Override
@ -671,11 +722,6 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
); );
} }
@Override
protected FetchOwnerDelegate getFetchOwnerDelegate() {
return delegate;
}
@Override @Override
public PropertyPath getPropertyPath() { public PropertyPath getPropertyPath() {
return propertyPath; return propertyPath;
@ -684,65 +730,12 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
protected static class NonEncapsulatedIdentifierAttributeCollector extends AbstractIdentifierAttributeCollector { protected static class NonEncapsulatedIdentifierAttributeCollector extends AbstractIdentifierAttributeCollector {
private final PropertyPath propertyPath; private final PropertyPath propertyPath;
private final FetchOwnerDelegate fetchOwnerDelegate;
public NonEncapsulatedIdentifierAttributeCollector( public NonEncapsulatedIdentifierAttributeCollector(
final SessionFactoryImplementor sessionfactory, final SessionFactoryImplementor sessionfactory,
final EntityReference entityReference) { final EntityReference entityReference) {
super( sessionfactory, entityReference ); super( sessionfactory, entityReference );
this.propertyPath = ( (FetchOwner) entityReference ).getPropertyPath().append( "<id>" ); this.propertyPath = ( (FetchOwner) entityReference ).getPropertyPath().append( "<id>" );
this.fetchOwnerDelegate = new AbstractFetchOwnerDelegate() {
final boolean isCompositeType = entityReference.getEntityPersister().getIdentifierType().isComponentType();
final CompositeType idType = (CompositeType) entityReference.getEntityPersister().getIdentifierType();
@Override
protected FetchMetadata buildFetchMetadata(Fetch fetch) {
if ( !isCompositeType ) {
throw new WalkingException( "Non-composite identifier cannot be a fetch owner" );
}
final int subPropertyIndex = locateSubPropertyIndex( idType, fetch.getOwnerPropertyName() );
return new FetchMetadata() {
final Type subType = idType.getSubtypes()[ subPropertyIndex ];
@Override
public boolean isNullable() {
return false;
}
@Override
public Type getType() {
return subType;
}
@Override
public String[] toSqlSelectFragments(String alias) {
// should not ever be called iiuc...
throw new WalkingException( "Should not be called" );
}
};
}
private int locateSubPropertyIndex(CompositeType idType, String ownerPropertyName) {
for ( int i = 0; i < idType.getPropertyNames().length; i++ ) {
if ( ownerPropertyName.equals( idType.getPropertyNames()[i] ) ) {
return i;
}
}
// does not bode well if we get here...
throw new IllegalStateException(
String.format(
"Unable to locate fetched attribute [%s] as part of composite identifier [%s]",
ownerPropertyName,
getPropertyPath().getFullPath()
)
);
}
};
} }
@Override @Override
@ -758,13 +751,6 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
public PropertyPath getPropertyPath() { public PropertyPath getPropertyPath() {
return propertyPath; return propertyPath;
} }
@Override
protected FetchOwnerDelegate getFetchOwnerDelegate() {
return fetchOwnerDelegate;
}
} }
private static class IdentifierDescriptionImpl implements IdentifierDescription { private static class IdentifierDescriptionImpl implements IdentifierDescription {
@ -786,26 +772,31 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
return identifierFetches; return identifierFetches;
} }
@Override
public HydratedCompoundValueHandler getHydratedStateHandler(Fetch fetch) {
return fetchToHydratedStateExtractorMap == null ? null : fetchToHydratedStateExtractorMap.get( fetch );
}
@Override @Override
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException { public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
final IdentifierResolutionContext ownerIdentifierResolutionContext = final ResultSetProcessingContext.EntityReferenceProcessingState ownerEntityReferenceProcessingState =
context.getIdentifierResolutionContext( entityReference ); context.getProcessingState( entityReference );
final Object ownerIdentifierHydratedState = ownerIdentifierResolutionContext.getHydratedForm(); final Object ownerIdentifierHydratedState = ownerEntityReferenceProcessingState.getIdentifierHydratedForm();
if ( ownerIdentifierHydratedState != null ) { if ( ownerIdentifierHydratedState != null ) {
for ( Fetch fetch : identifierFetches ) { for ( Fetch fetch : identifierFetches ) {
if ( fetch instanceof EntityFetch ) { if ( fetch instanceof EntityFetch ) {
final IdentifierResolutionContext identifierResolutionContext = final ResultSetProcessingContext.EntityReferenceProcessingState fetchEntityReferenceProcessingState =
context.getIdentifierResolutionContext( (EntityFetch) fetch ); context.getProcessingState( (EntityFetch) fetch );
// if the identifier was already hydrated, nothing to do // if the identifier was already hydrated, nothing to do
if ( identifierResolutionContext.getHydratedForm() != null ) { if ( fetchEntityReferenceProcessingState.getIdentifierHydratedForm() != null ) {
continue; continue;
} }
// try to extract the sub-hydrated value from the owners tuple array // try to extract the sub-hydrated value from the owners tuple array
if ( fetchToHydratedStateExtractorMap != null && ownerIdentifierHydratedState != null ) { if ( fetchToHydratedStateExtractorMap != null && ownerIdentifierHydratedState != null ) {
Serializable extracted = (Serializable) fetchToHydratedStateExtractorMap.get( fetch ) Serializable extracted = (Serializable) fetchToHydratedStateExtractorMap.get( fetch )
.extract( ownerIdentifierHydratedState ); .extract( ownerIdentifierHydratedState );
identifierResolutionContext.registerHydratedForm( extracted ); fetchEntityReferenceProcessingState.registerIdentifierHydratedForm( extracted );
continue; continue;
} }
@ -819,13 +810,40 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
return; return;
} }
final String[] columnNames;
if ( EntityFetch.class.isInstance( entityReference )
&& !FetchStrategyHelper.isJoinFetched( ((EntityFetch) entityReference).getFetchStrategy() ) ) {
final EntityFetch fetch = (EntityFetch) entityReference;
final FetchOwner fetchOwner = fetch.getOwner();
if ( EntityReference.class.isInstance( fetchOwner ) ) {
throw new NotYetImplementedException();
// final EntityReference ownerEntityReference = (EntityReference) fetchOwner;
// final EntityAliases ownerEntityAliases = context.getAliasResolutionContext()
// .resolveEntityColumnAliases( ownerEntityReference );
// final int propertyIndex = ownerEntityReference.getEntityPersister()
// .getEntityMetamodel()
// .getPropertyIndex( fetch.getOwnerPropertyName() );
// columnNames = ownerEntityAliases.getSuffixedPropertyAliases()[ propertyIndex ];
}
else {
// todo : better message here...
throw new WalkingException( "Cannot locate association column names" );
}
}
else {
columnNames = context.getAliasResolutionContext()
.resolveAliases( entityReference )
.getColumnAliases()
.getSuffixedKeyAliases();
}
final Object hydratedIdentifierState = entityReference.getEntityPersister().getIdentifierType().hydrate( final Object hydratedIdentifierState = entityReference.getEntityPersister().getIdentifierType().hydrate(
resultSet, resultSet,
context.getLoadQueryAliasResolutionContext().resolveEntityColumnAliases( entityReference ).getSuffixedKeyAliases(), columnNames,
context.getSession(), context.getSession(),
null null
); );
context.getIdentifierResolutionContext( entityReference ).registerHydratedForm( hydratedIdentifierState ); context.getProcessingState( entityReference ).registerIdentifierHydratedForm( hydratedIdentifierState );
} }
@Override @Override
@ -834,9 +852,9 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
resolveIdentifierFetch( resultSet, context, fetch ); resolveIdentifierFetch( resultSet, context, fetch );
} }
final IdentifierResolutionContext ownerIdentifierResolutionContext = final ResultSetProcessingContext.EntityReferenceProcessingState ownerEntityReferenceProcessingState =
context.getIdentifierResolutionContext( entityReference ); context.getProcessingState( entityReference );
Object hydratedState = ownerIdentifierResolutionContext.getHydratedForm(); Object hydratedState = ownerEntityReferenceProcessingState.getIdentifierHydratedForm();
Serializable resolvedId = (Serializable) entityReference.getEntityPersister() Serializable resolvedId = (Serializable) entityReference.getEntityPersister()
.getIdentifierType() .getIdentifierType()
.resolve( hydratedState, context.getSession(), null ); .resolve( hydratedState, context.getSession(), null );
@ -850,14 +868,14 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
Fetch fetch) throws SQLException { Fetch fetch) throws SQLException {
if ( fetch instanceof EntityFetch ) { if ( fetch instanceof EntityFetch ) {
EntityFetch entityFetch = (EntityFetch) fetch; EntityFetch entityFetch = (EntityFetch) fetch;
final IdentifierResolutionContext identifierResolutionContext = final ResultSetProcessingContext.EntityReferenceProcessingState entityReferenceProcessingState =
context.getIdentifierResolutionContext( entityFetch ); context.getProcessingState( entityFetch );
if ( identifierResolutionContext.getEntityKey() != null ) { if ( entityReferenceProcessingState.getEntityKey() != null ) {
return; return;
} }
EntityKey fetchKey = entityFetch.resolveInIdentifier( resultSet, context ); EntityKey fetchKey = entityFetch.resolveInIdentifier( resultSet, context );
identifierResolutionContext.registerEntityKey( fetchKey ); entityReferenceProcessingState.registerEntityKey( fetchKey );
} }
else if ( fetch instanceof CompositeFetch ) { else if ( fetch instanceof CompositeFetch ) {
for ( Fetch subFetch : ( (CompositeFetch) fetch ).getFetches() ) { for ( Fetch subFetch : ( (CompositeFetch) fetch ).getFetches() ) {

View File

@ -29,7 +29,8 @@ import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.walking.spi.MetadataDrivenModelGraphVisitor; import org.hibernate.persister.walking.spi.MetadataDrivenModelGraphVisitor;
/** /**
* Coordinates building of a {@link org.hibernate.loader.plan.spi.LoadPlan} between the {@link org.hibernate.persister.walking.spi.MetadataDrivenModelGraphVisitor} and * Coordinates building of a {@link org.hibernate.loader.plan.spi.LoadPlan} between the
* {@link org.hibernate.persister.walking.spi.MetadataDrivenModelGraphVisitor} and
* {@link LoadPlanBuilderStrategy} * {@link LoadPlanBuilderStrategy}
* *
* @author Steve Ebersole * @author Steve Ebersole

View File

@ -27,7 +27,8 @@ import org.hibernate.loader.plan.spi.LoadPlan;
import org.hibernate.persister.walking.spi.AssociationVisitationStrategy; import org.hibernate.persister.walking.spi.AssociationVisitationStrategy;
/** /**
* Specialized {@link org.hibernate.persister.walking.spi.AssociationVisitationStrategy} implementation for building {@link org.hibernate.loader.plan.spi.LoadPlan} instances. * Specialized {@link org.hibernate.persister.walking.spi.AssociationVisitationStrategy} implementation for
* building {@link org.hibernate.loader.plan.spi.LoadPlan} instances.
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */

View File

@ -26,8 +26,15 @@ package org.hibernate.loader.plan.spi.build;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
/** /**
* Provides access to context needed in building a LoadPlan.
*
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface LoadPlanBuildingContext { public interface LoadPlanBuildingContext {
/**
* Access to the SessionFactory
*
* @return The SessionFactory
*/
public SessionFactoryImplementor getSessionFactory(); public SessionFactoryImplementor getSessionFactory();
} }

View File

@ -23,6 +23,7 @@
*/ */
package org.hibernate.loader.plan.spi.visit; package org.hibernate.loader.plan.spi.visit;
import org.hibernate.loader.plan.spi.AnyFetch;
import org.hibernate.loader.plan.spi.CollectionFetch; import org.hibernate.loader.plan.spi.CollectionFetch;
import org.hibernate.loader.plan.spi.CollectionReturn; import org.hibernate.loader.plan.spi.CollectionReturn;
import org.hibernate.loader.plan.spi.CompositeFetch; import org.hibernate.loader.plan.spi.CompositeFetch;
@ -115,4 +116,14 @@ public class DelegatedLoadPlanVisitationStrategy implements LoadPlanVisitationSt
public void finishingCompositeFetch(CompositeFetch fetch) { public void finishingCompositeFetch(CompositeFetch fetch) {
returnGraphVisitationStrategy.finishingCompositeFetch( fetch ); returnGraphVisitationStrategy.finishingCompositeFetch( fetch );
} }
@Override
public void startingAnyFetch(AnyFetch fetch) {
returnGraphVisitationStrategy.startingAnyFetch( fetch );
}
@Override
public void finishingAnyFetch(AnyFetch fetch) {
returnGraphVisitationStrategy.finishingAnyFetch( fetch );
}
} }

View File

@ -23,6 +23,7 @@
*/ */
package org.hibernate.loader.plan.spi.visit; package org.hibernate.loader.plan.spi.visit;
import org.hibernate.loader.plan.spi.AnyFetch;
import org.hibernate.loader.plan.spi.CollectionFetch; import org.hibernate.loader.plan.spi.CollectionFetch;
import org.hibernate.loader.plan.spi.CollectionReturn; import org.hibernate.loader.plan.spi.CollectionReturn;
import org.hibernate.loader.plan.spi.CompositeFetch; import org.hibernate.loader.plan.spi.CompositeFetch;
@ -98,4 +99,12 @@ public class LoadPlanVisitationStrategyAdapter implements LoadPlanVisitationStra
@Override @Override
public void finishingCompositeFetch(CompositeFetch fetch) { public void finishingCompositeFetch(CompositeFetch fetch) {
} }
@Override
public void startingAnyFetch(AnyFetch fetch) {
}
@Override
public void finishingAnyFetch(AnyFetch fetch) {
}
} }

View File

@ -23,6 +23,7 @@
*/ */
package org.hibernate.loader.plan.spi.visit; package org.hibernate.loader.plan.spi.visit;
import org.hibernate.loader.plan.spi.AnyFetch;
import org.hibernate.loader.plan.spi.CollectionFetch; import org.hibernate.loader.plan.spi.CollectionFetch;
import org.hibernate.loader.plan.spi.CollectionReturn; import org.hibernate.loader.plan.spi.CollectionReturn;
import org.hibernate.loader.plan.spi.CompositeFetch; import org.hibernate.loader.plan.spi.CompositeFetch;
@ -136,4 +137,18 @@ public interface ReturnGraphVisitationStrategy {
* @param fetch The composite fetch * @param fetch The composite fetch
*/ */
public void finishingCompositeFetch(CompositeFetch fetch); public void finishingCompositeFetch(CompositeFetch fetch);
/**
* Notification we are starting the processing of a ANY fetch
*
* @param fetch The ANY fetch
*/
public void startingAnyFetch(AnyFetch fetch);
/**
* Notification that we are finishing up the processing of a ANY fetch
*
* @param fetch The ANY fetch
*/
public void finishingAnyFetch(AnyFetch fetch);
} }

View File

@ -23,6 +23,7 @@
*/ */
package org.hibernate.loader.plan.spi.visit; package org.hibernate.loader.plan.spi.visit;
import org.hibernate.loader.plan.spi.AnyFetch;
import org.hibernate.loader.plan.spi.CollectionFetch; import org.hibernate.loader.plan.spi.CollectionFetch;
import org.hibernate.loader.plan.spi.CollectionReturn; import org.hibernate.loader.plan.spi.CollectionReturn;
import org.hibernate.loader.plan.spi.CompositeFetch; import org.hibernate.loader.plan.spi.CompositeFetch;
@ -89,4 +90,12 @@ public class ReturnGraphVisitationStrategyAdapter implements ReturnGraphVisitati
@Override @Override
public void finishingCompositeFetch(CompositeFetch fetch) { public void finishingCompositeFetch(CompositeFetch fetch) {
} }
@Override
public void startingAnyFetch(AnyFetch fetch) {
}
@Override
public void finishingAnyFetch(AnyFetch fetch) {
}
} }

View File

@ -51,7 +51,7 @@ public class ReturnGraphVisitor {
} }
} }
public void visit(List<Return> rootReturns) { public void visit(List<? extends Return> rootReturns) {
for ( Return rootReturn : rootReturns ) { for ( Return rootReturn : rootReturns ) {
visitRootReturn( rootReturn ); visitRootReturn( rootReturn );
} }

View File

@ -26,7 +26,7 @@ package org.hibernate.loader.spi;
import org.hibernate.loader.plan.spi.LoadPlan; import org.hibernate.loader.plan.spi.LoadPlan;
/** /**
* An advisor that can be made available to the {@link ResultSetProcessor} and {@link ScrollableResultSetProcessor}. * An advisor that can be made available to the {@link org.hibernate.loader.plan.exec.process.spi.ResultSetProcessor} and {@link org.hibernate.loader.plan.exec.process.spi.ScrollableResultSetProcessor}.
* *
* The processors consult with the advisor, if one is provided, as a means to influence the load plan, meaning that * The processors consult with the advisor, if one is provided, as a means to influence the load plan, meaning that
* the advisor might add fetches. A caveat is that any added fetches cannot be join fetches (they cannot alter the * the advisor might add fetches. A caveat is that any added fetches cannot be join fetches (they cannot alter the

View File

@ -1,103 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.spi;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Set;
import org.hibernate.LockMode;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.loader.EntityAliases;
import org.hibernate.loader.plan.spi.EntityReference;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.type.EntityType;
/**
* @author Steve Ebersole
*/
public interface ResultSetProcessingContext {
public SessionImplementor getSession();
public QueryParameters getQueryParameters();
public EntityKey getDictatedRootEntityKey();
public static interface IdentifierResolutionContext {
public EntityReference getEntityReference();
public void registerHydratedForm(Object hydratedForm);
public Object getHydratedForm();
public void registerEntityKey(EntityKey entityKey);
public EntityKey getEntityKey();
}
public IdentifierResolutionContext getIdentifierResolutionContext(EntityReference entityReference);
public Set<IdentifierResolutionContext> getIdentifierResolutionContexts();
public LoadQueryAliasResolutionContext getLoadQueryAliasResolutionContext();
public void registerHydratedEntity(EntityPersister persister, EntityKey entityKey, Object entityInstance);
public static interface EntityKeyResolutionContext {
public EntityPersister getEntityPersister();
public LockMode getLockMode();
public EntityReference getEntityReference();
}
public Object resolveEntityKey(EntityKey entityKey, EntityKeyResolutionContext entityKeyContext);
// should be able to get rid of the methods below here from the interface ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public void checkVersion(
ResultSet resultSet,
EntityPersister persister,
EntityAliases entityAliases,
EntityKey entityKey,
Object entityInstance) throws SQLException;
public String getConcreteEntityTypeName(
ResultSet resultSet,
EntityPersister persister,
EntityAliases entityAliases,
EntityKey entityKey) throws SQLException;
public void loadFromResultSet(
ResultSet resultSet,
Object entityInstance,
String concreteEntityTypeName,
EntityKey entityKey,
EntityAliases entityAliases,
LockMode acquiredLockMode,
EntityPersister persister,
boolean eagerFetch,
EntityType associationType) throws SQLException;
}

View File

@ -81,6 +81,8 @@ import org.hibernate.persister.entity.Loadable;
import org.hibernate.persister.entity.PropertyMapping; import org.hibernate.persister.entity.PropertyMapping;
import org.hibernate.persister.entity.Queryable; import org.hibernate.persister.entity.Queryable;
import org.hibernate.persister.walking.internal.CompositionSingularSubAttributesHelper; import org.hibernate.persister.walking.internal.CompositionSingularSubAttributesHelper;
import org.hibernate.persister.walking.internal.StandardAnyTypeDefinition;
import org.hibernate.persister.walking.spi.AnyMappingDefinition;
import org.hibernate.persister.walking.spi.AttributeDefinition; import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.persister.walking.spi.AttributeSource; import org.hibernate.persister.walking.spi.AttributeSource;
import org.hibernate.persister.walking.spi.CollectionDefinition; import org.hibernate.persister.walking.spi.CollectionDefinition;
@ -89,6 +91,7 @@ import org.hibernate.persister.walking.spi.CollectionIndexDefinition;
import org.hibernate.persister.walking.spi.CompositeCollectionElementDefinition; import org.hibernate.persister.walking.spi.CompositeCollectionElementDefinition;
import org.hibernate.persister.walking.spi.CompositionDefinition; import org.hibernate.persister.walking.spi.CompositionDefinition;
import org.hibernate.persister.walking.spi.EntityDefinition; import org.hibernate.persister.walking.spi.EntityDefinition;
import org.hibernate.persister.walking.spi.WalkingException;
import org.hibernate.pretty.MessageHelper; import org.hibernate.pretty.MessageHelper;
import org.hibernate.sql.Alias; import org.hibernate.sql.Alias;
import org.hibernate.sql.SelectFragment; import org.hibernate.sql.SelectFragment;
@ -100,6 +103,7 @@ import org.hibernate.sql.ordering.antlr.FormulaReference;
import org.hibernate.sql.ordering.antlr.OrderByAliasResolver; import org.hibernate.sql.ordering.antlr.OrderByAliasResolver;
import org.hibernate.sql.ordering.antlr.OrderByTranslation; import org.hibernate.sql.ordering.antlr.OrderByTranslation;
import org.hibernate.sql.ordering.antlr.SqlValueReference; import org.hibernate.sql.ordering.antlr.SqlValueReference;
import org.hibernate.type.AnyType;
import org.hibernate.type.AssociationType; import org.hibernate.type.AssociationType;
import org.hibernate.type.CollectionType; import org.hibernate.type.CollectionType;
import org.hibernate.type.CompositeType; import org.hibernate.type.CompositeType;
@ -2017,10 +2021,19 @@ public abstract class AbstractCollectionPersister
return getElementType(); return getElementType();
} }
@Override
public AnyMappingDefinition toAnyMappingDefinition() {
final Type type = getType();
if ( ! type.isAnyType() ) {
throw new WalkingException( "Cannot treat collection element type as ManyToAny" );
}
return new StandardAnyTypeDefinition( (AnyType) type, isLazy() || isExtraLazy() );
}
@Override @Override
public EntityDefinition toEntityDefinition() { public EntityDefinition toEntityDefinition() {
if ( getType().isComponentType() ) { if ( getType().isComponentType() ) {
throw new IllegalStateException( "Cannot treat composite collection element type as entity" ); throw new WalkingException( "Cannot treat composite collection element type as entity" );
} }
return getElementPersister(); return getElementPersister();
} }
@ -2029,7 +2042,7 @@ public abstract class AbstractCollectionPersister
public CompositeCollectionElementDefinition toCompositeElementDefinition() { public CompositeCollectionElementDefinition toCompositeElementDefinition() {
if ( ! getType().isComponentType() ) { if ( ! getType().isComponentType() ) {
throw new IllegalStateException( "Cannot treat entity collection element type as composite" ); throw new WalkingException( "Cannot treat entity collection element type as composite" );
} }
return new CompositeCollectionElementDefinition() { return new CompositeCollectionElementDefinition() {
@ -2043,6 +2056,11 @@ public abstract class AbstractCollectionPersister
return getElementType(); return getElementType();
} }
@Override
public boolean isNullable() {
return false;
}
@Override @Override
public AttributeSource getSource() { public AttributeSource getSource() {
// TODO: what if this is a collection w/in an encapsulated composition attribute? // TODO: what if this is a collection w/in an encapsulated composition attribute?

View File

@ -29,6 +29,7 @@ import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@ -2495,6 +2496,15 @@ public abstract class AbstractEntityPersister
.buildLoader( this, batchSize, lockOptions, getFactory(), loadQueryInfluencers ); .buildLoader( this, batchSize, lockOptions, getFactory(), loadQueryInfluencers );
} }
/**
* Used internally to create static loaders. These are the default set of loaders used to handle get()/load()
* processing. lock() handling is done by the LockingStrategy instances (see {@link #getLocker})
*
* @param lockMode The lock mode to apply to the thing being loaded.
* @return
*
* @throws MappingException
*/
protected UniqueEntityLoader createEntityLoader(LockMode lockMode) throws MappingException { protected UniqueEntityLoader createEntityLoader(LockMode lockMode) throws MappingException {
return createEntityLoader( lockMode, LoadQueryInfluencers.NONE ); return createEntityLoader( lockMode, LoadQueryInfluencers.NONE );
} }
@ -3765,8 +3775,24 @@ public abstract class AbstractEntityPersister
return StringHelper.generateAlias( getEntityName() ); return StringHelper.generateAlias( getEntityName() );
} }
/**
* Post-construct is a callback for AbstractEntityPersister subclasses to call after they are all done with their
* constructor processing. It allows AbstractEntityPersister to extend its construction after all subclass-specific
* details have been handled.
*
* @param mapping The mapping
*
* @throws MappingException Indicates a problem accessing the Mapping
*/
protected void postConstruct(Mapping mapping) throws MappingException { protected void postConstruct(Mapping mapping) throws MappingException {
initPropertyPaths(mapping); initPropertyPaths( mapping );
//doLateInit();
prepareEntityIdentifierDefinition();
}
private void doLateInit() {
generateEntityDefinition();
//insert/update/delete SQL //insert/update/delete SQL
final int joinSpan = getTableSpan(); final int joinSpan = getTableSpan();
@ -3824,11 +3850,11 @@ public abstract class AbstractEntityPersister
} }
logStaticSQL(); logStaticSQL();
} }
public void postInstantiate() throws MappingException { public final void postInstantiate() throws MappingException {
generateEntityDefinition(); doLateInit();
// generateEntityDefinition();
createLoaders(); createLoaders();
createUniqueKeyLoaders(); createUniqueKeyLoaders();
@ -5112,6 +5138,9 @@ public abstract class AbstractEntityPersister
private void prepareEntityIdentifierDefinition() { private void prepareEntityIdentifierDefinition() {
if ( entityIdentifierDefinition != null ) {
return;
}
final Type idType = getIdentifierType(); final Type idType = getIdentifierType();
if ( !idType.isComponentType() ) { if ( !idType.isComponentType() ) {
@ -5131,35 +5160,119 @@ public abstract class AbstractEntityPersister
EntityIdentifierDefinitionHelper.buildNonEncapsulatedCompositeIdentifierDefinition( this ); EntityIdentifierDefinitionHelper.buildNonEncapsulatedCompositeIdentifierDefinition( this );
} }
private void collectAttributeDefinitions() { private void collectAttributeDefinitions(List<AttributeDefinition> definitions, EntityMetamodel metamodel) {
// todo : leverage the attribute definitions housed on EntityMetamodel for ( int i = 0; i < metamodel.getPropertySpan(); i++ ) {
// for that to work, we'd have to be able to walk our super entity persister(s) definitions.add( metamodel.getProperties()[i] );
attributeDefinitions = new Iterable<AttributeDefinition>() { }
@Override
public Iterator<AttributeDefinition> iterator() {
return new Iterator<AttributeDefinition>() {
// private final int numberOfAttributes = countSubclassProperties();
private final int numberOfAttributes = entityMetamodel.getPropertySpan();
private int currentAttributeNumber = 0;
@Override // see if there are any subclass persisters...
public boolean hasNext() { final Set<String> subClassEntityNames = metamodel.getSubclassEntityNames();
return currentAttributeNumber < numberOfAttributes; if ( subClassEntityNames == null ) {
} return;
}
@Override // see if we can find the persisters...
public AttributeDefinition next() { for ( String subClassEntityName : subClassEntityNames ) {
final int attributeNumber = currentAttributeNumber; if ( metamodel.getName().equals( subClassEntityName ) ) {
currentAttributeNumber++; // skip it
return entityMetamodel.getProperties()[ attributeNumber ]; continue;
}
@Override
public void remove() {
throw new UnsupportedOperationException( "Remove operation not supported here" );
}
};
} }
}; try {
final EntityPersister subClassEntityPersister = factory.getEntityPersister( subClassEntityName );
collectAttributeDefinitions( definitions, subClassEntityPersister.getEntityMetamodel() );
}
catch (MappingException e) {
throw new IllegalStateException(
String.format(
"Could not locate subclass EntityPersister [%s] while processing EntityPersister [%s]",
subClassEntityName,
metamodel.getName()
),
e
);
}
}
} }
private void collectAttributeDefinitions() {
// todo : I think this works purely based on luck atm
// specifically in terms of the sub/super class entity persister(s) being available. Bit of chicken-egg
// problem there:
// * If I do this during postConstruct (as it is now), it works as long as the
// super entity persister is already registered, but I don't think that is necessarily true.
// * If I do this during postInstantiate then lots of stuff in postConstruct breaks if we want
// to try and drive SQL generation on these (which we do ultimately). A possible solution there
// would be to delay all SQL generation until postInstantiate
List<AttributeDefinition> attributeDefinitions = new ArrayList<AttributeDefinition>();
collectAttributeDefinitions( attributeDefinitions, getEntityMetamodel() );
// EntityMetamodel currentEntityMetamodel = this.getEntityMetamodel();
// while ( currentEntityMetamodel != null ) {
// for ( int i = 0; i < currentEntityMetamodel.getPropertySpan(); i++ ) {
// attributeDefinitions.add( currentEntityMetamodel.getProperties()[i] );
// }
// // see if there is a super class EntityMetamodel
// final String superEntityName = currentEntityMetamodel.getSuperclass();
// if ( superEntityName != null ) {
// currentEntityMetamodel = factory.getEntityPersister( superEntityName ).getEntityMetamodel();
// }
// else {
// currentEntityMetamodel = null;
// }
// }
this.attributeDefinitions = Collections.unmodifiableList( attributeDefinitions );
// // todo : leverage the attribute definitions housed on EntityMetamodel
// // for that to work, we'd have to be able to walk our super entity persister(s)
// this.attributeDefinitions = new Iterable<AttributeDefinition>() {
// @Override
// public Iterator<AttributeDefinition> iterator() {
// return new Iterator<AttributeDefinition>() {
//// private final int numberOfAttributes = countSubclassProperties();
//// private final int numberOfAttributes = entityMetamodel.getPropertySpan();
//
// EntityMetamodel currentEntityMetamodel = entityMetamodel;
// int numberOfAttributesInCurrentEntityMetamodel = currentEntityMetamodel.getPropertySpan();
//
// private int currentAttributeNumber;
//
// @Override
// public boolean hasNext() {
// return currentEntityMetamodel != null
// && currentAttributeNumber < numberOfAttributesInCurrentEntityMetamodel;
// }
//
// @Override
// public AttributeDefinition next() {
// final int attributeNumber = currentAttributeNumber;
// currentAttributeNumber++;
// final AttributeDefinition next = currentEntityMetamodel.getProperties()[ attributeNumber ];
//
// if ( currentAttributeNumber >= numberOfAttributesInCurrentEntityMetamodel ) {
// // see if there is a super class EntityMetamodel
// final String superEntityName = currentEntityMetamodel.getSuperclass();
// if ( superEntityName != null ) {
// currentEntityMetamodel = factory.getEntityPersister( superEntityName ).getEntityMetamodel();
// if ( currentEntityMetamodel != null ) {
// numberOfAttributesInCurrentEntityMetamodel = currentEntityMetamodel.getPropertySpan();
// currentAttributeNumber = 0;
// }
// }
// }
//
// return next;
// }
//
// @Override
// public void remove() {
// throw new UnsupportedOperationException( "Remove operation not supported here" );
// }
// };
// }
// };
}
} }

Some files were not shown because too many files have changed in this diff Show More