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;
}
protected String originalMessage() {
return super.getMessage();
}
@Override
public String getMessage() {
return super.getMessage()
return originalMessage()
+ ( wasSetter ? " setter of " : " getter of " )
+ 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 Steve Ebersole
*
* @see AnyMetaDef
*/
@java.lang.annotation.Target({METHOD, FIELD})
@Retention(RUNTIME)

View File

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

View File

@ -52,12 +52,22 @@ public final class NameGenerator {
}
public static String scalarName(int x, int y) {
return new StringBuilder()
.append( "col_" )
.append( x )
.append( '_' )
.append( y )
.append( '_' )
.toString();
return scalarName( "col_" + x, y );
}
public static String scalarName(String base, int num) {
return base + '_' + num + '_';
}
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;
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(
getCurrentFromClause(),
dot.getLhs().getFromElement(),

View File

@ -35,7 +35,6 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
@ -268,7 +267,6 @@ public final class SessionFactoryImpl
this.jdbcServices = this.serviceRegistry.getService( JdbcServices.class );
this.dialect = this.jdbcServices.getDialect();
this.cacheAccess = this.serviceRegistry.getService( CacheImplementor.class );
final RegionFactory regionFactory = cacheAccess.getRegionFactory();
this.sqlFunctionRegistry = new SQLFunctionRegistry( getDialect(), cfg.getSqlFunctions() );
if ( observer != null ) {
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
// region/access-strategy
final RegionFactory regionFactory = cacheAccess.getRegionFactory();
final String cacheRegionPrefix = settings.getCacheRegionPrefix() == null ? "" : settings.getCacheRegionPrefix() + ".";
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();
Map entityAccessStrategies = new HashMap();
Map<String,ClassMetadata> classMeta = new HashMap<String,ClassMetadata>();
@ -358,15 +363,15 @@ public final class SessionFactoryImpl
cacheAccess.addCacheRegion( cacheRegionName, entityRegion );
}
}
NaturalIdRegionAccessStrategy naturalIdAccessStrategy = null;
if ( model.hasNaturalId() && model.getNaturalIdCacheRegionName() != null ) {
final String naturalIdCacheRegionName = cacheRegionPrefix + model.getNaturalIdCacheRegionName();
naturalIdAccessStrategy = ( NaturalIdRegionAccessStrategy ) entityAccessStrategies.get( naturalIdCacheRegionName );
if ( naturalIdAccessStrategy == null && settings.isSecondLevelCacheEnabled() ) {
final CacheDataDescriptionImpl cacheDataDescription = CacheDataDescriptionImpl.decode( model );
NaturalIdRegion naturalIdRegion = null;
try {
naturalIdRegion = regionFactory.buildNaturalIdRegion( naturalIdCacheRegionName, properties,
@ -380,7 +385,7 @@ public final class SessionFactoryImpl
model.getEntityName()
);
}
if (naturalIdRegion != null) {
naturalIdAccessStrategy = naturalIdRegion.buildAccessStrategy( regionFactory.getDefaultAccessType() );
entityAccessStrategies.put( naturalIdCacheRegionName, naturalIdAccessStrategy );
@ -388,7 +393,7 @@ public final class SessionFactoryImpl
}
}
}
EntityPersister cp = persisterFactory.createEntityPersister(
model,
accessStrategy,
@ -462,19 +467,13 @@ public final class SessionFactoryImpl
cfg.getSqlResultSetMappings().values(),
toProcedureCallMementos( cfg.getNamedProcedureCallMap(), cfg.getSqlResultSetMappings() )
);
imports = new HashMap<String,String>( cfg.getImports() );
// after *all* persisters and named queries are registered
Iterator iter = entityPersisters.values().iterator();
while ( iter.hasNext() ) {
final EntityPersister persister = ( ( EntityPersister ) iter.next() );
for ( EntityPersister persister : entityPersisters.values() ) {
persister.postInstantiate();
registerEntityNameResolvers( persister );
}
iter = collectionPersisters.values().iterator();
while ( iter.hasNext() ) {
final CollectionPersister persister = ( ( CollectionPersister ) iter.next() );
for ( CollectionPersister persister : collectionPersisters.values() ) {
persister.postInstantiate();
}
@ -1070,6 +1069,7 @@ public final class SessionFactoryImpl
entityNameResolvers.put( resolver, ENTITY_NAME_RESOLVER_MAP_VALUE );
}
@Override
public Iterable<EntityNameResolver> iterateEntityNameResolvers() {
return entityNameResolvers.keySet();
}

View File

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

View File

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

View File

@ -72,11 +72,19 @@ public abstract class BatchingEntityLoaderBuilder {
LoadQueryInfluencers influencers) {
if ( batchSize <= 1 ) {
// no batching
return new EntityLoader( persister, lockMode, factory, influencers );
return buildNonBatchingLoader( persister, 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(
OuterJoinLoadable persister,
int batchSize,
@ -103,11 +111,19 @@ public abstract class BatchingEntityLoaderBuilder {
LoadQueryInfluencers influencers) {
if ( batchSize <= 1 ) {
// no batching
return new EntityLoader( persister, lockOptions, factory, influencers );
return buildNonBatchingLoader( persister, 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(
OuterJoinLoadable persister,
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.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
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.NoopLimitHandler;
import org.hibernate.engine.jdbc.ColumnNameCache;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.QueryParameters;
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.CoreMessageLogger;
import org.hibernate.loader.entity.UniqueEntityLoader;
import org.hibernate.loader.internal.EntityLoadQueryBuilderImpl;
import org.hibernate.loader.internal.LoadQueryAliasResolutionContextImpl;
import org.hibernate.loader.internal.ResultSetProcessorImpl;
import org.hibernate.loader.plan.exec.query.spi.NamedParameterContext;
import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters;
import org.hibernate.loader.plan.exec.spi.LoadQueryDetails;
import org.hibernate.loader.plan.internal.SingleRootReturnLoadPlanBuilderStrategy;
import org.hibernate.loader.plan.spi.LoadPlan;
import org.hibernate.loader.plan.spi.build.LoadPlanBuilder;
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.ResultSetProcessor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.pretty.MessageHelper;
@ -63,63 +58,41 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
private final String entityName;
private final LoadPlan plan;
private final String staticSql;
private final LoadQueryAliasResolutionContext staticAliasResolutionContext;
private final ResultSetProcessor staticResultSetProcessor;
private final LoadQueryDetails staticLoadQuery;
private ColumnNameCache columnNameCache;
public AbstractLoadPlanBasedEntityLoader(
OuterJoinLoadable entityPersister,
Type uniqueKeyType,
SessionFactoryImplementor factory,
LoadQueryInfluencers loadQueryInfluencers) {
String[] uniqueKeyColumnNames,
Type uniqueKeyType,
QueryBuildingParameters buildingParameters) {
this.entityPersister = entityPersister;
this.factory = factory;
this.uniqueKeyType = uniqueKeyType;
this.entityName = entityPersister.getEntityName();
this.entityPersister = entityPersister;
final SingleRootReturnLoadPlanBuilderStrategy strategy = new SingleRootReturnLoadPlanBuilderStrategy(
factory,
loadQueryInfluencers
buildingParameters.getQueryInfluencers()
);
this.plan = LoadPlanBuilder.buildRootEntityLoadPlan( strategy, entityPersister );
this.staticAliasResolutionContext = buildAliasResolutionContext( plan, factory );
this.staticSql = generateStaticSql( plan, staticAliasResolutionContext, factory, loadQueryInfluencers );
this.staticResultSetProcessor = generateStaticResultSetProcessor( plan );
}
protected LoadQueryAliasResolutionContext buildAliasResolutionContext(LoadPlan plan, SessionFactoryImplementor factory) {
return new LoadQueryAliasResolutionContextImpl(
this.staticLoadQuery = LoadQueryDetails.makeForBatching(
uniqueKeyColumnNames,
plan,
factory,
0,
Collections.singletonMap( plan.getReturns().get( 0 ), new String[] {"abc"} )
buildingParameters
);
}
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() {
return factory;
}
protected String getSqlStatement() {
return staticSql;
protected LoadQueryDetails getStaticLoadQuery() {
return staticLoadQuery;
}
protected String getEntityName() {
@ -152,17 +125,12 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
final QueryParameters qp = new QueryParameters();
qp.setPositionalParameterTypes( types );
qp.setPositionalParameterValues( ids );
qp.setOptionalObject( optionalObject );
qp.setOptionalEntityName( optionalEntityName );
qp.setOptionalId( optionalId );
qp.setLockOptions( lockOptions );
result = executeLoad(
session,
qp,
staticSql,
staticResultSetProcessor,
staticAliasResolutionContext,
staticLoadQuery,
false,
null
);
@ -171,7 +139,7 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
throw factory.getSQLExceptionHelper().convert(
sqle,
"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(
session,
qp,
staticSql,
staticResultSetProcessor,
staticAliasResolutionContext,
staticLoadQuery,
false,
null
);
@ -220,7 +186,7 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
entityPersister.getIdentifierType(),
factory
),
getSqlStatement()
staticLoadQuery.getSqlStatement()
);
}
@ -231,18 +197,14 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
protected List executeLoad(
SessionImplementor session,
QueryParameters queryParameters,
String sql,
ResultSetProcessor resultSetProcessor,
LoadQueryAliasResolutionContext aliasResolutionContext,
LoadQueryDetails loadQueryDetails,
boolean returnProxies,
ResultTransformer forcedResultTransformer) throws SQLException {
final List<AfterLoadAction> afterLoadActions = new ArrayList<AfterLoadAction>();
return executeLoad(
session,
queryParameters,
sql,
resultSetProcessor,
aliasResolutionContext,
loadQueryDetails,
returnProxies,
forcedResultTransformer,
afterLoadActions
@ -252,9 +214,7 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
protected List executeLoad(
SessionImplementor session,
QueryParameters queryParameters,
String sql,
ResultSetProcessor resultSetProcessor,
LoadQueryAliasResolutionContext aliasResolutionContext,
LoadQueryDetails loadQueryDetails,
boolean returnProxies,
ResultTransformer forcedResultTransformer,
List<AfterLoadAction> afterLoadActions) throws SQLException {
@ -273,9 +233,10 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
persistenceContext.beforeLoad();
try {
List results;
final String sql = loadQueryDetails.getSqlStatement();
try {
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
NoOpLoadPlanAdvisor.INSTANCE,
wrapper.getResultSet(),
@ -287,7 +248,7 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
return AbstractLoadPlanBasedEntityLoader.this.getNamedParameterLocs( name );
}
},
aliasResolutionContext,
loadQueryDetails.getAliasResolutionContext(),
returnProxies,
queryParameters.isReadOnly(),
forcedResultTransformer,
@ -334,9 +295,7 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
protected Object doQueryAndLoadEntity(
SessionImplementor session,
QueryParameters queryParameters,
String sql,
ResultSetProcessor resultSetProcessor,
LoadQueryAliasResolutionContext aliasResolutionContext,
LoadQueryDetails loadQueryDetails,
boolean returnProxies,
ResultTransformer forcedResultTransformer) throws SQLException {
@ -345,7 +304,7 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
final SqlStatementWrapper wrapper = executeQueryStatement( queryParameters, false, afterLoadActions, session );
try {
final List results = resultSetProcessor.extractResults(
final List results = loadQueryDetails.getResultSetProcessor().extractResults(
NoOpLoadPlanAdvisor.INSTANCE,
wrapper.getResultSet(),
session,
@ -356,7 +315,7 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
return AbstractLoadPlanBasedEntityLoader.this.getNamedParameterLocs( name );
}
},
staticAliasResolutionContext,
loadQueryDetails.getAliasResolutionContext(),
returnProxies,
queryParameters.isReadOnly(),
forcedResultTransformer,
@ -398,7 +357,7 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
final boolean scroll,
List<AfterLoadAction> afterLoadActions,
final SessionImplementor session) throws SQLException {
return executeQueryStatement( getSqlStatement(), queryParameters, scroll, afterLoadActions, session );
return executeQueryStatement( staticLoadQuery.getSqlStatement(), queryParameters, scroll, afterLoadActions, session );
}
protected SqlStatementWrapper executeQueryStatement(

View File

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

View File

@ -32,148 +32,125 @@ import org.hibernate.MappingException;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.CoreLogging;
import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters;
import org.hibernate.persister.entity.OuterJoinLoadable;
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.
* <br>
* The <tt>EntityPersister</tt> must implement <tt>Loadable</tt>. For other entities,
* create a customized subclass of <tt>Loader</tt>.
*
* @author Gavin King
* @author Steve Ebersole
* @author Gail Badner
*/
public class EntityLoader extends AbstractLoadPlanBasedEntityLoader {
private static final Logger log = CoreLogging.logger( EntityLoader.class );
// private final boolean batchLoader;
// private final int[][] compositeKeyManyToOneTargetIndices;
//
// 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 static Builder forEntity(OuterJoinLoadable persister) {
return new Builder( persister );
}
public EntityLoader(
OuterJoinLoadable persister,
int batchSize,
LockOptions lockOptions,
SessionFactoryImplementor factory,
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
this(
persister,
persister.getIdentifierColumnNames(),
persister.getIdentifierType(),
batchSize,
lockOptions,
factory,
loadQueryInfluencers
);
}
public static class Builder {
private final OuterJoinLoadable persister;
private int batchSize = 1;
private LoadQueryInfluencers influencers = LoadQueryInfluencers.NONE;
private LockMode lockMode = LockMode.NONE;
private LockOptions lockOptions;
public EntityLoader(
OuterJoinLoadable 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 Builder(OuterJoinLoadable persister) {
this.persister = persister;
}
}
public EntityLoader(
OuterJoinLoadable persister,
String[] uniqueKey,
Type uniqueKeyType,
int batchSize,
LockOptions lockOptions,
SessionFactoryImplementor factory,
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
super( persister, uniqueKeyType, factory, loadQueryInfluencers );
//
// EntityJoinWalker walker = new EntityJoinWalker(
// persister,
// uniqueKey,
// batchSize,
// lockOptions,
// factory,
// loadQueryInfluencers
// );
// initFromWalker( walker );
// this.compositeKeyManyToOneTargetIndices = walker.getCompositeKeyManyToOneTargetIndices();
// postInstantiate();
//
// batchLoader = batchSize > 1;
//
if ( log.isDebugEnabled() ) {
log.debugf(
"Static select for entity %s [%s:%s]: %s",
getEntityName(),
lockOptions.getLockMode(),
lockOptions.getTimeOut(),
getSqlStatement()
public Builder withBatchSize(int batchSize) {
this.batchSize = batchSize;
return this;
}
public Builder withInfluencers(LoadQueryInfluencers influencers) {
this.influencers = influencers;
return this;
}
public Builder withLockMode(LockMode lockMode) {
this.lockMode = lockMode;
return this;
}
public Builder withLockOptions(LockOptions lockOptions) {
this.lockOptions = lockOptions;
return this;
}
public EntityLoader byPrimaryKey() {
return byUniqueKey( persister.getIdentifierColumnNames(), persister.getIdentifierType() );
}
public EntityLoader byUniqueKey(String[] keyColumnNames, Type keyType) {
return new EntityLoader(
persister.getFactory(),
persister,
keyColumnNames,
keyType,
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) {
// return load( session, key, null, null, LockOptions.NONE );
// }
//
// @Override
// protected boolean isSingleRowLoader() {
// return !batchLoader;
// }
//
// @Override
// public int[][] getCompositeKeyManyToOneTargetIndices() {
// return compositeKeyManyToOneTargetIndices;
// }
private EntityLoader(
SessionFactoryImplementor factory,
OuterJoinLoadable persister,
String[] uniqueKeyColumnNames,
Type uniqueKeyType,
QueryBuildingParameters buildingParameters) throws MappingException {
super( persister, factory, uniqueKeyColumnNames, uniqueKeyType, buildingParameters );
if ( log.isDebugEnabled() ) {
if ( buildingParameters.getLockOptions() != null ) {
log.debugf(
"Static select for entity %s [%s:%s]: %s",
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.SessionImplementor;
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.persister.entity.OuterJoinLoadable;
/**
* LoadPlan-based implementation of the the legacy batch loading strategy
*
* @author Steve Ebersole
*/
public class LegacyBatchingEntityLoaderBuilder extends BatchingEntityLoaderBuilder {
public class LegacyBatchingEntityLoaderBuilder extends AbstractBatchingEntityLoaderBuilder {
public static final LegacyBatchingEntityLoaderBuilder INSTANCE = new LegacyBatchingEntityLoaderBuilder();
@Override
@ -77,8 +76,11 @@ public class LegacyBatchingEntityLoaderBuilder extends BatchingEntityLoaderBuild
super( persister );
this.batchSizes = ArrayHelper.getBatchSizes( maxBatchSize );
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++ ) {
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 );
this.batchSizes = ArrayHelper.getBatchSizes( maxBatchSize );
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++ ) {
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.loader.CollectionAliases;
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.LoadQueryAliasResolutionContext;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.Joinable;
import org.hibernate.persister.walking.internal.FetchStrategyHelper;
import org.hibernate.sql.ConditionFragment;
import org.hibernate.sql.DisjunctionFragment;
import org.hibernate.sql.InFragment;
@ -58,7 +60,7 @@ public abstract class AbstractLoadQueryImpl {
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 );
}
@ -77,7 +79,7 @@ public abstract class AbstractLoadQueryImpl {
/**
* 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 {
JoinFragment joinFragment = factory.getDialect().createOuterJoinFragment();
JoinableAssociation previous = null;
@ -120,7 +122,7 @@ public abstract class AbstractLoadQueryImpl {
// TODO: why is this static?
protected static String orderBy(
List<JoinableAssociation> associations,
LoadQueryAliasResolutionContext aliasResolutionContext)
AliasResolutionContext aliasResolutionContext)
throws MappingException {
StringBuilder buf = new StringBuilder();
JoinableAssociation previous = null;
@ -197,7 +199,7 @@ public abstract class AbstractLoadQueryImpl {
/**
* 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 {
if ( associations.size() == 0 ) {
@ -210,15 +212,19 @@ public abstract class AbstractLoadQueryImpl {
JoinableAssociation next = ( i == associations.size() - 1 )
? null
: associations.get( i + 1 );
if ( !shouldAddToSql( association.getCurrentFetch() ) ) {
continue;
}
final Joinable joinable = association.getJoinable();
final EntityAliases currentEntityAliases =
association.getCurrentEntityReference() == null ?
null :
aliasResolutionContext.resolveEntityColumnAliases( association.getCurrentEntityReference() );
aliasResolutionContext.resolveAliases( association.getCurrentEntityReference() ).getColumnAliases();
final CollectionAliases currentCollectionAliases =
association.getCurrentCollectionReference() == null ?
null :
aliasResolutionContext.resolveCollectionColumnAliases( association.getCurrentCollectionReference() );
aliasResolutionContext.resolveAliases( association.getCurrentCollectionReference() ).getCollectionColumnAliases();
final String selectFragment = joinable.selectFragment(
next == null ? null : next.getJoinable(),
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(
JoinFragment joinFragment,
JoinableAssociation association,
@ -260,7 +270,7 @@ public abstract class AbstractLoadQueryImpl {
private String resolveOnCondition(
SessionFactoryImplementor factory,
JoinableAssociation joinableAssociation,
LoadQueryAliasResolutionContext aliasResolutionContext) {
AliasResolutionContext aliasResolutionContext) {
final String withClause = StringHelper.isEmpty( joinableAssociation.getWithClause() ) ?
"" :
" and ( " + joinableAssociation.getWithClause() + " )";

View File

@ -58,7 +58,7 @@ public class EntityJoinableAssociationImpl extends AbstractJoinableAssociationIm
hasRestriction,
enabledFilters
);
this.joinableType = entityFetch.getEntityType();
this.joinableType = entityFetch.getFetchedType();
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
* 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.Map;
import org.hibernate.cfg.NotYetImplementedException;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.hql.internal.NameGenerator;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.loader.CollectionAliases;
import org.hibernate.loader.DefaultEntityAliases;
import org.hibernate.loader.EntityAliases;
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.CollectionReturn;
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.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.Return;
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.LoadQueryAliasResolutionContext;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Loadable;
import org.hibernate.persister.walking.spi.WalkingException;
import org.hibernate.type.EntityType;
/**
* Provides aliases that are used by load queries and ResultSet processors.
*
* @author Gail Badner
* @author Steve Ebersole
*/
public class LoadQueryAliasResolutionContextImpl implements LoadQueryAliasResolutionContext {
private final Map<Return,String[]> aliasesByReturn;
private final Map<EntityReference,LoadQueryEntityAliasesImpl> aliasesByEntityReference =
new HashMap<EntityReference,LoadQueryEntityAliasesImpl>();
public class AliasResolutionContextImpl implements AliasResolutionContext {
private final SessionFactoryImplementor sessionFactory;
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 =
new HashMap<CollectionReference,LoadQueryCollectionAliasesImpl>();
private final Map<JoinableAssociation,JoinableAssociationAliasesImpl> aliasesByJoinableAssociation =
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,
int suffixSeed,
Map<Return,String[]> aliasesByReturn) {
Map<Return, String> sourceAliasByReturnMap,
Map<SourceQualifiable, String> sourceQualifiersByReturnMap) {
this.sessionFactory = sessionFactory;
this.currentAliasSuffix = suffixSeed;
checkAliasesByReturn( aliasesByReturn );
this.aliasesByReturn = new HashMap<Return, String[]>( aliasesByReturn );
this.sourceAliasByReturnMap = new HashMap<Return, String>( sourceAliasByReturnMap );
this.sourceQualifiersByReturnMap = new HashMap<SourceQualifiable, String>( sourceQualifiersByReturnMap );
}
private static void checkAliasesByReturn(Map<Return, String[]> aliasesByReturn) {
if ( aliasesByReturn == null || aliasesByReturn.size() == 0 ) {
throw new IllegalArgumentException( "No return aliases defined" );
@Override
public String getSourceAlias(Return theReturn) {
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() ) {
final Return aReturn = entry.getKey();
final String[] aliases = entry.getValue();
if ( aReturn == null ) {
throw new IllegalArgumentException( "null key found in aliasesByReturn" );
}
if ( aliases == null || aliases.length == 0 ) {
throw new IllegalArgumentException(
String.format( "No alias defined for [%s]", aReturn )
else {
// generate one from scratch
return NameGenerator.scalarNames( currentAliasSuffix++, numberOfColumns );
}
}
@Override
public EntityReferenceAliases resolveAliases(EntityReference entityReference) {
EntityReferenceAliasesImpl aliases = aliasesByEntityReference.get( entityReference );
if ( aliases == null ) {
if ( BidirectionalEntityFetch.class.isInstance( entityReference ) ) {
return resolveAliases(
( (BidirectionalEntityFetch) entityReference ).getTargetEntityReference()
);
}
if ( ( aliases.length > 1 ) &&
( aReturn instanceof EntityReturn || aReturn instanceof CollectionReturn ) ) {
throw new IllegalArgumentException( String.format( "More than 1 alias defined for [%s]", aReturn ) );
}
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 )
final EntityPersister entityPersister = entityReference.getEntityPersister();
aliases = new EntityReferenceAliasesImpl(
createTableAlias( entityPersister ),
createEntityAliases( entityPersister )
);
aliasesByEntityReference.put( entityReference, aliases );
}
return aliases;
}
@Override
public String resolveEntityTableAlias(EntityReference entityReference) {
return getOrGenerateLoadQueryEntityAliases( entityReference ).tableAlias;
public CollectionReferenceAliases resolveAliases(CollectionReference collectionReference) {
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
public String resolveAssociationRhsTableAlias(JoinableAssociation joinableAssociation) {
@ -179,48 +218,29 @@ public class LoadQueryAliasResolutionContextImpl implements LoadQueryAliasResolu
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) {
JoinableAssociationAliasesImpl aliases = aliasesByJoinableAssociation.get( joinableAssociation );
if ( aliases == null ) {
final Fetch currentFetch = joinableAssociation.getCurrentFetch();
final String lhsAlias;
if ( EntityReference.class.isInstance( currentFetch.getOwner() ) ) {
lhsAlias = resolveEntityTableAlias( (EntityReference) currentFetch.getOwner() );
if ( AnyFetch.class.isInstance( currentFetch ) ) {
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() ) ) {
CompositeElementGraph compositeElementGraph = (CompositeElementGraph) currentFetch.getOwner();
lhsAlias = resolveCollectionTableAlias( compositeElementGraph.getCollectionReference() );
lhsAlias = resolveAliases( compositeElementGraph.getCollectionReference() ).getElementTableAlias();
}
else if ( CompositeIndexGraph.class.isInstance( currentFetch.getOwner() ) ) {
CompositeIndexGraph compositeIndexGraph = (CompositeIndexGraph) currentFetch.getOwner();
lhsAlias = resolveCollectionTableAlias( compositeIndexGraph.getCollectionReference() );
lhsAlias = resolveAliases( compositeIndexGraph.getCollectionReference() ).getElementTableAlias();
}
else {
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 rhsAlias;
if ( EntityReference.class.isInstance( currentFetch ) ) {
rhsAlias = resolveEntityTableAlias( (EntityReference) currentFetch );
rhsAlias = resolveAliases( (EntityReference) currentFetch ).getTableAlias();
}
else if ( CollectionReference.class.isInstance( joinableAssociation.getCurrentFetch() ) ) {
rhsAlias = resolveCollectionTableAlias( (CollectionReference) currentFetch );
rhsAlias = resolveAliases( (CollectionReference) currentFetch ).getCollectionTableAlias();
}
else {
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 );
aliasesByJoinableAssociation.put( joinableAssociation, aliases );
@ -246,12 +266,24 @@ public class LoadQueryAliasResolutionContextImpl implements LoadQueryAliasResolu
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) {
return createTableAlias( StringHelper.unqualifyEntityName( entityPersister.getEntityName() ) );
}
private String createTableAlias(String name) {
return StringHelper.generateAlias( name ) + createSuffix();
return StringHelper.generateAlias( name, currentTableAliasUniqueness++ );
}
private EntityAliases createEntityAliases(EntityPersister entityPersister) {
@ -272,28 +304,43 @@ public class LoadQueryAliasResolutionContextImpl implements LoadQueryAliasResolu
}
}
private static class LoadQueryEntityAliasesImpl {
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 static class LoadQueryCollectionAliasesImpl implements CollectionReferenceAliases {
private final String tableAlias;
private final String manyToManyAssociationTableAlias;
private final CollectionAliases collectionAliases;
private final EntityAliases collectionElementAliases;
private final EntityAliases entityElementAliases;
public LoadQueryCollectionAliasesImpl(
String tableAlias,
String manyToManyAssociationTableAlias,
CollectionAliases collectionAliases,
EntityAliases collectionElementAliases) {
EntityAliases entityElementAliases) {
this.tableAlias = tableAlias;
this.manyToManyAssociationTableAlias = manyToManyAssociationTableAlias;
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
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.internal;
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.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jboss.logging.Logger;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.StaleObjectStateException;
import org.hibernate.WrongClassException;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.internal.TwoPhaseLoad;
import org.hibernate.engine.spi.EntityKey;
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.loader.CollectionAliases;
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.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.Fetch;
import org.hibernate.loader.plan.spi.FetchOwner;
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.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.entity.EntityPersister;
import org.hibernate.persister.entity.Loadable;
@ -82,46 +87,75 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
private final SessionImplementor session;
private final LoadPlan loadPlan;
private final boolean readOnly;
private final boolean shouldUseOptionalEntityInformation;
private final boolean forceFetchLazyAttributes;
private final boolean shouldReturnProxies;
private final QueryParameters queryParameters;
private final NamedParameterContext namedParameterContext;
private final LoadQueryAliasResolutionContext aliasResolutionContext;
private final AliasResolutionContext aliasResolutionContext;
private final boolean hadSubselectFetches;
private final EntityKey dictatedRootEntityKey;
private List<HydratedEntityRegistration> currentRowHydratedEntityRegistrationList;
private Map<EntityPersister,Set<EntityKey>> subselectLoadableEntityKeyMap;
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(
ResultSet resultSet,
SessionImplementor session,
LoadPlan loadPlan,
boolean readOnly,
boolean useOptionalEntityKey,
boolean shouldUseOptionalEntityInformation,
boolean forceFetchLazyAttributes,
boolean shouldReturnProxies,
QueryParameters queryParameters,
NamedParameterContext namedParameterContext,
LoadQueryAliasResolutionContext aliasResolutionContext,
AliasResolutionContext aliasResolutionContext,
boolean hadSubselectFetches) {
this.resultSet = resultSet;
this.session = session;
this.loadPlan = loadPlan;
this.readOnly = readOnly;
this.shouldUseOptionalEntityInformation = shouldUseOptionalEntityInformation;
this.forceFetchLazyAttributes = forceFetchLazyAttributes;
this.shouldReturnProxies = shouldReturnProxies;
this.queryParameters = queryParameters;
this.namedParameterContext = namedParameterContext;
this.aliasResolutionContext = aliasResolutionContext;
this.hadSubselectFetches = hadSubselectFetches;
if ( useOptionalEntityKey ) {
this.dictatedRootEntityKey = ResultSetProcessorHelper.getOptionalObjectKey( queryParameters, session );
if ( this.dictatedRootEntityKey == null ) {
throw new HibernateException( "Unable to resolve optional entity-key" );
if ( shouldUseOptionalEntityInformation ) {
if ( queryParameters.getOptionalId() != null ) {
// make sure we have only one return
if ( loadPlan.getReturns().size() > 1 ) {
throw new IllegalStateException( "Cannot specify 'optional entity' values with multi-return load plans" );
}
}
}
else {
this.dictatedRootEntityKey = null;
}
}
@Override
@ -129,28 +163,48 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
return session;
}
@Override
public boolean shouldUseOptionalEntityInformation() {
return shouldUseOptionalEntityInformation;
}
@Override
public QueryParameters getQueryParameters() {
return queryParameters;
}
@Override
public EntityKey getDictatedRootEntityKey() {
return dictatedRootEntityKey;
public boolean shouldReturnProxies() {
return shouldReturnProxies;
}
private Map<EntityReference,IdentifierResolutionContext> identifierResolutionContextMap;
@Override
public LoadPlan getLoadPlan() {
return loadPlan;
}
@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 ) {
identifierResolutionContextMap = new HashMap<EntityReference, IdentifierResolutionContext>();
identifierResolutionContextMap = new IdentityHashMap<EntityReference, EntityReferenceProcessingState>();
}
IdentifierResolutionContext context = identifierResolutionContextMap.get( entityReference );
EntityReferenceProcessingState context = identifierResolutionContextMap.get( entityReference );
if ( context == null ) {
context = new IdentifierResolutionContext() {
private Object hydratedForm;
context = new EntityReferenceProcessingState() {
private boolean wasMissingIdentifier;
private Object identifierHydratedForm;
private EntityKey entityKey;
private Object[] hydratedState;
private Object entityInstance;
@Override
public EntityReference getEntityReference() {
@ -158,23 +212,31 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
}
@Override
public void registerHydratedForm(Object hydratedForm) {
if ( this.hydratedForm != null ) {
// this could be bad...
public void registerMissingIdentifier() {
if ( !EntityFetch.class.isInstance( entityReference ) ) {
throw new IllegalStateException( "Missing return row identifier" );
}
this.hydratedForm = hydratedForm;
ResultSetProcessingContextImpl.this.registerNonExists( (EntityFetch) entityReference );
wasMissingIdentifier = true;
}
@Override
public Object getHydratedForm() {
return hydratedForm;
public boolean isMissingIdentifier() {
return wasMissingIdentifier;
}
@Override
public void registerIdentifierHydratedForm(Object identifierHydratedForm) {
this.identifierHydratedForm = identifierHydratedForm;
}
@Override
public Object getIdentifierHydratedForm() {
return identifierHydratedForm;
}
@Override
public void registerEntityKey(EntityKey entityKey) {
if ( this.entityKey != null ) {
// again, could be trouble...
}
this.entityKey = entityKey;
}
@ -182,6 +244,26 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
public EntityKey getEntityKey() {
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 );
}
@ -189,15 +271,55 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
return context;
}
@Override
public Set<IdentifierResolutionContext> getIdentifierResolutionContexts() {
return Collections.unmodifiableSet(
new HashSet<IdentifierResolutionContext>( identifierResolutionContextMap.values() )
private void registerNonExists(EntityFetch fetch) {
final EntityType fetchedType = fetch.getFetchedType();
if ( ! fetchedType.isOneToOne() ) {
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
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;
}
@ -309,7 +431,7 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
checkVersion(
resultSet,
entityKeyContext.getEntityPersister(),
aliasResolutionContext.resolveEntityColumnAliases( entityKeyContext.getEntityReference() ),
aliasResolutionContext.resolveAliases( entityKeyContext.getEntityReference() ).getColumnAliases(),
entityKey,
existing
);
@ -324,14 +446,26 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
final String concreteEntityTypeName = getConcreteEntityTypeName(
resultSet,
entityKeyContext.getEntityPersister(),
aliasResolutionContext.resolveEntityColumnAliases( entityKeyContext.getEntityReference() ),
aliasResolutionContext.resolveAliases( entityKeyContext.getEntityReference() ).getColumnAliases(),
entityKey
);
final Object entityInstance = getSession().instantiate(
concreteEntityTypeName,
entityKey.getIdentifier()
);
final Object entityInstance;
// if ( suppliedOptionalEntityKey != null && entityKey.equals( suppliedOptionalEntityKey ) ) {
// // 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.
@ -350,15 +484,16 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
entityInstance,
concreteEntityTypeName,
entityKey,
aliasResolutionContext.resolveEntityColumnAliases( entityKeyContext.getEntityReference() ),
aliasResolutionContext.resolveAliases( entityKeyContext.getEntityReference() ).getColumnAliases(),
acquiredLockMode,
entityKeyContext.getEntityPersister(),
fetchStrategy,
true,
entityKeyContext.getEntityPersister().getEntityMetamodel().getEntityType()
);
// materialize associations (and initialize the object) later
registerHydratedEntity( entityKeyContext.getEntityPersister(), entityKey, entityInstance );
registerHydratedEntity( entityKeyContext.getEntityReference(), entityKey, entityInstance );
return entityInstance;
}
@ -373,6 +508,7 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
EntityAliases entityAliases,
LockMode acquiredLockMode,
EntityPersister rootPersister,
FetchStrategy fetchStrategy,
boolean eagerFetch,
EntityType associationType) {
@ -400,7 +536,7 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
entityInstance,
persister,
acquiredLockMode,
!eagerFetch,
!forceFetchLazyAttributes,
session
);
@ -417,7 +553,7 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
entityInstance,
(Loadable) rootPersister,
cols,
eagerFetch,
loadPlan.areLazyAttributesForceFetched(),
session
);
}
@ -469,7 +605,7 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
rowId,
entityInstance,
acquiredLockMode,
!eagerFetch,
!loadPlan.areLazyAttributesForceFetched(),
session
);
@ -485,7 +621,7 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
null,
null,
rootCollectionReturn.getCollectionPersister(),
aliasResolutionContext.resolveCollectionColumnAliases( rootCollectionReturn ),
aliasResolutionContext.resolveAliases( rootCollectionReturn ).getCollectionColumnAliases(),
resultSet,
session
);
@ -499,7 +635,7 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
owner,
collectionFetch.getCollectionPersister().getCollectionType().getKeyOfOwner( owner, session ),
collectionFetch.getCollectionPersister(),
aliasResolutionContext.resolveCollectionColumnAliases( collectionFetch ),
aliasResolutionContext.resolveAliases( collectionFetch ).getCollectionColumnAliases(),
resultSet,
session
);
@ -583,11 +719,17 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
}
@Override
public void registerHydratedEntity(EntityPersister persister, EntityKey entityKey, Object entityInstance) {
public void registerHydratedEntity(EntityReference entityReference, EntityKey entityKey, Object entityInstance) {
if ( currentRowHydratedEntityRegistrationList == null ) {
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>>();
}
for ( HydratedEntityRegistration registration : currentRowHydratedEntityRegistrationList ) {
Set<EntityKey> entityKeys = subselectLoadableEntityKeyMap.get( registration.persister );
Set<EntityKey> entityKeys = subselectLoadableEntityKeyMap.get( registration.entityReference.getEntityPersister() );
if ( entityKeys == null ) {
entityKeys = new HashSet<EntityKey>();
subselectLoadableEntityKeyMap.put( registration.persister, entityKeys );
subselectLoadableEntityKeyMap.put( registration.entityReference.getEntityPersister(), entityKeys );
}
entityKeys.add( registration.key );
}
@ -623,6 +765,8 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
// release the currentRowHydratedEntityRegistrationList entries
currentRowHydratedEntityRegistrationList.clear();
identifierResolutionContextMap.clear();
}
/**
@ -753,7 +897,7 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
TwoPhaseLoad.postLoad( registration.instance, session, postLoadEvent );
if ( afterLoadActionList != null ) {
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 final EntityPersister persister;
private final EntityReference entityReference;
private final EntityKey key;
private final Object instance;
private Object instance;
private HydratedEntityRegistration(EntityPersister persister, EntityKey key, Object instance) {
this.persister = persister;
private HydratedEntityRegistration(EntityReference entityReference, EntityKey key, Object instance) {
this.entityReference = entityReference;
this.key = key;
this.instance = instance;
}

View File

@ -21,37 +21,57 @@
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.internal;
package org.hibernate.loader.plan.exec.process.internal;
import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
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.loader.plan.spi.Fetch;
import org.hibernate.loader.spi.NamedParameterContext;
import org.hibernate.loader.spi.ResultSetProcessingContext;
import org.hibernate.loader.plan.exec.query.spi.NamedParameterContext;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.type.CompositeType;
import org.hibernate.type.Type;
/**
* @author Steve Ebersole
*/
public class ResultSetProcessorHelper {
/**
* Singleton access
*/
public static final ResultSetProcessorHelper INSTANCE = new ResultSetProcessorHelper();
public static EntityKey getOptionalObjectKey(QueryParameters queryParameters, SessionImplementor session) {
final Object optionalObject = queryParameters.getOptionalObject();
final Serializable optionalId = queryParameters.getOptionalId();
final String optionalEntityName = queryParameters.getOptionalEntityName();
if ( optionalObject != null && optionalEntityName != null ) {
return session.generateEntityKey( optionalId, session.getEntityPersister( optionalEntityName, optionalObject ) );
return INSTANCE.interpretEntityKey( session, optionalEntityName, optionalId, 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 {
return null;
@ -74,4 +94,5 @@ public class ResultSetProcessorHelper {
}
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
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.internal;
package org.hibernate.loader.plan.exec.process.internal;
import java.sql.ResultSet;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.loader.spi.ScrollableResultSetProcessor;
import org.hibernate.loader.plan.exec.process.spi.ScrollableResultSetProcessor;
/**
* @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
* 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.SQLException;
@ -29,6 +29,10 @@ import java.util.List;
import org.hibernate.engine.spi.QueryParameters;
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;
/**
@ -66,7 +70,7 @@ public interface ResultSetProcessor {
SessionImplementor session,
QueryParameters queryParameters,
NamedParameterContext namedParameterContext,
LoadQueryAliasResolutionContext aliasResolutionContext,
AliasResolutionContext aliasResolutionContext,
boolean returnProxies,
boolean readOnly,
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
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.spi;
package org.hibernate.loader.plan.exec.process.spi;
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
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.spi;
package org.hibernate.loader.plan.exec.query.spi;
/**
* 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
* 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.EntityAliases;
import org.hibernate.loader.plan.spi.CollectionReturn;
import org.hibernate.loader.plan.spi.CollectionReference;
import org.hibernate.loader.plan.spi.EntityReference;
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.spi.JoinableAssociation;
/**
* Provides aliases that are used by load queries and ResultSet processors.
*
* @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}.
*/
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}.
* @return The SQL table alias for the specified {@link EntityReference}.
* @param collectionReference The collection reference for which to obtain alias info
*
* @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.

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.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.CompositeFetch;
import org.hibernate.loader.plan.spi.EntityFetch;
import org.hibernate.loader.plan.spi.FetchOwner;
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.AttributeDefinition;
import org.hibernate.persister.walking.spi.CompositionDefinition;
/**
@ -47,7 +51,7 @@ public class LoadPlanBuildingHelper {
LockMode.NONE, // todo : for now
fetchOwner,
fetchStrategy,
attributeDefinition.getName()
attributeDefinition
);
}
@ -56,12 +60,11 @@ public class LoadPlanBuildingHelper {
AssociationAttributeDefinition attributeDefinition,
FetchStrategy fetchStrategy,
LoadPlanBuildingContext loadPlanBuildingContext) {
return new EntityFetch(
loadPlanBuildingContext.getSessionFactory(),
LockMode.NONE, // todo : for now
fetchOwner,
attributeDefinition.getName(),
attributeDefinition,
fetchStrategy
);
}
@ -73,7 +76,21 @@ public class LoadPlanBuildingHelper {
return new CompositeFetch(
loadPlanBuildingContext.getSessionFactory(),
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.List;
import org.hibernate.loader.plan.spi.CollectionReturn;
import org.hibernate.loader.plan.spi.EntityReturn;
import org.hibernate.loader.plan.spi.LoadPlan;
import org.hibernate.loader.plan.spi.Return;
@ -36,29 +37,45 @@ import org.hibernate.loader.plan.spi.Return;
* @author Steve Ebersole
*/
public class LoadPlanImpl implements LoadPlan {
private final boolean hasScalars;
private final List<Return> returns;
private final List<? extends Return> returns;
private final Disposition disposition;
private final boolean areLazyAttributesForceFetched;
public LoadPlanImpl(boolean hasScalars, List<Return> returns) {
this.hasScalars = hasScalars;
protected LoadPlanImpl(List<? extends Return> returns, Disposition disposition, boolean areLazyAttributesForceFetched) {
this.returns = returns;
this.disposition = disposition;
this.areLazyAttributesForceFetched = areLazyAttributesForceFetched;
}
public LoadPlanImpl(boolean hasScalars, Return rootReturn) {
this( hasScalars, Collections.singletonList( rootReturn ) );
public LoadPlanImpl(EntityReturn rootReturn) {
this( Collections.singletonList( rootReturn ), Disposition.ENTITY_LOADER, false );
}
public LoadPlanImpl(EntityReturn entityReturn) {
this( false, entityReturn );
public LoadPlanImpl(CollectionReturn rootReturn) {
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
public boolean hasAnyScalarReturns() {
return hasScalars;
}
@Override
public List<Return> getReturns() {
return returns;
return disposition == Disposition.MIXED;
}
}

View File

@ -91,7 +91,15 @@ public class SingleRootReturnLoadPlanBuilderStrategy
@Override
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

View File

@ -27,6 +27,7 @@ import org.hibernate.LockMode;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.PropertyPath;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;
/**
@ -44,14 +45,15 @@ public abstract class AbstractCollectionReference extends AbstractPlanNode imple
SessionFactoryImplementor sessionFactory,
LockMode lockMode,
CollectionPersister collectionPersister,
PropertyPath propertyPath) {
PropertyPath propertyPath,
EntityReference ownerEntityReference) {
super( sessionFactory );
this.lockMode = lockMode;
this.collectionPersister = collectionPersister;
this.propertyPath = propertyPath;
this.indexGraph = buildIndexGraph( getCollectionPersister() );
this.elementGraph = buildElementGraph( getCollectionPersister() );
this.elementGraph = buildElementGraph( getCollectionPersister(), ownerEntityReference );
}
private FetchableCollectionIndex buildIndexGraph(CollectionPersister persister) {
@ -70,10 +72,30 @@ public abstract class AbstractCollectionReference extends AbstractPlanNode imple
return null;
}
private FetchableCollectionElement buildElementGraph(CollectionPersister persister) {
private FetchableCollectionElement buildElementGraph(
CollectionPersister persister,
EntityReference ownerEntityReference) {
final Type type = persister.getElementType();
if ( type.isAssociationType() ) {
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() );
}
}
@ -119,8 +141,22 @@ public abstract class AbstractCollectionReference extends AbstractPlanNode imple
return elementGraph;
}
@Override
public boolean hasEntityElements() {
return getCollectionPersister().isOneToMany() || getCollectionPersister().isManyToMany();
private class BidirectionalEntityElementGraph extends EntityElementGraph implements BidirectionalEntityFetch {
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.loader.plan.internal.LoadPlanBuildingHelper;
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.AttributeDefinition;
import org.hibernate.persister.walking.spi.CompositionDefinition;
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() ] );
}
/**
* Abstract method returning the delegate for obtaining details about an owned fetch.
* @return the delegate
*/
protected abstract FetchOwnerDelegate getFetchOwnerDelegate();
@Override
public boolean isNullable(Fetch fetch) {
return getFetchOwnerDelegate().locateFetchMetadata( fetch ).isNullable();
return fetch.isNullable();
}
@Override
public Type getType(Fetch fetch) {
return getFetchOwnerDelegate().locateFetchMetadata( fetch ).getType();
return fetch.getFetchedType();
}
@Override
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

View File

@ -28,6 +28,8 @@ import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.spi.SessionFactoryImplementor;
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}.
@ -37,7 +39,7 @@ import org.hibernate.loader.PropertyPath;
*/
public abstract class AbstractSingularAttributeFetch extends AbstractFetchOwner implements Fetch {
private final FetchOwner owner;
private final String ownerProperty;
private final AttributeDefinition fetchedAttribute;
private final FetchStrategy fetchStrategy;
private final PropertyPath propertyPath;
@ -47,22 +49,22 @@ public abstract class AbstractSingularAttributeFetch extends AbstractFetchOwner
*
* @param factory - the session factory.
* @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.
*/
public AbstractSingularAttributeFetch(
SessionFactoryImplementor factory,
FetchOwner owner,
String ownerProperty,
AttributeDefinition fetchedAttribute,
FetchStrategy fetchStrategy) {
super( factory );
this.owner = owner;
this.ownerProperty = ownerProperty;
this.fetchedAttribute = fetchedAttribute;
this.fetchStrategy = fetchStrategy;
owner.addFetch( this );
this.propertyPath = owner.getPropertyPath().append( ownerProperty );
this.propertyPath = owner.getPropertyPath().append( fetchedAttribute.getName() );
}
public AbstractSingularAttributeFetch(
@ -71,7 +73,7 @@ public abstract class AbstractSingularAttributeFetch extends AbstractFetchOwner
FetchOwner fetchOwnerCopy) {
super( original, copyContext );
this.owner = fetchOwnerCopy;
this.ownerProperty = original.ownerProperty;
this.fetchedAttribute = original.fetchedAttribute;
this.fetchStrategy = original.fetchStrategy;
this.propertyPath = original.propertyPath;
}
@ -81,14 +83,19 @@ public abstract class AbstractSingularAttributeFetch extends AbstractFetchOwner
return owner;
}
public AttributeDefinition getFetchedAttribute() {
return fetchedAttribute;
}
@Override
public String getOwnerPropertyName() {
return ownerProperty;
public Type getFetchedType() {
return fetchedAttribute.getType();
}
@Override
public boolean isNullable() {
return owner.isNullable( this );
return fetchedAttribute.isNullable();
// return owner.isNullable( this );
}
@Override
@ -102,10 +109,23 @@ public abstract class AbstractSingularAttributeFetch extends AbstractFetchOwner
}
@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 ( 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
* indicated by the @author tags or express copyright attribution
@ -21,24 +21,15 @@
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.spi;
import org.hibernate.engine.spi.SessionFactoryImplementor;
package org.hibernate.loader.plan.spi;
/**
* 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 {
/**
* 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);
public interface BidirectionalEntityFetch {
public EntityReference getTargetEntityReference();
}

View File

@ -23,19 +23,34 @@
*/
package org.hibernate.loader.plan.spi;
import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.jboss.logging.Logger;
import org.hibernate.LockMode;
import org.hibernate.collection.spi.PersistentCollection;
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.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
*/
public class CollectionFetch extends AbstractCollectionReference implements Fetch {
private static final Logger log = CoreLogging.logger( CollectionFetch.class );
private final FetchOwner fetchOwner;
private final AttributeDefinition fetchedAttribute;
private final FetchStrategy fetchStrategy;
public CollectionFetch(
@ -43,16 +58,16 @@ public class CollectionFetch extends AbstractCollectionReference implements Fetc
LockMode lockMode,
FetchOwner fetchOwner,
FetchStrategy fetchStrategy,
String ownerProperty) {
AttributeDefinition fetchedAttribute) {
super(
sessionFactory,
lockMode,
sessionFactory.getCollectionPersister(
fetchOwner.retrieveFetchSourcePersister().getEntityName() + '.' + ownerProperty
),
fetchOwner.getPropertyPath().append( ownerProperty )
sessionFactory.getCollectionPersister( ( (CollectionType) fetchedAttribute.getType() ).getRole() ),
fetchOwner.getPropertyPath().append( fetchedAttribute.getName() ),
(EntityReference) fetchOwner
);
this.fetchOwner = fetchOwner;
this.fetchedAttribute = fetchedAttribute;
this.fetchStrategy = fetchStrategy;
fetchOwner.addFetch( this );
}
@ -60,6 +75,7 @@ public class CollectionFetch extends AbstractCollectionReference implements Fetc
protected CollectionFetch(CollectionFetch original, CopyContext copyContext, FetchOwner fetchOwnerCopy) {
super( original, copyContext );
this.fetchOwner = fetchOwnerCopy;
this.fetchedAttribute = original.fetchedAttribute;
this.fetchStrategy = original.fetchStrategy;
}
@ -69,8 +85,8 @@ public class CollectionFetch extends AbstractCollectionReference implements Fetc
}
@Override
public String getOwnerPropertyName() {
return getPropertyPath().getProperty();
public CollectionType getFetchedType() {
return (CollectionType) fetchedAttribute.getType();
}
@Override
@ -78,9 +94,15 @@ public class CollectionFetch extends AbstractCollectionReference implements Fetc
return true;
}
@Override
public String getAdditionalJoinConditions() {
// only pertinent for HQL...
return null;
}
@Override
public String[] toSqlSelectFragments(String alias) {
return getOwner().toSqlSelectFragments( this, alias );
return getOwner().toSqlSelectFragmentResolver().toSqlSelectFragments( alias, fetchedAttribute );
}
@Override
@ -95,7 +117,84 @@ public class CollectionFetch extends AbstractCollectionReference implements Fetc
@Override
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

View File

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

View File

@ -23,13 +23,9 @@
*/
package org.hibernate.loader.plan.spi;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.hibernate.LockMode;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.PropertyPath;
import org.hibernate.loader.spi.ResultSetProcessingContext;
/**
* @author Steve Ebersole
@ -47,7 +43,10 @@ public class CollectionReturn extends AbstractCollectionReference implements Ret
sessionFactory,
lockMode,
sessionFactory.getCollectionPersister( ownerEntityName + '.' + ownerProperty ),
new PropertyPath() // its a root
// its a root
new PropertyPath(),
// no owner
null
);
this.ownerEntityName = ownerEntityName;
this.ownerProperty = ownerProperty;
@ -77,21 +76,6 @@ public class CollectionReturn extends AbstractCollectionReference implements Ret
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
public String toString() {
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.entity.EntityPersister;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.type.CompositeType;
/**
@ -21,7 +22,7 @@ public class CompositeElementGraph extends AbstractFetchOwner implements Fetchab
private final CollectionReference collectionReference;
private final PropertyPath propertyPath;
private final CollectionPersister collectionPersister;
private final FetchOwnerDelegate fetchOwnerDelegate;
private final CompositeBasedSqlSelectFragmentResolver sqlSelectFragmentResolver;
/**
* Constructs a {@link CompositeElementGraph}.
@ -39,15 +40,16 @@ public class CompositeElementGraph extends AbstractFetchOwner implements Fetchab
this.collectionReference = collectionReference;
this.collectionPersister = collectionReference.getCollectionPersister();
this.propertyPath = collectionPath.append( "<elements>" );
this.fetchOwnerDelegate = new CompositeFetchOwnerDelegate(
this.sqlSelectFragmentResolver = new CompositeBasedSqlSelectFragmentResolver(
sessionFactory,
(CompositeType) collectionPersister.getElementType(),
new CompositeFetchOwnerDelegate.PropertyMappingDelegate() {
new CompositeBasedSqlSelectFragmentResolver.BaseSqlSelectFragmentResolver() {
@Override
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.collectionPersister = original.collectionPersister;
this.propertyPath = original.propertyPath;
this.fetchOwnerDelegate = original.fetchOwnerDelegate;
this.sqlSelectFragmentResolver = original.sqlSelectFragmentResolver;
}
@Override
@ -65,7 +67,7 @@ public class CompositeElementGraph extends AbstractFetchOwner implements Fetchab
}
@Override
public void validateFetchPlan(FetchStrategy fetchStrategy) {
public void validateFetchPlan(FetchStrategy fetchStrategy, AttributeDefinition attributeDefinition) {
}
@Override
@ -83,11 +85,6 @@ public class CompositeElementGraph extends AbstractFetchOwner implements Fetchab
return new CompositeElementGraph( this, copyContext );
}
@Override
protected FetchOwnerDelegate getFetchOwnerDelegate() {
return fetchOwnerDelegate;
}
@Override
public CollectionFetch buildCollectionFetch(
AssociationAttributeDefinition attributeDefinition,
@ -95,4 +92,9 @@ public class CompositeElementGraph extends AbstractFetchOwner implements Fetchab
LoadPlanBuildingContext loadPlanBuildingContext) {
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.SQLException;
import org.hibernate.LockMode;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.EntityKey;
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.walking.spi.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.type.CompositeType;
/**
@ -43,27 +50,28 @@ import org.hibernate.type.CompositeType;
public class CompositeFetch extends AbstractSingularAttributeFetch {
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.
*
* @param sessionFactory - the session factory.
* @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(
SessionFactoryImplementor sessionFactory,
final FetchOwner owner,
String ownerProperty) {
super( sessionFactory, owner, ownerProperty, FETCH_PLAN );
this.delegate = new CompositeFetchOwnerDelegate(
final AttributeDefinition fetchedAttribute) {
super( sessionFactory, owner, fetchedAttribute, FETCH_PLAN );
this.sqlSelectFragmentResolver = new CompositeBasedSqlSelectFragmentResolver(
sessionFactory,
(CompositeType) getOwner().getType( this ),
new CompositeFetchOwnerDelegate.PropertyMappingDelegate() {
(CompositeType) fetchedAttribute.getType(),
new CompositeBasedSqlSelectFragmentResolver.BaseSqlSelectFragmentResolver() {
@Override
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) {
super( original, copyContext, fetchOwnerCopy );
this.delegate = original.getFetchOwnerDelegate();
this.sqlSelectFragmentResolver = original.sqlSelectFragmentResolver;
}
@Override
protected FetchOwnerDelegate getFetchOwnerDelegate() {
return delegate;
public SqlSelectFragmentResolver toSqlSelectFragmentResolver() {
return sqlSelectFragmentResolver;
}
@Override
@ -86,12 +94,26 @@ public class CompositeFetch extends AbstractSingularAttributeFetch {
@Override
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
//To change body of implemented methods use File | Settings | File Templates.
// anything to do?
}
@Override
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
@ -101,4 +123,18 @@ public class CompositeFetch extends AbstractSingularAttributeFetch {
copyContext.getReturnGraphVisitationStrategy().finishingCompositeFetch( this );
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.entity.EntityPersister;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.type.CompositeType;
/**
@ -21,7 +22,7 @@ public class CompositeIndexGraph extends AbstractFetchOwner implements Fetchable
private final CollectionReference collectionReference;
private final PropertyPath propertyPath;
private final CollectionPersister collectionPersister;
private final FetchOwnerDelegate fetchOwnerDelegate;
private final CompositeBasedSqlSelectFragmentResolver sqlSelectFragmentResolver;
/**
* Constructs a {@link CompositeElementGraph}.
@ -38,10 +39,10 @@ public class CompositeIndexGraph extends AbstractFetchOwner implements Fetchable
this.collectionReference = collectionReference;
this.collectionPersister = collectionReference.getCollectionPersister();
this.propertyPath = collectionPath.append( "<index>" );
this.fetchOwnerDelegate = new CompositeFetchOwnerDelegate(
this.sqlSelectFragmentResolver = new CompositeBasedSqlSelectFragmentResolver(
sessionFactory,
(CompositeType) collectionPersister.getIndexType(),
new CompositeFetchOwnerDelegate.PropertyMappingDelegate() {
new CompositeBasedSqlSelectFragmentResolver.BaseSqlSelectFragmentResolver() {
@Override
public String[] toSqlSelectFragments(String alias) {
return ( (QueryableCollection) collectionPersister ).getIndexColumnNames( alias );
@ -55,11 +56,11 @@ public class CompositeIndexGraph extends AbstractFetchOwner implements Fetchable
this.collectionReference = original.collectionReference;
this.collectionPersister = original.collectionPersister;
this.propertyPath = original.propertyPath;
this.fetchOwnerDelegate = original.fetchOwnerDelegate;
this.sqlSelectFragmentResolver = original.sqlSelectFragmentResolver;
}
@Override
public void validateFetchPlan(FetchStrategy fetchStrategy) {
public void validateFetchPlan(FetchStrategy fetchStrategy, AttributeDefinition attributeDefinition) {
}
@Override
@ -82,11 +83,6 @@ public class CompositeIndexGraph extends AbstractFetchOwner implements Fetchable
return new CompositeIndexGraph( this, copyContext );
}
@Override
protected FetchOwnerDelegate getFetchOwnerDelegate() {
return fetchOwnerDelegate;
}
@Override
public CollectionFetch buildCollectionFetch(
AssociationAttributeDefinition attributeDefinition,
@ -95,4 +91,9 @@ public class CompositeIndexGraph extends AbstractFetchOwner implements Fetchable
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.spi.SessionFactoryImplementor;
import org.hibernate.loader.PropertyPath;
import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext;
import org.hibernate.persister.collection.CollectionPersister;
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.EntityType;
/**
* 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 EntityPersister elementPersister;
private final PropertyPath propertyPath;
private final FetchOwnerDelegate fetchOwnerDelegate;
private final EntityPersisterBasedSqlSelectFragmentResolver sqlSelectFragmentResolver;
private IdentifierDescription identifierDescription;
@ -42,7 +47,7 @@ public class EntityElementGraph extends AbstractFetchOwner implements FetchableC
this.elementType = (AssociationType) collectionPersister.getElementType();
this.elementPersister = (EntityPersister) this.elementType.getAssociatedJoinable( sessionFactory() );
this.propertyPath = collectionPath;
this.fetchOwnerDelegate = new EntityFetchOwnerDelegate( elementPersister );
this.sqlSelectFragmentResolver = new EntityPersisterBasedSqlSelectFragmentResolver( (Queryable) elementPersister );
}
public EntityElementGraph(EntityElementGraph original, CopyContext copyContext) {
@ -53,7 +58,7 @@ public class EntityElementGraph extends AbstractFetchOwner implements FetchableC
this.elementType = original.elementType;
this.elementPersister = original.elementPersister;
this.propertyPath = original.propertyPath;
this.fetchOwnerDelegate = original.fetchOwnerDelegate;
this.sqlSelectFragmentResolver = original.sqlSelectFragmentResolver;
}
@Override
@ -77,7 +82,7 @@ public class EntityElementGraph extends AbstractFetchOwner implements FetchableC
}
@Override
public void validateFetchPlan(FetchStrategy fetchStrategy) {
public void validateFetchPlan(FetchStrategy fetchStrategy, AttributeDefinition attributeDefinition) {
}
@Override
@ -111,7 +116,65 @@ public class EntityElementGraph extends AbstractFetchOwner implements FetchableC
}
@Override
protected FetchOwnerDelegate getFetchOwnerDelegate() {
return fetchOwnerDelegate;
public SqlSelectFragmentResolver toSqlSelectFragmentResolver() {
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.WrongClassException;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.EntityKey;
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.Queryable;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.persister.walking.spi.WalkingException;
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
* {@link FetchOwner} of the entity association sub-attribute fetches.
@ -43,35 +47,44 @@ import org.hibernate.type.EntityType;
* @author Steve Ebersole
*/
public class EntityFetch extends AbstractSingularAttributeFetch implements EntityReference, Fetch {
private final EntityPersister persister;
private final LockMode lockMode;
private final FetchOwnerDelegate fetchOwnerDelegate;
private final EntityPersisterBasedSqlSelectFragmentResolver sqlSelectFragmentResolver;
private IdentifierDescription identifierDescription;
// todo : remove these
private final LockMode lockMode;
/**
* Constructs an {@link EntityFetch} object.
*
* @param sessionFactory - the session factory.
* @param lockMode - the lock mode.
* @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.
*/
public EntityFetch(
SessionFactoryImplementor sessionFactory,
LockMode lockMode,
FetchOwner owner,
String ownerProperty,
AttributeDefinition fetchedAttribute,
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.fetchOwnerDelegate = new EntityFetchOwnerDelegate( persister );
}
/**
@ -83,16 +96,19 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit
protected EntityFetch(EntityFetch original, CopyContext copyContext, FetchOwner fetchOwnerCopy) {
super( original, copyContext, fetchOwnerCopy );
this.persister = original.persister;
this.sqlSelectFragmentResolver = original.sqlSelectFragmentResolver;
this.lockMode = original.lockMode;
this.fetchOwnerDelegate = original.fetchOwnerDelegate;
}
/**
* Returns the entity type for this fetch.
* @return the entity type for this fetch.
*/
public final EntityType getEntityType() {
return (EntityType) getOwner().getType( this );
@Override
public EntityType getFetchedType() {
return (EntityType) super.getFetchedType();
}
@Override
public String[] toSqlSelectFragments(String alias) {
return getOwner().toSqlSelectFragmentResolver().toSqlSelectFragments( alias, getFetchedAttribute() );
}
@Override
@ -105,6 +121,11 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit
return persister;
}
@Override
public SqlSelectFragmentResolver toSqlSelectFragmentResolver() {
return sqlSelectFragmentResolver;
}
@Override
public IdentifierDescription getIdentifierDescription() {
return identifierDescription;
@ -127,12 +148,6 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit
@Override
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 );
for ( Fetch fetch : getFetches() ) {
@ -142,23 +157,25 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit
@Override
public EntityKey resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
final ResultSetProcessingContext.IdentifierResolutionContext identifierResolutionContext = context.getIdentifierResolutionContext( this );
EntityKey entityKey = identifierResolutionContext.getEntityKey();
final ResultSetProcessingContext.EntityReferenceProcessingState entityReferenceProcessingState = context.getProcessingState(
this
);
EntityKey entityKey = entityReferenceProcessingState.getEntityKey();
if ( entityKey == null ) {
entityKey = identifierDescription.resolve( resultSet, context );
if ( entityKey == null ) {
// register the non-existence (though only for one-to-one associations)
if ( getEntityType().isOneToOne() ) {
if ( getFetchedType().isOneToOne() ) {
// 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 ) {
context.getSession().getPersistenceContext()
.addNullProperty( ownersEntityKey, getEntityType().getPropertyName() );
.addNullProperty( ownersEntityKey, getFetchedType().getPropertyName() );
}
}
}
identifierResolutionContext.registerEntityKey( entityKey );
entityReferenceProcessingState.registerEntityKey( entityKey );
for ( Fetch fetch : getFetches() ) {
fetch.resolve( resultSet, context );
@ -168,6 +185,19 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit
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
* as the entity key for this fetch..
@ -205,7 +235,7 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit
context.checkVersion(
resultSet,
persister,
context.getLoadQueryAliasResolutionContext().resolveEntityColumnAliases( this ),
context.getAliasResolutionContext().resolveAliases( this ).getColumnAliases(),
entityKey,
existing
);
@ -218,7 +248,7 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit
final String concreteEntityTypeName = context.getConcreteEntityTypeName(
resultSet,
persister,
context.getLoadQueryAliasResolutionContext().resolveEntityColumnAliases( this ),
context.getAliasResolutionContext().resolveAliases( this ).getColumnAliases(),
entityKey
);
@ -239,15 +269,16 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit
entityInstance,
concreteEntityTypeName,
entityKey,
context.getLoadQueryAliasResolutionContext().resolveEntityColumnAliases( this ),
context.getAliasResolutionContext().resolveAliases( this ).getColumnAliases(),
acquiredLockMode,
persister,
getFetchStrategy().getTiming() == FetchTiming.IMMEDIATE,
getEntityType()
getFetchStrategy(),
true,
getFetchedType()
);
// materialize associations (and initialize the object) later
context.registerHydratedEntity( persister, entityKey, entityInstance );
context.registerHydratedEntity( this, entityKey, entityInstance );
}
return entityKey;
@ -265,9 +296,4 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit
copyContext.getReturnGraphVisitationStrategy().finishingEntityFetch( this );
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.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.type.AssociationType;
/**
@ -42,7 +44,7 @@ public class EntityIndexGraph extends AbstractFetchOwner implements FetchableCol
private final AssociationType indexType;
private final EntityPersister indexPersister;
private final PropertyPath propertyPath;
private final FetchOwnerDelegate fetchOwnerDelegate;
private final EntityPersisterBasedSqlSelectFragmentResolver sqlSelectFragmentResolver;
private IdentifierDescription identifierDescription;
@ -63,7 +65,7 @@ public class EntityIndexGraph extends AbstractFetchOwner implements FetchableCol
this.indexType = (AssociationType) collectionPersister.getIndexType();
this.indexPersister = (EntityPersister) this.indexType.getAssociatedJoinable( sessionFactory() );
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) {
@ -73,7 +75,7 @@ public class EntityIndexGraph extends AbstractFetchOwner implements FetchableCol
this.indexType = original.indexType;
this.indexPersister = original.indexPersister;
this.propertyPath = original.propertyPath;
this.fetchOwnerDelegate = original.fetchOwnerDelegate;
this.sqlSelectFragmentResolver = original.sqlSelectFragmentResolver;
}
/**
@ -100,7 +102,7 @@ public class EntityIndexGraph extends AbstractFetchOwner implements FetchableCol
}
@Override
public void validateFetchPlan(FetchStrategy fetchStrategy) {
public void validateFetchPlan(FetchStrategy fetchStrategy, AttributeDefinition attributeDefinition) {
}
@Override
@ -129,7 +131,7 @@ public class EntityIndexGraph extends AbstractFetchOwner implements FetchableCol
}
@Override
protected FetchOwnerDelegate getFetchOwnerDelegate() {
return fetchOwnerDelegate;
public SqlSelectFragmentResolver toSqlSelectFragmentResolver() {
return sqlSelectFragmentResolver;
}
}

View File

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

View File

@ -24,7 +24,7 @@
package org.hibernate.loader.plan.spi;
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;
/**

View File

@ -23,19 +23,13 @@
*/
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.engine.FetchStrategy;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.PropertyPath;
import org.hibernate.loader.spi.ResultSetProcessingContext;
import org.hibernate.persister.entity.EntityPersister;
import static org.hibernate.loader.spi.ResultSetProcessingContext.IdentifierResolutionContext;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.persister.walking.spi.AttributeDefinition;
/**
* 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
*/
public class EntityReturn extends AbstractFetchOwner implements Return, EntityReference, CopyableReturn {
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 FetchOwnerDelegate fetchOwnerDelegate;
private IdentifierDescription identifierDescription;
/**
* Construct an {@link EntityReturn}.
*
@ -70,15 +61,19 @@ public class EntityReturn extends AbstractFetchOwner implements Return, EntityRe
String entityName) {
super( sessionFactory );
this.persister = sessionFactory.getEntityPersister( entityName );
this.propertyPath = new PropertyPath( entityName );
this.sqlSelectFragmentResolver = new EntityPersisterBasedSqlSelectFragmentResolver( (Queryable) persister );
this.lockMode = lockMode;
this.fetchOwnerDelegate = new EntityFetchOwnerDelegate( persister );
}
protected EntityReturn(EntityReturn original, CopyContext copyContext) {
super( original, copyContext );
this.persister = original.persister;
this.propertyPath = original.propertyPath;
this.sqlSelectFragmentResolver = original.sqlSelectFragmentResolver;
this.lockMode = original.lockMode;
this.fetchOwnerDelegate = original.fetchOwnerDelegate;
}
@Override
@ -102,7 +97,7 @@ public class EntityReturn extends AbstractFetchOwner implements Return, EntityRe
}
@Override
public void validateFetchPlan(FetchStrategy fetchStrategy) {
public void validateFetchPlan(FetchStrategy fetchStrategy, AttributeDefinition attributeDefinition) {
}
@Override
@ -115,67 +110,6 @@ public class EntityReturn extends AbstractFetchOwner implements Return, EntityRe
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
public void injectIdentifierDescription(IdentifierDescription identifierDescription) {
this.identifierDescription = identifierDescription;
@ -192,7 +126,7 @@ public class EntityReturn extends AbstractFetchOwner implements Return, EntityRe
}
@Override
protected FetchOwnerDelegate getFetchOwnerDelegate() {
return fetchOwnerDelegate;
public SqlSelectFragmentResolver toSqlSelectFragmentResolver() {
return sqlSelectFragmentResolver;
}
}

View File

@ -28,7 +28,8 @@ import java.sql.SQLException;
import org.hibernate.engine.FetchStrategy;
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.
@ -46,25 +47,13 @@ public interface Fetch extends CopyableFetch {
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();
/**
* 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);
public Type getFetchedType();
/**
* Gets the fetch strategy for this fetch.
@ -74,16 +63,27 @@ public interface Fetch extends CopyableFetch {
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 Object resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException;
public void read(ResultSet resultSet, ResultSetProcessingContext context, Object owner) throws SQLException;
@Override
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.loader.PropertyPath;
import org.hibernate.loader.plan.spi.build.AbstractLoadPlanBuilderStrategy;
import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext;
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.AttributeDefinition;
import org.hibernate.persister.walking.spi.CompositionDefinition;
import org.hibernate.type.Type;
@ -89,9 +93,10 @@ public interface FetchOwner {
/**
* 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.
@ -121,5 +126,11 @@ public interface FetchOwner {
CompositionDefinition attributeDefinition,
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 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
@ -38,4 +39,6 @@ public interface IdentifierDescription {
public void hydrate(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
*/
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.
*
@ -53,7 +73,26 @@ public interface LoadPlan {
*/
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:
// 1) follow-on locking

View File

@ -23,11 +23,6 @@
*/
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!
* <p/>
@ -40,28 +35,4 @@ import org.hibernate.loader.spi.ResultSetProcessingContext;
* @author Steve Ebersole
*/
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;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.spi.ResultSetProcessingContext;
import org.hibernate.type.Type;
/**
* Represent a simple scalar return within a query result. Generally this would be values of basic (String, Integer,
* 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
*/
@ -47,23 +46,4 @@ public class ScalarReturn extends AbstractPlanNode implements Return {
public Type getType() {
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.cfg.NotYetImplementedException;
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.SessionFactoryImplementor;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.loader.PropertyPath;
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.CollectionReference;
import org.hibernate.loader.plan.spi.CollectionReturn;
import org.hibernate.loader.plan.spi.CompositeElementGraph;
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.EntityPersisterBasedSqlSelectFragmentResolver;
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.FetchOwnerDelegate;
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.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.Loadable;
import org.hibernate.persister.entity.Queryable;
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.AttributeDefinition;
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.EntityIdentifierDefinition;
import org.hibernate.persister.walking.spi.WalkingException;
import org.hibernate.type.CompositeType;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;
import static org.hibernate.loader.spi.ResultSetProcessingContext.IdentifierResolutionContext;
/**
* @author Steve Ebersole
*/
@ -313,12 +314,10 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
@Override
public void startingCompositeCollectionElement(CompositeCollectionElementDefinition compositeElementDefinition) {
System.out.println(
String.format(
"%s Starting composite collection element for (%s)",
StringHelper.repeat( ">>", fetchOwnerStack.size() ),
compositeElementDefinition.getCollectionDefinition().getCollectionPersister().getRole()
)
log.tracef(
"%s Starting composite collection element for (%s)",
StringHelper.repeat( ">>", fetchOwnerStack.size() ),
compositeElementDefinition.getCollectionDefinition().getCollectionPersister().getRole()
);
}
@ -397,16 +396,17 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
final Type attributeType = attributeDefinition.getType();
final boolean isComponentType = attributeType.isComponentType();
final boolean isBasicType = ! ( isComponentType || attributeType.isAssociationType() );
final boolean isAssociationType = attributeType.isAssociationType();
final boolean isBasicType = ! ( isComponentType || isAssociationType );
if ( isBasicType ) {
return true;
}
else if ( isComponentType ) {
return handleCompositeAttribute( (CompositionDefinition) attributeDefinition );
else if ( isAssociationType ) {
return handleAssociationAttribute( (AssociationAttributeDefinition) attributeDefinition );
}
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) {
final FetchOwner fetchOwner = currentFetchOwner();
final CompositeFetch fetch = fetchOwner.buildCompositeFetch( attributeDefinition, this );
@ -427,26 +445,35 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
}
protected boolean handleAssociationAttribute(AssociationAttributeDefinition attributeDefinition) {
// todo : this seems to not be correct for one-to-one
final FetchStrategy fetchStrategy = determineFetchPlan( attributeDefinition );
if ( fetchStrategy.getTiming() != FetchTiming.IMMEDIATE ) {
if ( fetchStrategy.getStyle() != FetchStyle.JOIN ) {
return false;
}
// if ( fetchStrategy.getTiming() != FetchTiming.IMMEDIATE ) {
// return false;
// }
final FetchOwner fetchOwner = currentFetchOwner();
fetchOwner.validateFetchPlan( fetchStrategy );
fetchOwner.validateFetchPlan( fetchStrategy, attributeDefinition );
final Fetch associationFetch;
if ( attributeDefinition.isCollection() ) {
associationFetch = fetchOwner.buildCollectionFetch( attributeDefinition, fetchStrategy, this );
pushToCollectionStack( (CollectionReference) associationFetch );
final AssociationAttributeDefinition.AssociationNature nature = attributeDefinition.getAssociationNature();
if ( nature == AssociationAttributeDefinition.AssociationNature.ANY ) {
return false;
// throw new NotYetImplementedException( "AnyType support still in progress" );
}
else {
else if ( nature == AssociationAttributeDefinition.AssociationNature.ENTITY ) {
associationFetch = fetchOwner.buildEntityFetch(
attributeDefinition,
fetchStrategy,
this
);
}
else {
associationFetch = fetchOwner.buildCollectionFetch( attributeDefinition, fetchStrategy, this );
pushToCollectionStack( (CollectionReference) associationFetch );
}
if ( FetchOwner.class.isInstance( associationFetch) ) {
pushToStack( (FetchOwner) associationFetch );
@ -521,12 +548,16 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
protected static abstract class AbstractIdentifierAttributeCollector extends AbstractFetchOwner
implements FetchOwner, EntityReference, FetchStackAware {
protected final EntityReference entityReference;
private final EntityPersisterBasedSqlSelectFragmentResolver sqlSelectFragmentResolver;
protected final Map<Fetch,HydratedCompoundValueHandler> fetchToHydratedStateExtractorMap
= new HashMap<Fetch, HydratedCompoundValueHandler>();
public AbstractIdentifierAttributeCollector(SessionFactoryImplementor sessionFactory, EntityReference entityReference) {
super( sessionFactory );
this.entityReference = entityReference;
this.sqlSelectFragmentResolver = new EntityPersisterBasedSqlSelectFragmentResolver(
(Queryable) entityReference.getEntityPersister()
);
}
@Override
@ -557,6 +588,15 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
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
public EntityFetch buildEntityFetch(
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. 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 );
// pretty sure this HydratedCompoundValueExtractor stuff is not needed...
fetchToHydratedStateExtractorMap.put( fetch, attributeDefinition.getHydratedCompoundValueExtractor() );
return fetch;
}
@Override
public Type getType(Fetch fetch) {
return getFetchOwnerDelegate().locateFetchMetadata( fetch ).getType();
return fetch.getFetchedType();
}
@Override
public boolean isNullable(Fetch fetch) {
return getFetchOwnerDelegate().locateFetchMetadata( fetch ).isNullable();
return fetch.isNullable();
}
@Override
public String[] toSqlSelectFragments(Fetch fetch, String alias) {
return getFetchOwnerDelegate().locateFetchMetadata( fetch ).toSqlSelectFragments( alias );
return fetch.toSqlSelectFragments( alias );
}
@Override
public SqlSelectFragmentResolver toSqlSelectFragmentResolver() {
return sqlSelectFragmentResolver;
}
@Override
@ -596,8 +686,8 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
protected abstract IdentifierDescription buildIdentifierDescription();
@Override
public void validateFetchPlan(FetchStrategy fetchStrategy) {
( (FetchOwner) entityReference ).validateFetchPlan( fetchStrategy );
public void validateFetchPlan(FetchStrategy fetchStrategy, AttributeDefinition attributeDefinition) {
( (FetchOwner) entityReference ).validateFetchPlan( fetchStrategy, attributeDefinition );
}
@Override
@ -615,51 +705,12 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
protected static class EncapsulatedIdentifierAttributeCollector extends AbstractIdentifierAttributeCollector {
private final PropertyPath propertyPath;
private final FetchOwnerDelegate delegate;
public EncapsulatedIdentifierAttributeCollector(
final SessionFactoryImplementor sessionFactory,
final EntityReference entityReference) {
super( sessionFactory, entityReference );
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
@ -671,11 +722,6 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
);
}
@Override
protected FetchOwnerDelegate getFetchOwnerDelegate() {
return delegate;
}
@Override
public PropertyPath getPropertyPath() {
return propertyPath;
@ -684,65 +730,12 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
protected static class NonEncapsulatedIdentifierAttributeCollector extends AbstractIdentifierAttributeCollector {
private final PropertyPath propertyPath;
private final FetchOwnerDelegate fetchOwnerDelegate;
public NonEncapsulatedIdentifierAttributeCollector(
final SessionFactoryImplementor sessionfactory,
final EntityReference entityReference) {
super( sessionfactory, entityReference );
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
@ -758,13 +751,6 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
public PropertyPath getPropertyPath() {
return propertyPath;
}
@Override
protected FetchOwnerDelegate getFetchOwnerDelegate() {
return fetchOwnerDelegate;
}
}
private static class IdentifierDescriptionImpl implements IdentifierDescription {
@ -786,26 +772,31 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
return identifierFetches;
}
@Override
public HydratedCompoundValueHandler getHydratedStateHandler(Fetch fetch) {
return fetchToHydratedStateExtractorMap == null ? null : fetchToHydratedStateExtractorMap.get( fetch );
}
@Override
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
final IdentifierResolutionContext ownerIdentifierResolutionContext =
context.getIdentifierResolutionContext( entityReference );
final Object ownerIdentifierHydratedState = ownerIdentifierResolutionContext.getHydratedForm();
final ResultSetProcessingContext.EntityReferenceProcessingState ownerEntityReferenceProcessingState =
context.getProcessingState( entityReference );
final Object ownerIdentifierHydratedState = ownerEntityReferenceProcessingState.getIdentifierHydratedForm();
if ( ownerIdentifierHydratedState != null ) {
for ( Fetch fetch : identifierFetches ) {
if ( fetch instanceof EntityFetch ) {
final IdentifierResolutionContext identifierResolutionContext =
context.getIdentifierResolutionContext( (EntityFetch) fetch );
final ResultSetProcessingContext.EntityReferenceProcessingState fetchEntityReferenceProcessingState =
context.getProcessingState( (EntityFetch) fetch );
// if the identifier was already hydrated, nothing to do
if ( identifierResolutionContext.getHydratedForm() != null ) {
if ( fetchEntityReferenceProcessingState.getIdentifierHydratedForm() != null ) {
continue;
}
// try to extract the sub-hydrated value from the owners tuple array
if ( fetchToHydratedStateExtractorMap != null && ownerIdentifierHydratedState != null ) {
Serializable extracted = (Serializable) fetchToHydratedStateExtractorMap.get( fetch )
.extract( ownerIdentifierHydratedState );
identifierResolutionContext.registerHydratedForm( extracted );
fetchEntityReferenceProcessingState.registerIdentifierHydratedForm( extracted );
continue;
}
@ -819,13 +810,40 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
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(
resultSet,
context.getLoadQueryAliasResolutionContext().resolveEntityColumnAliases( entityReference ).getSuffixedKeyAliases(),
columnNames,
context.getSession(),
null
);
context.getIdentifierResolutionContext( entityReference ).registerHydratedForm( hydratedIdentifierState );
context.getProcessingState( entityReference ).registerIdentifierHydratedForm( hydratedIdentifierState );
}
@Override
@ -834,9 +852,9 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
resolveIdentifierFetch( resultSet, context, fetch );
}
final IdentifierResolutionContext ownerIdentifierResolutionContext =
context.getIdentifierResolutionContext( entityReference );
Object hydratedState = ownerIdentifierResolutionContext.getHydratedForm();
final ResultSetProcessingContext.EntityReferenceProcessingState ownerEntityReferenceProcessingState =
context.getProcessingState( entityReference );
Object hydratedState = ownerEntityReferenceProcessingState.getIdentifierHydratedForm();
Serializable resolvedId = (Serializable) entityReference.getEntityPersister()
.getIdentifierType()
.resolve( hydratedState, context.getSession(), null );
@ -850,14 +868,14 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
Fetch fetch) throws SQLException {
if ( fetch instanceof EntityFetch ) {
EntityFetch entityFetch = (EntityFetch) fetch;
final IdentifierResolutionContext identifierResolutionContext =
context.getIdentifierResolutionContext( entityFetch );
if ( identifierResolutionContext.getEntityKey() != null ) {
final ResultSetProcessingContext.EntityReferenceProcessingState entityReferenceProcessingState =
context.getProcessingState( entityFetch );
if ( entityReferenceProcessingState.getEntityKey() != null ) {
return;
}
EntityKey fetchKey = entityFetch.resolveInIdentifier( resultSet, context );
identifierResolutionContext.registerEntityKey( fetchKey );
entityReferenceProcessingState.registerEntityKey( fetchKey );
}
else if ( fetch instanceof CompositeFetch ) {
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;
/**
* 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}
*
* @author Steve Ebersole

View File

@ -27,7 +27,8 @@ import org.hibernate.loader.plan.spi.LoadPlan;
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
*/

View File

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

View File

@ -23,6 +23,7 @@
*/
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.CollectionReturn;
import org.hibernate.loader.plan.spi.CompositeFetch;
@ -115,4 +116,14 @@ public class DelegatedLoadPlanVisitationStrategy implements LoadPlanVisitationSt
public void finishingCompositeFetch(CompositeFetch 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;
import org.hibernate.loader.plan.spi.AnyFetch;
import org.hibernate.loader.plan.spi.CollectionFetch;
import org.hibernate.loader.plan.spi.CollectionReturn;
import org.hibernate.loader.plan.spi.CompositeFetch;
@ -98,4 +99,12 @@ public class LoadPlanVisitationStrategyAdapter implements LoadPlanVisitationStra
@Override
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;
import org.hibernate.loader.plan.spi.AnyFetch;
import org.hibernate.loader.plan.spi.CollectionFetch;
import org.hibernate.loader.plan.spi.CollectionReturn;
import org.hibernate.loader.plan.spi.CompositeFetch;
@ -136,4 +137,18 @@ public interface ReturnGraphVisitationStrategy {
* @param fetch The composite 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;
import org.hibernate.loader.plan.spi.AnyFetch;
import org.hibernate.loader.plan.spi.CollectionFetch;
import org.hibernate.loader.plan.spi.CollectionReturn;
import org.hibernate.loader.plan.spi.CompositeFetch;
@ -89,4 +90,12 @@ public class ReturnGraphVisitationStrategyAdapter implements ReturnGraphVisitati
@Override
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 ) {
visitRootReturn( rootReturn );
}

View File

@ -26,7 +26,7 @@ package org.hibernate.loader.spi;
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 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.Queryable;
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.AttributeSource;
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.CompositionDefinition;
import org.hibernate.persister.walking.spi.EntityDefinition;
import org.hibernate.persister.walking.spi.WalkingException;
import org.hibernate.pretty.MessageHelper;
import org.hibernate.sql.Alias;
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.OrderByTranslation;
import org.hibernate.sql.ordering.antlr.SqlValueReference;
import org.hibernate.type.AnyType;
import org.hibernate.type.AssociationType;
import org.hibernate.type.CollectionType;
import org.hibernate.type.CompositeType;
@ -2017,10 +2021,19 @@ public abstract class AbstractCollectionPersister
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
public EntityDefinition toEntityDefinition() {
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();
}
@ -2029,7 +2042,7 @@ public abstract class AbstractCollectionPersister
public CompositeCollectionElementDefinition toCompositeElementDefinition() {
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() {
@ -2043,6 +2056,11 @@ public abstract class AbstractCollectionPersister
return getElementType();
}
@Override
public boolean isNullable() {
return false;
}
@Override
public AttributeSource getSource() {
// 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.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
@ -2495,6 +2496,15 @@ public abstract class AbstractEntityPersister
.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 {
return createEntityLoader( lockMode, LoadQueryInfluencers.NONE );
}
@ -3765,8 +3775,24 @@ public abstract class AbstractEntityPersister
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 {
initPropertyPaths(mapping);
initPropertyPaths( mapping );
//doLateInit();
prepareEntityIdentifierDefinition();
}
private void doLateInit() {
generateEntityDefinition();
//insert/update/delete SQL
final int joinSpan = getTableSpan();
@ -3824,11 +3850,11 @@ public abstract class AbstractEntityPersister
}
logStaticSQL();
}
public void postInstantiate() throws MappingException {
generateEntityDefinition();
public final void postInstantiate() throws MappingException {
doLateInit();
// generateEntityDefinition();
createLoaders();
createUniqueKeyLoaders();
@ -5112,6 +5138,9 @@ public abstract class AbstractEntityPersister
private void prepareEntityIdentifierDefinition() {
if ( entityIdentifierDefinition != null ) {
return;
}
final Type idType = getIdentifierType();
if ( !idType.isComponentType() ) {
@ -5131,35 +5160,119 @@ public abstract class AbstractEntityPersister
EntityIdentifierDefinitionHelper.buildNonEncapsulatedCompositeIdentifierDefinition( this );
}
private void collectAttributeDefinitions() {
// 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)
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;
private void collectAttributeDefinitions(List<AttributeDefinition> definitions, EntityMetamodel metamodel) {
for ( int i = 0; i < metamodel.getPropertySpan(); i++ ) {
definitions.add( metamodel.getProperties()[i] );
}
@Override
public boolean hasNext() {
return currentAttributeNumber < numberOfAttributes;
}
// see if there are any subclass persisters...
final Set<String> subClassEntityNames = metamodel.getSubclassEntityNames();
if ( subClassEntityNames == null ) {
return;
}
@Override
public AttributeDefinition next() {
final int attributeNumber = currentAttributeNumber;
currentAttributeNumber++;
return entityMetamodel.getProperties()[ attributeNumber ];
}
@Override
public void remove() {
throw new UnsupportedOperationException( "Remove operation not supported here" );
}
};
// see if we can find the persisters...
for ( String subClassEntityName : subClassEntityNames ) {
if ( metamodel.getName().equals( subClassEntityName ) ) {
// skip it
continue;
}
};
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