HHH-8276 - Integrate LoadPlans into UniqueEntityLoader (PoC)
This commit is contained in:
parent
456d61bd4e
commit
dc7cdf9d88
|
@ -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 );
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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>();
|
||||
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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() + ')';
|
||||
}
|
||||
}
|
|
@ -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() + " )";
|
||||
|
|
|
@ -58,7 +58,7 @@ public class EntityJoinableAssociationImpl extends AbstractJoinableAssociationIm
|
|||
hasRestriction,
|
||||
enabledFilters
|
||||
);
|
||||
this.joinableType = entityFetch.getEntityType();
|
||||
this.joinableType = entityFetch.getFetchedType();
|
||||
this.joinable = (Joinable) entityFetch.getEntityPersister();
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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.
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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() )
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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 );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* Defines support for processing ResultSet values as defined by a LoadPlan
|
||||
*/
|
||||
package org.hibernate.loader.plan.exec.process;
|
|
@ -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;
|
||||
}
|
|
@ -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,
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
|
@ -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() );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 );
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
|
@ -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.
|
|
@ -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();
|
||||
}
|
|
@ -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.
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -53,6 +53,4 @@ public interface CollectionReference {
|
|||
public FetchOwner getElementGraph();
|
||||
|
||||
public PropertyPath getPropertyPath();
|
||||
|
||||
public boolean hasEntityElements();
|
||||
}
|
||||
|
|
|
@ -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() + ")";
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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() ) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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?
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue