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;
|
return propertyName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected String originalMessage() {
|
||||||
|
return super.getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getMessage() {
|
public String getMessage() {
|
||||||
return super.getMessage()
|
return originalMessage()
|
||||||
+ ( wasSetter ? " setter of " : " getter of " )
|
+ ( wasSetter ? " setter of " : " getter of " )
|
||||||
+ StringHelper.qualify( persistentClass.getName(), propertyName );
|
+ StringHelper.qualify( persistentClass.getName(), propertyName );
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 Emmanuel Bernard
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
|
*
|
||||||
|
* @see AnyMetaDef
|
||||||
*/
|
*/
|
||||||
@java.lang.annotation.Target({METHOD, FIELD})
|
@java.lang.annotation.Target({METHOD, FIELD})
|
||||||
@Retention(RUNTIME)
|
@Retention(RUNTIME)
|
||||||
|
|
|
@ -28,6 +28,7 @@ import java.util.Properties;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.hibernate.CustomEntityDirtinessStrategy;
|
import org.hibernate.CustomEntityDirtinessStrategy;
|
||||||
|
import org.hibernate.EntityNameResolver;
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.HibernateException;
|
||||||
import org.hibernate.Interceptor;
|
import org.hibernate.Interceptor;
|
||||||
import org.hibernate.MappingException;
|
import org.hibernate.MappingException;
|
||||||
|
@ -290,4 +291,6 @@ public interface SessionFactoryImplementor extends Mapping, SessionFactory {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public NamedQueryRepository getNamedQueryRepository();
|
public NamedQueryRepository getNamedQueryRepository();
|
||||||
|
|
||||||
|
Iterable<EntityNameResolver> iterateEntityNameResolvers();
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,12 +52,22 @@ public final class NameGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String scalarName(int x, int y) {
|
public static String scalarName(int x, int y) {
|
||||||
return new StringBuilder()
|
return scalarName( "col_" + x, y );
|
||||||
.append( "col_" )
|
}
|
||||||
.append( x )
|
|
||||||
.append( '_' )
|
public static String scalarName(String base, int num) {
|
||||||
.append( y )
|
return base + '_' + num + '_';
|
||||||
.append( '_' )
|
}
|
||||||
.toString();
|
|
||||||
|
public static String[] scalarNames(String base, int count) {
|
||||||
|
final String[] names = new String[count];
|
||||||
|
for ( int j = 0; j < count; j++ ) {
|
||||||
|
names[j] = scalarName( base, j );
|
||||||
|
}
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String[] scalarNames(int uniqueness, int count) {
|
||||||
|
return scalarNames( "col_" + uniqueness, count );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -378,6 +378,11 @@ public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, Par
|
||||||
|
|
||||||
final FromElement fromElement;
|
final FromElement fromElement;
|
||||||
if ( dot.getDataType() != null && dot.getDataType().isComponentType() ) {
|
if ( dot.getDataType() != null && dot.getDataType().isComponentType() ) {
|
||||||
|
if ( dot.getDataType().isAnyType() ) {
|
||||||
|
throw new SemanticException( "An AnyType attribute cannot be join fetched" );
|
||||||
|
// ^^ because the discriminator (aka, the "meta columns") must be known to the SQL in
|
||||||
|
// a non-parameterized way.
|
||||||
|
}
|
||||||
FromElementFactory factory = new FromElementFactory(
|
FromElementFactory factory = new FromElementFactory(
|
||||||
getCurrentFromClause(),
|
getCurrentFromClause(),
|
||||||
dot.getLhs().getFromElement(),
|
dot.getLhs().getFromElement(),
|
||||||
|
|
|
@ -35,7 +35,6 @@ import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -268,7 +267,6 @@ public final class SessionFactoryImpl
|
||||||
this.jdbcServices = this.serviceRegistry.getService( JdbcServices.class );
|
this.jdbcServices = this.serviceRegistry.getService( JdbcServices.class );
|
||||||
this.dialect = this.jdbcServices.getDialect();
|
this.dialect = this.jdbcServices.getDialect();
|
||||||
this.cacheAccess = this.serviceRegistry.getService( CacheImplementor.class );
|
this.cacheAccess = this.serviceRegistry.getService( CacheImplementor.class );
|
||||||
final RegionFactory regionFactory = cacheAccess.getRegionFactory();
|
|
||||||
this.sqlFunctionRegistry = new SQLFunctionRegistry( getDialect(), cfg.getSqlFunctions() );
|
this.sqlFunctionRegistry = new SQLFunctionRegistry( getDialect(), cfg.getSqlFunctions() );
|
||||||
if ( observer != null ) {
|
if ( observer != null ) {
|
||||||
this.observer.addObserver( observer );
|
this.observer.addObserver( observer );
|
||||||
|
@ -329,15 +327,22 @@ public final class SessionFactoryImpl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
imports = new HashMap<String,String>( cfg.getImports() );
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////
|
||||||
// Prepare persisters and link them up with their cache
|
// Prepare persisters and link them up with their cache
|
||||||
// region/access-strategy
|
// region/access-strategy
|
||||||
|
|
||||||
|
final RegionFactory regionFactory = cacheAccess.getRegionFactory();
|
||||||
final String cacheRegionPrefix = settings.getCacheRegionPrefix() == null ? "" : settings.getCacheRegionPrefix() + ".";
|
final String cacheRegionPrefix = settings.getCacheRegionPrefix() == null ? "" : settings.getCacheRegionPrefix() + ".";
|
||||||
|
|
||||||
final PersisterFactory persisterFactory = serviceRegistry.getService( PersisterFactory.class );
|
final PersisterFactory persisterFactory = serviceRegistry.getService( PersisterFactory.class );
|
||||||
|
|
||||||
|
// todo : consider removing this silliness and just have EntityPersister directly implement ClassMetadata
|
||||||
|
// EntityPersister.getClassMetadata() for the internal impls simply "return this";
|
||||||
|
// collapsing those would allow us to remove this "extra" Map
|
||||||
|
//
|
||||||
|
// todo : similar for CollectionPersister/CollectionMetadata
|
||||||
|
|
||||||
entityPersisters = new HashMap();
|
entityPersisters = new HashMap();
|
||||||
Map entityAccessStrategies = new HashMap();
|
Map entityAccessStrategies = new HashMap();
|
||||||
Map<String,ClassMetadata> classMeta = new HashMap<String,ClassMetadata>();
|
Map<String,ClassMetadata> classMeta = new HashMap<String,ClassMetadata>();
|
||||||
|
@ -462,19 +467,13 @@ public final class SessionFactoryImpl
|
||||||
cfg.getSqlResultSetMappings().values(),
|
cfg.getSqlResultSetMappings().values(),
|
||||||
toProcedureCallMementos( cfg.getNamedProcedureCallMap(), cfg.getSqlResultSetMappings() )
|
toProcedureCallMementos( cfg.getNamedProcedureCallMap(), cfg.getSqlResultSetMappings() )
|
||||||
);
|
);
|
||||||
imports = new HashMap<String,String>( cfg.getImports() );
|
|
||||||
|
|
||||||
// after *all* persisters and named queries are registered
|
// after *all* persisters and named queries are registered
|
||||||
Iterator iter = entityPersisters.values().iterator();
|
for ( EntityPersister persister : entityPersisters.values() ) {
|
||||||
while ( iter.hasNext() ) {
|
|
||||||
final EntityPersister persister = ( ( EntityPersister ) iter.next() );
|
|
||||||
persister.postInstantiate();
|
persister.postInstantiate();
|
||||||
registerEntityNameResolvers( persister );
|
registerEntityNameResolvers( persister );
|
||||||
|
|
||||||
}
|
}
|
||||||
iter = collectionPersisters.values().iterator();
|
for ( CollectionPersister persister : collectionPersisters.values() ) {
|
||||||
while ( iter.hasNext() ) {
|
|
||||||
final CollectionPersister persister = ( ( CollectionPersister ) iter.next() );
|
|
||||||
persister.postInstantiate();
|
persister.postInstantiate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1070,6 +1069,7 @@ public final class SessionFactoryImpl
|
||||||
entityNameResolvers.put( resolver, ENTITY_NAME_RESOLVER_MAP_VALUE );
|
entityNameResolvers.put( resolver, ENTITY_NAME_RESOLVER_MAP_VALUE );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Iterable<EntityNameResolver> iterateEntityNameResolvers() {
|
public Iterable<EntityNameResolver> iterateEntityNameResolvers() {
|
||||||
return entityNameResolvers.keySet();
|
return entityNameResolvers.keySet();
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,20 +51,22 @@ public class GeneratedCollectionAliases implements CollectionAliases {
|
||||||
this.keyAliases = getUserProvidedAliases(
|
this.keyAliases = getUserProvidedAliases(
|
||||||
"key",
|
"key",
|
||||||
persister.getKeyColumnAliases( suffix )
|
persister.getKeyColumnAliases( suffix )
|
||||||
);
|
);
|
||||||
|
|
||||||
this.indexAliases = getUserProvidedAliases(
|
this.indexAliases = getUserProvidedAliases(
|
||||||
"index",
|
"index",
|
||||||
persister.getIndexColumnAliases( suffix )
|
persister.getIndexColumnAliases( suffix )
|
||||||
);
|
);
|
||||||
|
|
||||||
this.elementAliases = getUserProvidedAliases( "element",
|
this.elementAliases = getUserProvidedAliases(
|
||||||
|
"element",
|
||||||
persister.getElementColumnAliases( suffix )
|
persister.getElementColumnAliases( suffix )
|
||||||
);
|
);
|
||||||
|
|
||||||
this.identifierAlias = getUserProvidedAlias( "id",
|
this.identifierAlias = getUserProvidedAlias(
|
||||||
|
"id",
|
||||||
persister.getIdentifierColumnAlias( suffix )
|
persister.getIdentifierColumnAlias( suffix )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public GeneratedCollectionAliases(CollectionPersister persister, String string) {
|
public GeneratedCollectionAliases(CollectionPersister persister, String string) {
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
package org.hibernate.loader;
|
package org.hibernate.loader;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -91,6 +92,9 @@ public class JoinWalker {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List getAssociations() {
|
||||||
|
return Collections.unmodifiableList( associations );
|
||||||
|
}
|
||||||
|
|
||||||
public String[] getCollectionSuffixes() {
|
public String[] getCollectionSuffixes() {
|
||||||
return collectionSuffixes;
|
return collectionSuffixes;
|
||||||
|
|
|
@ -72,11 +72,19 @@ public abstract class BatchingEntityLoaderBuilder {
|
||||||
LoadQueryInfluencers influencers) {
|
LoadQueryInfluencers influencers) {
|
||||||
if ( batchSize <= 1 ) {
|
if ( batchSize <= 1 ) {
|
||||||
// no batching
|
// no batching
|
||||||
return new EntityLoader( persister, lockMode, factory, influencers );
|
return buildNonBatchingLoader( persister, lockMode, factory, influencers );
|
||||||
}
|
}
|
||||||
return buildBatchingLoader( persister, batchSize, lockMode, factory, influencers );
|
return buildBatchingLoader( persister, batchSize, lockMode, factory, influencers );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected UniqueEntityLoader buildNonBatchingLoader(
|
||||||
|
OuterJoinLoadable persister,
|
||||||
|
LockMode lockMode,
|
||||||
|
SessionFactoryImplementor factory,
|
||||||
|
LoadQueryInfluencers influencers) {
|
||||||
|
return new EntityLoader( persister, lockMode, factory, influencers );
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract UniqueEntityLoader buildBatchingLoader(
|
protected abstract UniqueEntityLoader buildBatchingLoader(
|
||||||
OuterJoinLoadable persister,
|
OuterJoinLoadable persister,
|
||||||
int batchSize,
|
int batchSize,
|
||||||
|
@ -103,11 +111,19 @@ public abstract class BatchingEntityLoaderBuilder {
|
||||||
LoadQueryInfluencers influencers) {
|
LoadQueryInfluencers influencers) {
|
||||||
if ( batchSize <= 1 ) {
|
if ( batchSize <= 1 ) {
|
||||||
// no batching
|
// no batching
|
||||||
return new EntityLoader( persister, lockOptions, factory, influencers );
|
return buildNonBatchingLoader( persister, lockOptions, factory, influencers );
|
||||||
}
|
}
|
||||||
return buildBatchingLoader( persister, batchSize, lockOptions, factory, influencers );
|
return buildBatchingLoader( persister, batchSize, lockOptions, factory, influencers );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected UniqueEntityLoader buildNonBatchingLoader(
|
||||||
|
OuterJoinLoadable persister,
|
||||||
|
LockOptions lockOptions,
|
||||||
|
SessionFactoryImplementor factory,
|
||||||
|
LoadQueryInfluencers influencers) {
|
||||||
|
return new EntityLoader( persister, lockOptions, factory, influencers );
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract UniqueEntityLoader buildBatchingLoader(
|
protected abstract UniqueEntityLoader buildBatchingLoader(
|
||||||
OuterJoinLoadable persister,
|
OuterJoinLoadable persister,
|
||||||
int batchSize,
|
int batchSize,
|
||||||
|
|
|
@ -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.sql.Statement;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -22,7 +21,6 @@ import org.hibernate.dialect.pagination.LimitHandler;
|
||||||
import org.hibernate.dialect.pagination.LimitHelper;
|
import org.hibernate.dialect.pagination.LimitHelper;
|
||||||
import org.hibernate.dialect.pagination.NoopLimitHandler;
|
import org.hibernate.dialect.pagination.NoopLimitHandler;
|
||||||
import org.hibernate.engine.jdbc.ColumnNameCache;
|
import org.hibernate.engine.jdbc.ColumnNameCache;
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|
||||||
import org.hibernate.engine.spi.PersistenceContext;
|
import org.hibernate.engine.spi.PersistenceContext;
|
||||||
import org.hibernate.engine.spi.QueryParameters;
|
import org.hibernate.engine.spi.QueryParameters;
|
||||||
import org.hibernate.engine.spi.RowSelection;
|
import org.hibernate.engine.spi.RowSelection;
|
||||||
|
@ -32,17 +30,14 @@ import org.hibernate.engine.spi.TypedValue;
|
||||||
import org.hibernate.internal.CoreLogging;
|
import org.hibernate.internal.CoreLogging;
|
||||||
import org.hibernate.internal.CoreMessageLogger;
|
import org.hibernate.internal.CoreMessageLogger;
|
||||||
import org.hibernate.loader.entity.UniqueEntityLoader;
|
import org.hibernate.loader.entity.UniqueEntityLoader;
|
||||||
import org.hibernate.loader.internal.EntityLoadQueryBuilderImpl;
|
import org.hibernate.loader.plan.exec.query.spi.NamedParameterContext;
|
||||||
import org.hibernate.loader.internal.LoadQueryAliasResolutionContextImpl;
|
import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters;
|
||||||
import org.hibernate.loader.internal.ResultSetProcessorImpl;
|
import org.hibernate.loader.plan.exec.spi.LoadQueryDetails;
|
||||||
import org.hibernate.loader.plan.internal.SingleRootReturnLoadPlanBuilderStrategy;
|
import org.hibernate.loader.plan.internal.SingleRootReturnLoadPlanBuilderStrategy;
|
||||||
import org.hibernate.loader.plan.spi.LoadPlan;
|
import org.hibernate.loader.plan.spi.LoadPlan;
|
||||||
import org.hibernate.loader.plan.spi.build.LoadPlanBuilder;
|
import org.hibernate.loader.plan.spi.build.LoadPlanBuilder;
|
||||||
import org.hibernate.loader.spi.AfterLoadAction;
|
import org.hibernate.loader.spi.AfterLoadAction;
|
||||||
import org.hibernate.loader.spi.LoadQueryAliasResolutionContext;
|
|
||||||
import org.hibernate.loader.spi.NamedParameterContext;
|
|
||||||
import org.hibernate.loader.spi.NoOpLoadPlanAdvisor;
|
import org.hibernate.loader.spi.NoOpLoadPlanAdvisor;
|
||||||
import org.hibernate.loader.spi.ResultSetProcessor;
|
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
import org.hibernate.persister.entity.OuterJoinLoadable;
|
||||||
import org.hibernate.pretty.MessageHelper;
|
import org.hibernate.pretty.MessageHelper;
|
||||||
|
@ -63,63 +58,41 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
|
||||||
private final String entityName;
|
private final String entityName;
|
||||||
|
|
||||||
private final LoadPlan plan;
|
private final LoadPlan plan;
|
||||||
private final String staticSql;
|
private final LoadQueryDetails staticLoadQuery;
|
||||||
private final LoadQueryAliasResolutionContext staticAliasResolutionContext;
|
|
||||||
private final ResultSetProcessor staticResultSetProcessor;
|
|
||||||
|
|
||||||
private ColumnNameCache columnNameCache;
|
private ColumnNameCache columnNameCache;
|
||||||
|
|
||||||
public AbstractLoadPlanBasedEntityLoader(
|
public AbstractLoadPlanBasedEntityLoader(
|
||||||
OuterJoinLoadable entityPersister,
|
OuterJoinLoadable entityPersister,
|
||||||
Type uniqueKeyType,
|
|
||||||
SessionFactoryImplementor factory,
|
SessionFactoryImplementor factory,
|
||||||
LoadQueryInfluencers loadQueryInfluencers) {
|
String[] uniqueKeyColumnNames,
|
||||||
|
Type uniqueKeyType,
|
||||||
|
QueryBuildingParameters buildingParameters) {
|
||||||
|
this.entityPersister = entityPersister;
|
||||||
this.factory = factory;
|
this.factory = factory;
|
||||||
this.uniqueKeyType = uniqueKeyType;
|
this.uniqueKeyType = uniqueKeyType;
|
||||||
this.entityName = entityPersister.getEntityName();
|
this.entityName = entityPersister.getEntityName();
|
||||||
this.entityPersister = entityPersister;
|
|
||||||
|
|
||||||
final SingleRootReturnLoadPlanBuilderStrategy strategy = new SingleRootReturnLoadPlanBuilderStrategy(
|
final SingleRootReturnLoadPlanBuilderStrategy strategy = new SingleRootReturnLoadPlanBuilderStrategy(
|
||||||
factory,
|
factory,
|
||||||
loadQueryInfluencers
|
buildingParameters.getQueryInfluencers()
|
||||||
);
|
);
|
||||||
|
|
||||||
this.plan = LoadPlanBuilder.buildRootEntityLoadPlan( strategy, entityPersister );
|
this.plan = LoadPlanBuilder.buildRootEntityLoadPlan( strategy, entityPersister );
|
||||||
this.staticAliasResolutionContext = buildAliasResolutionContext( plan, factory );
|
this.staticLoadQuery = LoadQueryDetails.makeForBatching(
|
||||||
this.staticSql = generateStaticSql( plan, staticAliasResolutionContext, factory, loadQueryInfluencers );
|
uniqueKeyColumnNames,
|
||||||
this.staticResultSetProcessor = generateStaticResultSetProcessor( plan );
|
plan,
|
||||||
}
|
|
||||||
|
|
||||||
protected LoadQueryAliasResolutionContext buildAliasResolutionContext(LoadPlan plan, SessionFactoryImplementor factory) {
|
|
||||||
return new LoadQueryAliasResolutionContextImpl(
|
|
||||||
factory,
|
factory,
|
||||||
0,
|
buildingParameters
|
||||||
Collections.singletonMap( plan.getReturns().get( 0 ), new String[] {"abc"} )
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String generateStaticSql(
|
|
||||||
LoadPlan plan,
|
|
||||||
LoadQueryAliasResolutionContext aliasResolutionContext,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) {
|
|
||||||
return new EntityLoadQueryBuilderImpl( loadQueryInfluencers, plan ).generateSql(
|
|
||||||
1,
|
|
||||||
factory,
|
|
||||||
aliasResolutionContext
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ResultSetProcessor generateStaticResultSetProcessor(LoadPlan plan) {
|
|
||||||
return new ResultSetProcessorImpl( plan );
|
|
||||||
}
|
|
||||||
|
|
||||||
protected SessionFactoryImplementor getFactory() {
|
protected SessionFactoryImplementor getFactory() {
|
||||||
return factory;
|
return factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String getSqlStatement() {
|
protected LoadQueryDetails getStaticLoadQuery() {
|
||||||
return staticSql;
|
return staticLoadQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String getEntityName() {
|
protected String getEntityName() {
|
||||||
|
@ -152,17 +125,12 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
|
||||||
final QueryParameters qp = new QueryParameters();
|
final QueryParameters qp = new QueryParameters();
|
||||||
qp.setPositionalParameterTypes( types );
|
qp.setPositionalParameterTypes( types );
|
||||||
qp.setPositionalParameterValues( ids );
|
qp.setPositionalParameterValues( ids );
|
||||||
qp.setOptionalObject( optionalObject );
|
|
||||||
qp.setOptionalEntityName( optionalEntityName );
|
|
||||||
qp.setOptionalId( optionalId );
|
|
||||||
qp.setLockOptions( lockOptions );
|
qp.setLockOptions( lockOptions );
|
||||||
|
|
||||||
result = executeLoad(
|
result = executeLoad(
|
||||||
session,
|
session,
|
||||||
qp,
|
qp,
|
||||||
staticSql,
|
staticLoadQuery,
|
||||||
staticResultSetProcessor,
|
|
||||||
staticAliasResolutionContext,
|
|
||||||
false,
|
false,
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
|
@ -171,7 +139,7 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
|
||||||
throw factory.getSQLExceptionHelper().convert(
|
throw factory.getSQLExceptionHelper().convert(
|
||||||
sqle,
|
sqle,
|
||||||
"could not load an entity batch: " + MessageHelper.infoString( entityPersister, ids, getFactory() ),
|
"could not load an entity batch: " + MessageHelper.infoString( entityPersister, ids, getFactory() ),
|
||||||
getSqlStatement()
|
staticLoadQuery.getSqlStatement()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,9 +171,7 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
|
||||||
final List results = executeLoad(
|
final List results = executeLoad(
|
||||||
session,
|
session,
|
||||||
qp,
|
qp,
|
||||||
staticSql,
|
staticLoadQuery,
|
||||||
staticResultSetProcessor,
|
|
||||||
staticAliasResolutionContext,
|
|
||||||
false,
|
false,
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
|
@ -220,7 +186,7 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
|
||||||
entityPersister.getIdentifierType(),
|
entityPersister.getIdentifierType(),
|
||||||
factory
|
factory
|
||||||
),
|
),
|
||||||
getSqlStatement()
|
staticLoadQuery.getSqlStatement()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,18 +197,14 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
|
||||||
protected List executeLoad(
|
protected List executeLoad(
|
||||||
SessionImplementor session,
|
SessionImplementor session,
|
||||||
QueryParameters queryParameters,
|
QueryParameters queryParameters,
|
||||||
String sql,
|
LoadQueryDetails loadQueryDetails,
|
||||||
ResultSetProcessor resultSetProcessor,
|
|
||||||
LoadQueryAliasResolutionContext aliasResolutionContext,
|
|
||||||
boolean returnProxies,
|
boolean returnProxies,
|
||||||
ResultTransformer forcedResultTransformer) throws SQLException {
|
ResultTransformer forcedResultTransformer) throws SQLException {
|
||||||
final List<AfterLoadAction> afterLoadActions = new ArrayList<AfterLoadAction>();
|
final List<AfterLoadAction> afterLoadActions = new ArrayList<AfterLoadAction>();
|
||||||
return executeLoad(
|
return executeLoad(
|
||||||
session,
|
session,
|
||||||
queryParameters,
|
queryParameters,
|
||||||
sql,
|
loadQueryDetails,
|
||||||
resultSetProcessor,
|
|
||||||
aliasResolutionContext,
|
|
||||||
returnProxies,
|
returnProxies,
|
||||||
forcedResultTransformer,
|
forcedResultTransformer,
|
||||||
afterLoadActions
|
afterLoadActions
|
||||||
|
@ -252,9 +214,7 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
|
||||||
protected List executeLoad(
|
protected List executeLoad(
|
||||||
SessionImplementor session,
|
SessionImplementor session,
|
||||||
QueryParameters queryParameters,
|
QueryParameters queryParameters,
|
||||||
String sql,
|
LoadQueryDetails loadQueryDetails,
|
||||||
ResultSetProcessor resultSetProcessor,
|
|
||||||
LoadQueryAliasResolutionContext aliasResolutionContext,
|
|
||||||
boolean returnProxies,
|
boolean returnProxies,
|
||||||
ResultTransformer forcedResultTransformer,
|
ResultTransformer forcedResultTransformer,
|
||||||
List<AfterLoadAction> afterLoadActions) throws SQLException {
|
List<AfterLoadAction> afterLoadActions) throws SQLException {
|
||||||
|
@ -273,9 +233,10 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
|
||||||
persistenceContext.beforeLoad();
|
persistenceContext.beforeLoad();
|
||||||
try {
|
try {
|
||||||
List results;
|
List results;
|
||||||
|
final String sql = loadQueryDetails.getSqlStatement();
|
||||||
try {
|
try {
|
||||||
final SqlStatementWrapper wrapper = executeQueryStatement( sql, queryParameters, false, afterLoadActions, session );
|
final SqlStatementWrapper wrapper = executeQueryStatement( sql, queryParameters, false, afterLoadActions, session );
|
||||||
results = resultSetProcessor.extractResults(
|
results = loadQueryDetails.getResultSetProcessor().extractResults(
|
||||||
// todo : hook in the JPA 2.1 entity graph advisor
|
// todo : hook in the JPA 2.1 entity graph advisor
|
||||||
NoOpLoadPlanAdvisor.INSTANCE,
|
NoOpLoadPlanAdvisor.INSTANCE,
|
||||||
wrapper.getResultSet(),
|
wrapper.getResultSet(),
|
||||||
|
@ -287,7 +248,7 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
|
||||||
return AbstractLoadPlanBasedEntityLoader.this.getNamedParameterLocs( name );
|
return AbstractLoadPlanBasedEntityLoader.this.getNamedParameterLocs( name );
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
aliasResolutionContext,
|
loadQueryDetails.getAliasResolutionContext(),
|
||||||
returnProxies,
|
returnProxies,
|
||||||
queryParameters.isReadOnly(),
|
queryParameters.isReadOnly(),
|
||||||
forcedResultTransformer,
|
forcedResultTransformer,
|
||||||
|
@ -334,9 +295,7 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
|
||||||
protected Object doQueryAndLoadEntity(
|
protected Object doQueryAndLoadEntity(
|
||||||
SessionImplementor session,
|
SessionImplementor session,
|
||||||
QueryParameters queryParameters,
|
QueryParameters queryParameters,
|
||||||
String sql,
|
LoadQueryDetails loadQueryDetails,
|
||||||
ResultSetProcessor resultSetProcessor,
|
|
||||||
LoadQueryAliasResolutionContext aliasResolutionContext,
|
|
||||||
boolean returnProxies,
|
boolean returnProxies,
|
||||||
ResultTransformer forcedResultTransformer) throws SQLException {
|
ResultTransformer forcedResultTransformer) throws SQLException {
|
||||||
|
|
||||||
|
@ -345,7 +304,7 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
|
||||||
final SqlStatementWrapper wrapper = executeQueryStatement( queryParameters, false, afterLoadActions, session );
|
final SqlStatementWrapper wrapper = executeQueryStatement( queryParameters, false, afterLoadActions, session );
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final List results = resultSetProcessor.extractResults(
|
final List results = loadQueryDetails.getResultSetProcessor().extractResults(
|
||||||
NoOpLoadPlanAdvisor.INSTANCE,
|
NoOpLoadPlanAdvisor.INSTANCE,
|
||||||
wrapper.getResultSet(),
|
wrapper.getResultSet(),
|
||||||
session,
|
session,
|
||||||
|
@ -356,7 +315,7 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
|
||||||
return AbstractLoadPlanBasedEntityLoader.this.getNamedParameterLocs( name );
|
return AbstractLoadPlanBasedEntityLoader.this.getNamedParameterLocs( name );
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
staticAliasResolutionContext,
|
loadQueryDetails.getAliasResolutionContext(),
|
||||||
returnProxies,
|
returnProxies,
|
||||||
queryParameters.isReadOnly(),
|
queryParameters.isReadOnly(),
|
||||||
forcedResultTransformer,
|
forcedResultTransformer,
|
||||||
|
@ -398,7 +357,7 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
|
||||||
final boolean scroll,
|
final boolean scroll,
|
||||||
List<AfterLoadAction> afterLoadActions,
|
List<AfterLoadAction> afterLoadActions,
|
||||||
final SessionImplementor session) throws SQLException {
|
final SessionImplementor session) throws SQLException {
|
||||||
return executeQueryStatement( getSqlStatement(), queryParameters, scroll, afterLoadActions, session );
|
return executeQueryStatement( staticLoadQuery.getSqlStatement(), queryParameters, scroll, afterLoadActions, session );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected SqlStatementWrapper executeQueryStatement(
|
protected SqlStatementWrapper executeQueryStatement(
|
||||||
|
|
|
@ -40,8 +40,10 @@ import org.hibernate.pretty.MessageHelper;
|
||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The base contract for loaders capable of performing batch-fetch loading of entities using multiple primary key
|
* The base contract for UniqueEntityLoader implementations capable of performing batch-fetch loading of entities
|
||||||
* values in the SQL <tt>WHERE</tt> clause.
|
* using multiple primary key values in the SQL <tt>WHERE</tt> clause.
|
||||||
|
* <p/>
|
||||||
|
* Typically these are
|
||||||
*
|
*
|
||||||
* @author Gavin King
|
* @author Gavin King
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
|
|
|
@ -32,148 +32,125 @@ import org.hibernate.MappingException;
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.internal.CoreLogging;
|
import org.hibernate.internal.CoreLogging;
|
||||||
|
import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters;
|
||||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
import org.hibernate.persister.entity.OuterJoinLoadable;
|
||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* UniqueEntityLoader implementation that is the main functionality for LoadPlan-based Entity loading.
|
||||||
|
* <p/>
|
||||||
|
* Can handle batch-loading as well as non-pk, unique-key loading,
|
||||||
|
* <p/>
|
||||||
|
* Much is ultimately delegated to its superclass, AbstractLoadPlanBasedEntityLoader. However:
|
||||||
|
* todo How much of AbstractLoadPlanBasedEntityLoader is actually needed?
|
||||||
|
*
|
||||||
* Loads an entity instance using outerjoin fetching to fetch associated entities.
|
* Loads an entity instance using outerjoin fetching to fetch associated entities.
|
||||||
* <br>
|
* <br>
|
||||||
* The <tt>EntityPersister</tt> must implement <tt>Loadable</tt>. For other entities,
|
* The <tt>EntityPersister</tt> must implement <tt>Loadable</tt>. For other entities,
|
||||||
* create a customized subclass of <tt>Loader</tt>.
|
* create a customized subclass of <tt>Loader</tt>.
|
||||||
*
|
*
|
||||||
* @author Gavin King
|
* @author Gavin King
|
||||||
|
* @author Steve Ebersole
|
||||||
|
* @author Gail Badner
|
||||||
*/
|
*/
|
||||||
public class EntityLoader extends AbstractLoadPlanBasedEntityLoader {
|
public class EntityLoader extends AbstractLoadPlanBasedEntityLoader {
|
||||||
private static final Logger log = CoreLogging.logger( EntityLoader.class );
|
private static final Logger log = CoreLogging.logger( EntityLoader.class );
|
||||||
|
|
||||||
// private final boolean batchLoader;
|
public static Builder forEntity(OuterJoinLoadable persister) {
|
||||||
// private final int[][] compositeKeyManyToOneTargetIndices;
|
return new Builder( persister );
|
||||||
//
|
|
||||||
// public EntityLoader(
|
|
||||||
// OuterJoinLoadable persister,
|
|
||||||
// LockMode lockMode,
|
|
||||||
// SessionFactoryImplementor factory,
|
|
||||||
// LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
|
||||||
// this( persister, 1, lockMode, factory, loadQueryInfluencers );
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public EntityLoader(
|
|
||||||
// OuterJoinLoadable persister,
|
|
||||||
// LockOptions lockOptions,
|
|
||||||
// SessionFactoryImplementor factory,
|
|
||||||
// LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
|
||||||
// this( persister, 1, lockOptions, factory, loadQueryInfluencers );
|
|
||||||
// }
|
|
||||||
|
|
||||||
public EntityLoader(
|
|
||||||
OuterJoinLoadable persister,
|
|
||||||
int batchSize,
|
|
||||||
LockMode lockMode,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
|
||||||
this(
|
|
||||||
persister,
|
|
||||||
persister.getIdentifierColumnNames(),
|
|
||||||
persister.getIdentifierType(),
|
|
||||||
batchSize,
|
|
||||||
lockMode,
|
|
||||||
factory,
|
|
||||||
loadQueryInfluencers
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntityLoader(
|
public static class Builder {
|
||||||
OuterJoinLoadable persister,
|
private final OuterJoinLoadable persister;
|
||||||
int batchSize,
|
private int batchSize = 1;
|
||||||
LockOptions lockOptions,
|
private LoadQueryInfluencers influencers = LoadQueryInfluencers.NONE;
|
||||||
SessionFactoryImplementor factory,
|
private LockMode lockMode = LockMode.NONE;
|
||||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
private LockOptions lockOptions;
|
||||||
this(
|
|
||||||
persister,
|
|
||||||
persister.getIdentifierColumnNames(),
|
|
||||||
persister.getIdentifierType(),
|
|
||||||
batchSize,
|
|
||||||
lockOptions,
|
|
||||||
factory,
|
|
||||||
loadQueryInfluencers
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public EntityLoader(
|
public Builder(OuterJoinLoadable persister) {
|
||||||
OuterJoinLoadable persister,
|
this.persister = persister;
|
||||||
String[] uniqueKey,
|
|
||||||
Type uniqueKeyType,
|
|
||||||
int batchSize,
|
|
||||||
LockMode lockMode,
|
|
||||||
SessionFactoryImplementor factory,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
|
||||||
super( persister, uniqueKeyType, factory, loadQueryInfluencers );
|
|
||||||
|
|
||||||
// EntityJoinWalker walker = new EntityJoinWalker(
|
|
||||||
// persister,
|
|
||||||
// uniqueKey,
|
|
||||||
// batchSize,
|
|
||||||
// lockMode,
|
|
||||||
// factory,
|
|
||||||
// loadQueryInfluencers
|
|
||||||
// );
|
|
||||||
// initFromWalker( walker );
|
|
||||||
// this.compositeKeyManyToOneTargetIndices = walker.getCompositeKeyManyToOneTargetIndices();
|
|
||||||
// postInstantiate();
|
|
||||||
//
|
|
||||||
// batchLoader = batchSize > 1;
|
|
||||||
//
|
|
||||||
if ( log.isDebugEnabled() ) {
|
|
||||||
log.debugf( "Static select for entity %s [%s]: %s", getEntityName(), lockMode, getSqlStatement() );
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public EntityLoader(
|
public Builder withBatchSize(int batchSize) {
|
||||||
OuterJoinLoadable persister,
|
this.batchSize = batchSize;
|
||||||
String[] uniqueKey,
|
return this;
|
||||||
Type uniqueKeyType,
|
}
|
||||||
int batchSize,
|
|
||||||
LockOptions lockOptions,
|
public Builder withInfluencers(LoadQueryInfluencers influencers) {
|
||||||
SessionFactoryImplementor factory,
|
this.influencers = influencers;
|
||||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
return this;
|
||||||
super( persister, uniqueKeyType, factory, loadQueryInfluencers );
|
}
|
||||||
//
|
|
||||||
// EntityJoinWalker walker = new EntityJoinWalker(
|
public Builder withLockMode(LockMode lockMode) {
|
||||||
// persister,
|
this.lockMode = lockMode;
|
||||||
// uniqueKey,
|
return this;
|
||||||
// batchSize,
|
}
|
||||||
// lockOptions,
|
|
||||||
// factory,
|
public Builder withLockOptions(LockOptions lockOptions) {
|
||||||
// loadQueryInfluencers
|
this.lockOptions = lockOptions;
|
||||||
// );
|
return this;
|
||||||
// initFromWalker( walker );
|
}
|
||||||
// this.compositeKeyManyToOneTargetIndices = walker.getCompositeKeyManyToOneTargetIndices();
|
|
||||||
// postInstantiate();
|
public EntityLoader byPrimaryKey() {
|
||||||
//
|
return byUniqueKey( persister.getIdentifierColumnNames(), persister.getIdentifierType() );
|
||||||
// batchLoader = batchSize > 1;
|
}
|
||||||
//
|
|
||||||
if ( log.isDebugEnabled() ) {
|
public EntityLoader byUniqueKey(String[] keyColumnNames, Type keyType) {
|
||||||
log.debugf(
|
return new EntityLoader(
|
||||||
"Static select for entity %s [%s:%s]: %s",
|
persister.getFactory(),
|
||||||
getEntityName(),
|
persister,
|
||||||
lockOptions.getLockMode(),
|
keyColumnNames,
|
||||||
lockOptions.getTimeOut(),
|
keyType,
|
||||||
getSqlStatement()
|
new QueryBuildingParameters() {
|
||||||
|
@Override
|
||||||
|
public LoadQueryInfluencers getQueryInfluencers() {
|
||||||
|
return influencers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBatchSize() {
|
||||||
|
return batchSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LockMode getLockMode() {
|
||||||
|
return lockMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LockOptions getLockOptions() {
|
||||||
|
return lockOptions;
|
||||||
|
}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// public Object loadByUniqueKey(SessionImplementor session,Object key) {
|
private EntityLoader(
|
||||||
// return load( session, key, null, null, LockOptions.NONE );
|
SessionFactoryImplementor factory,
|
||||||
// }
|
OuterJoinLoadable persister,
|
||||||
//
|
String[] uniqueKeyColumnNames,
|
||||||
// @Override
|
Type uniqueKeyType,
|
||||||
// protected boolean isSingleRowLoader() {
|
QueryBuildingParameters buildingParameters) throws MappingException {
|
||||||
// return !batchLoader;
|
super( persister, factory, uniqueKeyColumnNames, uniqueKeyType, buildingParameters );
|
||||||
// }
|
if ( log.isDebugEnabled() ) {
|
||||||
//
|
if ( buildingParameters.getLockOptions() != null ) {
|
||||||
// @Override
|
log.debugf(
|
||||||
// public int[][] getCompositeKeyManyToOneTargetIndices() {
|
"Static select for entity %s [%s:%s]: %s",
|
||||||
// return compositeKeyManyToOneTargetIndices;
|
getEntityName(),
|
||||||
// }
|
buildingParameters.getLockOptions().getLockMode(),
|
||||||
|
buildingParameters.getLockOptions().getTimeOut(),
|
||||||
|
getStaticLoadQuery().getSqlStatement()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else if ( buildingParameters.getLockMode() != null ) {
|
||||||
|
log.debugf(
|
||||||
|
"Static select for entity %s [%s]: %s",
|
||||||
|
getEntityName(),
|
||||||
|
buildingParameters.getLockMode(),
|
||||||
|
getStaticLoadQuery().getSqlStatement()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -32,16 +32,15 @@ import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.engine.spi.SessionImplementor;
|
import org.hibernate.engine.spi.SessionImplementor;
|
||||||
import org.hibernate.internal.util.collections.ArrayHelper;
|
import org.hibernate.internal.util.collections.ArrayHelper;
|
||||||
import org.hibernate.loader.Loader;
|
|
||||||
import org.hibernate.loader.entity.BatchingEntityLoader;
|
|
||||||
import org.hibernate.loader.entity.BatchingEntityLoaderBuilder;
|
|
||||||
import org.hibernate.loader.entity.UniqueEntityLoader;
|
import org.hibernate.loader.entity.UniqueEntityLoader;
|
||||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
import org.hibernate.persister.entity.OuterJoinLoadable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* LoadPlan-based implementation of the the legacy batch loading strategy
|
||||||
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class LegacyBatchingEntityLoaderBuilder extends BatchingEntityLoaderBuilder {
|
public class LegacyBatchingEntityLoaderBuilder extends AbstractBatchingEntityLoaderBuilder {
|
||||||
public static final LegacyBatchingEntityLoaderBuilder INSTANCE = new LegacyBatchingEntityLoaderBuilder();
|
public static final LegacyBatchingEntityLoaderBuilder INSTANCE = new LegacyBatchingEntityLoaderBuilder();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -77,8 +76,11 @@ public class LegacyBatchingEntityLoaderBuilder extends BatchingEntityLoaderBuild
|
||||||
super( persister );
|
super( persister );
|
||||||
this.batchSizes = ArrayHelper.getBatchSizes( maxBatchSize );
|
this.batchSizes = ArrayHelper.getBatchSizes( maxBatchSize );
|
||||||
this.loaders = new EntityLoader[ batchSizes.length ];
|
this.loaders = new EntityLoader[ batchSizes.length ];
|
||||||
|
final EntityLoader.Builder entityLoaderBuilder = EntityLoader.forEntity( persister )
|
||||||
|
.withInfluencers( loadQueryInfluencers )
|
||||||
|
.withLockMode( lockMode );
|
||||||
for ( int i = 0; i < batchSizes.length; i++ ) {
|
for ( int i = 0; i < batchSizes.length; i++ ) {
|
||||||
this.loaders[i] = new EntityLoader( persister, batchSizes[i], lockMode, factory, loadQueryInfluencers);
|
this.loaders[i] = entityLoaderBuilder.withBatchSize( batchSizes[i] ).byPrimaryKey();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,8 +93,11 @@ public class LegacyBatchingEntityLoaderBuilder extends BatchingEntityLoaderBuild
|
||||||
super( persister );
|
super( persister );
|
||||||
this.batchSizes = ArrayHelper.getBatchSizes( maxBatchSize );
|
this.batchSizes = ArrayHelper.getBatchSizes( maxBatchSize );
|
||||||
this.loaders = new EntityLoader[ batchSizes.length ];
|
this.loaders = new EntityLoader[ batchSizes.length ];
|
||||||
|
final EntityLoader.Builder entityLoaderBuilder = EntityLoader.forEntity( persister )
|
||||||
|
.withInfluencers( loadQueryInfluencers )
|
||||||
|
.withLockOptions( lockOptions );
|
||||||
for ( int i = 0; i < batchSizes.length; i++ ) {
|
for ( int i = 0; i < batchSizes.length; i++ ) {
|
||||||
this.loaders[i] = new EntityLoader( persister, batchSizes[i], lockOptions, factory, loadQueryInfluencers);
|
this.loaders[i] = entityLoaderBuilder.withBatchSize( batchSizes[i] ).byPrimaryKey();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.internal.util.StringHelper;
|
||||||
import org.hibernate.loader.CollectionAliases;
|
import org.hibernate.loader.CollectionAliases;
|
||||||
import org.hibernate.loader.EntityAliases;
|
import org.hibernate.loader.EntityAliases;
|
||||||
|
import org.hibernate.loader.plan.exec.spi.AliasResolutionContext;
|
||||||
|
import org.hibernate.loader.plan.spi.Fetch;
|
||||||
import org.hibernate.loader.spi.JoinableAssociation;
|
import org.hibernate.loader.spi.JoinableAssociation;
|
||||||
import org.hibernate.loader.spi.LoadQueryAliasResolutionContext;
|
|
||||||
import org.hibernate.persister.collection.QueryableCollection;
|
import org.hibernate.persister.collection.QueryableCollection;
|
||||||
import org.hibernate.persister.entity.Joinable;
|
import org.hibernate.persister.entity.Joinable;
|
||||||
|
import org.hibernate.persister.walking.internal.FetchStrategyHelper;
|
||||||
import org.hibernate.sql.ConditionFragment;
|
import org.hibernate.sql.ConditionFragment;
|
||||||
import org.hibernate.sql.DisjunctionFragment;
|
import org.hibernate.sql.DisjunctionFragment;
|
||||||
import org.hibernate.sql.InFragment;
|
import org.hibernate.sql.InFragment;
|
||||||
|
@ -58,7 +60,7 @@ public abstract class AbstractLoadQueryImpl {
|
||||||
this.associations = associations;
|
this.associations = associations;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String orderBy(final String orderBy, LoadQueryAliasResolutionContext aliasResolutionContext) {
|
protected String orderBy(final String orderBy, AliasResolutionContext aliasResolutionContext) {
|
||||||
return mergeOrderings( orderBy( associations, aliasResolutionContext ), orderBy );
|
return mergeOrderings( orderBy( associations, aliasResolutionContext ), orderBy );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +79,7 @@ public abstract class AbstractLoadQueryImpl {
|
||||||
/**
|
/**
|
||||||
* Generate a sequence of <tt>LEFT OUTER JOIN</tt> clauses for the given associations.
|
* Generate a sequence of <tt>LEFT OUTER JOIN</tt> clauses for the given associations.
|
||||||
*/
|
*/
|
||||||
protected final JoinFragment mergeOuterJoins(SessionFactoryImplementor factory, LoadQueryAliasResolutionContext aliasResolutionContext)
|
protected final JoinFragment mergeOuterJoins(SessionFactoryImplementor factory, AliasResolutionContext aliasResolutionContext)
|
||||||
throws MappingException {
|
throws MappingException {
|
||||||
JoinFragment joinFragment = factory.getDialect().createOuterJoinFragment();
|
JoinFragment joinFragment = factory.getDialect().createOuterJoinFragment();
|
||||||
JoinableAssociation previous = null;
|
JoinableAssociation previous = null;
|
||||||
|
@ -120,7 +122,7 @@ public abstract class AbstractLoadQueryImpl {
|
||||||
// TODO: why is this static?
|
// TODO: why is this static?
|
||||||
protected static String orderBy(
|
protected static String orderBy(
|
||||||
List<JoinableAssociation> associations,
|
List<JoinableAssociation> associations,
|
||||||
LoadQueryAliasResolutionContext aliasResolutionContext)
|
AliasResolutionContext aliasResolutionContext)
|
||||||
throws MappingException {
|
throws MappingException {
|
||||||
StringBuilder buf = new StringBuilder();
|
StringBuilder buf = new StringBuilder();
|
||||||
JoinableAssociation previous = null;
|
JoinableAssociation previous = null;
|
||||||
|
@ -197,7 +199,7 @@ public abstract class AbstractLoadQueryImpl {
|
||||||
/**
|
/**
|
||||||
* Generate a select list of columns containing all properties of the entity classes
|
* Generate a select list of columns containing all properties of the entity classes
|
||||||
*/
|
*/
|
||||||
protected final String associationSelectString(LoadQueryAliasResolutionContext aliasResolutionContext)
|
protected final String associationSelectString(AliasResolutionContext aliasResolutionContext)
|
||||||
throws MappingException {
|
throws MappingException {
|
||||||
|
|
||||||
if ( associations.size() == 0 ) {
|
if ( associations.size() == 0 ) {
|
||||||
|
@ -210,15 +212,19 @@ public abstract class AbstractLoadQueryImpl {
|
||||||
JoinableAssociation next = ( i == associations.size() - 1 )
|
JoinableAssociation next = ( i == associations.size() - 1 )
|
||||||
? null
|
? null
|
||||||
: associations.get( i + 1 );
|
: associations.get( i + 1 );
|
||||||
|
if ( !shouldAddToSql( association.getCurrentFetch() ) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
final Joinable joinable = association.getJoinable();
|
final Joinable joinable = association.getJoinable();
|
||||||
final EntityAliases currentEntityAliases =
|
final EntityAliases currentEntityAliases =
|
||||||
association.getCurrentEntityReference() == null ?
|
association.getCurrentEntityReference() == null ?
|
||||||
null :
|
null :
|
||||||
aliasResolutionContext.resolveEntityColumnAliases( association.getCurrentEntityReference() );
|
aliasResolutionContext.resolveAliases( association.getCurrentEntityReference() ).getColumnAliases();
|
||||||
final CollectionAliases currentCollectionAliases =
|
final CollectionAliases currentCollectionAliases =
|
||||||
association.getCurrentCollectionReference() == null ?
|
association.getCurrentCollectionReference() == null ?
|
||||||
null :
|
null :
|
||||||
aliasResolutionContext.resolveCollectionColumnAliases( association.getCurrentCollectionReference() );
|
aliasResolutionContext.resolveAliases( association.getCurrentCollectionReference() ).getCollectionColumnAliases();
|
||||||
final String selectFragment = joinable.selectFragment(
|
final String selectFragment = joinable.selectFragment(
|
||||||
next == null ? null : next.getJoinable(),
|
next == null ? null : next.getJoinable(),
|
||||||
next == null ? null : aliasResolutionContext.resolveAssociationRhsTableAlias( next ),
|
next == null ? null : aliasResolutionContext.resolveAssociationRhsTableAlias( next ),
|
||||||
|
@ -236,6 +242,10 @@ public abstract class AbstractLoadQueryImpl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean shouldAddToSql(Fetch fetch) {
|
||||||
|
return FetchStrategyHelper.isJoinFetched( fetch.getFetchStrategy() );
|
||||||
|
}
|
||||||
|
|
||||||
private void addJoins(
|
private void addJoins(
|
||||||
JoinFragment joinFragment,
|
JoinFragment joinFragment,
|
||||||
JoinableAssociation association,
|
JoinableAssociation association,
|
||||||
|
@ -260,7 +270,7 @@ public abstract class AbstractLoadQueryImpl {
|
||||||
private String resolveOnCondition(
|
private String resolveOnCondition(
|
||||||
SessionFactoryImplementor factory,
|
SessionFactoryImplementor factory,
|
||||||
JoinableAssociation joinableAssociation,
|
JoinableAssociation joinableAssociation,
|
||||||
LoadQueryAliasResolutionContext aliasResolutionContext) {
|
AliasResolutionContext aliasResolutionContext) {
|
||||||
final String withClause = StringHelper.isEmpty( joinableAssociation.getWithClause() ) ?
|
final String withClause = StringHelper.isEmpty( joinableAssociation.getWithClause() ) ?
|
||||||
"" :
|
"" :
|
||||||
" and ( " + joinableAssociation.getWithClause() + " )";
|
" and ( " + joinableAssociation.getWithClause() + " )";
|
||||||
|
|
|
@ -58,7 +58,7 @@ public class EntityJoinableAssociationImpl extends AbstractJoinableAssociationIm
|
||||||
hasRestriction,
|
hasRestriction,
|
||||||
enabledFilters
|
enabledFilters
|
||||||
);
|
);
|
||||||
this.joinableType = entityFetch.getEntityType();
|
this.joinableType = entityFetch.getFetchedType();
|
||||||
this.joinable = (Joinable) entityFetch.getEntityPersister();
|
this.joinable = (Joinable) entityFetch.getEntityPersister();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
* 51 Franklin Street, Fifth Floor
|
||||||
* Boston, MA 02110-1301 USA
|
* Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
package org.hibernate.loader.internal;
|
package org.hibernate.loader.plan.exec.internal;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.hibernate.cfg.NotYetImplementedException;
|
import org.hibernate.cfg.NotYetImplementedException;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.hql.internal.NameGenerator;
|
||||||
import org.hibernate.internal.util.StringHelper;
|
import org.hibernate.internal.util.StringHelper;
|
||||||
import org.hibernate.loader.CollectionAliases;
|
import org.hibernate.loader.CollectionAliases;
|
||||||
import org.hibernate.loader.DefaultEntityAliases;
|
import org.hibernate.loader.DefaultEntityAliases;
|
||||||
import org.hibernate.loader.EntityAliases;
|
import org.hibernate.loader.EntityAliases;
|
||||||
import org.hibernate.loader.GeneratedCollectionAliases;
|
import org.hibernate.loader.GeneratedCollectionAliases;
|
||||||
|
import org.hibernate.loader.plan.spi.BidirectionalEntityFetch;
|
||||||
|
import org.hibernate.loader.plan.exec.spi.AliasResolutionContext;
|
||||||
|
import org.hibernate.loader.plan.exec.spi.CollectionReferenceAliases;
|
||||||
|
import org.hibernate.loader.plan.exec.spi.EntityReferenceAliases;
|
||||||
|
import org.hibernate.loader.plan.spi.AnyFetch;
|
||||||
import org.hibernate.loader.plan.spi.CollectionReference;
|
import org.hibernate.loader.plan.spi.CollectionReference;
|
||||||
import org.hibernate.loader.plan.spi.CollectionReturn;
|
|
||||||
import org.hibernate.loader.plan.spi.CompositeElementGraph;
|
import org.hibernate.loader.plan.spi.CompositeElementGraph;
|
||||||
|
import org.hibernate.loader.plan.spi.CompositeFetch;
|
||||||
import org.hibernate.loader.plan.spi.CompositeIndexGraph;
|
import org.hibernate.loader.plan.spi.CompositeIndexGraph;
|
||||||
import org.hibernate.loader.plan.spi.EntityReference;
|
import org.hibernate.loader.plan.spi.EntityReference;
|
||||||
import org.hibernate.loader.plan.spi.EntityReturn;
|
|
||||||
import org.hibernate.loader.plan.spi.Fetch;
|
import org.hibernate.loader.plan.spi.Fetch;
|
||||||
|
import org.hibernate.loader.plan.spi.FetchOwner;
|
||||||
import org.hibernate.loader.plan.spi.Return;
|
import org.hibernate.loader.plan.spi.Return;
|
||||||
import org.hibernate.loader.plan.spi.ScalarReturn;
|
import org.hibernate.loader.plan.spi.ScalarReturn;
|
||||||
|
import org.hibernate.loader.plan.spi.SourceQualifiable;
|
||||||
import org.hibernate.loader.spi.JoinableAssociation;
|
import org.hibernate.loader.spi.JoinableAssociation;
|
||||||
import org.hibernate.loader.spi.LoadQueryAliasResolutionContext;
|
|
||||||
import org.hibernate.persister.collection.CollectionPersister;
|
import org.hibernate.persister.collection.CollectionPersister;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
import org.hibernate.persister.entity.Loadable;
|
import org.hibernate.persister.entity.Loadable;
|
||||||
|
import org.hibernate.persister.walking.spi.WalkingException;
|
||||||
import org.hibernate.type.EntityType;
|
import org.hibernate.type.EntityType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides aliases that are used by load queries and ResultSet processors.
|
* Provides aliases that are used by load queries and ResultSet processors.
|
||||||
*
|
*
|
||||||
* @author Gail Badner
|
* @author Gail Badner
|
||||||
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class LoadQueryAliasResolutionContextImpl implements LoadQueryAliasResolutionContext {
|
public class AliasResolutionContextImpl implements AliasResolutionContext {
|
||||||
private final Map<Return,String[]> aliasesByReturn;
|
private final SessionFactoryImplementor sessionFactory;
|
||||||
private final Map<EntityReference,LoadQueryEntityAliasesImpl> aliasesByEntityReference =
|
|
||||||
new HashMap<EntityReference,LoadQueryEntityAliasesImpl>();
|
private final Map<Return,String> sourceAliasByReturnMap;
|
||||||
|
private final Map<SourceQualifiable,String> sourceQualifiersByReturnMap;
|
||||||
|
|
||||||
|
private final Map<EntityReference,EntityReferenceAliasesImpl> aliasesByEntityReference =
|
||||||
|
new HashMap<EntityReference,EntityReferenceAliasesImpl>();
|
||||||
private final Map<CollectionReference,LoadQueryCollectionAliasesImpl> aliasesByCollectionReference =
|
private final Map<CollectionReference,LoadQueryCollectionAliasesImpl> aliasesByCollectionReference =
|
||||||
new HashMap<CollectionReference,LoadQueryCollectionAliasesImpl>();
|
new HashMap<CollectionReference,LoadQueryCollectionAliasesImpl>();
|
||||||
private final Map<JoinableAssociation,JoinableAssociationAliasesImpl> aliasesByJoinableAssociation =
|
private final Map<JoinableAssociation,JoinableAssociationAliasesImpl> aliasesByJoinableAssociation =
|
||||||
new HashMap<JoinableAssociation, JoinableAssociationAliasesImpl>();
|
new HashMap<JoinableAssociation, JoinableAssociationAliasesImpl>();
|
||||||
private final SessionFactoryImplementor sessionFactory;
|
|
||||||
|
|
||||||
private int currentAliasSuffix = 0;
|
private int currentAliasSuffix;
|
||||||
|
private int currentTableAliasUniqueness;
|
||||||
|
|
||||||
public LoadQueryAliasResolutionContextImpl(
|
/**
|
||||||
|
* Constructs a AliasResolutionContextImpl without any source aliases. This form is used in
|
||||||
|
* non-query (HQL, criteria, etc) contexts.
|
||||||
|
*
|
||||||
|
* @param sessionFactory The session factory
|
||||||
|
*/
|
||||||
|
public AliasResolutionContextImpl(SessionFactoryImplementor sessionFactory) {
|
||||||
|
this( sessionFactory, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a AliasResolutionContextImpl without any source aliases. This form is used in
|
||||||
|
* non-query (HQL, criteria, etc) contexts.
|
||||||
|
*
|
||||||
|
* @param sessionFactory The session factory
|
||||||
|
* @param suffixSeed The seed value to use for generating the suffix used when generating SQL aliases.
|
||||||
|
*/
|
||||||
|
public AliasResolutionContextImpl(SessionFactoryImplementor sessionFactory, int suffixSeed) {
|
||||||
|
this(
|
||||||
|
sessionFactory,
|
||||||
|
suffixSeed,
|
||||||
|
Collections.<Return,String>emptyMap(),
|
||||||
|
Collections.<SourceQualifiable,String>emptyMap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a AliasResolutionContextImpl with source aliases. See the notes on
|
||||||
|
* {@link org.hibernate.loader.plan.exec.spi.AliasResolutionContext#getSourceAlias(Return)} for discussion of "source aliases".
|
||||||
|
*
|
||||||
|
* @param sessionFactory The session factory
|
||||||
|
* @param suffixSeed The seed value to use for generating the suffix used when generating SQL aliases.
|
||||||
|
* @param sourceAliasByReturnMap Mapping of the source alias for each return (select-clause assigned alias).
|
||||||
|
* @param sourceQualifiersByReturnMap Mapping of source query qualifiers (from-clause assigned alias).
|
||||||
|
*/
|
||||||
|
public AliasResolutionContextImpl(
|
||||||
SessionFactoryImplementor sessionFactory,
|
SessionFactoryImplementor sessionFactory,
|
||||||
int suffixSeed,
|
int suffixSeed,
|
||||||
Map<Return,String[]> aliasesByReturn) {
|
Map<Return, String> sourceAliasByReturnMap,
|
||||||
|
Map<SourceQualifiable, String> sourceQualifiersByReturnMap) {
|
||||||
this.sessionFactory = sessionFactory;
|
this.sessionFactory = sessionFactory;
|
||||||
this.currentAliasSuffix = suffixSeed;
|
this.currentAliasSuffix = suffixSeed;
|
||||||
|
this.sourceAliasByReturnMap = new HashMap<Return, String>( sourceAliasByReturnMap );
|
||||||
checkAliasesByReturn( aliasesByReturn );
|
this.sourceQualifiersByReturnMap = new HashMap<SourceQualifiable, String>( sourceQualifiersByReturnMap );
|
||||||
this.aliasesByReturn = new HashMap<Return, String[]>( aliasesByReturn );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void checkAliasesByReturn(Map<Return, String[]> aliasesByReturn) {
|
@Override
|
||||||
if ( aliasesByReturn == null || aliasesByReturn.size() == 0 ) {
|
public String getSourceAlias(Return theReturn) {
|
||||||
throw new IllegalArgumentException( "No return aliases defined" );
|
return sourceAliasByReturnMap.get( theReturn );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] resolveScalarColumnAliases(ScalarReturn scalarReturn) {
|
||||||
|
final int numberOfColumns = scalarReturn.getType().getColumnSpan( sessionFactory );
|
||||||
|
|
||||||
|
// if the scalar return was assigned an alias in the source query, use that as the basis for generating
|
||||||
|
// the SQL aliases
|
||||||
|
final String sourceAlias = getSourceAlias( scalarReturn );
|
||||||
|
if ( sourceAlias != null ) {
|
||||||
|
// generate one based on the source alias
|
||||||
|
// todo : to do this properly requires dialect involvement ++
|
||||||
|
// due to needing uniqueness even across identifier length based truncation; just truncating is
|
||||||
|
// *not* enough since truncated names might clash
|
||||||
|
//
|
||||||
|
// for now, don't even truncate...
|
||||||
|
return NameGenerator.scalarNames( sourceAlias, numberOfColumns );
|
||||||
}
|
}
|
||||||
for ( Map.Entry<Return,String[]> entry : aliasesByReturn.entrySet() ) {
|
else {
|
||||||
final Return aReturn = entry.getKey();
|
// generate one from scratch
|
||||||
final String[] aliases = entry.getValue();
|
return NameGenerator.scalarNames( currentAliasSuffix++, numberOfColumns );
|
||||||
if ( aReturn == null ) {
|
}
|
||||||
throw new IllegalArgumentException( "null key found in aliasesByReturn" );
|
}
|
||||||
}
|
|
||||||
if ( aliases == null || aliases.length == 0 ) {
|
@Override
|
||||||
throw new IllegalArgumentException(
|
public EntityReferenceAliases resolveAliases(EntityReference entityReference) {
|
||||||
String.format( "No alias defined for [%s]", aReturn )
|
EntityReferenceAliasesImpl aliases = aliasesByEntityReference.get( entityReference );
|
||||||
|
if ( aliases == null ) {
|
||||||
|
if ( BidirectionalEntityFetch.class.isInstance( entityReference ) ) {
|
||||||
|
return resolveAliases(
|
||||||
|
( (BidirectionalEntityFetch) entityReference ).getTargetEntityReference()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if ( ( aliases.length > 1 ) &&
|
final EntityPersister entityPersister = entityReference.getEntityPersister();
|
||||||
( aReturn instanceof EntityReturn || aReturn instanceof CollectionReturn ) ) {
|
aliases = new EntityReferenceAliasesImpl(
|
||||||
throw new IllegalArgumentException( String.format( "More than 1 alias defined for [%s]", aReturn ) );
|
createTableAlias( entityPersister ),
|
||||||
}
|
createEntityAliases( entityPersister )
|
||||||
for ( String alias : aliases ) {
|
|
||||||
if ( StringHelper.isEmpty( alias ) ) {
|
|
||||||
throw new IllegalArgumentException( String.format( "An alias for [%s] is null or empty.", aReturn ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String resolveEntityReturnAlias(EntityReturn entityReturn) {
|
|
||||||
return getAndCheckReturnAliasExists( entityReturn )[ 0 ];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String resolveCollectionReturnAlias(CollectionReturn collectionReturn) {
|
|
||||||
return getAndCheckReturnAliasExists( collectionReturn )[ 0 ];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String[] resolveScalarReturnAliases(ScalarReturn scalarReturn) {
|
|
||||||
throw new NotYetImplementedException( "Cannot resolve scalar column aliases yet." );
|
|
||||||
}
|
|
||||||
|
|
||||||
private String[] getAndCheckReturnAliasExists(Return aReturn) {
|
|
||||||
// There is already a check for the appropriate number of aliases stored in aliasesByReturn,
|
|
||||||
// so just check for existence here.
|
|
||||||
final String[] aliases = aliasesByReturn.get( aReturn );
|
|
||||||
if ( aliases == null ) {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
String.format( "No alias is defined for [%s]", aReturn )
|
|
||||||
);
|
);
|
||||||
|
aliasesByEntityReference.put( entityReference, aliases );
|
||||||
}
|
}
|
||||||
return aliases;
|
return aliases;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String resolveEntityTableAlias(EntityReference entityReference) {
|
public CollectionReferenceAliases resolveAliases(CollectionReference collectionReference) {
|
||||||
return getOrGenerateLoadQueryEntityAliases( entityReference ).tableAlias;
|
LoadQueryCollectionAliasesImpl aliases = aliasesByCollectionReference.get( collectionReference );
|
||||||
|
if ( aliases == null ) {
|
||||||
|
final CollectionPersister collectionPersister = collectionReference.getCollectionPersister();
|
||||||
|
aliases = new LoadQueryCollectionAliasesImpl(
|
||||||
|
createTableAlias( collectionPersister.getRole() ),
|
||||||
|
collectionPersister.isManyToMany()
|
||||||
|
? createTableAlias( collectionPersister.getRole() )
|
||||||
|
: null,
|
||||||
|
createCollectionAliases( collectionPersister ),
|
||||||
|
createCollectionElementAliases( collectionPersister )
|
||||||
|
);
|
||||||
|
aliasesByCollectionReference.put( collectionReference, aliases );
|
||||||
|
}
|
||||||
|
return aliases;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public EntityAliases resolveEntityColumnAliases(EntityReference entityReference) {
|
|
||||||
return getOrGenerateLoadQueryEntityAliases( entityReference ).columnAliases;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String resolveCollectionTableAlias(CollectionReference collectionReference) {
|
|
||||||
return getOrGenerateLoadQueryCollectionAliases( collectionReference ).tableAlias;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CollectionAliases resolveCollectionColumnAliases(CollectionReference collectionReference) {
|
|
||||||
return getOrGenerateLoadQueryCollectionAliases( collectionReference ).collectionAliases;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public EntityAliases resolveCollectionElementColumnAliases(CollectionReference collectionReference) {
|
|
||||||
return getOrGenerateLoadQueryCollectionAliases( collectionReference ).collectionElementAliases;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String resolveAssociationRhsTableAlias(JoinableAssociation joinableAssociation) {
|
public String resolveAssociationRhsTableAlias(JoinableAssociation joinableAssociation) {
|
||||||
|
@ -179,48 +218,29 @@ public class LoadQueryAliasResolutionContextImpl implements LoadQueryAliasResolu
|
||||||
return Integer.toString( currentAliasSuffix++ ) + '_';
|
return Integer.toString( currentAliasSuffix++ ) + '_';
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoadQueryEntityAliasesImpl getOrGenerateLoadQueryEntityAliases(EntityReference entityReference) {
|
|
||||||
LoadQueryEntityAliasesImpl aliases = aliasesByEntityReference.get( entityReference );
|
|
||||||
if ( aliases == null ) {
|
|
||||||
final EntityPersister entityPersister = entityReference.getEntityPersister();
|
|
||||||
aliases = new LoadQueryEntityAliasesImpl(
|
|
||||||
createTableAlias( entityPersister ),
|
|
||||||
createEntityAliases( entityPersister )
|
|
||||||
);
|
|
||||||
aliasesByEntityReference.put( entityReference, aliases );
|
|
||||||
}
|
|
||||||
return aliases;
|
|
||||||
}
|
|
||||||
|
|
||||||
private LoadQueryCollectionAliasesImpl getOrGenerateLoadQueryCollectionAliases(CollectionReference collectionReference) {
|
|
||||||
LoadQueryCollectionAliasesImpl aliases = aliasesByCollectionReference.get( collectionReference );
|
|
||||||
if ( aliases == null ) {
|
|
||||||
final CollectionPersister collectionPersister = collectionReference.getCollectionPersister();
|
|
||||||
aliases = new LoadQueryCollectionAliasesImpl(
|
|
||||||
createTableAlias( collectionPersister.getRole() ),
|
|
||||||
createCollectionAliases( collectionPersister ),
|
|
||||||
createCollectionElementAliases( collectionPersister )
|
|
||||||
);
|
|
||||||
aliasesByCollectionReference.put( collectionReference, aliases );
|
|
||||||
}
|
|
||||||
return aliases;
|
|
||||||
}
|
|
||||||
|
|
||||||
private JoinableAssociationAliasesImpl getOrGenerateJoinAssocationAliases(JoinableAssociation joinableAssociation) {
|
private JoinableAssociationAliasesImpl getOrGenerateJoinAssocationAliases(JoinableAssociation joinableAssociation) {
|
||||||
JoinableAssociationAliasesImpl aliases = aliasesByJoinableAssociation.get( joinableAssociation );
|
JoinableAssociationAliasesImpl aliases = aliasesByJoinableAssociation.get( joinableAssociation );
|
||||||
if ( aliases == null ) {
|
if ( aliases == null ) {
|
||||||
final Fetch currentFetch = joinableAssociation.getCurrentFetch();
|
final Fetch currentFetch = joinableAssociation.getCurrentFetch();
|
||||||
final String lhsAlias;
|
final String lhsAlias;
|
||||||
if ( EntityReference.class.isInstance( currentFetch.getOwner() ) ) {
|
if ( AnyFetch.class.isInstance( currentFetch ) ) {
|
||||||
lhsAlias = resolveEntityTableAlias( (EntityReference) currentFetch.getOwner() );
|
throw new WalkingException( "Any type should never be joined!" );
|
||||||
|
}
|
||||||
|
else if ( EntityReference.class.isInstance( currentFetch.getOwner() ) ) {
|
||||||
|
lhsAlias = resolveAliases( (EntityReference) currentFetch.getOwner() ).getTableAlias();
|
||||||
|
}
|
||||||
|
else if ( CompositeFetch.class.isInstance( currentFetch.getOwner() ) ) {
|
||||||
|
lhsAlias = resolveAliases(
|
||||||
|
locateCompositeFetchEntityReferenceSource( (CompositeFetch) currentFetch.getOwner() )
|
||||||
|
).getTableAlias();
|
||||||
}
|
}
|
||||||
else if ( CompositeElementGraph.class.isInstance( currentFetch.getOwner() ) ) {
|
else if ( CompositeElementGraph.class.isInstance( currentFetch.getOwner() ) ) {
|
||||||
CompositeElementGraph compositeElementGraph = (CompositeElementGraph) currentFetch.getOwner();
|
CompositeElementGraph compositeElementGraph = (CompositeElementGraph) currentFetch.getOwner();
|
||||||
lhsAlias = resolveCollectionTableAlias( compositeElementGraph.getCollectionReference() );
|
lhsAlias = resolveAliases( compositeElementGraph.getCollectionReference() ).getElementTableAlias();
|
||||||
}
|
}
|
||||||
else if ( CompositeIndexGraph.class.isInstance( currentFetch.getOwner() ) ) {
|
else if ( CompositeIndexGraph.class.isInstance( currentFetch.getOwner() ) ) {
|
||||||
CompositeIndexGraph compositeIndexGraph = (CompositeIndexGraph) currentFetch.getOwner();
|
CompositeIndexGraph compositeIndexGraph = (CompositeIndexGraph) currentFetch.getOwner();
|
||||||
lhsAlias = resolveCollectionTableAlias( compositeIndexGraph.getCollectionReference() );
|
lhsAlias = resolveAliases( compositeIndexGraph.getCollectionReference() ).getElementTableAlias();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new NotYetImplementedException( "Cannot determine LHS alias for FetchOwner." );
|
throw new NotYetImplementedException( "Cannot determine LHS alias for FetchOwner." );
|
||||||
|
@ -229,16 +249,16 @@ public class LoadQueryAliasResolutionContextImpl implements LoadQueryAliasResolu
|
||||||
final String[] aliasedLhsColumnNames = currentFetch.toSqlSelectFragments( lhsAlias );
|
final String[] aliasedLhsColumnNames = currentFetch.toSqlSelectFragments( lhsAlias );
|
||||||
final String rhsAlias;
|
final String rhsAlias;
|
||||||
if ( EntityReference.class.isInstance( currentFetch ) ) {
|
if ( EntityReference.class.isInstance( currentFetch ) ) {
|
||||||
rhsAlias = resolveEntityTableAlias( (EntityReference) currentFetch );
|
rhsAlias = resolveAliases( (EntityReference) currentFetch ).getTableAlias();
|
||||||
}
|
}
|
||||||
else if ( CollectionReference.class.isInstance( joinableAssociation.getCurrentFetch() ) ) {
|
else if ( CollectionReference.class.isInstance( joinableAssociation.getCurrentFetch() ) ) {
|
||||||
rhsAlias = resolveCollectionTableAlias( (CollectionReference) currentFetch );
|
rhsAlias = resolveAliases( (CollectionReference) currentFetch ).getCollectionTableAlias();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new NotYetImplementedException( "Cannot determine RHS alis for a fetch that is not an EntityReference or CollectionReference." );
|
throw new NotYetImplementedException( "Cannot determine RHS alis for a fetch that is not an EntityReference or CollectionReference." );
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: can't this be found in CollectionAliases or EntityAliases? should be moved to LoadQueryAliasResolutionContextImpl
|
// TODO: can't this be found in CollectionAliases or EntityAliases? should be moved to AliasResolutionContextImpl
|
||||||
|
|
||||||
aliases = new JoinableAssociationAliasesImpl( lhsAlias, aliasedLhsColumnNames, rhsAlias );
|
aliases = new JoinableAssociationAliasesImpl( lhsAlias, aliasedLhsColumnNames, rhsAlias );
|
||||||
aliasesByJoinableAssociation.put( joinableAssociation, aliases );
|
aliasesByJoinableAssociation.put( joinableAssociation, aliases );
|
||||||
|
@ -246,12 +266,24 @@ public class LoadQueryAliasResolutionContextImpl implements LoadQueryAliasResolu
|
||||||
return aliases;
|
return aliases;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private EntityReference locateCompositeFetchEntityReferenceSource(CompositeFetch composite) {
|
||||||
|
final FetchOwner owner = composite.getOwner();
|
||||||
|
if ( EntityReference.class.isInstance( owner ) ) {
|
||||||
|
return (EntityReference) owner;
|
||||||
|
}
|
||||||
|
if ( CompositeFetch.class.isInstance( owner ) ) {
|
||||||
|
return locateCompositeFetchEntityReferenceSource( (CompositeFetch) owner );
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new WalkingException( "Cannot resolve entity source for a CompositeFetch" );
|
||||||
|
}
|
||||||
|
|
||||||
private String createTableAlias(EntityPersister entityPersister) {
|
private String createTableAlias(EntityPersister entityPersister) {
|
||||||
return createTableAlias( StringHelper.unqualifyEntityName( entityPersister.getEntityName() ) );
|
return createTableAlias( StringHelper.unqualifyEntityName( entityPersister.getEntityName() ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
private String createTableAlias(String name) {
|
private String createTableAlias(String name) {
|
||||||
return StringHelper.generateAlias( name ) + createSuffix();
|
return StringHelper.generateAlias( name, currentTableAliasUniqueness++ );
|
||||||
}
|
}
|
||||||
|
|
||||||
private EntityAliases createEntityAliases(EntityPersister entityPersister) {
|
private EntityAliases createEntityAliases(EntityPersister entityPersister) {
|
||||||
|
@ -272,28 +304,43 @@ public class LoadQueryAliasResolutionContextImpl implements LoadQueryAliasResolu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class LoadQueryEntityAliasesImpl {
|
private static class LoadQueryCollectionAliasesImpl implements CollectionReferenceAliases {
|
||||||
private final String tableAlias;
|
|
||||||
private final EntityAliases columnAliases;
|
|
||||||
|
|
||||||
public LoadQueryEntityAliasesImpl(String tableAlias, EntityAliases columnAliases) {
|
|
||||||
this.tableAlias = tableAlias;
|
|
||||||
this.columnAliases = columnAliases;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class LoadQueryCollectionAliasesImpl {
|
|
||||||
private final String tableAlias;
|
private final String tableAlias;
|
||||||
|
private final String manyToManyAssociationTableAlias;
|
||||||
private final CollectionAliases collectionAliases;
|
private final CollectionAliases collectionAliases;
|
||||||
private final EntityAliases collectionElementAliases;
|
private final EntityAliases entityElementAliases;
|
||||||
|
|
||||||
public LoadQueryCollectionAliasesImpl(
|
public LoadQueryCollectionAliasesImpl(
|
||||||
String tableAlias,
|
String tableAlias,
|
||||||
|
String manyToManyAssociationTableAlias,
|
||||||
CollectionAliases collectionAliases,
|
CollectionAliases collectionAliases,
|
||||||
EntityAliases collectionElementAliases) {
|
EntityAliases entityElementAliases) {
|
||||||
this.tableAlias = tableAlias;
|
this.tableAlias = tableAlias;
|
||||||
|
this.manyToManyAssociationTableAlias = manyToManyAssociationTableAlias;
|
||||||
this.collectionAliases = collectionAliases;
|
this.collectionAliases = collectionAliases;
|
||||||
this.collectionElementAliases = collectionElementAliases;
|
this.entityElementAliases = entityElementAliases;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCollectionTableAlias() {
|
||||||
|
return StringHelper.isNotEmpty( manyToManyAssociationTableAlias )
|
||||||
|
? manyToManyAssociationTableAlias
|
||||||
|
: tableAlias;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getElementTableAlias() {
|
||||||
|
return tableAlias;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CollectionAliases getCollectionColumnAliases() {
|
||||||
|
return collectionAliases;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EntityAliases getEntityElementColumnAliases() {
|
||||||
|
return entityElementAliases;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
* 51 Franklin Street, Fifth Floor
|
||||||
* Boston, MA 02110-1301 USA
|
* Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
package org.hibernate.loader.internal;
|
package org.hibernate.loader.plan.exec.process.internal;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.IdentityHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.StaleObjectStateException;
|
import org.hibernate.StaleObjectStateException;
|
||||||
import org.hibernate.WrongClassException;
|
import org.hibernate.WrongClassException;
|
||||||
import org.hibernate.collection.spi.PersistentCollection;
|
import org.hibernate.collection.spi.PersistentCollection;
|
||||||
|
import org.hibernate.engine.FetchStrategy;
|
||||||
import org.hibernate.engine.internal.TwoPhaseLoad;
|
import org.hibernate.engine.internal.TwoPhaseLoad;
|
||||||
import org.hibernate.engine.spi.EntityKey;
|
import org.hibernate.engine.spi.EntityKey;
|
||||||
import org.hibernate.engine.spi.EntityUniqueKey;
|
import org.hibernate.engine.spi.EntityUniqueKey;
|
||||||
|
@ -53,16 +53,21 @@ import org.hibernate.event.spi.PostLoadEvent;
|
||||||
import org.hibernate.event.spi.PreLoadEvent;
|
import org.hibernate.event.spi.PreLoadEvent;
|
||||||
import org.hibernate.loader.CollectionAliases;
|
import org.hibernate.loader.CollectionAliases;
|
||||||
import org.hibernate.loader.EntityAliases;
|
import org.hibernate.loader.EntityAliases;
|
||||||
|
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext;
|
||||||
|
import org.hibernate.loader.plan.exec.query.spi.NamedParameterContext;
|
||||||
|
import org.hibernate.loader.plan.exec.spi.AliasResolutionContext;
|
||||||
|
import org.hibernate.loader.plan.exec.spi.LockModeResolver;
|
||||||
import org.hibernate.loader.plan.spi.CollectionFetch;
|
import org.hibernate.loader.plan.spi.CollectionFetch;
|
||||||
import org.hibernate.loader.plan.spi.CollectionReturn;
|
import org.hibernate.loader.plan.spi.CollectionReturn;
|
||||||
|
import org.hibernate.loader.plan.spi.CompositeFetch;
|
||||||
|
import org.hibernate.loader.plan.spi.EntityFetch;
|
||||||
import org.hibernate.loader.plan.spi.EntityReference;
|
import org.hibernate.loader.plan.spi.EntityReference;
|
||||||
|
import org.hibernate.loader.plan.spi.Fetch;
|
||||||
|
import org.hibernate.loader.plan.spi.FetchOwner;
|
||||||
import org.hibernate.loader.plan.spi.LoadPlan;
|
import org.hibernate.loader.plan.spi.LoadPlan;
|
||||||
import org.hibernate.loader.plan.spi.visit.LoadPlanVisitationStrategyAdapter;
|
import org.hibernate.loader.plan.spi.visit.LoadPlanVisitationStrategyAdapter;
|
||||||
import org.hibernate.loader.plan.spi.visit.LoadPlanVisitor;
|
import org.hibernate.loader.plan.spi.visit.LoadPlanVisitor;
|
||||||
import org.hibernate.loader.spi.AfterLoadAction;
|
import org.hibernate.loader.spi.AfterLoadAction;
|
||||||
import org.hibernate.loader.spi.LoadQueryAliasResolutionContext;
|
|
||||||
import org.hibernate.loader.spi.NamedParameterContext;
|
|
||||||
import org.hibernate.loader.spi.ResultSetProcessingContext;
|
|
||||||
import org.hibernate.persister.collection.CollectionPersister;
|
import org.hibernate.persister.collection.CollectionPersister;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
import org.hibernate.persister.entity.Loadable;
|
import org.hibernate.persister.entity.Loadable;
|
||||||
|
@ -82,46 +87,75 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
|
||||||
private final SessionImplementor session;
|
private final SessionImplementor session;
|
||||||
private final LoadPlan loadPlan;
|
private final LoadPlan loadPlan;
|
||||||
private final boolean readOnly;
|
private final boolean readOnly;
|
||||||
|
private final boolean shouldUseOptionalEntityInformation;
|
||||||
|
private final boolean forceFetchLazyAttributes;
|
||||||
|
private final boolean shouldReturnProxies;
|
||||||
private final QueryParameters queryParameters;
|
private final QueryParameters queryParameters;
|
||||||
private final NamedParameterContext namedParameterContext;
|
private final NamedParameterContext namedParameterContext;
|
||||||
private final LoadQueryAliasResolutionContext aliasResolutionContext;
|
private final AliasResolutionContext aliasResolutionContext;
|
||||||
private final boolean hadSubselectFetches;
|
private final boolean hadSubselectFetches;
|
||||||
|
|
||||||
private final EntityKey dictatedRootEntityKey;
|
|
||||||
|
|
||||||
private List<HydratedEntityRegistration> currentRowHydratedEntityRegistrationList;
|
private List<HydratedEntityRegistration> currentRowHydratedEntityRegistrationList;
|
||||||
|
|
||||||
private Map<EntityPersister,Set<EntityKey>> subselectLoadableEntityKeyMap;
|
private Map<EntityPersister,Set<EntityKey>> subselectLoadableEntityKeyMap;
|
||||||
private List<HydratedEntityRegistration> hydratedEntityRegistrationList;
|
private List<HydratedEntityRegistration> hydratedEntityRegistrationList;
|
||||||
|
|
||||||
|
private LockModeResolver lockModeResolverDelegate = new LockModeResolver() {
|
||||||
|
@Override
|
||||||
|
public LockMode resolveLockMode(EntityReference entityReference) {
|
||||||
|
return LockMode.NONE;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a ResultSetProcessingContextImpl
|
||||||
|
*
|
||||||
|
* @param resultSet
|
||||||
|
* @param session
|
||||||
|
* @param loadPlan
|
||||||
|
* @param readOnly
|
||||||
|
* @param shouldUseOptionalEntityInformation There are times when the "optional entity information" on
|
||||||
|
* QueryParameters should be used and times when they should not. Collection initializers, batch loaders, etc
|
||||||
|
* are times when it should NOT be used.
|
||||||
|
* @param forceFetchLazyAttributes
|
||||||
|
* @param shouldReturnProxies
|
||||||
|
* @param queryParameters
|
||||||
|
* @param namedParameterContext
|
||||||
|
* @param aliasResolutionContext
|
||||||
|
* @param hadSubselectFetches
|
||||||
|
*/
|
||||||
public ResultSetProcessingContextImpl(
|
public ResultSetProcessingContextImpl(
|
||||||
ResultSet resultSet,
|
ResultSet resultSet,
|
||||||
SessionImplementor session,
|
SessionImplementor session,
|
||||||
LoadPlan loadPlan,
|
LoadPlan loadPlan,
|
||||||
boolean readOnly,
|
boolean readOnly,
|
||||||
boolean useOptionalEntityKey,
|
boolean shouldUseOptionalEntityInformation,
|
||||||
|
boolean forceFetchLazyAttributes,
|
||||||
|
boolean shouldReturnProxies,
|
||||||
QueryParameters queryParameters,
|
QueryParameters queryParameters,
|
||||||
NamedParameterContext namedParameterContext,
|
NamedParameterContext namedParameterContext,
|
||||||
LoadQueryAliasResolutionContext aliasResolutionContext,
|
AliasResolutionContext aliasResolutionContext,
|
||||||
boolean hadSubselectFetches) {
|
boolean hadSubselectFetches) {
|
||||||
this.resultSet = resultSet;
|
this.resultSet = resultSet;
|
||||||
this.session = session;
|
this.session = session;
|
||||||
this.loadPlan = loadPlan;
|
this.loadPlan = loadPlan;
|
||||||
this.readOnly = readOnly;
|
this.readOnly = readOnly;
|
||||||
|
this.shouldUseOptionalEntityInformation = shouldUseOptionalEntityInformation;
|
||||||
|
this.forceFetchLazyAttributes = forceFetchLazyAttributes;
|
||||||
|
this.shouldReturnProxies = shouldReturnProxies;
|
||||||
this.queryParameters = queryParameters;
|
this.queryParameters = queryParameters;
|
||||||
this.namedParameterContext = namedParameterContext;
|
this.namedParameterContext = namedParameterContext;
|
||||||
this.aliasResolutionContext = aliasResolutionContext;
|
this.aliasResolutionContext = aliasResolutionContext;
|
||||||
this.hadSubselectFetches = hadSubselectFetches;
|
this.hadSubselectFetches = hadSubselectFetches;
|
||||||
|
|
||||||
if ( useOptionalEntityKey ) {
|
if ( shouldUseOptionalEntityInformation ) {
|
||||||
this.dictatedRootEntityKey = ResultSetProcessorHelper.getOptionalObjectKey( queryParameters, session );
|
if ( queryParameters.getOptionalId() != null ) {
|
||||||
if ( this.dictatedRootEntityKey == null ) {
|
// make sure we have only one return
|
||||||
throw new HibernateException( "Unable to resolve optional entity-key" );
|
if ( loadPlan.getReturns().size() > 1 ) {
|
||||||
|
throw new IllegalStateException( "Cannot specify 'optional entity' values with multi-return load plans" );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
this.dictatedRootEntityKey = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -129,28 +163,48 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldUseOptionalEntityInformation() {
|
||||||
|
return shouldUseOptionalEntityInformation;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public QueryParameters getQueryParameters() {
|
public QueryParameters getQueryParameters() {
|
||||||
return queryParameters;
|
return queryParameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EntityKey getDictatedRootEntityKey() {
|
public boolean shouldReturnProxies() {
|
||||||
return dictatedRootEntityKey;
|
return shouldReturnProxies;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<EntityReference,IdentifierResolutionContext> identifierResolutionContextMap;
|
@Override
|
||||||
|
public LoadPlan getLoadPlan() {
|
||||||
|
return loadPlan;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IdentifierResolutionContext getIdentifierResolutionContext(final EntityReference entityReference) {
|
public LockMode resolveLockMode(EntityReference entityReference) {
|
||||||
|
final LockMode lockMode = lockModeResolverDelegate.resolveLockMode( entityReference );
|
||||||
|
return LockMode.NONE == lockMode ? LockMode.NONE : lockMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<EntityReference,EntityReferenceProcessingState> identifierResolutionContextMap;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EntityReferenceProcessingState getProcessingState(final EntityReference entityReference) {
|
||||||
if ( identifierResolutionContextMap == null ) {
|
if ( identifierResolutionContextMap == null ) {
|
||||||
identifierResolutionContextMap = new HashMap<EntityReference, IdentifierResolutionContext>();
|
identifierResolutionContextMap = new IdentityHashMap<EntityReference, EntityReferenceProcessingState>();
|
||||||
}
|
}
|
||||||
IdentifierResolutionContext context = identifierResolutionContextMap.get( entityReference );
|
|
||||||
|
EntityReferenceProcessingState context = identifierResolutionContextMap.get( entityReference );
|
||||||
if ( context == null ) {
|
if ( context == null ) {
|
||||||
context = new IdentifierResolutionContext() {
|
context = new EntityReferenceProcessingState() {
|
||||||
private Object hydratedForm;
|
private boolean wasMissingIdentifier;
|
||||||
|
private Object identifierHydratedForm;
|
||||||
private EntityKey entityKey;
|
private EntityKey entityKey;
|
||||||
|
private Object[] hydratedState;
|
||||||
|
private Object entityInstance;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EntityReference getEntityReference() {
|
public EntityReference getEntityReference() {
|
||||||
|
@ -158,23 +212,31 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerHydratedForm(Object hydratedForm) {
|
public void registerMissingIdentifier() {
|
||||||
if ( this.hydratedForm != null ) {
|
if ( !EntityFetch.class.isInstance( entityReference ) ) {
|
||||||
// this could be bad...
|
throw new IllegalStateException( "Missing return row identifier" );
|
||||||
}
|
}
|
||||||
this.hydratedForm = hydratedForm;
|
ResultSetProcessingContextImpl.this.registerNonExists( (EntityFetch) entityReference );
|
||||||
|
wasMissingIdentifier = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getHydratedForm() {
|
public boolean isMissingIdentifier() {
|
||||||
return hydratedForm;
|
return wasMissingIdentifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerIdentifierHydratedForm(Object identifierHydratedForm) {
|
||||||
|
this.identifierHydratedForm = identifierHydratedForm;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getIdentifierHydratedForm() {
|
||||||
|
return identifierHydratedForm;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerEntityKey(EntityKey entityKey) {
|
public void registerEntityKey(EntityKey entityKey) {
|
||||||
if ( this.entityKey != null ) {
|
|
||||||
// again, could be trouble...
|
|
||||||
}
|
|
||||||
this.entityKey = entityKey;
|
this.entityKey = entityKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,6 +244,26 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
|
||||||
public EntityKey getEntityKey() {
|
public EntityKey getEntityKey() {
|
||||||
return entityKey;
|
return entityKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerHydratedState(Object[] hydratedState) {
|
||||||
|
this.hydratedState = hydratedState;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object[] getHydratedState() {
|
||||||
|
return hydratedState;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerEntityInstance(Object entityInstance) {
|
||||||
|
this.entityInstance = entityInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getEntityInstance() {
|
||||||
|
return entityInstance;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
identifierResolutionContextMap.put( entityReference, context );
|
identifierResolutionContextMap.put( entityReference, context );
|
||||||
}
|
}
|
||||||
|
@ -189,15 +271,55 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void registerNonExists(EntityFetch fetch) {
|
||||||
public Set<IdentifierResolutionContext> getIdentifierResolutionContexts() {
|
final EntityType fetchedType = fetch.getFetchedType();
|
||||||
return Collections.unmodifiableSet(
|
if ( ! fetchedType.isOneToOne() ) {
|
||||||
new HashSet<IdentifierResolutionContext>( identifierResolutionContextMap.values() )
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final EntityReferenceProcessingState fetchOwnerState = getOwnerProcessingState( fetch );
|
||||||
|
if ( fetchOwnerState == null ) {
|
||||||
|
throw new IllegalStateException( "Could not locate fetch owner state" );
|
||||||
|
}
|
||||||
|
|
||||||
|
final EntityKey ownerEntityKey = fetchOwnerState.getEntityKey();
|
||||||
|
if ( ownerEntityKey == null ) {
|
||||||
|
throw new IllegalStateException( "Could not locate fetch owner EntityKey" );
|
||||||
|
}
|
||||||
|
|
||||||
|
session.getPersistenceContext().addNullProperty(
|
||||||
|
ownerEntityKey,
|
||||||
|
fetchedType.getPropertyName()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LoadQueryAliasResolutionContext getLoadQueryAliasResolutionContext() {
|
public EntityReferenceProcessingState getOwnerProcessingState(Fetch fetch) {
|
||||||
|
return getProcessingState( resolveFetchOwnerEntityReference( fetch ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private EntityReference resolveFetchOwnerEntityReference(Fetch fetch) {
|
||||||
|
final FetchOwner fetchOwner = fetch.getOwner();
|
||||||
|
|
||||||
|
if ( EntityReference.class.isInstance( fetchOwner ) ) {
|
||||||
|
return (EntityReference) fetchOwner;
|
||||||
|
}
|
||||||
|
else if ( CompositeFetch.class.isInstance( fetchOwner ) ) {
|
||||||
|
return resolveFetchOwnerEntityReference( (CompositeFetch) fetchOwner );
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalStateException(
|
||||||
|
String.format(
|
||||||
|
"Cannot resolve FetchOwner [%s] of Fetch [%s (%s)] to an EntityReference",
|
||||||
|
fetchOwner,
|
||||||
|
fetch,
|
||||||
|
fetch.getPropertyPath()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AliasResolutionContext getAliasResolutionContext() {
|
||||||
return aliasResolutionContext;
|
return aliasResolutionContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,7 +431,7 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
|
||||||
checkVersion(
|
checkVersion(
|
||||||
resultSet,
|
resultSet,
|
||||||
entityKeyContext.getEntityPersister(),
|
entityKeyContext.getEntityPersister(),
|
||||||
aliasResolutionContext.resolveEntityColumnAliases( entityKeyContext.getEntityReference() ),
|
aliasResolutionContext.resolveAliases( entityKeyContext.getEntityReference() ).getColumnAliases(),
|
||||||
entityKey,
|
entityKey,
|
||||||
existing
|
existing
|
||||||
);
|
);
|
||||||
|
@ -324,14 +446,26 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
|
||||||
final String concreteEntityTypeName = getConcreteEntityTypeName(
|
final String concreteEntityTypeName = getConcreteEntityTypeName(
|
||||||
resultSet,
|
resultSet,
|
||||||
entityKeyContext.getEntityPersister(),
|
entityKeyContext.getEntityPersister(),
|
||||||
aliasResolutionContext.resolveEntityColumnAliases( entityKeyContext.getEntityReference() ),
|
aliasResolutionContext.resolveAliases( entityKeyContext.getEntityReference() ).getColumnAliases(),
|
||||||
entityKey
|
entityKey
|
||||||
);
|
);
|
||||||
|
|
||||||
final Object entityInstance = getSession().instantiate(
|
final Object entityInstance;
|
||||||
concreteEntityTypeName,
|
// if ( suppliedOptionalEntityKey != null && entityKey.equals( suppliedOptionalEntityKey ) ) {
|
||||||
entityKey.getIdentifier()
|
// // its the given optional object
|
||||||
);
|
// entityInstance = queryParameters.getOptionalObject();
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
// instantiate a new instance
|
||||||
|
entityInstance = session.instantiate( concreteEntityTypeName, entityKey.getIdentifier() );
|
||||||
|
// }
|
||||||
|
|
||||||
|
FetchStrategy fetchStrategy = null;
|
||||||
|
final EntityReference entityReference = entityKeyContext.getEntityReference();
|
||||||
|
if ( EntityFetch.class.isInstance( entityReference ) ) {
|
||||||
|
final EntityFetch fetch = (EntityFetch) entityReference;
|
||||||
|
fetchStrategy = fetch.getFetchStrategy();
|
||||||
|
}
|
||||||
|
|
||||||
//need to hydrate it.
|
//need to hydrate it.
|
||||||
|
|
||||||
|
@ -350,15 +484,16 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
|
||||||
entityInstance,
|
entityInstance,
|
||||||
concreteEntityTypeName,
|
concreteEntityTypeName,
|
||||||
entityKey,
|
entityKey,
|
||||||
aliasResolutionContext.resolveEntityColumnAliases( entityKeyContext.getEntityReference() ),
|
aliasResolutionContext.resolveAliases( entityKeyContext.getEntityReference() ).getColumnAliases(),
|
||||||
acquiredLockMode,
|
acquiredLockMode,
|
||||||
entityKeyContext.getEntityPersister(),
|
entityKeyContext.getEntityPersister(),
|
||||||
|
fetchStrategy,
|
||||||
true,
|
true,
|
||||||
entityKeyContext.getEntityPersister().getEntityMetamodel().getEntityType()
|
entityKeyContext.getEntityPersister().getEntityMetamodel().getEntityType()
|
||||||
);
|
);
|
||||||
|
|
||||||
// materialize associations (and initialize the object) later
|
// materialize associations (and initialize the object) later
|
||||||
registerHydratedEntity( entityKeyContext.getEntityPersister(), entityKey, entityInstance );
|
registerHydratedEntity( entityKeyContext.getEntityReference(), entityKey, entityInstance );
|
||||||
|
|
||||||
return entityInstance;
|
return entityInstance;
|
||||||
}
|
}
|
||||||
|
@ -373,6 +508,7 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
|
||||||
EntityAliases entityAliases,
|
EntityAliases entityAliases,
|
||||||
LockMode acquiredLockMode,
|
LockMode acquiredLockMode,
|
||||||
EntityPersister rootPersister,
|
EntityPersister rootPersister,
|
||||||
|
FetchStrategy fetchStrategy,
|
||||||
boolean eagerFetch,
|
boolean eagerFetch,
|
||||||
EntityType associationType) {
|
EntityType associationType) {
|
||||||
|
|
||||||
|
@ -400,7 +536,7 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
|
||||||
entityInstance,
|
entityInstance,
|
||||||
persister,
|
persister,
|
||||||
acquiredLockMode,
|
acquiredLockMode,
|
||||||
!eagerFetch,
|
!forceFetchLazyAttributes,
|
||||||
session
|
session
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -417,7 +553,7 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
|
||||||
entityInstance,
|
entityInstance,
|
||||||
(Loadable) rootPersister,
|
(Loadable) rootPersister,
|
||||||
cols,
|
cols,
|
||||||
eagerFetch,
|
loadPlan.areLazyAttributesForceFetched(),
|
||||||
session
|
session
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -469,7 +605,7 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
|
||||||
rowId,
|
rowId,
|
||||||
entityInstance,
|
entityInstance,
|
||||||
acquiredLockMode,
|
acquiredLockMode,
|
||||||
!eagerFetch,
|
!loadPlan.areLazyAttributesForceFetched(),
|
||||||
session
|
session
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -485,7 +621,7 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
rootCollectionReturn.getCollectionPersister(),
|
rootCollectionReturn.getCollectionPersister(),
|
||||||
aliasResolutionContext.resolveCollectionColumnAliases( rootCollectionReturn ),
|
aliasResolutionContext.resolveAliases( rootCollectionReturn ).getCollectionColumnAliases(),
|
||||||
resultSet,
|
resultSet,
|
||||||
session
|
session
|
||||||
);
|
);
|
||||||
|
@ -499,7 +635,7 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
|
||||||
owner,
|
owner,
|
||||||
collectionFetch.getCollectionPersister().getCollectionType().getKeyOfOwner( owner, session ),
|
collectionFetch.getCollectionPersister().getCollectionType().getKeyOfOwner( owner, session ),
|
||||||
collectionFetch.getCollectionPersister(),
|
collectionFetch.getCollectionPersister(),
|
||||||
aliasResolutionContext.resolveCollectionColumnAliases( collectionFetch ),
|
aliasResolutionContext.resolveAliases( collectionFetch ).getCollectionColumnAliases(),
|
||||||
resultSet,
|
resultSet,
|
||||||
session
|
session
|
||||||
);
|
);
|
||||||
|
@ -583,11 +719,17 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerHydratedEntity(EntityPersister persister, EntityKey entityKey, Object entityInstance) {
|
public void registerHydratedEntity(EntityReference entityReference, EntityKey entityKey, Object entityInstance) {
|
||||||
if ( currentRowHydratedEntityRegistrationList == null ) {
|
if ( currentRowHydratedEntityRegistrationList == null ) {
|
||||||
currentRowHydratedEntityRegistrationList = new ArrayList<HydratedEntityRegistration>();
|
currentRowHydratedEntityRegistrationList = new ArrayList<HydratedEntityRegistration>();
|
||||||
}
|
}
|
||||||
currentRowHydratedEntityRegistrationList.add( new HydratedEntityRegistration( persister, entityKey, entityInstance ) );
|
currentRowHydratedEntityRegistrationList.add(
|
||||||
|
new HydratedEntityRegistration(
|
||||||
|
entityReference,
|
||||||
|
entityKey,
|
||||||
|
entityInstance
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -612,10 +754,10 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
|
||||||
subselectLoadableEntityKeyMap = new HashMap<EntityPersister, Set<EntityKey>>();
|
subselectLoadableEntityKeyMap = new HashMap<EntityPersister, Set<EntityKey>>();
|
||||||
}
|
}
|
||||||
for ( HydratedEntityRegistration registration : currentRowHydratedEntityRegistrationList ) {
|
for ( HydratedEntityRegistration registration : currentRowHydratedEntityRegistrationList ) {
|
||||||
Set<EntityKey> entityKeys = subselectLoadableEntityKeyMap.get( registration.persister );
|
Set<EntityKey> entityKeys = subselectLoadableEntityKeyMap.get( registration.entityReference.getEntityPersister() );
|
||||||
if ( entityKeys == null ) {
|
if ( entityKeys == null ) {
|
||||||
entityKeys = new HashSet<EntityKey>();
|
entityKeys = new HashSet<EntityKey>();
|
||||||
subselectLoadableEntityKeyMap.put( registration.persister, entityKeys );
|
subselectLoadableEntityKeyMap.put( registration.entityReference.getEntityPersister(), entityKeys );
|
||||||
}
|
}
|
||||||
entityKeys.add( registration.key );
|
entityKeys.add( registration.key );
|
||||||
}
|
}
|
||||||
|
@ -623,6 +765,8 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
|
||||||
|
|
||||||
// release the currentRowHydratedEntityRegistrationList entries
|
// release the currentRowHydratedEntityRegistrationList entries
|
||||||
currentRowHydratedEntityRegistrationList.clear();
|
currentRowHydratedEntityRegistrationList.clear();
|
||||||
|
|
||||||
|
identifierResolutionContextMap.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -753,7 +897,7 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
|
||||||
TwoPhaseLoad.postLoad( registration.instance, session, postLoadEvent );
|
TwoPhaseLoad.postLoad( registration.instance, session, postLoadEvent );
|
||||||
if ( afterLoadActionList != null ) {
|
if ( afterLoadActionList != null ) {
|
||||||
for ( AfterLoadAction afterLoadAction : afterLoadActionList ) {
|
for ( AfterLoadAction afterLoadAction : afterLoadActionList ) {
|
||||||
afterLoadAction.afterLoad( session, registration.instance, (Loadable) registration.persister );
|
afterLoadAction.afterLoad( session, registration.instance, (Loadable) registration.entityReference.getEntityPersister() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -790,12 +934,12 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class HydratedEntityRegistration {
|
private static class HydratedEntityRegistration {
|
||||||
private final EntityPersister persister;
|
private final EntityReference entityReference;
|
||||||
private final EntityKey key;
|
private final EntityKey key;
|
||||||
private final Object instance;
|
private Object instance;
|
||||||
|
|
||||||
private HydratedEntityRegistration(EntityPersister persister, EntityKey key, Object instance) {
|
private HydratedEntityRegistration(EntityReference entityReference, EntityKey key, Object instance) {
|
||||||
this.persister = persister;
|
this.entityReference = entityReference;
|
||||||
this.key = key;
|
this.key = key;
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
}
|
}
|
|
@ -21,37 +21,57 @@
|
||||||
* 51 Franklin Street, Fifth Floor
|
* 51 Franklin Street, Fifth Floor
|
||||||
* Boston, MA 02110-1301 USA
|
* Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
package org.hibernate.loader.internal;
|
package org.hibernate.loader.plan.exec.process.internal;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.hibernate.engine.spi.EntityKey;
|
import org.hibernate.engine.spi.EntityKey;
|
||||||
import org.hibernate.engine.spi.QueryParameters;
|
import org.hibernate.engine.spi.QueryParameters;
|
||||||
import org.hibernate.engine.spi.SessionImplementor;
|
import org.hibernate.engine.spi.SessionImplementor;
|
||||||
import org.hibernate.loader.EntityAliases;
|
import org.hibernate.loader.plan.exec.query.spi.NamedParameterContext;
|
||||||
import org.hibernate.loader.plan.spi.EntityReference;
|
|
||||||
import org.hibernate.loader.plan.spi.Fetch;
|
|
||||||
import org.hibernate.loader.spi.NamedParameterContext;
|
|
||||||
import org.hibernate.loader.spi.ResultSetProcessingContext;
|
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
import org.hibernate.type.CompositeType;
|
import org.hibernate.type.CompositeType;
|
||||||
import org.hibernate.type.Type;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class ResultSetProcessorHelper {
|
public class ResultSetProcessorHelper {
|
||||||
|
/**
|
||||||
|
* Singleton access
|
||||||
|
*/
|
||||||
|
public static final ResultSetProcessorHelper INSTANCE = new ResultSetProcessorHelper();
|
||||||
|
|
||||||
public static EntityKey getOptionalObjectKey(QueryParameters queryParameters, SessionImplementor session) {
|
public static EntityKey getOptionalObjectKey(QueryParameters queryParameters, SessionImplementor session) {
|
||||||
final Object optionalObject = queryParameters.getOptionalObject();
|
final Object optionalObject = queryParameters.getOptionalObject();
|
||||||
final Serializable optionalId = queryParameters.getOptionalId();
|
final Serializable optionalId = queryParameters.getOptionalId();
|
||||||
final String optionalEntityName = queryParameters.getOptionalEntityName();
|
final String optionalEntityName = queryParameters.getOptionalEntityName();
|
||||||
|
|
||||||
if ( optionalObject != null && optionalEntityName != null ) {
|
return INSTANCE.interpretEntityKey( session, optionalEntityName, optionalId, optionalObject );
|
||||||
return session.generateEntityKey( optionalId, session.getEntityPersister( optionalEntityName, optionalObject ) );
|
}
|
||||||
|
|
||||||
|
public EntityKey interpretEntityKey(
|
||||||
|
SessionImplementor session,
|
||||||
|
String optionalEntityName,
|
||||||
|
Serializable optionalId,
|
||||||
|
Object optionalObject) {
|
||||||
|
if ( optionalEntityName != null ) {
|
||||||
|
final EntityPersister entityPersister;
|
||||||
|
if ( optionalObject != null ) {
|
||||||
|
entityPersister = session.getEntityPersister( optionalEntityName, optionalObject );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
entityPersister = session.getFactory().getEntityPersister( optionalEntityName );
|
||||||
|
}
|
||||||
|
if ( entityPersister.isInstance( optionalId ) ) {
|
||||||
|
// embedded (non-encapsulated) composite identifier
|
||||||
|
final Serializable identifierState = ( (CompositeType) entityPersister.getIdentifierType() ).getPropertyValues( optionalId, session );
|
||||||
|
return session.generateEntityKey( identifierState, entityPersister );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return session.generateEntityKey( optionalId, entityPersister );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return null;
|
return null;
|
||||||
|
@ -74,4 +94,5 @@ public class ResultSetProcessorHelper {
|
||||||
}
|
}
|
||||||
return namedParameterLocMap;
|
return namedParameterLocMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -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
|
* 51 Franklin Street, Fifth Floor
|
||||||
* Boston, MA 02110-1301 USA
|
* Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
package org.hibernate.loader.internal;
|
package org.hibernate.loader.plan.exec.process.internal;
|
||||||
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
|
|
||||||
import org.hibernate.engine.spi.QueryParameters;
|
import org.hibernate.engine.spi.QueryParameters;
|
||||||
import org.hibernate.engine.spi.SessionImplementor;
|
import org.hibernate.engine.spi.SessionImplementor;
|
||||||
import org.hibernate.loader.spi.ScrollableResultSetProcessor;
|
import org.hibernate.loader.plan.exec.process.spi.ScrollableResultSetProcessor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
|
@ -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
|
* 51 Franklin Street, Fifth Floor
|
||||||
* Boston, MA 02110-1301 USA
|
* Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
package org.hibernate.loader.spi;
|
package org.hibernate.loader.plan.exec.process.spi;
|
||||||
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
@ -29,6 +29,10 @@ import java.util.List;
|
||||||
|
|
||||||
import org.hibernate.engine.spi.QueryParameters;
|
import org.hibernate.engine.spi.QueryParameters;
|
||||||
import org.hibernate.engine.spi.SessionImplementor;
|
import org.hibernate.engine.spi.SessionImplementor;
|
||||||
|
import org.hibernate.loader.plan.exec.query.spi.NamedParameterContext;
|
||||||
|
import org.hibernate.loader.plan.exec.spi.AliasResolutionContext;
|
||||||
|
import org.hibernate.loader.spi.AfterLoadAction;
|
||||||
|
import org.hibernate.loader.spi.LoadPlanAdvisor;
|
||||||
import org.hibernate.transform.ResultTransformer;
|
import org.hibernate.transform.ResultTransformer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -66,7 +70,7 @@ public interface ResultSetProcessor {
|
||||||
SessionImplementor session,
|
SessionImplementor session,
|
||||||
QueryParameters queryParameters,
|
QueryParameters queryParameters,
|
||||||
NamedParameterContext namedParameterContext,
|
NamedParameterContext namedParameterContext,
|
||||||
LoadQueryAliasResolutionContext aliasResolutionContext,
|
AliasResolutionContext aliasResolutionContext,
|
||||||
boolean returnProxies,
|
boolean returnProxies,
|
||||||
boolean readOnly,
|
boolean readOnly,
|
||||||
ResultTransformer forcedResultTransformer,
|
ResultTransformer forcedResultTransformer,
|
|
@ -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
|
* 51 Franklin Street, Fifth Floor
|
||||||
* Boston, MA 02110-1301 USA
|
* Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
package org.hibernate.loader.spi;
|
package org.hibernate.loader.plan.exec.process.spi;
|
||||||
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
|
|
|
@ -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
|
* 51 Franklin Street, Fifth Floor
|
||||||
* Boston, MA 02110-1301 USA
|
* Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
package org.hibernate.loader.spi;
|
package org.hibernate.loader.plan.exec.query.spi;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The context for named parameters.
|
* The context for named parameters.
|
|
@ -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
|
* 51 Franklin Street, Fifth Floor
|
||||||
* Boston, MA 02110-1301 USA
|
* Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
package org.hibernate.loader.spi;
|
package org.hibernate.loader.plan.exec.spi;
|
||||||
|
|
||||||
import org.hibernate.loader.CollectionAliases;
|
import org.hibernate.loader.CollectionAliases;
|
||||||
import org.hibernate.loader.EntityAliases;
|
import org.hibernate.loader.EntityAliases;
|
||||||
import org.hibernate.loader.plan.spi.CollectionReturn;
|
|
||||||
import org.hibernate.loader.plan.spi.CollectionReference;
|
import org.hibernate.loader.plan.spi.CollectionReference;
|
||||||
import org.hibernate.loader.plan.spi.EntityReference;
|
import org.hibernate.loader.plan.spi.EntityReference;
|
||||||
import org.hibernate.loader.plan.spi.EntityReturn;
|
import org.hibernate.loader.plan.spi.EntityReturn;
|
||||||
|
import org.hibernate.loader.plan.spi.Return;
|
||||||
import org.hibernate.loader.plan.spi.ScalarReturn;
|
import org.hibernate.loader.plan.spi.ScalarReturn;
|
||||||
|
import org.hibernate.loader.spi.JoinableAssociation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides aliases that are used by load queries and ResultSet processors.
|
* Provides aliases that are used by load queries and ResultSet processors.
|
||||||
*
|
*
|
||||||
* @author Gail Badner
|
* @author Gail Badner
|
||||||
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public interface LoadQueryAliasResolutionContext {
|
public interface AliasResolutionContext {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve the alias associated with the specified {@link EntityReturn}.
|
* Resolve the source alias (select-clause assigned alias) associated with the specified Return. The source
|
||||||
|
* alias is the alias associated with the Return in the source query.
|
||||||
|
* <p/>
|
||||||
|
* The concept of a source alias only has meaning in the case of queries (HQL, Criteria, etc). Not sure we
|
||||||
|
* really need to keep these here. One argument for keeping them is that I always thought it would be nice to
|
||||||
|
* base the SQL aliases on the source aliases. Keeping the source aliases here would allow us to do that as
|
||||||
|
* we are generating those SQL aliases internally.
|
||||||
|
* <p/>
|
||||||
|
* Should also consider pushing the source "from clause aliases" here if we keep pushing the select aliases
|
||||||
*
|
*
|
||||||
* @param entityReturn - the {@link EntityReturn}.
|
* @param theReturn The Return to locate
|
||||||
*
|
*
|
||||||
* @return the alias associated with the specified {@link EntityReturn}.
|
* @return the alias associated with the specified {@link EntityReturn}.
|
||||||
*/
|
*/
|
||||||
public String resolveEntityReturnAlias(EntityReturn entityReturn);
|
public String getSourceAlias(Return theReturn);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve the alias associated with the specified {@link CollectionReturn}.
|
* Resolve the SQL column aliases associated with the specified {@link ScalarReturn}.
|
||||||
*
|
*
|
||||||
* @param collectionReturn - the {@link CollectionReturn}.
|
* @param scalarReturn The {@link ScalarReturn} for which we want SQL column aliases
|
||||||
*
|
*
|
||||||
* @return the alias associated with {@link CollectionReturn}.
|
* @return The SQL column aliases associated with {@link ScalarReturn}.
|
||||||
*/
|
*/
|
||||||
public String resolveCollectionReturnAlias(CollectionReturn collectionReturn);
|
public String[] resolveScalarColumnAliases(ScalarReturn scalarReturn);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve the aliases associated with the specified {@link ScalarReturn}.
|
* Resolve the alias information related to the given entity reference.
|
||||||
*
|
*
|
||||||
* @param scalarReturn - the {@link ScalarReturn}.
|
* @param entityReference The entity reference for which to obtain alias info
|
||||||
*
|
*
|
||||||
* @return the alias associated with {@link ScalarReturn}.
|
* @return The resolved alias info,
|
||||||
*/
|
*/
|
||||||
String[] resolveScalarReturnAliases(ScalarReturn scalarReturn);
|
public EntityReferenceAliases resolveAliases(EntityReference entityReference);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve the SQL table alias for the specified {@link EntityReference}.
|
* Resolve the alias information related to the given collection reference.
|
||||||
*
|
*
|
||||||
* @param entityReference - the {@link EntityReference}.
|
* @param collectionReference The collection reference for which to obtain alias info
|
||||||
* @return The SQL table alias for the specified {@link EntityReference}.
|
*
|
||||||
|
* @return The resolved alias info,
|
||||||
*/
|
*/
|
||||||
String resolveEntityTableAlias(EntityReference entityReference);
|
public CollectionReferenceAliases resolveAliases(CollectionReference collectionReference);
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the description of the aliases in the JDBC ResultSet that identify values "belonging" to
|
|
||||||
* an entity.
|
|
||||||
*
|
|
||||||
* @param entityReference - the {@link EntityReference} for the entity.
|
|
||||||
*
|
|
||||||
* @return The ResultSet alias descriptor for the {@link EntityReference}
|
|
||||||
*/
|
|
||||||
EntityAliases resolveEntityColumnAliases(EntityReference entityReference);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolve the SQL table alias for the specified {@link CollectionReference}.
|
|
||||||
*
|
|
||||||
* @param collectionReference - the {@link CollectionReference}.
|
|
||||||
* @return The SQL table alias for the specified {@link CollectionReference}.
|
|
||||||
*/
|
|
||||||
String resolveCollectionTableAlias(CollectionReference collectionReference);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the description of the aliases in the JDBC ResultSet that identify values "belonging" to
|
|
||||||
* the specified {@link CollectionReference}.
|
|
||||||
*
|
|
||||||
* @return The ResultSet alias descriptor for the {@link CollectionReference}
|
|
||||||
*/
|
|
||||||
CollectionAliases resolveCollectionColumnAliases(CollectionReference collectionReference);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If the elements of this collection are entities, this methods returns the JDBC ResultSet alias descriptions
|
|
||||||
* for that entity; {@code null} indicates a non-entity collection.
|
|
||||||
*
|
|
||||||
* @return The ResultSet alias descriptor for the collection's entity element, or {@code null}
|
|
||||||
*/
|
|
||||||
EntityAliases resolveCollectionElementColumnAliases(CollectionReference collectionReference);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve the table alias on the right-hand-side of the specified association.
|
* Resolve the table alias on the right-hand-side of the specified association.
|
|
@ -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.LockMode;
|
||||||
import org.hibernate.engine.FetchStrategy;
|
import org.hibernate.engine.FetchStrategy;
|
||||||
|
import org.hibernate.loader.plan.spi.AbstractFetchOwner;
|
||||||
|
import org.hibernate.loader.plan.spi.AnyFetch;
|
||||||
import org.hibernate.loader.plan.spi.CollectionFetch;
|
import org.hibernate.loader.plan.spi.CollectionFetch;
|
||||||
import org.hibernate.loader.plan.spi.CompositeFetch;
|
import org.hibernate.loader.plan.spi.CompositeFetch;
|
||||||
import org.hibernate.loader.plan.spi.EntityFetch;
|
import org.hibernate.loader.plan.spi.EntityFetch;
|
||||||
import org.hibernate.loader.plan.spi.FetchOwner;
|
import org.hibernate.loader.plan.spi.FetchOwner;
|
||||||
import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext;
|
import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext;
|
||||||
|
import org.hibernate.persister.walking.spi.AnyMappingDefinition;
|
||||||
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
|
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
|
||||||
|
import org.hibernate.persister.walking.spi.AttributeDefinition;
|
||||||
import org.hibernate.persister.walking.spi.CompositionDefinition;
|
import org.hibernate.persister.walking.spi.CompositionDefinition;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -47,7 +51,7 @@ public class LoadPlanBuildingHelper {
|
||||||
LockMode.NONE, // todo : for now
|
LockMode.NONE, // todo : for now
|
||||||
fetchOwner,
|
fetchOwner,
|
||||||
fetchStrategy,
|
fetchStrategy,
|
||||||
attributeDefinition.getName()
|
attributeDefinition
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,12 +60,11 @@ public class LoadPlanBuildingHelper {
|
||||||
AssociationAttributeDefinition attributeDefinition,
|
AssociationAttributeDefinition attributeDefinition,
|
||||||
FetchStrategy fetchStrategy,
|
FetchStrategy fetchStrategy,
|
||||||
LoadPlanBuildingContext loadPlanBuildingContext) {
|
LoadPlanBuildingContext loadPlanBuildingContext) {
|
||||||
|
|
||||||
return new EntityFetch(
|
return new EntityFetch(
|
||||||
loadPlanBuildingContext.getSessionFactory(),
|
loadPlanBuildingContext.getSessionFactory(),
|
||||||
LockMode.NONE, // todo : for now
|
LockMode.NONE, // todo : for now
|
||||||
fetchOwner,
|
fetchOwner,
|
||||||
attributeDefinition.getName(),
|
attributeDefinition,
|
||||||
fetchStrategy
|
fetchStrategy
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -73,7 +76,21 @@ public class LoadPlanBuildingHelper {
|
||||||
return new CompositeFetch(
|
return new CompositeFetch(
|
||||||
loadPlanBuildingContext.getSessionFactory(),
|
loadPlanBuildingContext.getSessionFactory(),
|
||||||
fetchOwner,
|
fetchOwner,
|
||||||
attributeDefinition.getName()
|
attributeDefinition
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AnyFetch buildAnyFetch(
|
||||||
|
FetchOwner fetchOwner,
|
||||||
|
AttributeDefinition attribute,
|
||||||
|
AnyMappingDefinition anyDefinition,
|
||||||
|
FetchStrategy fetchStrategy, LoadPlanBuildingContext loadPlanBuildingContext) {
|
||||||
|
return new AnyFetch(
|
||||||
|
loadPlanBuildingContext.getSessionFactory(),
|
||||||
|
fetchOwner,
|
||||||
|
attribute,
|
||||||
|
anyDefinition,
|
||||||
|
fetchStrategy
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ package org.hibernate.loader.plan.internal;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hibernate.loader.plan.spi.CollectionReturn;
|
||||||
import org.hibernate.loader.plan.spi.EntityReturn;
|
import org.hibernate.loader.plan.spi.EntityReturn;
|
||||||
import org.hibernate.loader.plan.spi.LoadPlan;
|
import org.hibernate.loader.plan.spi.LoadPlan;
|
||||||
import org.hibernate.loader.plan.spi.Return;
|
import org.hibernate.loader.plan.spi.Return;
|
||||||
|
@ -36,29 +37,45 @@ import org.hibernate.loader.plan.spi.Return;
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class LoadPlanImpl implements LoadPlan {
|
public class LoadPlanImpl implements LoadPlan {
|
||||||
private final boolean hasScalars;
|
private final List<? extends Return> returns;
|
||||||
private final List<Return> returns;
|
private final Disposition disposition;
|
||||||
|
private final boolean areLazyAttributesForceFetched;
|
||||||
|
|
||||||
public LoadPlanImpl(boolean hasScalars, List<Return> returns) {
|
protected LoadPlanImpl(List<? extends Return> returns, Disposition disposition, boolean areLazyAttributesForceFetched) {
|
||||||
this.hasScalars = hasScalars;
|
|
||||||
this.returns = returns;
|
this.returns = returns;
|
||||||
|
this.disposition = disposition;
|
||||||
|
this.areLazyAttributesForceFetched = areLazyAttributesForceFetched;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoadPlanImpl(boolean hasScalars, Return rootReturn) {
|
public LoadPlanImpl(EntityReturn rootReturn) {
|
||||||
this( hasScalars, Collections.singletonList( rootReturn ) );
|
this( Collections.singletonList( rootReturn ), Disposition.ENTITY_LOADER, false );
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoadPlanImpl(EntityReturn entityReturn) {
|
public LoadPlanImpl(CollectionReturn rootReturn) {
|
||||||
this( false, entityReturn );
|
this( Collections.singletonList( rootReturn ), Disposition.ENTITY_LOADER, false );
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoadPlanImpl(List<? extends Return> returns, boolean areLazyAttributesForceFetched) {
|
||||||
|
this( returns, Disposition.MIXED, areLazyAttributesForceFetched );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<? extends Return> getReturns() {
|
||||||
|
return returns;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Disposition getDisposition() {
|
||||||
|
return disposition;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean areLazyAttributesForceFetched() {
|
||||||
|
return areLazyAttributesForceFetched;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasAnyScalarReturns() {
|
public boolean hasAnyScalarReturns() {
|
||||||
return hasScalars;
|
return disposition == Disposition.MIXED;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Return> getReturns() {
|
|
||||||
return returns;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,7 +91,15 @@ public class SingleRootReturnLoadPlanBuilderStrategy
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LoadPlan buildLoadPlan() {
|
public LoadPlan buildLoadPlan() {
|
||||||
return new LoadPlanImpl( false, rootReturn );
|
if ( EntityReturn.class.isInstance( rootReturn ) ) {
|
||||||
|
return new LoadPlanImpl( (EntityReturn) rootReturn );
|
||||||
|
}
|
||||||
|
else if ( CollectionReturn.class.isInstance( rootReturn ) ) {
|
||||||
|
return new LoadPlanImpl( (CollectionReturn) rootReturn );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new IllegalStateException( "Unexpected root Return type : " + rootReturn );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -27,6 +27,7 @@ import org.hibernate.LockMode;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.loader.PropertyPath;
|
import org.hibernate.loader.PropertyPath;
|
||||||
import org.hibernate.persister.collection.CollectionPersister;
|
import org.hibernate.persister.collection.CollectionPersister;
|
||||||
|
import org.hibernate.type.EntityType;
|
||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -44,14 +45,15 @@ public abstract class AbstractCollectionReference extends AbstractPlanNode imple
|
||||||
SessionFactoryImplementor sessionFactory,
|
SessionFactoryImplementor sessionFactory,
|
||||||
LockMode lockMode,
|
LockMode lockMode,
|
||||||
CollectionPersister collectionPersister,
|
CollectionPersister collectionPersister,
|
||||||
PropertyPath propertyPath) {
|
PropertyPath propertyPath,
|
||||||
|
EntityReference ownerEntityReference) {
|
||||||
super( sessionFactory );
|
super( sessionFactory );
|
||||||
this.lockMode = lockMode;
|
this.lockMode = lockMode;
|
||||||
this.collectionPersister = collectionPersister;
|
this.collectionPersister = collectionPersister;
|
||||||
this.propertyPath = propertyPath;
|
this.propertyPath = propertyPath;
|
||||||
|
|
||||||
this.indexGraph = buildIndexGraph( getCollectionPersister() );
|
this.indexGraph = buildIndexGraph( getCollectionPersister() );
|
||||||
this.elementGraph = buildElementGraph( getCollectionPersister() );
|
this.elementGraph = buildElementGraph( getCollectionPersister(), ownerEntityReference );
|
||||||
}
|
}
|
||||||
|
|
||||||
private FetchableCollectionIndex buildIndexGraph(CollectionPersister persister) {
|
private FetchableCollectionIndex buildIndexGraph(CollectionPersister persister) {
|
||||||
|
@ -70,10 +72,30 @@ public abstract class AbstractCollectionReference extends AbstractPlanNode imple
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private FetchableCollectionElement buildElementGraph(CollectionPersister persister) {
|
private FetchableCollectionElement buildElementGraph(
|
||||||
|
CollectionPersister persister,
|
||||||
|
EntityReference ownerEntityReference) {
|
||||||
final Type type = persister.getElementType();
|
final Type type = persister.getElementType();
|
||||||
if ( type.isAssociationType() ) {
|
if ( type.isAssociationType() ) {
|
||||||
if ( type.isEntityType() ) {
|
if ( type.isEntityType() ) {
|
||||||
|
final EntityType elementEntityType = (EntityType) type;
|
||||||
|
|
||||||
|
if ( ownerEntityReference != null ) {
|
||||||
|
// check for bi-directionality
|
||||||
|
final boolean sameType = elementEntityType.getAssociatedEntityName().equals(
|
||||||
|
ownerEntityReference.getEntityPersister().getEntityName()
|
||||||
|
);
|
||||||
|
if ( sameType ) {
|
||||||
|
// todo : check for columns too...
|
||||||
|
|
||||||
|
return new BidirectionalEntityElementGraph(
|
||||||
|
sessionFactory(),
|
||||||
|
this,
|
||||||
|
getPropertyPath(),
|
||||||
|
ownerEntityReference
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
return new EntityElementGraph( sessionFactory(), this, getPropertyPath() );
|
return new EntityElementGraph( sessionFactory(), this, getPropertyPath() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,8 +141,22 @@ public abstract class AbstractCollectionReference extends AbstractPlanNode imple
|
||||||
return elementGraph;
|
return elementGraph;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasEntityElements() {
|
private class BidirectionalEntityElementGraph extends EntityElementGraph implements BidirectionalEntityFetch {
|
||||||
return getCollectionPersister().isOneToMany() || getCollectionPersister().isManyToMany();
|
private final EntityReference targetEntityReference;
|
||||||
|
|
||||||
|
private BidirectionalEntityElementGraph(
|
||||||
|
SessionFactoryImplementor sessionFactory,
|
||||||
|
CollectionReference collectionReference,
|
||||||
|
PropertyPath propertyPath,
|
||||||
|
EntityReference targetEntityReference) {
|
||||||
|
super( sessionFactory, collectionReference, propertyPath );
|
||||||
|
this.targetEntityReference = targetEntityReference;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EntityReference getTargetEntityReference() {
|
||||||
|
return targetEntityReference;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,9 @@ import org.hibernate.engine.FetchStrategy;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.loader.plan.internal.LoadPlanBuildingHelper;
|
import org.hibernate.loader.plan.internal.LoadPlanBuildingHelper;
|
||||||
import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext;
|
import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext;
|
||||||
|
import org.hibernate.persister.walking.spi.AnyMappingDefinition;
|
||||||
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
|
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
|
||||||
|
import org.hibernate.persister.walking.spi.AttributeDefinition;
|
||||||
import org.hibernate.persister.walking.spi.CompositionDefinition;
|
import org.hibernate.persister.walking.spi.CompositionDefinition;
|
||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
|
|
||||||
|
@ -100,25 +102,34 @@ public abstract class AbstractFetchOwner extends AbstractPlanNode implements Fet
|
||||||
return fetches == null ? NO_FETCHES : fetches.toArray( new Fetch[ fetches.size() ] );
|
return fetches == null ? NO_FETCHES : fetches.toArray( new Fetch[ fetches.size() ] );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Abstract method returning the delegate for obtaining details about an owned fetch.
|
|
||||||
* @return the delegate
|
|
||||||
*/
|
|
||||||
protected abstract FetchOwnerDelegate getFetchOwnerDelegate();
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isNullable(Fetch fetch) {
|
public boolean isNullable(Fetch fetch) {
|
||||||
return getFetchOwnerDelegate().locateFetchMetadata( fetch ).isNullable();
|
return fetch.isNullable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type getType(Fetch fetch) {
|
public Type getType(Fetch fetch) {
|
||||||
return getFetchOwnerDelegate().locateFetchMetadata( fetch ).getType();
|
return fetch.getFetchedType();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] toSqlSelectFragments(Fetch fetch, String alias) {
|
public String[] toSqlSelectFragments(Fetch fetch, String alias) {
|
||||||
return getFetchOwnerDelegate().locateFetchMetadata( fetch ).toSqlSelectFragments( alias );
|
return fetch.toSqlSelectFragments( alias );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnyFetch buildAnyFetch(
|
||||||
|
AttributeDefinition attribute,
|
||||||
|
AnyMappingDefinition anyDefinition,
|
||||||
|
FetchStrategy fetchStrategy,
|
||||||
|
LoadPlanBuildingContext loadPlanBuildingContext) {
|
||||||
|
return LoadPlanBuildingHelper.buildAnyFetch(
|
||||||
|
this,
|
||||||
|
attribute,
|
||||||
|
anyDefinition,
|
||||||
|
fetchStrategy,
|
||||||
|
loadPlanBuildingContext
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -28,6 +28,8 @@ import org.hibernate.engine.FetchStrategy;
|
||||||
import org.hibernate.engine.FetchStyle;
|
import org.hibernate.engine.FetchStyle;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.loader.PropertyPath;
|
import org.hibernate.loader.PropertyPath;
|
||||||
|
import org.hibernate.persister.walking.spi.AttributeDefinition;
|
||||||
|
import org.hibernate.type.Type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a singular attribute that is both a {@link FetchOwner} and a {@link Fetch}.
|
* Represents a singular attribute that is both a {@link FetchOwner} and a {@link Fetch}.
|
||||||
|
@ -37,7 +39,7 @@ import org.hibernate.loader.PropertyPath;
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractSingularAttributeFetch extends AbstractFetchOwner implements Fetch {
|
public abstract class AbstractSingularAttributeFetch extends AbstractFetchOwner implements Fetch {
|
||||||
private final FetchOwner owner;
|
private final FetchOwner owner;
|
||||||
private final String ownerProperty;
|
private final AttributeDefinition fetchedAttribute;
|
||||||
private final FetchStrategy fetchStrategy;
|
private final FetchStrategy fetchStrategy;
|
||||||
|
|
||||||
private final PropertyPath propertyPath;
|
private final PropertyPath propertyPath;
|
||||||
|
@ -47,22 +49,22 @@ public abstract class AbstractSingularAttributeFetch extends AbstractFetchOwner
|
||||||
*
|
*
|
||||||
* @param factory - the session factory.
|
* @param factory - the session factory.
|
||||||
* @param owner - the fetch owner for this fetch.
|
* @param owner - the fetch owner for this fetch.
|
||||||
* @param ownerProperty - the owner's property referring to this fetch.
|
* @param fetchedAttribute - the attribute being fetched
|
||||||
* @param fetchStrategy - the fetch strategy for this fetch.
|
* @param fetchStrategy - the fetch strategy for this fetch.
|
||||||
*/
|
*/
|
||||||
public AbstractSingularAttributeFetch(
|
public AbstractSingularAttributeFetch(
|
||||||
SessionFactoryImplementor factory,
|
SessionFactoryImplementor factory,
|
||||||
FetchOwner owner,
|
FetchOwner owner,
|
||||||
String ownerProperty,
|
AttributeDefinition fetchedAttribute,
|
||||||
FetchStrategy fetchStrategy) {
|
FetchStrategy fetchStrategy) {
|
||||||
super( factory );
|
super( factory );
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
this.ownerProperty = ownerProperty;
|
this.fetchedAttribute = fetchedAttribute;
|
||||||
this.fetchStrategy = fetchStrategy;
|
this.fetchStrategy = fetchStrategy;
|
||||||
|
|
||||||
owner.addFetch( this );
|
owner.addFetch( this );
|
||||||
|
|
||||||
this.propertyPath = owner.getPropertyPath().append( ownerProperty );
|
this.propertyPath = owner.getPropertyPath().append( fetchedAttribute.getName() );
|
||||||
}
|
}
|
||||||
|
|
||||||
public AbstractSingularAttributeFetch(
|
public AbstractSingularAttributeFetch(
|
||||||
|
@ -71,7 +73,7 @@ public abstract class AbstractSingularAttributeFetch extends AbstractFetchOwner
|
||||||
FetchOwner fetchOwnerCopy) {
|
FetchOwner fetchOwnerCopy) {
|
||||||
super( original, copyContext );
|
super( original, copyContext );
|
||||||
this.owner = fetchOwnerCopy;
|
this.owner = fetchOwnerCopy;
|
||||||
this.ownerProperty = original.ownerProperty;
|
this.fetchedAttribute = original.fetchedAttribute;
|
||||||
this.fetchStrategy = original.fetchStrategy;
|
this.fetchStrategy = original.fetchStrategy;
|
||||||
this.propertyPath = original.propertyPath;
|
this.propertyPath = original.propertyPath;
|
||||||
}
|
}
|
||||||
|
@ -81,14 +83,19 @@ public abstract class AbstractSingularAttributeFetch extends AbstractFetchOwner
|
||||||
return owner;
|
return owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AttributeDefinition getFetchedAttribute() {
|
||||||
|
return fetchedAttribute;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getOwnerPropertyName() {
|
public Type getFetchedType() {
|
||||||
return ownerProperty;
|
return fetchedAttribute.getType();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isNullable() {
|
public boolean isNullable() {
|
||||||
return owner.isNullable( this );
|
return fetchedAttribute.isNullable();
|
||||||
|
// return owner.isNullable( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -102,10 +109,23 @@ public abstract class AbstractSingularAttributeFetch extends AbstractFetchOwner
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void validateFetchPlan(FetchStrategy fetchStrategy) {
|
public String getAdditionalJoinConditions() {
|
||||||
|
// only pertinent for HQL...
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validateFetchPlan(FetchStrategy fetchStrategy, AttributeDefinition attributeDefinition) {
|
||||||
if ( fetchStrategy.getStyle() == FetchStyle.JOIN ) {
|
if ( fetchStrategy.getStyle() == FetchStyle.JOIN ) {
|
||||||
if ( this.fetchStrategy.getStyle() != FetchStyle.JOIN ) {
|
if ( this.fetchStrategy.getStyle() != FetchStyle.JOIN ) {
|
||||||
throw new HibernateException( "Cannot specify join fetch from owner that is a non-joined fetch" );
|
|
||||||
|
throw new HibernateException(
|
||||||
|
String.format(
|
||||||
|
"Cannot specify join fetch from owner [%s] that is a non-joined fetch : %s",
|
||||||
|
getPropertyPath().getFullPath(),
|
||||||
|
attributeDefinition.getName()
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
|
||||||
* indicated by the @author tags or express copyright attribution
|
* indicated by the @author tags or express copyright attribution
|
||||||
|
@ -21,24 +21,15 @@
|
||||||
* 51 Franklin Street, Fifth Floor
|
* 51 Franklin Street, Fifth Floor
|
||||||
* Boston, MA 02110-1301 USA
|
* Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
package org.hibernate.loader.spi;
|
package org.hibernate.loader.plan.spi;
|
||||||
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds a load query for generating SQL.
|
* Represents the circular side of a bi-directional entity fetch. Wraps a reference to an EntityReference
|
||||||
|
* as an EntityFetch. We can use the special type as a trigger in AliasResolutionContext, etc to lookup information
|
||||||
|
* based on the wrapped reference.
|
||||||
*
|
*
|
||||||
* @author Gail Badner
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public interface LoadQueryBuilder {
|
public interface BidirectionalEntityFetch {
|
||||||
|
public EntityReference getTargetEntityReference();
|
||||||
/**
|
|
||||||
* Generates SQL for the performing the load.
|
|
||||||
* @param batchSize - the batch size.
|
|
||||||
* @param factory - the session factory.
|
|
||||||
* @param aliasResolutionContext - the alias resolution context.
|
|
||||||
*
|
|
||||||
* @return the SQL string for performing the load
|
|
||||||
*/
|
|
||||||
String generateSql(int batchSize, SessionFactoryImplementor factory, LoadQueryAliasResolutionContext aliasResolutionContext);
|
|
||||||
}
|
}
|
|
@ -23,19 +23,34 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.loader.plan.spi;
|
package org.hibernate.loader.plan.spi;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
|
import org.hibernate.collection.spi.PersistentCollection;
|
||||||
import org.hibernate.engine.FetchStrategy;
|
import org.hibernate.engine.FetchStrategy;
|
||||||
|
import org.hibernate.engine.spi.EntityKey;
|
||||||
|
import org.hibernate.engine.spi.PersistenceContext;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.loader.spi.ResultSetProcessingContext;
|
import org.hibernate.internal.CoreLogging;
|
||||||
|
import org.hibernate.loader.CollectionAliases;
|
||||||
|
import org.hibernate.loader.plan.exec.process.internal.Helper;
|
||||||
|
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext;
|
||||||
|
import org.hibernate.persister.walking.spi.AttributeDefinition;
|
||||||
|
import org.hibernate.pretty.MessageHelper;
|
||||||
|
import org.hibernate.type.CollectionType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class CollectionFetch extends AbstractCollectionReference implements Fetch {
|
public class CollectionFetch extends AbstractCollectionReference implements Fetch {
|
||||||
|
private static final Logger log = CoreLogging.logger( CollectionFetch.class );
|
||||||
|
|
||||||
private final FetchOwner fetchOwner;
|
private final FetchOwner fetchOwner;
|
||||||
|
private final AttributeDefinition fetchedAttribute;
|
||||||
private final FetchStrategy fetchStrategy;
|
private final FetchStrategy fetchStrategy;
|
||||||
|
|
||||||
public CollectionFetch(
|
public CollectionFetch(
|
||||||
|
@ -43,16 +58,16 @@ public class CollectionFetch extends AbstractCollectionReference implements Fetc
|
||||||
LockMode lockMode,
|
LockMode lockMode,
|
||||||
FetchOwner fetchOwner,
|
FetchOwner fetchOwner,
|
||||||
FetchStrategy fetchStrategy,
|
FetchStrategy fetchStrategy,
|
||||||
String ownerProperty) {
|
AttributeDefinition fetchedAttribute) {
|
||||||
super(
|
super(
|
||||||
sessionFactory,
|
sessionFactory,
|
||||||
lockMode,
|
lockMode,
|
||||||
sessionFactory.getCollectionPersister(
|
sessionFactory.getCollectionPersister( ( (CollectionType) fetchedAttribute.getType() ).getRole() ),
|
||||||
fetchOwner.retrieveFetchSourcePersister().getEntityName() + '.' + ownerProperty
|
fetchOwner.getPropertyPath().append( fetchedAttribute.getName() ),
|
||||||
),
|
(EntityReference) fetchOwner
|
||||||
fetchOwner.getPropertyPath().append( ownerProperty )
|
|
||||||
);
|
);
|
||||||
this.fetchOwner = fetchOwner;
|
this.fetchOwner = fetchOwner;
|
||||||
|
this.fetchedAttribute = fetchedAttribute;
|
||||||
this.fetchStrategy = fetchStrategy;
|
this.fetchStrategy = fetchStrategy;
|
||||||
fetchOwner.addFetch( this );
|
fetchOwner.addFetch( this );
|
||||||
}
|
}
|
||||||
|
@ -60,6 +75,7 @@ public class CollectionFetch extends AbstractCollectionReference implements Fetc
|
||||||
protected CollectionFetch(CollectionFetch original, CopyContext copyContext, FetchOwner fetchOwnerCopy) {
|
protected CollectionFetch(CollectionFetch original, CopyContext copyContext, FetchOwner fetchOwnerCopy) {
|
||||||
super( original, copyContext );
|
super( original, copyContext );
|
||||||
this.fetchOwner = fetchOwnerCopy;
|
this.fetchOwner = fetchOwnerCopy;
|
||||||
|
this.fetchedAttribute = original.fetchedAttribute;
|
||||||
this.fetchStrategy = original.fetchStrategy;
|
this.fetchStrategy = original.fetchStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,8 +85,8 @@ public class CollectionFetch extends AbstractCollectionReference implements Fetc
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getOwnerPropertyName() {
|
public CollectionType getFetchedType() {
|
||||||
return getPropertyPath().getProperty();
|
return (CollectionType) fetchedAttribute.getType();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -78,9 +94,15 @@ public class CollectionFetch extends AbstractCollectionReference implements Fetc
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAdditionalJoinConditions() {
|
||||||
|
// only pertinent for HQL...
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] toSqlSelectFragments(String alias) {
|
public String[] toSqlSelectFragments(String alias) {
|
||||||
return getOwner().toSqlSelectFragments( this, alias );
|
return getOwner().toSqlSelectFragmentResolver().toSqlSelectFragments( alias, fetchedAttribute );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -95,7 +117,84 @@ public class CollectionFetch extends AbstractCollectionReference implements Fetc
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
|
public Object resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
|
||||||
return null; //To change body of implemented methods use File | Settings | File Templates.
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(ResultSet resultSet, ResultSetProcessingContext context, Object owner) throws SQLException {
|
||||||
|
final Serializable collectionRowKey = (Serializable) getCollectionPersister().readKey(
|
||||||
|
resultSet,
|
||||||
|
context.getAliasResolutionContext().resolveAliases( this ).getCollectionColumnAliases().getSuffixedKeyAliases(),
|
||||||
|
context.getSession()
|
||||||
|
);
|
||||||
|
|
||||||
|
final PersistenceContext persistenceContext = context.getSession().getPersistenceContext();
|
||||||
|
|
||||||
|
if ( collectionRowKey == null ) {
|
||||||
|
// we did not find a collection element in the result set, so we
|
||||||
|
// ensure that a collection is created with the owner's identifier,
|
||||||
|
// since what we have is an empty collection
|
||||||
|
final EntityKey ownerEntityKey = findOwnerEntityKey( context );
|
||||||
|
if ( ownerEntityKey == null ) {
|
||||||
|
// should not happen
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Could not locate owner's EntityKey during attempt to read collection element fro JDBC row : " +
|
||||||
|
getPropertyPath().getFullPath()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( log.isDebugEnabled() ) {
|
||||||
|
log.debugf(
|
||||||
|
"Result set contains (possibly empty) collection: %s",
|
||||||
|
MessageHelper.collectionInfoString(
|
||||||
|
getCollectionPersister(),
|
||||||
|
ownerEntityKey,
|
||||||
|
context.getSession().getFactory()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
persistenceContext.getLoadContexts()
|
||||||
|
.getCollectionLoadContext( resultSet )
|
||||||
|
.getLoadingCollection( getCollectionPersister(), ownerEntityKey );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// we found a collection element in the result set
|
||||||
|
if ( log.isDebugEnabled() ) {
|
||||||
|
log.debugf(
|
||||||
|
"Found row of collection: %s",
|
||||||
|
MessageHelper.collectionInfoString(
|
||||||
|
getCollectionPersister(),
|
||||||
|
collectionRowKey,
|
||||||
|
context.getSession().getFactory()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
PersistentCollection rowCollection = persistenceContext.getLoadContexts()
|
||||||
|
.getCollectionLoadContext( resultSet )
|
||||||
|
.getLoadingCollection( getCollectionPersister(), collectionRowKey );
|
||||||
|
|
||||||
|
final CollectionAliases descriptor = context.getAliasResolutionContext().resolveAliases( this ).getCollectionColumnAliases();
|
||||||
|
|
||||||
|
if ( rowCollection != null ) {
|
||||||
|
final Object element = rowCollection.readFrom( resultSet, getCollectionPersister(), descriptor, owner );
|
||||||
|
|
||||||
|
if ( getElementGraph() != null ) {
|
||||||
|
for ( Fetch fetch : getElementGraph().getFetches() ) {
|
||||||
|
fetch.read( resultSet, context, element );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private EntityKey findOwnerEntityKey(ResultSetProcessingContext context) {
|
||||||
|
return context.getProcessingState( findOwnerEntityReference( getOwner() ) ).getEntityKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
private EntityReference findOwnerEntityReference(FetchOwner owner) {
|
||||||
|
return Helper.INSTANCE.findOwnerEntityReference( owner );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -53,6 +53,4 @@ public interface CollectionReference {
|
||||||
public FetchOwner getElementGraph();
|
public FetchOwner getElementGraph();
|
||||||
|
|
||||||
public PropertyPath getPropertyPath();
|
public PropertyPath getPropertyPath();
|
||||||
|
|
||||||
public boolean hasEntityElements();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,13 +23,9 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.loader.plan.spi;
|
package org.hibernate.loader.plan.spi;
|
||||||
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.loader.PropertyPath;
|
import org.hibernate.loader.PropertyPath;
|
||||||
import org.hibernate.loader.spi.ResultSetProcessingContext;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
|
@ -47,7 +43,10 @@ public class CollectionReturn extends AbstractCollectionReference implements Ret
|
||||||
sessionFactory,
|
sessionFactory,
|
||||||
lockMode,
|
lockMode,
|
||||||
sessionFactory.getCollectionPersister( ownerEntityName + '.' + ownerProperty ),
|
sessionFactory.getCollectionPersister( ownerEntityName + '.' + ownerProperty ),
|
||||||
new PropertyPath() // its a root
|
// its a root
|
||||||
|
new PropertyPath(),
|
||||||
|
// no owner
|
||||||
|
null
|
||||||
);
|
);
|
||||||
this.ownerEntityName = ownerEntityName;
|
this.ownerEntityName = ownerEntityName;
|
||||||
this.ownerProperty = ownerProperty;
|
this.ownerProperty = ownerProperty;
|
||||||
|
@ -77,21 +76,6 @@ public class CollectionReturn extends AbstractCollectionReference implements Ret
|
||||||
return ownerProperty;
|
return ownerProperty;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
|
|
||||||
// todo : anything to do here?
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
|
|
||||||
// todo : anything to do here?
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object read(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
|
|
||||||
return null; //To change body of implemented methods use File | Settings | File Templates.
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "CollectionReturn(" + getCollectionPersister().getRole() + ")";
|
return "CollectionReturn(" + getCollectionPersister().getRole() + ")";
|
||||||
|
|
|
@ -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.collection.QueryableCollection;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
|
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
|
||||||
|
import org.hibernate.persister.walking.spi.AttributeDefinition;
|
||||||
import org.hibernate.type.CompositeType;
|
import org.hibernate.type.CompositeType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -21,7 +22,7 @@ public class CompositeElementGraph extends AbstractFetchOwner implements Fetchab
|
||||||
private final CollectionReference collectionReference;
|
private final CollectionReference collectionReference;
|
||||||
private final PropertyPath propertyPath;
|
private final PropertyPath propertyPath;
|
||||||
private final CollectionPersister collectionPersister;
|
private final CollectionPersister collectionPersister;
|
||||||
private final FetchOwnerDelegate fetchOwnerDelegate;
|
private final CompositeBasedSqlSelectFragmentResolver sqlSelectFragmentResolver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a {@link CompositeElementGraph}.
|
* Constructs a {@link CompositeElementGraph}.
|
||||||
|
@ -39,15 +40,16 @@ public class CompositeElementGraph extends AbstractFetchOwner implements Fetchab
|
||||||
this.collectionReference = collectionReference;
|
this.collectionReference = collectionReference;
|
||||||
this.collectionPersister = collectionReference.getCollectionPersister();
|
this.collectionPersister = collectionReference.getCollectionPersister();
|
||||||
this.propertyPath = collectionPath.append( "<elements>" );
|
this.propertyPath = collectionPath.append( "<elements>" );
|
||||||
this.fetchOwnerDelegate = new CompositeFetchOwnerDelegate(
|
this.sqlSelectFragmentResolver = new CompositeBasedSqlSelectFragmentResolver(
|
||||||
sessionFactory,
|
sessionFactory,
|
||||||
(CompositeType) collectionPersister.getElementType(),
|
(CompositeType) collectionPersister.getElementType(),
|
||||||
new CompositeFetchOwnerDelegate.PropertyMappingDelegate() {
|
new CompositeBasedSqlSelectFragmentResolver.BaseSqlSelectFragmentResolver() {
|
||||||
@Override
|
@Override
|
||||||
public String[] toSqlSelectFragments(String alias) {
|
public String[] toSqlSelectFragments(String alias) {
|
||||||
return ( (QueryableCollection) collectionPersister ).getElementColumnNames( alias );
|
return ( (QueryableCollection) collectionPersister ).getElementColumnNames( alias );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +58,7 @@ public class CompositeElementGraph extends AbstractFetchOwner implements Fetchab
|
||||||
this.collectionReference = original.collectionReference;
|
this.collectionReference = original.collectionReference;
|
||||||
this.collectionPersister = original.collectionPersister;
|
this.collectionPersister = original.collectionPersister;
|
||||||
this.propertyPath = original.propertyPath;
|
this.propertyPath = original.propertyPath;
|
||||||
this.fetchOwnerDelegate = original.fetchOwnerDelegate;
|
this.sqlSelectFragmentResolver = original.sqlSelectFragmentResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -65,7 +67,7 @@ public class CompositeElementGraph extends AbstractFetchOwner implements Fetchab
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void validateFetchPlan(FetchStrategy fetchStrategy) {
|
public void validateFetchPlan(FetchStrategy fetchStrategy, AttributeDefinition attributeDefinition) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -83,11 +85,6 @@ public class CompositeElementGraph extends AbstractFetchOwner implements Fetchab
|
||||||
return new CompositeElementGraph( this, copyContext );
|
return new CompositeElementGraph( this, copyContext );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected FetchOwnerDelegate getFetchOwnerDelegate() {
|
|
||||||
return fetchOwnerDelegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CollectionFetch buildCollectionFetch(
|
public CollectionFetch buildCollectionFetch(
|
||||||
AssociationAttributeDefinition attributeDefinition,
|
AssociationAttributeDefinition attributeDefinition,
|
||||||
|
@ -95,4 +92,9 @@ public class CompositeElementGraph extends AbstractFetchOwner implements Fetchab
|
||||||
LoadPlanBuildingContext loadPlanBuildingContext) {
|
LoadPlanBuildingContext loadPlanBuildingContext) {
|
||||||
throw new HibernateException( "Collection composite element cannot define collections" );
|
throw new HibernateException( "Collection composite element cannot define collections" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqlSelectFragmentResolver toSqlSelectFragmentResolver() {
|
||||||
|
return sqlSelectFragmentResolver;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,12 +25,19 @@ package org.hibernate.loader.plan.spi;
|
||||||
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.engine.FetchStrategy;
|
import org.hibernate.engine.FetchStrategy;
|
||||||
import org.hibernate.engine.FetchStyle;
|
import org.hibernate.engine.FetchStyle;
|
||||||
import org.hibernate.engine.FetchTiming;
|
import org.hibernate.engine.FetchTiming;
|
||||||
|
import org.hibernate.engine.spi.EntityKey;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.loader.spi.ResultSetProcessingContext;
|
import org.hibernate.loader.plan.exec.process.internal.Helper;
|
||||||
|
import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext;
|
||||||
|
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
|
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
|
||||||
|
import org.hibernate.persister.walking.spi.AttributeDefinition;
|
||||||
import org.hibernate.type.CompositeType;
|
import org.hibernate.type.CompositeType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -43,27 +50,28 @@ import org.hibernate.type.CompositeType;
|
||||||
public class CompositeFetch extends AbstractSingularAttributeFetch {
|
public class CompositeFetch extends AbstractSingularAttributeFetch {
|
||||||
private static final FetchStrategy FETCH_PLAN = new FetchStrategy( FetchTiming.IMMEDIATE, FetchStyle.JOIN );
|
private static final FetchStrategy FETCH_PLAN = new FetchStrategy( FetchTiming.IMMEDIATE, FetchStyle.JOIN );
|
||||||
|
|
||||||
private final FetchOwnerDelegate delegate;
|
private final CompositeBasedSqlSelectFragmentResolver sqlSelectFragmentResolver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a {@link CompositeFetch} object.
|
* Constructs a {@link CompositeFetch} object.
|
||||||
*
|
*
|
||||||
* @param sessionFactory - the session factory.
|
* @param sessionFactory - the session factory.
|
||||||
* @param owner - the fetch owner for this fetch.
|
* @param owner - the fetch owner for this fetch.
|
||||||
* @param ownerProperty - the owner's property referring to this fetch.
|
* @param fetchedAttribute - the owner's property referring to this fetch.
|
||||||
*/
|
*/
|
||||||
public CompositeFetch(
|
public CompositeFetch(
|
||||||
SessionFactoryImplementor sessionFactory,
|
SessionFactoryImplementor sessionFactory,
|
||||||
final FetchOwner owner,
|
final FetchOwner owner,
|
||||||
String ownerProperty) {
|
final AttributeDefinition fetchedAttribute) {
|
||||||
super( sessionFactory, owner, ownerProperty, FETCH_PLAN );
|
super( sessionFactory, owner, fetchedAttribute, FETCH_PLAN );
|
||||||
this.delegate = new CompositeFetchOwnerDelegate(
|
|
||||||
|
this.sqlSelectFragmentResolver = new CompositeBasedSqlSelectFragmentResolver(
|
||||||
sessionFactory,
|
sessionFactory,
|
||||||
(CompositeType) getOwner().getType( this ),
|
(CompositeType) fetchedAttribute.getType(),
|
||||||
new CompositeFetchOwnerDelegate.PropertyMappingDelegate() {
|
new CompositeBasedSqlSelectFragmentResolver.BaseSqlSelectFragmentResolver() {
|
||||||
@Override
|
@Override
|
||||||
public String[] toSqlSelectFragments(String alias) {
|
public String[] toSqlSelectFragments(String alias) {
|
||||||
return owner.toSqlSelectFragments( CompositeFetch.this, alias );
|
return owner.toSqlSelectFragmentResolver().toSqlSelectFragments( alias, fetchedAttribute );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -71,12 +79,12 @@ public class CompositeFetch extends AbstractSingularAttributeFetch {
|
||||||
|
|
||||||
public CompositeFetch(CompositeFetch original, CopyContext copyContext, FetchOwner fetchOwnerCopy) {
|
public CompositeFetch(CompositeFetch original, CopyContext copyContext, FetchOwner fetchOwnerCopy) {
|
||||||
super( original, copyContext, fetchOwnerCopy );
|
super( original, copyContext, fetchOwnerCopy );
|
||||||
this.delegate = original.getFetchOwnerDelegate();
|
this.sqlSelectFragmentResolver = original.sqlSelectFragmentResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected FetchOwnerDelegate getFetchOwnerDelegate() {
|
public SqlSelectFragmentResolver toSqlSelectFragmentResolver() {
|
||||||
return delegate;
|
return sqlSelectFragmentResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -86,12 +94,26 @@ public class CompositeFetch extends AbstractSingularAttributeFetch {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
|
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
|
||||||
//To change body of implemented methods use File | Settings | File Templates.
|
// anything to do?
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
|
public Object resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
|
||||||
return null; //To change body of implemented methods use File | Settings | File Templates.
|
// anything to do?
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(ResultSet resultSet, ResultSetProcessingContext context, Object owner) throws SQLException {
|
||||||
|
final EntityReference ownerEntityReference = Helper.INSTANCE.findOwnerEntityReference( this );
|
||||||
|
final ResultSetProcessingContext.EntityReferenceProcessingState entityReferenceProcessingState = context.getProcessingState(
|
||||||
|
ownerEntityReference
|
||||||
|
);
|
||||||
|
final EntityKey entityKey = entityReferenceProcessingState.getEntityKey();
|
||||||
|
final Object entity = context.resolveEntityKey( entityKey, Helper.INSTANCE.findOwnerEntityReference( (FetchOwner) ownerEntityReference ) );
|
||||||
|
for ( Fetch fetch : getFetches() ) {
|
||||||
|
fetch.read( resultSet, context, entity );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -101,4 +123,18 @@ public class CompositeFetch extends AbstractSingularAttributeFetch {
|
||||||
copyContext.getReturnGraphVisitationStrategy().finishingCompositeFetch( this );
|
copyContext.getReturnGraphVisitationStrategy().finishingCompositeFetch( this );
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CollectionFetch buildCollectionFetch(
|
||||||
|
AssociationAttributeDefinition attributeDefinition,
|
||||||
|
FetchStrategy fetchStrategy,
|
||||||
|
LoadPlanBuildingContext loadPlanBuildingContext) {
|
||||||
|
return new CollectionFetch(
|
||||||
|
loadPlanBuildingContext.getSessionFactory(),
|
||||||
|
LockMode.NONE, // todo : for now
|
||||||
|
this,
|
||||||
|
fetchStrategy,
|
||||||
|
attributeDefinition
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.collection.QueryableCollection;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
|
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
|
||||||
|
import org.hibernate.persister.walking.spi.AttributeDefinition;
|
||||||
import org.hibernate.type.CompositeType;
|
import org.hibernate.type.CompositeType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -21,7 +22,7 @@ public class CompositeIndexGraph extends AbstractFetchOwner implements Fetchable
|
||||||
private final CollectionReference collectionReference;
|
private final CollectionReference collectionReference;
|
||||||
private final PropertyPath propertyPath;
|
private final PropertyPath propertyPath;
|
||||||
private final CollectionPersister collectionPersister;
|
private final CollectionPersister collectionPersister;
|
||||||
private final FetchOwnerDelegate fetchOwnerDelegate;
|
private final CompositeBasedSqlSelectFragmentResolver sqlSelectFragmentResolver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a {@link CompositeElementGraph}.
|
* Constructs a {@link CompositeElementGraph}.
|
||||||
|
@ -38,10 +39,10 @@ public class CompositeIndexGraph extends AbstractFetchOwner implements Fetchable
|
||||||
this.collectionReference = collectionReference;
|
this.collectionReference = collectionReference;
|
||||||
this.collectionPersister = collectionReference.getCollectionPersister();
|
this.collectionPersister = collectionReference.getCollectionPersister();
|
||||||
this.propertyPath = collectionPath.append( "<index>" );
|
this.propertyPath = collectionPath.append( "<index>" );
|
||||||
this.fetchOwnerDelegate = new CompositeFetchOwnerDelegate(
|
this.sqlSelectFragmentResolver = new CompositeBasedSqlSelectFragmentResolver(
|
||||||
sessionFactory,
|
sessionFactory,
|
||||||
(CompositeType) collectionPersister.getIndexType(),
|
(CompositeType) collectionPersister.getIndexType(),
|
||||||
new CompositeFetchOwnerDelegate.PropertyMappingDelegate() {
|
new CompositeBasedSqlSelectFragmentResolver.BaseSqlSelectFragmentResolver() {
|
||||||
@Override
|
@Override
|
||||||
public String[] toSqlSelectFragments(String alias) {
|
public String[] toSqlSelectFragments(String alias) {
|
||||||
return ( (QueryableCollection) collectionPersister ).getIndexColumnNames( alias );
|
return ( (QueryableCollection) collectionPersister ).getIndexColumnNames( alias );
|
||||||
|
@ -55,11 +56,11 @@ public class CompositeIndexGraph extends AbstractFetchOwner implements Fetchable
|
||||||
this.collectionReference = original.collectionReference;
|
this.collectionReference = original.collectionReference;
|
||||||
this.collectionPersister = original.collectionPersister;
|
this.collectionPersister = original.collectionPersister;
|
||||||
this.propertyPath = original.propertyPath;
|
this.propertyPath = original.propertyPath;
|
||||||
this.fetchOwnerDelegate = original.fetchOwnerDelegate;
|
this.sqlSelectFragmentResolver = original.sqlSelectFragmentResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void validateFetchPlan(FetchStrategy fetchStrategy) {
|
public void validateFetchPlan(FetchStrategy fetchStrategy, AttributeDefinition attributeDefinition) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -82,11 +83,6 @@ public class CompositeIndexGraph extends AbstractFetchOwner implements Fetchable
|
||||||
return new CompositeIndexGraph( this, copyContext );
|
return new CompositeIndexGraph( this, copyContext );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected FetchOwnerDelegate getFetchOwnerDelegate() {
|
|
||||||
return fetchOwnerDelegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CollectionFetch buildCollectionFetch(
|
public CollectionFetch buildCollectionFetch(
|
||||||
AssociationAttributeDefinition attributeDefinition,
|
AssociationAttributeDefinition attributeDefinition,
|
||||||
|
@ -95,4 +91,9 @@ public class CompositeIndexGraph extends AbstractFetchOwner implements Fetchable
|
||||||
throw new HibernateException( "Composite index cannot define collections" );
|
throw new HibernateException( "Composite index cannot define collections" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqlSelectFragmentResolver toSqlSelectFragmentResolver() {
|
||||||
|
return sqlSelectFragmentResolver;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,14 @@ import org.hibernate.LockMode;
|
||||||
import org.hibernate.engine.FetchStrategy;
|
import org.hibernate.engine.FetchStrategy;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.loader.PropertyPath;
|
import org.hibernate.loader.PropertyPath;
|
||||||
|
import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext;
|
||||||
import org.hibernate.persister.collection.CollectionPersister;
|
import org.hibernate.persister.collection.CollectionPersister;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
|
import org.hibernate.persister.entity.Queryable;
|
||||||
|
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
|
||||||
|
import org.hibernate.persister.walking.spi.AttributeDefinition;
|
||||||
import org.hibernate.type.AssociationType;
|
import org.hibernate.type.AssociationType;
|
||||||
|
import org.hibernate.type.EntityType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the {@link FetchOwner} for a collection element that is
|
* Represents the {@link FetchOwner} for a collection element that is
|
||||||
|
@ -20,7 +25,7 @@ public class EntityElementGraph extends AbstractFetchOwner implements FetchableC
|
||||||
private final AssociationType elementType;
|
private final AssociationType elementType;
|
||||||
private final EntityPersister elementPersister;
|
private final EntityPersister elementPersister;
|
||||||
private final PropertyPath propertyPath;
|
private final PropertyPath propertyPath;
|
||||||
private final FetchOwnerDelegate fetchOwnerDelegate;
|
private final EntityPersisterBasedSqlSelectFragmentResolver sqlSelectFragmentResolver;
|
||||||
|
|
||||||
private IdentifierDescription identifierDescription;
|
private IdentifierDescription identifierDescription;
|
||||||
|
|
||||||
|
@ -42,7 +47,7 @@ public class EntityElementGraph extends AbstractFetchOwner implements FetchableC
|
||||||
this.elementType = (AssociationType) collectionPersister.getElementType();
|
this.elementType = (AssociationType) collectionPersister.getElementType();
|
||||||
this.elementPersister = (EntityPersister) this.elementType.getAssociatedJoinable( sessionFactory() );
|
this.elementPersister = (EntityPersister) this.elementType.getAssociatedJoinable( sessionFactory() );
|
||||||
this.propertyPath = collectionPath;
|
this.propertyPath = collectionPath;
|
||||||
this.fetchOwnerDelegate = new EntityFetchOwnerDelegate( elementPersister );
|
this.sqlSelectFragmentResolver = new EntityPersisterBasedSqlSelectFragmentResolver( (Queryable) elementPersister );
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntityElementGraph(EntityElementGraph original, CopyContext copyContext) {
|
public EntityElementGraph(EntityElementGraph original, CopyContext copyContext) {
|
||||||
|
@ -53,7 +58,7 @@ public class EntityElementGraph extends AbstractFetchOwner implements FetchableC
|
||||||
this.elementType = original.elementType;
|
this.elementType = original.elementType;
|
||||||
this.elementPersister = original.elementPersister;
|
this.elementPersister = original.elementPersister;
|
||||||
this.propertyPath = original.propertyPath;
|
this.propertyPath = original.propertyPath;
|
||||||
this.fetchOwnerDelegate = original.fetchOwnerDelegate;
|
this.sqlSelectFragmentResolver = original.sqlSelectFragmentResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -77,7 +82,7 @@ public class EntityElementGraph extends AbstractFetchOwner implements FetchableC
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void validateFetchPlan(FetchStrategy fetchStrategy) {
|
public void validateFetchPlan(FetchStrategy fetchStrategy, AttributeDefinition attributeDefinition) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -111,7 +116,65 @@ public class EntityElementGraph extends AbstractFetchOwner implements FetchableC
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected FetchOwnerDelegate getFetchOwnerDelegate() {
|
public SqlSelectFragmentResolver toSqlSelectFragmentResolver() {
|
||||||
return fetchOwnerDelegate;
|
return sqlSelectFragmentResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EntityFetch buildEntityFetch(
|
||||||
|
AssociationAttributeDefinition attributeDefinition,
|
||||||
|
FetchStrategy fetchStrategy,
|
||||||
|
LoadPlanBuildingContext loadPlanBuildingContext) {
|
||||||
|
final EntityType attributeType = (EntityType) attributeDefinition.getType();
|
||||||
|
|
||||||
|
final FetchOwner collectionOwner = CollectionFetch.class.isInstance( collectionReference )
|
||||||
|
? ( (CollectionFetch) collectionReference ).getOwner()
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if ( collectionOwner != null ) {
|
||||||
|
// check for bi-directionality
|
||||||
|
final boolean sameType = attributeType.getAssociatedEntityName().equals(
|
||||||
|
collectionOwner.retrieveFetchSourcePersister().getEntityName()
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( sameType ) {
|
||||||
|
// todo : check for columns too...
|
||||||
|
|
||||||
|
return new BidirectionalEntityElementGraphFetch(
|
||||||
|
sessionFactory(),
|
||||||
|
LockMode.READ,
|
||||||
|
this,
|
||||||
|
attributeDefinition,
|
||||||
|
fetchStrategy,
|
||||||
|
collectionOwner
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.buildEntityFetch(
|
||||||
|
attributeDefinition,
|
||||||
|
fetchStrategy,
|
||||||
|
loadPlanBuildingContext
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BidirectionalEntityElementGraphFetch extends EntityFetch implements BidirectionalEntityFetch {
|
||||||
|
private final FetchOwner collectionOwner;
|
||||||
|
|
||||||
|
public BidirectionalEntityElementGraphFetch(
|
||||||
|
SessionFactoryImplementor sessionFactory,
|
||||||
|
LockMode lockMode,
|
||||||
|
FetchOwner owner,
|
||||||
|
AttributeDefinition fetchedAttribute,
|
||||||
|
FetchStrategy fetchStrategy,
|
||||||
|
FetchOwner collectionOwner) {
|
||||||
|
super( sessionFactory, lockMode, owner, fetchedAttribute, fetchStrategy );
|
||||||
|
this.collectionOwner = collectionOwner;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EntityReference getTargetEntityReference() {
|
||||||
|
return (EntityReference) collectionOwner;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,13 +29,17 @@ import java.sql.SQLException;
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.WrongClassException;
|
import org.hibernate.WrongClassException;
|
||||||
import org.hibernate.engine.FetchStrategy;
|
import org.hibernate.engine.FetchStrategy;
|
||||||
import org.hibernate.engine.FetchTiming;
|
|
||||||
import org.hibernate.engine.spi.EntityKey;
|
import org.hibernate.engine.spi.EntityKey;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.loader.spi.ResultSetProcessingContext;
|
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
|
import org.hibernate.persister.entity.Queryable;
|
||||||
|
import org.hibernate.persister.walking.spi.AttributeDefinition;
|
||||||
|
import org.hibernate.persister.walking.spi.WalkingException;
|
||||||
import org.hibernate.type.EntityType;
|
import org.hibernate.type.EntityType;
|
||||||
|
|
||||||
|
import static org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext.EntityReferenceProcessingState;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a {@link Fetch} for an entity association attribute as well as a
|
* Represents a {@link Fetch} for an entity association attribute as well as a
|
||||||
* {@link FetchOwner} of the entity association sub-attribute fetches.
|
* {@link FetchOwner} of the entity association sub-attribute fetches.
|
||||||
|
@ -43,35 +47,44 @@ import org.hibernate.type.EntityType;
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class EntityFetch extends AbstractSingularAttributeFetch implements EntityReference, Fetch {
|
public class EntityFetch extends AbstractSingularAttributeFetch implements EntityReference, Fetch {
|
||||||
|
|
||||||
private final EntityPersister persister;
|
private final EntityPersister persister;
|
||||||
private final LockMode lockMode;
|
private final EntityPersisterBasedSqlSelectFragmentResolver sqlSelectFragmentResolver;
|
||||||
private final FetchOwnerDelegate fetchOwnerDelegate;
|
|
||||||
|
|
||||||
private IdentifierDescription identifierDescription;
|
private IdentifierDescription identifierDescription;
|
||||||
|
|
||||||
|
// todo : remove these
|
||||||
|
private final LockMode lockMode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an {@link EntityFetch} object.
|
* Constructs an {@link EntityFetch} object.
|
||||||
*
|
*
|
||||||
* @param sessionFactory - the session factory.
|
* @param sessionFactory - the session factory.
|
||||||
* @param lockMode - the lock mode.
|
* @param lockMode - the lock mode.
|
||||||
* @param owner - the fetch owner for this fetch.
|
* @param owner - the fetch owner for this fetch.
|
||||||
* @param ownerProperty - the owner's property referring to this fetch.
|
* @param fetchedAttribute - the attribute being fetched
|
||||||
* @param fetchStrategy - the fetch strategy for this fetch.
|
* @param fetchStrategy - the fetch strategy for this fetch.
|
||||||
*/
|
*/
|
||||||
public EntityFetch(
|
public EntityFetch(
|
||||||
SessionFactoryImplementor sessionFactory,
|
SessionFactoryImplementor sessionFactory,
|
||||||
LockMode lockMode,
|
LockMode lockMode,
|
||||||
FetchOwner owner,
|
FetchOwner owner,
|
||||||
String ownerProperty,
|
AttributeDefinition fetchedAttribute,
|
||||||
FetchStrategy fetchStrategy) {
|
FetchStrategy fetchStrategy) {
|
||||||
super( sessionFactory, owner, ownerProperty, fetchStrategy );
|
super( sessionFactory, owner, fetchedAttribute, fetchStrategy );
|
||||||
|
|
||||||
|
this.persister = sessionFactory.getEntityPersister( getFetchedType().getAssociatedEntityName() );
|
||||||
|
if ( persister == null ) {
|
||||||
|
throw new WalkingException(
|
||||||
|
String.format(
|
||||||
|
"Unable to locate EntityPersister [%s] for fetch [%s]",
|
||||||
|
getFetchedType().getAssociatedEntityName(),
|
||||||
|
fetchedAttribute.getName()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this.sqlSelectFragmentResolver = new EntityPersisterBasedSqlSelectFragmentResolver( (Queryable) persister );
|
||||||
|
|
||||||
this.persister = sessionFactory.getEntityPersister(
|
|
||||||
getEntityType().getAssociatedEntityName()
|
|
||||||
);
|
|
||||||
this.lockMode = lockMode;
|
this.lockMode = lockMode;
|
||||||
this.fetchOwnerDelegate = new EntityFetchOwnerDelegate( persister );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -83,16 +96,19 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit
|
||||||
protected EntityFetch(EntityFetch original, CopyContext copyContext, FetchOwner fetchOwnerCopy) {
|
protected EntityFetch(EntityFetch original, CopyContext copyContext, FetchOwner fetchOwnerCopy) {
|
||||||
super( original, copyContext, fetchOwnerCopy );
|
super( original, copyContext, fetchOwnerCopy );
|
||||||
this.persister = original.persister;
|
this.persister = original.persister;
|
||||||
|
this.sqlSelectFragmentResolver = original.sqlSelectFragmentResolver;
|
||||||
|
|
||||||
this.lockMode = original.lockMode;
|
this.lockMode = original.lockMode;
|
||||||
this.fetchOwnerDelegate = original.fetchOwnerDelegate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Returns the entity type for this fetch.
|
public EntityType getFetchedType() {
|
||||||
* @return the entity type for this fetch.
|
return (EntityType) super.getFetchedType();
|
||||||
*/
|
}
|
||||||
public final EntityType getEntityType() {
|
|
||||||
return (EntityType) getOwner().getType( this );
|
@Override
|
||||||
|
public String[] toSqlSelectFragments(String alias) {
|
||||||
|
return getOwner().toSqlSelectFragmentResolver().toSqlSelectFragments( alias, getFetchedAttribute() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -105,6 +121,11 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit
|
||||||
return persister;
|
return persister;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqlSelectFragmentResolver toSqlSelectFragmentResolver() {
|
||||||
|
return sqlSelectFragmentResolver;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IdentifierDescription getIdentifierDescription() {
|
public IdentifierDescription getIdentifierDescription() {
|
||||||
return identifierDescription;
|
return identifierDescription;
|
||||||
|
@ -127,12 +148,6 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
|
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
|
||||||
EntityKey entityKey = context.getDictatedRootEntityKey();
|
|
||||||
if ( entityKey != null ) {
|
|
||||||
context.getIdentifierResolutionContext( this ).registerEntityKey( entityKey );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
identifierDescription.hydrate( resultSet, context );
|
identifierDescription.hydrate( resultSet, context );
|
||||||
|
|
||||||
for ( Fetch fetch : getFetches() ) {
|
for ( Fetch fetch : getFetches() ) {
|
||||||
|
@ -142,23 +157,25 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EntityKey resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
|
public EntityKey resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
|
||||||
final ResultSetProcessingContext.IdentifierResolutionContext identifierResolutionContext = context.getIdentifierResolutionContext( this );
|
final ResultSetProcessingContext.EntityReferenceProcessingState entityReferenceProcessingState = context.getProcessingState(
|
||||||
EntityKey entityKey = identifierResolutionContext.getEntityKey();
|
this
|
||||||
|
);
|
||||||
|
EntityKey entityKey = entityReferenceProcessingState.getEntityKey();
|
||||||
if ( entityKey == null ) {
|
if ( entityKey == null ) {
|
||||||
entityKey = identifierDescription.resolve( resultSet, context );
|
entityKey = identifierDescription.resolve( resultSet, context );
|
||||||
if ( entityKey == null ) {
|
if ( entityKey == null ) {
|
||||||
// register the non-existence (though only for one-to-one associations)
|
// register the non-existence (though only for one-to-one associations)
|
||||||
if ( getEntityType().isOneToOne() ) {
|
if ( getFetchedType().isOneToOne() ) {
|
||||||
// first, find our owner's entity-key...
|
// first, find our owner's entity-key...
|
||||||
final EntityKey ownersEntityKey = context.getIdentifierResolutionContext( (EntityReference) getOwner() ).getEntityKey();
|
final EntityKey ownersEntityKey = context.getProcessingState( (EntityReference) getOwner() ).getEntityKey();
|
||||||
if ( ownersEntityKey != null ) {
|
if ( ownersEntityKey != null ) {
|
||||||
context.getSession().getPersistenceContext()
|
context.getSession().getPersistenceContext()
|
||||||
.addNullProperty( ownersEntityKey, getEntityType().getPropertyName() );
|
.addNullProperty( ownersEntityKey, getFetchedType().getPropertyName() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
identifierResolutionContext.registerEntityKey( entityKey );
|
entityReferenceProcessingState.registerEntityKey( entityKey );
|
||||||
|
|
||||||
for ( Fetch fetch : getFetches() ) {
|
for ( Fetch fetch : getFetches() ) {
|
||||||
fetch.resolve( resultSet, context );
|
fetch.resolve( resultSet, context );
|
||||||
|
@ -168,6 +185,19 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit
|
||||||
return entityKey;
|
return entityKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(ResultSet resultSet, ResultSetProcessingContext context, Object owner) throws SQLException {
|
||||||
|
final EntityReferenceProcessingState entityReferenceProcessingState = context.getProcessingState( this );
|
||||||
|
final EntityKey entityKey = entityReferenceProcessingState.getEntityKey();
|
||||||
|
if ( entityKey == null ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final Object entity = context.resolveEntityKey( entityKey, this );
|
||||||
|
for ( Fetch fetch : getFetches() ) {
|
||||||
|
fetch.read( resultSet, context, entity );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve any fetches required to resolve the identifier as well
|
* Resolve any fetches required to resolve the identifier as well
|
||||||
* as the entity key for this fetch..
|
* as the entity key for this fetch..
|
||||||
|
@ -205,7 +235,7 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit
|
||||||
context.checkVersion(
|
context.checkVersion(
|
||||||
resultSet,
|
resultSet,
|
||||||
persister,
|
persister,
|
||||||
context.getLoadQueryAliasResolutionContext().resolveEntityColumnAliases( this ),
|
context.getAliasResolutionContext().resolveAliases( this ).getColumnAliases(),
|
||||||
entityKey,
|
entityKey,
|
||||||
existing
|
existing
|
||||||
);
|
);
|
||||||
|
@ -218,7 +248,7 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit
|
||||||
final String concreteEntityTypeName = context.getConcreteEntityTypeName(
|
final String concreteEntityTypeName = context.getConcreteEntityTypeName(
|
||||||
resultSet,
|
resultSet,
|
||||||
persister,
|
persister,
|
||||||
context.getLoadQueryAliasResolutionContext().resolveEntityColumnAliases( this ),
|
context.getAliasResolutionContext().resolveAliases( this ).getColumnAliases(),
|
||||||
entityKey
|
entityKey
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -239,15 +269,16 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit
|
||||||
entityInstance,
|
entityInstance,
|
||||||
concreteEntityTypeName,
|
concreteEntityTypeName,
|
||||||
entityKey,
|
entityKey,
|
||||||
context.getLoadQueryAliasResolutionContext().resolveEntityColumnAliases( this ),
|
context.getAliasResolutionContext().resolveAliases( this ).getColumnAliases(),
|
||||||
acquiredLockMode,
|
acquiredLockMode,
|
||||||
persister,
|
persister,
|
||||||
getFetchStrategy().getTiming() == FetchTiming.IMMEDIATE,
|
getFetchStrategy(),
|
||||||
getEntityType()
|
true,
|
||||||
|
getFetchedType()
|
||||||
);
|
);
|
||||||
|
|
||||||
// materialize associations (and initialize the object) later
|
// materialize associations (and initialize the object) later
|
||||||
context.registerHydratedEntity( persister, entityKey, entityInstance );
|
context.registerHydratedEntity( this, entityKey, entityInstance );
|
||||||
}
|
}
|
||||||
|
|
||||||
return entityKey;
|
return entityKey;
|
||||||
|
@ -265,9 +296,4 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit
|
||||||
copyContext.getReturnGraphVisitationStrategy().finishingEntityFetch( this );
|
copyContext.getReturnGraphVisitationStrategy().finishingEntityFetch( this );
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected FetchOwnerDelegate getFetchOwnerDelegate() {
|
|
||||||
return fetchOwnerDelegate;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.loader.PropertyPath;
|
||||||
import org.hibernate.persister.collection.CollectionPersister;
|
import org.hibernate.persister.collection.CollectionPersister;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
|
import org.hibernate.persister.entity.Queryable;
|
||||||
|
import org.hibernate.persister.walking.spi.AttributeDefinition;
|
||||||
import org.hibernate.type.AssociationType;
|
import org.hibernate.type.AssociationType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -42,7 +44,7 @@ public class EntityIndexGraph extends AbstractFetchOwner implements FetchableCol
|
||||||
private final AssociationType indexType;
|
private final AssociationType indexType;
|
||||||
private final EntityPersister indexPersister;
|
private final EntityPersister indexPersister;
|
||||||
private final PropertyPath propertyPath;
|
private final PropertyPath propertyPath;
|
||||||
private final FetchOwnerDelegate fetchOwnerDelegate;
|
private final EntityPersisterBasedSqlSelectFragmentResolver sqlSelectFragmentResolver;
|
||||||
|
|
||||||
private IdentifierDescription identifierDescription;
|
private IdentifierDescription identifierDescription;
|
||||||
|
|
||||||
|
@ -63,7 +65,7 @@ public class EntityIndexGraph extends AbstractFetchOwner implements FetchableCol
|
||||||
this.indexType = (AssociationType) collectionPersister.getIndexType();
|
this.indexType = (AssociationType) collectionPersister.getIndexType();
|
||||||
this.indexPersister = (EntityPersister) this.indexType.getAssociatedJoinable( sessionFactory() );
|
this.indexPersister = (EntityPersister) this.indexType.getAssociatedJoinable( sessionFactory() );
|
||||||
this.propertyPath = collectionPath.append( "<index>" ); // todo : do we want the <index> part?
|
this.propertyPath = collectionPath.append( "<index>" ); // todo : do we want the <index> part?
|
||||||
this.fetchOwnerDelegate = new EntityFetchOwnerDelegate( indexPersister );
|
this.sqlSelectFragmentResolver = new EntityPersisterBasedSqlSelectFragmentResolver( (Queryable) indexPersister );
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntityIndexGraph(EntityIndexGraph original, CopyContext copyContext) {
|
public EntityIndexGraph(EntityIndexGraph original, CopyContext copyContext) {
|
||||||
|
@ -73,7 +75,7 @@ public class EntityIndexGraph extends AbstractFetchOwner implements FetchableCol
|
||||||
this.indexType = original.indexType;
|
this.indexType = original.indexType;
|
||||||
this.indexPersister = original.indexPersister;
|
this.indexPersister = original.indexPersister;
|
||||||
this.propertyPath = original.propertyPath;
|
this.propertyPath = original.propertyPath;
|
||||||
this.fetchOwnerDelegate = original.fetchOwnerDelegate;
|
this.sqlSelectFragmentResolver = original.sqlSelectFragmentResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -100,7 +102,7 @@ public class EntityIndexGraph extends AbstractFetchOwner implements FetchableCol
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void validateFetchPlan(FetchStrategy fetchStrategy) {
|
public void validateFetchPlan(FetchStrategy fetchStrategy, AttributeDefinition attributeDefinition) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -129,7 +131,7 @@ public class EntityIndexGraph extends AbstractFetchOwner implements FetchableCol
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected FetchOwnerDelegate getFetchOwnerDelegate() {
|
public SqlSelectFragmentResolver toSqlSelectFragmentResolver() {
|
||||||
return fetchOwnerDelegate;
|
return sqlSelectFragmentResolver;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,29 +23,22 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.loader.plan.spi;
|
package org.hibernate.loader.plan.spi;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import org.hibernate.persister.entity.Queryable;
|
||||||
import java.util.Map;
|
import org.hibernate.persister.walking.spi.AttributeDefinition;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base implementation of FetchOwnerDelegate providing caching of FetchMetadata.
|
|
||||||
*
|
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractFetchOwnerDelegate implements FetchOwnerDelegate {
|
public class EntityPersisterBasedSqlSelectFragmentResolver implements SqlSelectFragmentResolver {
|
||||||
private Map<String,FetchMetadata> fetchMetadataMap;
|
private final Queryable entityPersister;
|
||||||
|
|
||||||
@Override
|
public EntityPersisterBasedSqlSelectFragmentResolver(Queryable entityPersister) {
|
||||||
public FetchMetadata locateFetchMetadata(Fetch fetch) {
|
this.entityPersister = entityPersister;
|
||||||
FetchMetadata metadata = fetchMetadataMap == null ? null : fetchMetadataMap.get( fetch.getOwnerPropertyName() );
|
}
|
||||||
if ( metadata == null ) {
|
|
||||||
if ( fetchMetadataMap == null ) {
|
@Override
|
||||||
fetchMetadataMap = new HashMap<String, FetchMetadata>();
|
public String[] toSqlSelectFragments(String alias, AttributeDefinition attributeDefinition) {
|
||||||
}
|
return entityPersister.toColumns( alias, attributeDefinition.getName() );
|
||||||
metadata = buildFetchMetadata( fetch );
|
|
||||||
fetchMetadataMap.put( fetch.getOwnerPropertyName(), metadata );
|
|
||||||
}
|
|
||||||
return metadata;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract FetchMetadata buildFetchMetadata(Fetch fetch);
|
|
||||||
}
|
}
|
|
@ -24,7 +24,7 @@
|
||||||
package org.hibernate.loader.plan.spi;
|
package org.hibernate.loader.plan.spi;
|
||||||
|
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.loader.spi.ResultSetProcessingContext;
|
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -23,19 +23,13 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.loader.plan.spi;
|
package org.hibernate.loader.plan.spi;
|
||||||
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
import org.hibernate.AssertionFailure;
|
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.engine.FetchStrategy;
|
import org.hibernate.engine.FetchStrategy;
|
||||||
import org.hibernate.engine.spi.EntityKey;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.loader.PropertyPath;
|
import org.hibernate.loader.PropertyPath;
|
||||||
import org.hibernate.loader.spi.ResultSetProcessingContext;
|
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
|
import org.hibernate.persister.entity.Queryable;
|
||||||
import static org.hibernate.loader.spi.ResultSetProcessingContext.IdentifierResolutionContext;
|
import org.hibernate.persister.walking.spi.AttributeDefinition;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an entity return value in the query results. Not the same
|
* Represents an entity return value in the query results. Not the same
|
||||||
|
@ -46,17 +40,14 @@ import static org.hibernate.loader.spi.ResultSetProcessingContext.IdentifierReso
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class EntityReturn extends AbstractFetchOwner implements Return, EntityReference, CopyableReturn {
|
public class EntityReturn extends AbstractFetchOwner implements Return, EntityReference, CopyableReturn {
|
||||||
|
|
||||||
private final EntityPersister persister;
|
private final EntityPersister persister;
|
||||||
|
private final EntityPersisterBasedSqlSelectFragmentResolver sqlSelectFragmentResolver;
|
||||||
|
private IdentifierDescription identifierDescription;
|
||||||
|
|
||||||
private final PropertyPath propertyPath = new PropertyPath(); // it's a root
|
private final PropertyPath propertyPath;
|
||||||
|
|
||||||
private final LockMode lockMode;
|
private final LockMode lockMode;
|
||||||
|
|
||||||
private final FetchOwnerDelegate fetchOwnerDelegate;
|
|
||||||
|
|
||||||
private IdentifierDescription identifierDescription;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct an {@link EntityReturn}.
|
* Construct an {@link EntityReturn}.
|
||||||
*
|
*
|
||||||
|
@ -70,15 +61,19 @@ public class EntityReturn extends AbstractFetchOwner implements Return, EntityRe
|
||||||
String entityName) {
|
String entityName) {
|
||||||
super( sessionFactory );
|
super( sessionFactory );
|
||||||
this.persister = sessionFactory.getEntityPersister( entityName );
|
this.persister = sessionFactory.getEntityPersister( entityName );
|
||||||
|
this.propertyPath = new PropertyPath( entityName );
|
||||||
|
this.sqlSelectFragmentResolver = new EntityPersisterBasedSqlSelectFragmentResolver( (Queryable) persister );
|
||||||
|
|
||||||
this.lockMode = lockMode;
|
this.lockMode = lockMode;
|
||||||
this.fetchOwnerDelegate = new EntityFetchOwnerDelegate( persister );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected EntityReturn(EntityReturn original, CopyContext copyContext) {
|
protected EntityReturn(EntityReturn original, CopyContext copyContext) {
|
||||||
super( original, copyContext );
|
super( original, copyContext );
|
||||||
this.persister = original.persister;
|
this.persister = original.persister;
|
||||||
|
this.propertyPath = original.propertyPath;
|
||||||
|
this.sqlSelectFragmentResolver = original.sqlSelectFragmentResolver;
|
||||||
|
|
||||||
this.lockMode = original.lockMode;
|
this.lockMode = original.lockMode;
|
||||||
this.fetchOwnerDelegate = original.fetchOwnerDelegate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -102,7 +97,7 @@ public class EntityReturn extends AbstractFetchOwner implements Return, EntityRe
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void validateFetchPlan(FetchStrategy fetchStrategy) {
|
public void validateFetchPlan(FetchStrategy fetchStrategy, AttributeDefinition attributeDefinition) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -115,67 +110,6 @@ public class EntityReturn extends AbstractFetchOwner implements Return, EntityRe
|
||||||
return propertyPath;
|
return propertyPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
|
|
||||||
EntityKey entityKey = getEntityKeyFromContext( context );
|
|
||||||
if ( entityKey != null ) {
|
|
||||||
context.getIdentifierResolutionContext( this ).registerEntityKey( entityKey );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
identifierDescription.hydrate( resultSet, context );
|
|
||||||
|
|
||||||
for ( Fetch fetch : getFetches() ) {
|
|
||||||
fetch.hydrate( resultSet, context );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private EntityKey getEntityKeyFromContext(ResultSetProcessingContext context) {
|
|
||||||
if ( context.getDictatedRootEntityKey() != null ) {
|
|
||||||
return context.getDictatedRootEntityKey();
|
|
||||||
}
|
|
||||||
else if ( context.getQueryParameters().getOptionalId() != null ) {
|
|
||||||
return context.getSession().generateEntityKey(
|
|
||||||
context.getQueryParameters().getOptionalId(),
|
|
||||||
getEntityPersister()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
|
|
||||||
final IdentifierResolutionContext identifierResolutionContext = context.getIdentifierResolutionContext( this );
|
|
||||||
EntityKey entityKey = identifierResolutionContext.getEntityKey();
|
|
||||||
if ( entityKey != null ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
entityKey = identifierDescription.resolve( resultSet, context );
|
|
||||||
identifierResolutionContext.registerEntityKey( entityKey );
|
|
||||||
|
|
||||||
for ( Fetch fetch : getFetches() ) {
|
|
||||||
fetch.resolve( resultSet, context );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object read(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
|
|
||||||
Object objectForThisEntityReturn = null;
|
|
||||||
for ( IdentifierResolutionContext identifierResolutionContext : context.getIdentifierResolutionContexts() ) {
|
|
||||||
final EntityReference entityReference = identifierResolutionContext.getEntityReference();
|
|
||||||
final EntityKey entityKey = identifierResolutionContext.getEntityKey();
|
|
||||||
if ( entityKey == null ) {
|
|
||||||
throw new AssertionFailure( "Could not locate resolved EntityKey");
|
|
||||||
}
|
|
||||||
final Object object = context.resolveEntityKey( entityKey, entityReference );
|
|
||||||
if ( this == entityReference ) {
|
|
||||||
objectForThisEntityReturn = object;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return objectForThisEntityReturn;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void injectIdentifierDescription(IdentifierDescription identifierDescription) {
|
public void injectIdentifierDescription(IdentifierDescription identifierDescription) {
|
||||||
this.identifierDescription = identifierDescription;
|
this.identifierDescription = identifierDescription;
|
||||||
|
@ -192,7 +126,7 @@ public class EntityReturn extends AbstractFetchOwner implements Return, EntityRe
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected FetchOwnerDelegate getFetchOwnerDelegate() {
|
public SqlSelectFragmentResolver toSqlSelectFragmentResolver() {
|
||||||
return fetchOwnerDelegate;
|
return sqlSelectFragmentResolver;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,8 @@ import java.sql.SQLException;
|
||||||
|
|
||||||
import org.hibernate.engine.FetchStrategy;
|
import org.hibernate.engine.FetchStrategy;
|
||||||
import org.hibernate.loader.PropertyPath;
|
import org.hibernate.loader.PropertyPath;
|
||||||
import org.hibernate.loader.spi.ResultSetProcessingContext;
|
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext;
|
||||||
|
import org.hibernate.type.Type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contract for associations that are being fetched.
|
* Contract for associations that are being fetched.
|
||||||
|
@ -46,25 +47,13 @@ public interface Fetch extends CopyableFetch {
|
||||||
public FetchOwner getOwner();
|
public FetchOwner getOwner();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtain the name of the property, relative to the owner, being fetched.
|
* Get the property path to this fetch
|
||||||
*
|
*
|
||||||
* @return The fetched property name.
|
* @return The property path
|
||||||
*/
|
*/
|
||||||
public String getOwnerPropertyName();
|
public PropertyPath getPropertyPath();
|
||||||
|
|
||||||
/**
|
public Type getFetchedType();
|
||||||
* Is this fetch nullable?
|
|
||||||
*
|
|
||||||
* @return true, if this fetch is nullable; false, otherwise.
|
|
||||||
*/
|
|
||||||
public boolean isNullable();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates the SQL select fragments for this fetch. A select fragment is the column and formula references.
|
|
||||||
*
|
|
||||||
* @return the select fragments
|
|
||||||
*/
|
|
||||||
public String[] toSqlSelectFragments(String alias);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the fetch strategy for this fetch.
|
* Gets the fetch strategy for this fetch.
|
||||||
|
@ -74,16 +63,27 @@ public interface Fetch extends CopyableFetch {
|
||||||
public FetchStrategy getFetchStrategy();
|
public FetchStrategy getFetchStrategy();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the property path to this fetch
|
* Is this fetch nullable?
|
||||||
*
|
*
|
||||||
* @return The property path
|
* @return true, if this fetch is nullable; false, otherwise.
|
||||||
*/
|
*/
|
||||||
public PropertyPath getPropertyPath();
|
public boolean isNullable();
|
||||||
|
|
||||||
|
public String getAdditionalJoinConditions();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the SQL select fragments for this fetch. A select fragment is the column and formula references.
|
||||||
|
*
|
||||||
|
* @return the select fragments
|
||||||
|
*/
|
||||||
|
public String[] toSqlSelectFragments(String alias);
|
||||||
|
|
||||||
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException;
|
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException;
|
||||||
|
|
||||||
public Object resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException;
|
public Object resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException;
|
||||||
|
|
||||||
|
public void read(ResultSet resultSet, ResultSetProcessingContext context, Object owner) throws SQLException;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Fetch makeCopy(CopyContext copyContext, FetchOwner fetchOwnerCopy);
|
public Fetch makeCopy(CopyContext copyContext, FetchOwner fetchOwnerCopy);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,9 +25,13 @@ package org.hibernate.loader.plan.spi;
|
||||||
|
|
||||||
import org.hibernate.engine.FetchStrategy;
|
import org.hibernate.engine.FetchStrategy;
|
||||||
import org.hibernate.loader.PropertyPath;
|
import org.hibernate.loader.PropertyPath;
|
||||||
|
import org.hibernate.loader.plan.spi.build.AbstractLoadPlanBuilderStrategy;
|
||||||
import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext;
|
import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
|
import org.hibernate.persister.entity.PropertyMapping;
|
||||||
|
import org.hibernate.persister.walking.spi.AnyMappingDefinition;
|
||||||
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
|
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
|
||||||
|
import org.hibernate.persister.walking.spi.AttributeDefinition;
|
||||||
import org.hibernate.persister.walking.spi.CompositionDefinition;
|
import org.hibernate.persister.walking.spi.CompositionDefinition;
|
||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
|
|
||||||
|
@ -89,9 +93,10 @@ public interface FetchOwner {
|
||||||
/**
|
/**
|
||||||
* Is the asserted plan valid from this owner to a fetch?
|
* Is the asserted plan valid from this owner to a fetch?
|
||||||
*
|
*
|
||||||
* @param fetchStrategy The pla to validate
|
* @param fetchStrategy The type of fetch to validate
|
||||||
|
* @param attributeDefinition The attribute to be fetched
|
||||||
*/
|
*/
|
||||||
public void validateFetchPlan(FetchStrategy fetchStrategy);
|
public void validateFetchPlan(FetchStrategy fetchStrategy, AttributeDefinition attributeDefinition);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the EntityPersister that is the base for any property references in the fetches it owns.
|
* Retrieve the EntityPersister that is the base for any property references in the fetches it owns.
|
||||||
|
@ -121,5 +126,11 @@ public interface FetchOwner {
|
||||||
CompositionDefinition attributeDefinition,
|
CompositionDefinition attributeDefinition,
|
||||||
LoadPlanBuildingContext loadPlanBuildingContext);
|
LoadPlanBuildingContext loadPlanBuildingContext);
|
||||||
|
|
||||||
|
public AnyFetch buildAnyFetch(
|
||||||
|
AttributeDefinition attribute,
|
||||||
|
AnyMappingDefinition anyDefinition,
|
||||||
|
FetchStrategy fetchStrategy,
|
||||||
|
LoadPlanBuildingContext loadPlanBuildingContext);
|
||||||
|
|
||||||
|
public SqlSelectFragmentResolver toSqlSelectFragmentResolver();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 java.sql.SQLException;
|
||||||
|
|
||||||
import org.hibernate.engine.spi.EntityKey;
|
import org.hibernate.engine.spi.EntityKey;
|
||||||
import org.hibernate.loader.spi.ResultSetProcessingContext;
|
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext;
|
||||||
|
import org.hibernate.persister.spi.HydratedCompoundValueHandler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
|
@ -38,4 +39,6 @@ public interface IdentifierDescription {
|
||||||
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException;
|
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException;
|
||||||
|
|
||||||
public EntityKey resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException;
|
public EntityKey resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException;
|
||||||
|
|
||||||
|
HydratedCompoundValueHandler getHydratedStateHandler(Fetch fetch);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public interface LoadPlan {
|
public interface LoadPlan {
|
||||||
|
public List<? extends Return> getReturns();
|
||||||
|
|
||||||
|
public Disposition getDisposition();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does this load plan indicate that lazy attributes are to be force fetched?
|
||||||
|
* <p/>
|
||||||
|
* Here we are talking about laziness in regards to the legacy bytecode enhancement which adds support for
|
||||||
|
* partial selects of an entity's state (e.g., skip loading a lob initially, wait until/if it is needed)
|
||||||
|
* <p/>
|
||||||
|
* This one would effect the SQL that needs to get generated as well as how the result set would be read.
|
||||||
|
* Therefore we make this part of the LoadPlan contract.
|
||||||
|
* <p/>
|
||||||
|
* NOTE that currently this is only relevant for HQL loaders when the HQL has specified the {@code FETCH ALL PROPERTIES}
|
||||||
|
* key-phrase. In all other cases, this returns false.
|
||||||
|
|
||||||
|
* @return Whether or not to
|
||||||
|
*/
|
||||||
|
public boolean areLazyAttributesForceFetched();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenient form of checking {@link #getReturns()} for scalar root returns.
|
* Convenient form of checking {@link #getReturns()} for scalar root returns.
|
||||||
*
|
*
|
||||||
|
@ -53,7 +73,26 @@ public interface LoadPlan {
|
||||||
*/
|
*/
|
||||||
public boolean hasAnyScalarReturns();
|
public boolean hasAnyScalarReturns();
|
||||||
|
|
||||||
public List<Return> getReturns();
|
/**
|
||||||
|
* Enumerated possibilities for describing the disposition of this LoadPlan.
|
||||||
|
*/
|
||||||
|
public static enum Disposition {
|
||||||
|
/**
|
||||||
|
* This is an "entity loader" load plan, which describes a plan for loading one or more entity instances of
|
||||||
|
* the same entity type. There is a single return, which will be of type {@link EntityReturn}
|
||||||
|
*/
|
||||||
|
ENTITY_LOADER,
|
||||||
|
/**
|
||||||
|
* This is a "collection initializer" load plan, which describes a plan for loading one or more entity instances of
|
||||||
|
* the same collection type. There is a single return, which will be of type {@link CollectionReturn}
|
||||||
|
*/
|
||||||
|
COLLECTION_INITIALIZER,
|
||||||
|
/**
|
||||||
|
* We have a mixed load plan, which will have one or more returns of {@link EntityReturn} and {@link ScalarReturn}
|
||||||
|
* (NOT {@link CollectionReturn}).
|
||||||
|
*/
|
||||||
|
MIXED
|
||||||
|
}
|
||||||
|
|
||||||
// todo : would also like to see "call back" style access for handling "subsequent actions" such as:
|
// todo : would also like to see "call back" style access for handling "subsequent actions" such as:
|
||||||
// 1) follow-on locking
|
// 1) follow-on locking
|
||||||
|
|
|
@ -23,11 +23,6 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.loader.plan.spi;
|
package org.hibernate.loader.plan.spi;
|
||||||
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
import org.hibernate.loader.spi.ResultSetProcessingContext;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a return value in the query results. Not the same as a result (column) in the JDBC ResultSet!
|
* Represents a return value in the query results. Not the same as a result (column) in the JDBC ResultSet!
|
||||||
* <p/>
|
* <p/>
|
||||||
|
@ -40,28 +35,4 @@ import org.hibernate.loader.spi.ResultSetProcessingContext;
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public interface Return {
|
public interface Return {
|
||||||
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Effectively performs first phase of two-phase loading. For scalar results first/second phase is one. For
|
|
||||||
* entities, first phase is to resolve identifiers; second phase is to resolve the entity instances.
|
|
||||||
*
|
|
||||||
* @param resultSet The result set being processed
|
|
||||||
* @param context The context for the processing
|
|
||||||
*
|
|
||||||
* @throws SQLException Indicates a problem access the JDBC result set
|
|
||||||
*/
|
|
||||||
public void resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Essentially performs the second phase of two-phase loading.
|
|
||||||
*
|
|
||||||
* @param resultSet The result set being processed
|
|
||||||
* @param context The context for the processing
|
|
||||||
*
|
|
||||||
* @return The read object
|
|
||||||
*
|
|
||||||
* @throws SQLException Indicates a problem access the JDBC result set
|
|
||||||
*/
|
|
||||||
public Object read(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,16 +23,15 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.loader.plan.spi;
|
package org.hibernate.loader.plan.spi;
|
||||||
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.loader.spi.ResultSetProcessingContext;
|
|
||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represent a simple scalar return within a query result. Generally this would be values of basic (String, Integer,
|
* Represent a simple scalar return within a query result. Generally this would be values of basic (String, Integer,
|
||||||
* etc) or composite types.
|
* etc) or composite types.
|
||||||
|
* <p/>
|
||||||
|
* todo : we should link the Returns back to their "source"
|
||||||
|
* aka the entity/collection/etc that defines the qualifier used to qualify this Return's columns
|
||||||
*
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
|
@ -47,23 +46,4 @@ public class ScalarReturn extends AbstractPlanNode implements Return {
|
||||||
public Type getType() {
|
public Type getType() {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) {
|
|
||||||
// nothing to do
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void resolve(ResultSet resultSet, ResultSetProcessingContext context) {
|
|
||||||
// nothing to do
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object read(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
|
|
||||||
return type.nullSafeGet(
|
|
||||||
resultSet,
|
|
||||||
context.getLoadQueryAliasResolutionContext().resolveScalarReturnAliases( this ),
|
|
||||||
context.getSession(),
|
|
||||||
null );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.LockMode;
|
||||||
import org.hibernate.cfg.NotYetImplementedException;
|
import org.hibernate.cfg.NotYetImplementedException;
|
||||||
import org.hibernate.engine.FetchStrategy;
|
import org.hibernate.engine.FetchStrategy;
|
||||||
import org.hibernate.engine.FetchTiming;
|
import org.hibernate.engine.FetchStyle;
|
||||||
import org.hibernate.engine.spi.EntityKey;
|
import org.hibernate.engine.spi.EntityKey;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.internal.util.StringHelper;
|
import org.hibernate.internal.util.StringHelper;
|
||||||
import org.hibernate.loader.PropertyPath;
|
import org.hibernate.loader.PropertyPath;
|
||||||
import org.hibernate.loader.plan.spi.AbstractFetchOwner;
|
import org.hibernate.loader.plan.spi.AbstractFetchOwner;
|
||||||
import org.hibernate.loader.plan.spi.AbstractFetchOwnerDelegate;
|
import org.hibernate.loader.plan.spi.AnyFetch;
|
||||||
import org.hibernate.loader.plan.spi.CollectionFetch;
|
import org.hibernate.loader.plan.spi.CollectionFetch;
|
||||||
import org.hibernate.loader.plan.spi.CollectionReference;
|
import org.hibernate.loader.plan.spi.CollectionReference;
|
||||||
import org.hibernate.loader.plan.spi.CollectionReturn;
|
import org.hibernate.loader.plan.spi.CollectionReturn;
|
||||||
import org.hibernate.loader.plan.spi.CompositeElementGraph;
|
import org.hibernate.loader.plan.spi.CompositeElementGraph;
|
||||||
import org.hibernate.loader.plan.spi.CompositeFetch;
|
import org.hibernate.loader.plan.spi.CompositeFetch;
|
||||||
import org.hibernate.loader.plan.spi.CompositeFetchOwnerDelegate;
|
|
||||||
import org.hibernate.loader.plan.spi.EntityFetch;
|
import org.hibernate.loader.plan.spi.EntityFetch;
|
||||||
|
import org.hibernate.loader.plan.spi.EntityPersisterBasedSqlSelectFragmentResolver;
|
||||||
import org.hibernate.loader.plan.spi.EntityReference;
|
import org.hibernate.loader.plan.spi.EntityReference;
|
||||||
import org.hibernate.loader.plan.spi.EntityReturn;
|
import org.hibernate.loader.plan.spi.EntityReturn;
|
||||||
import org.hibernate.loader.plan.spi.Fetch;
|
import org.hibernate.loader.plan.spi.Fetch;
|
||||||
import org.hibernate.loader.plan.spi.FetchOwner;
|
import org.hibernate.loader.plan.spi.FetchOwner;
|
||||||
import org.hibernate.loader.plan.spi.FetchOwnerDelegate;
|
|
||||||
import org.hibernate.loader.plan.spi.IdentifierDescription;
|
import org.hibernate.loader.plan.spi.IdentifierDescription;
|
||||||
|
import org.hibernate.loader.plan.spi.KeyManyToOneBidirectionalEntityFetch;
|
||||||
import org.hibernate.loader.plan.spi.Return;
|
import org.hibernate.loader.plan.spi.Return;
|
||||||
import org.hibernate.loader.spi.ResultSetProcessingContext;
|
import org.hibernate.loader.plan.spi.SqlSelectFragmentResolver;
|
||||||
|
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
import org.hibernate.persister.entity.Loadable;
|
import org.hibernate.persister.entity.Queryable;
|
||||||
import org.hibernate.persister.spi.HydratedCompoundValueHandler;
|
import org.hibernate.persister.spi.HydratedCompoundValueHandler;
|
||||||
|
import org.hibernate.persister.walking.internal.FetchStrategyHelper;
|
||||||
|
import org.hibernate.persister.walking.spi.AnyMappingDefinition;
|
||||||
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
|
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
|
||||||
import org.hibernate.persister.walking.spi.AttributeDefinition;
|
import org.hibernate.persister.walking.spi.AttributeDefinition;
|
||||||
import org.hibernate.persister.walking.spi.CollectionDefinition;
|
import org.hibernate.persister.walking.spi.CollectionDefinition;
|
||||||
|
@ -72,11 +75,9 @@ import org.hibernate.persister.walking.spi.CompositionDefinition;
|
||||||
import org.hibernate.persister.walking.spi.EntityDefinition;
|
import org.hibernate.persister.walking.spi.EntityDefinition;
|
||||||
import org.hibernate.persister.walking.spi.EntityIdentifierDefinition;
|
import org.hibernate.persister.walking.spi.EntityIdentifierDefinition;
|
||||||
import org.hibernate.persister.walking.spi.WalkingException;
|
import org.hibernate.persister.walking.spi.WalkingException;
|
||||||
import org.hibernate.type.CompositeType;
|
import org.hibernate.type.EntityType;
|
||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
|
|
||||||
import static org.hibernate.loader.spi.ResultSetProcessingContext.IdentifierResolutionContext;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
|
@ -313,12 +314,10 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startingCompositeCollectionElement(CompositeCollectionElementDefinition compositeElementDefinition) {
|
public void startingCompositeCollectionElement(CompositeCollectionElementDefinition compositeElementDefinition) {
|
||||||
System.out.println(
|
log.tracef(
|
||||||
String.format(
|
"%s Starting composite collection element for (%s)",
|
||||||
"%s Starting composite collection element for (%s)",
|
StringHelper.repeat( ">>", fetchOwnerStack.size() ),
|
||||||
StringHelper.repeat( ">>", fetchOwnerStack.size() ),
|
compositeElementDefinition.getCollectionDefinition().getCollectionPersister().getRole()
|
||||||
compositeElementDefinition.getCollectionDefinition().getCollectionPersister().getRole()
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -397,16 +396,17 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
|
||||||
final Type attributeType = attributeDefinition.getType();
|
final Type attributeType = attributeDefinition.getType();
|
||||||
|
|
||||||
final boolean isComponentType = attributeType.isComponentType();
|
final boolean isComponentType = attributeType.isComponentType();
|
||||||
final boolean isBasicType = ! ( isComponentType || attributeType.isAssociationType() );
|
final boolean isAssociationType = attributeType.isAssociationType();
|
||||||
|
final boolean isBasicType = ! ( isComponentType || isAssociationType );
|
||||||
|
|
||||||
if ( isBasicType ) {
|
if ( isBasicType ) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if ( isComponentType ) {
|
else if ( isAssociationType ) {
|
||||||
return handleCompositeAttribute( (CompositionDefinition) attributeDefinition );
|
return handleAssociationAttribute( (AssociationAttributeDefinition) attributeDefinition );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return handleAssociationAttribute( (AssociationAttributeDefinition) attributeDefinition );
|
return handleCompositeAttribute( (CompositionDefinition) attributeDefinition );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -419,6 +419,24 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void foundAny(AssociationAttributeDefinition attributeDefinition, AnyMappingDefinition anyDefinition) {
|
||||||
|
// for ANY mappings we need to build a Fetch:
|
||||||
|
// 1) fetch type is SELECT, timing might be IMMEDIATE or DELAYED depending on whether it was defined as lazy
|
||||||
|
// 2) (because the fetch cannot be a JOIN...) do not push it to the stack
|
||||||
|
final FetchStrategy fetchStrategy = determineFetchPlan( attributeDefinition );
|
||||||
|
|
||||||
|
final FetchOwner fetchOwner = currentFetchOwner();
|
||||||
|
fetchOwner.validateFetchPlan( fetchStrategy, attributeDefinition );
|
||||||
|
|
||||||
|
fetchOwner.buildAnyFetch(
|
||||||
|
attributeDefinition,
|
||||||
|
anyDefinition,
|
||||||
|
fetchStrategy,
|
||||||
|
this
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
protected boolean handleCompositeAttribute(CompositionDefinition attributeDefinition) {
|
protected boolean handleCompositeAttribute(CompositionDefinition attributeDefinition) {
|
||||||
final FetchOwner fetchOwner = currentFetchOwner();
|
final FetchOwner fetchOwner = currentFetchOwner();
|
||||||
final CompositeFetch fetch = fetchOwner.buildCompositeFetch( attributeDefinition, this );
|
final CompositeFetch fetch = fetchOwner.buildCompositeFetch( attributeDefinition, this );
|
||||||
|
@ -427,26 +445,35 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean handleAssociationAttribute(AssociationAttributeDefinition attributeDefinition) {
|
protected boolean handleAssociationAttribute(AssociationAttributeDefinition attributeDefinition) {
|
||||||
|
// todo : this seems to not be correct for one-to-one
|
||||||
final FetchStrategy fetchStrategy = determineFetchPlan( attributeDefinition );
|
final FetchStrategy fetchStrategy = determineFetchPlan( attributeDefinition );
|
||||||
if ( fetchStrategy.getTiming() != FetchTiming.IMMEDIATE ) {
|
if ( fetchStrategy.getStyle() != FetchStyle.JOIN ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// if ( fetchStrategy.getTiming() != FetchTiming.IMMEDIATE ) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
final FetchOwner fetchOwner = currentFetchOwner();
|
final FetchOwner fetchOwner = currentFetchOwner();
|
||||||
fetchOwner.validateFetchPlan( fetchStrategy );
|
fetchOwner.validateFetchPlan( fetchStrategy, attributeDefinition );
|
||||||
|
|
||||||
final Fetch associationFetch;
|
final Fetch associationFetch;
|
||||||
if ( attributeDefinition.isCollection() ) {
|
final AssociationAttributeDefinition.AssociationNature nature = attributeDefinition.getAssociationNature();
|
||||||
associationFetch = fetchOwner.buildCollectionFetch( attributeDefinition, fetchStrategy, this );
|
if ( nature == AssociationAttributeDefinition.AssociationNature.ANY ) {
|
||||||
pushToCollectionStack( (CollectionReference) associationFetch );
|
return false;
|
||||||
|
// throw new NotYetImplementedException( "AnyType support still in progress" );
|
||||||
}
|
}
|
||||||
else {
|
else if ( nature == AssociationAttributeDefinition.AssociationNature.ENTITY ) {
|
||||||
associationFetch = fetchOwner.buildEntityFetch(
|
associationFetch = fetchOwner.buildEntityFetch(
|
||||||
attributeDefinition,
|
attributeDefinition,
|
||||||
fetchStrategy,
|
fetchStrategy,
|
||||||
this
|
this
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
associationFetch = fetchOwner.buildCollectionFetch( attributeDefinition, fetchStrategy, this );
|
||||||
|
pushToCollectionStack( (CollectionReference) associationFetch );
|
||||||
|
}
|
||||||
|
|
||||||
if ( FetchOwner.class.isInstance( associationFetch) ) {
|
if ( FetchOwner.class.isInstance( associationFetch) ) {
|
||||||
pushToStack( (FetchOwner) associationFetch );
|
pushToStack( (FetchOwner) associationFetch );
|
||||||
|
@ -521,12 +548,16 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
|
||||||
protected static abstract class AbstractIdentifierAttributeCollector extends AbstractFetchOwner
|
protected static abstract class AbstractIdentifierAttributeCollector extends AbstractFetchOwner
|
||||||
implements FetchOwner, EntityReference, FetchStackAware {
|
implements FetchOwner, EntityReference, FetchStackAware {
|
||||||
protected final EntityReference entityReference;
|
protected final EntityReference entityReference;
|
||||||
|
private final EntityPersisterBasedSqlSelectFragmentResolver sqlSelectFragmentResolver;
|
||||||
protected final Map<Fetch,HydratedCompoundValueHandler> fetchToHydratedStateExtractorMap
|
protected final Map<Fetch,HydratedCompoundValueHandler> fetchToHydratedStateExtractorMap
|
||||||
= new HashMap<Fetch, HydratedCompoundValueHandler>();
|
= new HashMap<Fetch, HydratedCompoundValueHandler>();
|
||||||
|
|
||||||
public AbstractIdentifierAttributeCollector(SessionFactoryImplementor sessionFactory, EntityReference entityReference) {
|
public AbstractIdentifierAttributeCollector(SessionFactoryImplementor sessionFactory, EntityReference entityReference) {
|
||||||
super( sessionFactory );
|
super( sessionFactory );
|
||||||
this.entityReference = entityReference;
|
this.entityReference = entityReference;
|
||||||
|
this.sqlSelectFragmentResolver = new EntityPersisterBasedSqlSelectFragmentResolver(
|
||||||
|
(Queryable) entityReference.getEntityPersister()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -557,6 +588,15 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
|
||||||
throw new WalkingException( "Entity identifier cannot contain persistent collections" );
|
throw new WalkingException( "Entity identifier cannot contain persistent collections" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnyFetch buildAnyFetch(
|
||||||
|
AttributeDefinition attribute,
|
||||||
|
AnyMappingDefinition anyDefinition,
|
||||||
|
FetchStrategy fetchStrategy,
|
||||||
|
LoadPlanBuildingContext loadPlanBuildingContext) {
|
||||||
|
throw new WalkingException( "Entity identifier cannot contain ANY type mappings" );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EntityFetch buildEntityFetch(
|
public EntityFetch buildEntityFetch(
|
||||||
AssociationAttributeDefinition attributeDefinition,
|
AssociationAttributeDefinition attributeDefinition,
|
||||||
|
@ -566,25 +606,75 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
|
||||||
//
|
//
|
||||||
// IMPL NOTE: we pass ourselves as the FetchOwner which will route the fetch back through our #addFetch
|
// IMPL NOTE: we pass ourselves as the FetchOwner which will route the fetch back through our #addFetch
|
||||||
// impl. We collect them there and later build the IdentifierDescription
|
// impl. We collect them there and later build the IdentifierDescription
|
||||||
|
|
||||||
|
// if `this` is a fetch and its owner is "the same" (bi-directionality) as the attribute to be join fetched
|
||||||
|
// we should wrap our FetchOwner as an EntityFetch. That should solve everything except for the alias
|
||||||
|
// context lookups because of the different instances (because of wrapping). So somehow the consumer of this
|
||||||
|
// needs to be able to unwrap it to do the alias lookup, and would have to know to do that.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// we are processing the EntityReference(Address) identifier. we come across its key-many-to-one reference
|
||||||
|
// to Person. Now, if EntityReference(Address) is an instance of EntityFetch(Address) there is a strong
|
||||||
|
// likelihood that we have a bi-directionality and need to handle that specially.
|
||||||
|
//
|
||||||
|
// how to best (a) find the bi-directionality and (b) represent that?
|
||||||
|
|
||||||
|
if ( EntityFetch.class.isInstance( entityReference ) ) {
|
||||||
|
// we just confirmed that EntityReference(Address) is an instance of EntityFetch(Address),
|
||||||
|
final EntityFetch entityFetch = (EntityFetch) entityReference;
|
||||||
|
final FetchOwner entityFetchOwner = entityFetch.getOwner();
|
||||||
|
// so at this point we need to see if entityFetchOwner and attributeDefinition refer to the
|
||||||
|
// "same thing". "same thing" == "same type" && "same column(s)"?
|
||||||
|
//
|
||||||
|
// i make assumptions here that that the attribute type is the EntityType, is that always valid?
|
||||||
|
final EntityType attributeDefinitionTypeAsEntityType = (EntityType) attributeDefinition.getType();
|
||||||
|
|
||||||
|
final boolean sameType = attributeDefinitionTypeAsEntityType.getAssociatedEntityName().equals(
|
||||||
|
entityFetchOwner.retrieveFetchSourcePersister().getEntityName()
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( sameType ) {
|
||||||
|
// check same columns as well?
|
||||||
|
|
||||||
|
return new KeyManyToOneBidirectionalEntityFetch(
|
||||||
|
sessionFactory(),
|
||||||
|
//ugh
|
||||||
|
LockMode.READ,
|
||||||
|
this,
|
||||||
|
attributeDefinition,
|
||||||
|
(EntityReference) entityFetchOwner,
|
||||||
|
fetchStrategy
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final EntityFetch fetch = super.buildEntityFetch( attributeDefinition, fetchStrategy, loadPlanBuildingContext );
|
final EntityFetch fetch = super.buildEntityFetch( attributeDefinition, fetchStrategy, loadPlanBuildingContext );
|
||||||
|
|
||||||
|
// pretty sure this HydratedCompoundValueExtractor stuff is not needed...
|
||||||
fetchToHydratedStateExtractorMap.put( fetch, attributeDefinition.getHydratedCompoundValueExtractor() );
|
fetchToHydratedStateExtractorMap.put( fetch, attributeDefinition.getHydratedCompoundValueExtractor() );
|
||||||
|
|
||||||
return fetch;
|
return fetch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type getType(Fetch fetch) {
|
public Type getType(Fetch fetch) {
|
||||||
return getFetchOwnerDelegate().locateFetchMetadata( fetch ).getType();
|
return fetch.getFetchedType();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isNullable(Fetch fetch) {
|
public boolean isNullable(Fetch fetch) {
|
||||||
return getFetchOwnerDelegate().locateFetchMetadata( fetch ).isNullable();
|
return fetch.isNullable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] toSqlSelectFragments(Fetch fetch, String alias) {
|
public String[] toSqlSelectFragments(Fetch fetch, String alias) {
|
||||||
return getFetchOwnerDelegate().locateFetchMetadata( fetch ).toSqlSelectFragments( alias );
|
return fetch.toSqlSelectFragments( alias );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqlSelectFragmentResolver toSqlSelectFragmentResolver() {
|
||||||
|
return sqlSelectFragmentResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -596,8 +686,8 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
|
||||||
protected abstract IdentifierDescription buildIdentifierDescription();
|
protected abstract IdentifierDescription buildIdentifierDescription();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void validateFetchPlan(FetchStrategy fetchStrategy) {
|
public void validateFetchPlan(FetchStrategy fetchStrategy, AttributeDefinition attributeDefinition) {
|
||||||
( (FetchOwner) entityReference ).validateFetchPlan( fetchStrategy );
|
( (FetchOwner) entityReference ).validateFetchPlan( fetchStrategy, attributeDefinition );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -615,51 +705,12 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
|
||||||
|
|
||||||
protected static class EncapsulatedIdentifierAttributeCollector extends AbstractIdentifierAttributeCollector {
|
protected static class EncapsulatedIdentifierAttributeCollector extends AbstractIdentifierAttributeCollector {
|
||||||
private final PropertyPath propertyPath;
|
private final PropertyPath propertyPath;
|
||||||
private final FetchOwnerDelegate delegate;
|
|
||||||
|
|
||||||
public EncapsulatedIdentifierAttributeCollector(
|
public EncapsulatedIdentifierAttributeCollector(
|
||||||
final SessionFactoryImplementor sessionFactory,
|
final SessionFactoryImplementor sessionFactory,
|
||||||
final EntityReference entityReference) {
|
final EntityReference entityReference) {
|
||||||
super( sessionFactory, entityReference );
|
super( sessionFactory, entityReference );
|
||||||
this.propertyPath = ( (FetchOwner) entityReference ).getPropertyPath();
|
this.propertyPath = ( (FetchOwner) entityReference ).getPropertyPath();
|
||||||
this.delegate = new AbstractFetchOwnerDelegate() {
|
|
||||||
final boolean isCompositeType = entityReference.getEntityPersister().getIdentifierType().isComponentType();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected FetchMetadata buildFetchMetadata(Fetch fetch) {
|
|
||||||
if ( !isCompositeType ) {
|
|
||||||
throw new WalkingException( "Non-composite identifier cannot be a fetch owner" );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !fetch.getOwnerPropertyName().equals( entityReference.getEntityPersister().getIdentifierPropertyName() ) ) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
String.format(
|
|
||||||
"Fetch owner property name [%s] is not the same as the identifier prop" +
|
|
||||||
fetch.getOwnerPropertyName(),
|
|
||||||
entityReference.getEntityPersister().getIdentifierPropertyName()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new FetchMetadata() {
|
|
||||||
@Override
|
|
||||||
public boolean isNullable() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Type getType() {
|
|
||||||
return entityReference.getEntityPersister().getIdentifierType();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String[] toSqlSelectFragments(String alias) {
|
|
||||||
// should not ever be called iiuc...
|
|
||||||
throw new WalkingException( "Should not be called" );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -671,11 +722,6 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected FetchOwnerDelegate getFetchOwnerDelegate() {
|
|
||||||
return delegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PropertyPath getPropertyPath() {
|
public PropertyPath getPropertyPath() {
|
||||||
return propertyPath;
|
return propertyPath;
|
||||||
|
@ -684,65 +730,12 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
|
||||||
|
|
||||||
protected static class NonEncapsulatedIdentifierAttributeCollector extends AbstractIdentifierAttributeCollector {
|
protected static class NonEncapsulatedIdentifierAttributeCollector extends AbstractIdentifierAttributeCollector {
|
||||||
private final PropertyPath propertyPath;
|
private final PropertyPath propertyPath;
|
||||||
private final FetchOwnerDelegate fetchOwnerDelegate;
|
|
||||||
|
|
||||||
public NonEncapsulatedIdentifierAttributeCollector(
|
public NonEncapsulatedIdentifierAttributeCollector(
|
||||||
final SessionFactoryImplementor sessionfactory,
|
final SessionFactoryImplementor sessionfactory,
|
||||||
final EntityReference entityReference) {
|
final EntityReference entityReference) {
|
||||||
super( sessionfactory, entityReference );
|
super( sessionfactory, entityReference );
|
||||||
this.propertyPath = ( (FetchOwner) entityReference ).getPropertyPath().append( "<id>" );
|
this.propertyPath = ( (FetchOwner) entityReference ).getPropertyPath().append( "<id>" );
|
||||||
this.fetchOwnerDelegate = new AbstractFetchOwnerDelegate() {
|
|
||||||
final boolean isCompositeType = entityReference.getEntityPersister().getIdentifierType().isComponentType();
|
|
||||||
final CompositeType idType = (CompositeType) entityReference.getEntityPersister().getIdentifierType();
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected FetchMetadata buildFetchMetadata(Fetch fetch) {
|
|
||||||
if ( !isCompositeType ) {
|
|
||||||
throw new WalkingException( "Non-composite identifier cannot be a fetch owner" );
|
|
||||||
}
|
|
||||||
|
|
||||||
final int subPropertyIndex = locateSubPropertyIndex( idType, fetch.getOwnerPropertyName() );
|
|
||||||
|
|
||||||
return new FetchMetadata() {
|
|
||||||
final Type subType = idType.getSubtypes()[ subPropertyIndex ];
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isNullable() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Type getType() {
|
|
||||||
return subType;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String[] toSqlSelectFragments(String alias) {
|
|
||||||
// should not ever be called iiuc...
|
|
||||||
throw new WalkingException( "Should not be called" );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private int locateSubPropertyIndex(CompositeType idType, String ownerPropertyName) {
|
|
||||||
for ( int i = 0; i < idType.getPropertyNames().length; i++ ) {
|
|
||||||
if ( ownerPropertyName.equals( idType.getPropertyNames()[i] ) ) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// does not bode well if we get here...
|
|
||||||
throw new IllegalStateException(
|
|
||||||
String.format(
|
|
||||||
"Unable to locate fetched attribute [%s] as part of composite identifier [%s]",
|
|
||||||
ownerPropertyName,
|
|
||||||
getPropertyPath().getFullPath()
|
|
||||||
)
|
|
||||||
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -758,13 +751,6 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
|
||||||
public PropertyPath getPropertyPath() {
|
public PropertyPath getPropertyPath() {
|
||||||
return propertyPath;
|
return propertyPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected FetchOwnerDelegate getFetchOwnerDelegate() {
|
|
||||||
return fetchOwnerDelegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class IdentifierDescriptionImpl implements IdentifierDescription {
|
private static class IdentifierDescriptionImpl implements IdentifierDescription {
|
||||||
|
@ -786,26 +772,31 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
|
||||||
return identifierFetches;
|
return identifierFetches;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HydratedCompoundValueHandler getHydratedStateHandler(Fetch fetch) {
|
||||||
|
return fetchToHydratedStateExtractorMap == null ? null : fetchToHydratedStateExtractorMap.get( fetch );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
|
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
|
||||||
final IdentifierResolutionContext ownerIdentifierResolutionContext =
|
final ResultSetProcessingContext.EntityReferenceProcessingState ownerEntityReferenceProcessingState =
|
||||||
context.getIdentifierResolutionContext( entityReference );
|
context.getProcessingState( entityReference );
|
||||||
final Object ownerIdentifierHydratedState = ownerIdentifierResolutionContext.getHydratedForm();
|
final Object ownerIdentifierHydratedState = ownerEntityReferenceProcessingState.getIdentifierHydratedForm();
|
||||||
|
|
||||||
if ( ownerIdentifierHydratedState != null ) {
|
if ( ownerIdentifierHydratedState != null ) {
|
||||||
for ( Fetch fetch : identifierFetches ) {
|
for ( Fetch fetch : identifierFetches ) {
|
||||||
if ( fetch instanceof EntityFetch ) {
|
if ( fetch instanceof EntityFetch ) {
|
||||||
final IdentifierResolutionContext identifierResolutionContext =
|
final ResultSetProcessingContext.EntityReferenceProcessingState fetchEntityReferenceProcessingState =
|
||||||
context.getIdentifierResolutionContext( (EntityFetch) fetch );
|
context.getProcessingState( (EntityFetch) fetch );
|
||||||
// if the identifier was already hydrated, nothing to do
|
// if the identifier was already hydrated, nothing to do
|
||||||
if ( identifierResolutionContext.getHydratedForm() != null ) {
|
if ( fetchEntityReferenceProcessingState.getIdentifierHydratedForm() != null ) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// try to extract the sub-hydrated value from the owners tuple array
|
// try to extract the sub-hydrated value from the owners tuple array
|
||||||
if ( fetchToHydratedStateExtractorMap != null && ownerIdentifierHydratedState != null ) {
|
if ( fetchToHydratedStateExtractorMap != null && ownerIdentifierHydratedState != null ) {
|
||||||
Serializable extracted = (Serializable) fetchToHydratedStateExtractorMap.get( fetch )
|
Serializable extracted = (Serializable) fetchToHydratedStateExtractorMap.get( fetch )
|
||||||
.extract( ownerIdentifierHydratedState );
|
.extract( ownerIdentifierHydratedState );
|
||||||
identifierResolutionContext.registerHydratedForm( extracted );
|
fetchEntityReferenceProcessingState.registerIdentifierHydratedForm( extracted );
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -819,13 +810,40 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final String[] columnNames;
|
||||||
|
if ( EntityFetch.class.isInstance( entityReference )
|
||||||
|
&& !FetchStrategyHelper.isJoinFetched( ((EntityFetch) entityReference).getFetchStrategy() ) ) {
|
||||||
|
final EntityFetch fetch = (EntityFetch) entityReference;
|
||||||
|
final FetchOwner fetchOwner = fetch.getOwner();
|
||||||
|
if ( EntityReference.class.isInstance( fetchOwner ) ) {
|
||||||
|
throw new NotYetImplementedException();
|
||||||
|
// final EntityReference ownerEntityReference = (EntityReference) fetchOwner;
|
||||||
|
// final EntityAliases ownerEntityAliases = context.getAliasResolutionContext()
|
||||||
|
// .resolveEntityColumnAliases( ownerEntityReference );
|
||||||
|
// final int propertyIndex = ownerEntityReference.getEntityPersister()
|
||||||
|
// .getEntityMetamodel()
|
||||||
|
// .getPropertyIndex( fetch.getOwnerPropertyName() );
|
||||||
|
// columnNames = ownerEntityAliases.getSuffixedPropertyAliases()[ propertyIndex ];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// todo : better message here...
|
||||||
|
throw new WalkingException( "Cannot locate association column names" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
columnNames = context.getAliasResolutionContext()
|
||||||
|
.resolveAliases( entityReference )
|
||||||
|
.getColumnAliases()
|
||||||
|
.getSuffixedKeyAliases();
|
||||||
|
}
|
||||||
|
|
||||||
final Object hydratedIdentifierState = entityReference.getEntityPersister().getIdentifierType().hydrate(
|
final Object hydratedIdentifierState = entityReference.getEntityPersister().getIdentifierType().hydrate(
|
||||||
resultSet,
|
resultSet,
|
||||||
context.getLoadQueryAliasResolutionContext().resolveEntityColumnAliases( entityReference ).getSuffixedKeyAliases(),
|
columnNames,
|
||||||
context.getSession(),
|
context.getSession(),
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
context.getIdentifierResolutionContext( entityReference ).registerHydratedForm( hydratedIdentifierState );
|
context.getProcessingState( entityReference ).registerIdentifierHydratedForm( hydratedIdentifierState );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -834,9 +852,9 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
|
||||||
resolveIdentifierFetch( resultSet, context, fetch );
|
resolveIdentifierFetch( resultSet, context, fetch );
|
||||||
}
|
}
|
||||||
|
|
||||||
final IdentifierResolutionContext ownerIdentifierResolutionContext =
|
final ResultSetProcessingContext.EntityReferenceProcessingState ownerEntityReferenceProcessingState =
|
||||||
context.getIdentifierResolutionContext( entityReference );
|
context.getProcessingState( entityReference );
|
||||||
Object hydratedState = ownerIdentifierResolutionContext.getHydratedForm();
|
Object hydratedState = ownerEntityReferenceProcessingState.getIdentifierHydratedForm();
|
||||||
Serializable resolvedId = (Serializable) entityReference.getEntityPersister()
|
Serializable resolvedId = (Serializable) entityReference.getEntityPersister()
|
||||||
.getIdentifierType()
|
.getIdentifierType()
|
||||||
.resolve( hydratedState, context.getSession(), null );
|
.resolve( hydratedState, context.getSession(), null );
|
||||||
|
@ -850,14 +868,14 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
|
||||||
Fetch fetch) throws SQLException {
|
Fetch fetch) throws SQLException {
|
||||||
if ( fetch instanceof EntityFetch ) {
|
if ( fetch instanceof EntityFetch ) {
|
||||||
EntityFetch entityFetch = (EntityFetch) fetch;
|
EntityFetch entityFetch = (EntityFetch) fetch;
|
||||||
final IdentifierResolutionContext identifierResolutionContext =
|
final ResultSetProcessingContext.EntityReferenceProcessingState entityReferenceProcessingState =
|
||||||
context.getIdentifierResolutionContext( entityFetch );
|
context.getProcessingState( entityFetch );
|
||||||
if ( identifierResolutionContext.getEntityKey() != null ) {
|
if ( entityReferenceProcessingState.getEntityKey() != null ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityKey fetchKey = entityFetch.resolveInIdentifier( resultSet, context );
|
EntityKey fetchKey = entityFetch.resolveInIdentifier( resultSet, context );
|
||||||
identifierResolutionContext.registerEntityKey( fetchKey );
|
entityReferenceProcessingState.registerEntityKey( fetchKey );
|
||||||
}
|
}
|
||||||
else if ( fetch instanceof CompositeFetch ) {
|
else if ( fetch instanceof CompositeFetch ) {
|
||||||
for ( Fetch subFetch : ( (CompositeFetch) fetch ).getFetches() ) {
|
for ( Fetch subFetch : ( (CompositeFetch) fetch ).getFetches() ) {
|
||||||
|
|
|
@ -29,7 +29,8 @@ import org.hibernate.persister.entity.EntityPersister;
|
||||||
import org.hibernate.persister.walking.spi.MetadataDrivenModelGraphVisitor;
|
import org.hibernate.persister.walking.spi.MetadataDrivenModelGraphVisitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Coordinates building of a {@link org.hibernate.loader.plan.spi.LoadPlan} between the {@link org.hibernate.persister.walking.spi.MetadataDrivenModelGraphVisitor} and
|
* Coordinates building of a {@link org.hibernate.loader.plan.spi.LoadPlan} between the
|
||||||
|
* {@link org.hibernate.persister.walking.spi.MetadataDrivenModelGraphVisitor} and
|
||||||
* {@link LoadPlanBuilderStrategy}
|
* {@link LoadPlanBuilderStrategy}
|
||||||
*
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
|
|
|
@ -27,7 +27,8 @@ import org.hibernate.loader.plan.spi.LoadPlan;
|
||||||
import org.hibernate.persister.walking.spi.AssociationVisitationStrategy;
|
import org.hibernate.persister.walking.spi.AssociationVisitationStrategy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specialized {@link org.hibernate.persister.walking.spi.AssociationVisitationStrategy} implementation for building {@link org.hibernate.loader.plan.spi.LoadPlan} instances.
|
* Specialized {@link org.hibernate.persister.walking.spi.AssociationVisitationStrategy} implementation for
|
||||||
|
* building {@link org.hibernate.loader.plan.spi.LoadPlan} instances.
|
||||||
*
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -26,8 +26,15 @@ package org.hibernate.loader.plan.spi.build;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Provides access to context needed in building a LoadPlan.
|
||||||
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public interface LoadPlanBuildingContext {
|
public interface LoadPlanBuildingContext {
|
||||||
|
/**
|
||||||
|
* Access to the SessionFactory
|
||||||
|
*
|
||||||
|
* @return The SessionFactory
|
||||||
|
*/
|
||||||
public SessionFactoryImplementor getSessionFactory();
|
public SessionFactoryImplementor getSessionFactory();
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.loader.plan.spi.visit;
|
package org.hibernate.loader.plan.spi.visit;
|
||||||
|
|
||||||
|
import org.hibernate.loader.plan.spi.AnyFetch;
|
||||||
import org.hibernate.loader.plan.spi.CollectionFetch;
|
import org.hibernate.loader.plan.spi.CollectionFetch;
|
||||||
import org.hibernate.loader.plan.spi.CollectionReturn;
|
import org.hibernate.loader.plan.spi.CollectionReturn;
|
||||||
import org.hibernate.loader.plan.spi.CompositeFetch;
|
import org.hibernate.loader.plan.spi.CompositeFetch;
|
||||||
|
@ -115,4 +116,14 @@ public class DelegatedLoadPlanVisitationStrategy implements LoadPlanVisitationSt
|
||||||
public void finishingCompositeFetch(CompositeFetch fetch) {
|
public void finishingCompositeFetch(CompositeFetch fetch) {
|
||||||
returnGraphVisitationStrategy.finishingCompositeFetch( fetch );
|
returnGraphVisitationStrategy.finishingCompositeFetch( fetch );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startingAnyFetch(AnyFetch fetch) {
|
||||||
|
returnGraphVisitationStrategy.startingAnyFetch( fetch );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finishingAnyFetch(AnyFetch fetch) {
|
||||||
|
returnGraphVisitationStrategy.finishingAnyFetch( fetch );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.loader.plan.spi.visit;
|
package org.hibernate.loader.plan.spi.visit;
|
||||||
|
|
||||||
|
import org.hibernate.loader.plan.spi.AnyFetch;
|
||||||
import org.hibernate.loader.plan.spi.CollectionFetch;
|
import org.hibernate.loader.plan.spi.CollectionFetch;
|
||||||
import org.hibernate.loader.plan.spi.CollectionReturn;
|
import org.hibernate.loader.plan.spi.CollectionReturn;
|
||||||
import org.hibernate.loader.plan.spi.CompositeFetch;
|
import org.hibernate.loader.plan.spi.CompositeFetch;
|
||||||
|
@ -98,4 +99,12 @@ public class LoadPlanVisitationStrategyAdapter implements LoadPlanVisitationStra
|
||||||
@Override
|
@Override
|
||||||
public void finishingCompositeFetch(CompositeFetch fetch) {
|
public void finishingCompositeFetch(CompositeFetch fetch) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startingAnyFetch(AnyFetch fetch) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finishingAnyFetch(AnyFetch fetch) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.loader.plan.spi.visit;
|
package org.hibernate.loader.plan.spi.visit;
|
||||||
|
|
||||||
|
import org.hibernate.loader.plan.spi.AnyFetch;
|
||||||
import org.hibernate.loader.plan.spi.CollectionFetch;
|
import org.hibernate.loader.plan.spi.CollectionFetch;
|
||||||
import org.hibernate.loader.plan.spi.CollectionReturn;
|
import org.hibernate.loader.plan.spi.CollectionReturn;
|
||||||
import org.hibernate.loader.plan.spi.CompositeFetch;
|
import org.hibernate.loader.plan.spi.CompositeFetch;
|
||||||
|
@ -136,4 +137,18 @@ public interface ReturnGraphVisitationStrategy {
|
||||||
* @param fetch The composite fetch
|
* @param fetch The composite fetch
|
||||||
*/
|
*/
|
||||||
public void finishingCompositeFetch(CompositeFetch fetch);
|
public void finishingCompositeFetch(CompositeFetch fetch);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notification we are starting the processing of a ANY fetch
|
||||||
|
*
|
||||||
|
* @param fetch The ANY fetch
|
||||||
|
*/
|
||||||
|
public void startingAnyFetch(AnyFetch fetch);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notification that we are finishing up the processing of a ANY fetch
|
||||||
|
*
|
||||||
|
* @param fetch The ANY fetch
|
||||||
|
*/
|
||||||
|
public void finishingAnyFetch(AnyFetch fetch);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.loader.plan.spi.visit;
|
package org.hibernate.loader.plan.spi.visit;
|
||||||
|
|
||||||
|
import org.hibernate.loader.plan.spi.AnyFetch;
|
||||||
import org.hibernate.loader.plan.spi.CollectionFetch;
|
import org.hibernate.loader.plan.spi.CollectionFetch;
|
||||||
import org.hibernate.loader.plan.spi.CollectionReturn;
|
import org.hibernate.loader.plan.spi.CollectionReturn;
|
||||||
import org.hibernate.loader.plan.spi.CompositeFetch;
|
import org.hibernate.loader.plan.spi.CompositeFetch;
|
||||||
|
@ -89,4 +90,12 @@ public class ReturnGraphVisitationStrategyAdapter implements ReturnGraphVisitati
|
||||||
@Override
|
@Override
|
||||||
public void finishingCompositeFetch(CompositeFetch fetch) {
|
public void finishingCompositeFetch(CompositeFetch fetch) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startingAnyFetch(AnyFetch fetch) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finishingAnyFetch(AnyFetch fetch) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ public class ReturnGraphVisitor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void visit(List<Return> rootReturns) {
|
public void visit(List<? extends Return> rootReturns) {
|
||||||
for ( Return rootReturn : rootReturns ) {
|
for ( Return rootReturn : rootReturns ) {
|
||||||
visitRootReturn( rootReturn );
|
visitRootReturn( rootReturn );
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ package org.hibernate.loader.spi;
|
||||||
import org.hibernate.loader.plan.spi.LoadPlan;
|
import org.hibernate.loader.plan.spi.LoadPlan;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An advisor that can be made available to the {@link ResultSetProcessor} and {@link ScrollableResultSetProcessor}.
|
* An advisor that can be made available to the {@link org.hibernate.loader.plan.exec.process.spi.ResultSetProcessor} and {@link org.hibernate.loader.plan.exec.process.spi.ScrollableResultSetProcessor}.
|
||||||
*
|
*
|
||||||
* The processors consult with the advisor, if one is provided, as a means to influence the load plan, meaning that
|
* The processors consult with the advisor, if one is provided, as a means to influence the load plan, meaning that
|
||||||
* the advisor might add fetches. A caveat is that any added fetches cannot be join fetches (they cannot alter the
|
* the advisor might add fetches. A caveat is that any added fetches cannot be join fetches (they cannot alter the
|
||||||
|
|
|
@ -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.PropertyMapping;
|
||||||
import org.hibernate.persister.entity.Queryable;
|
import org.hibernate.persister.entity.Queryable;
|
||||||
import org.hibernate.persister.walking.internal.CompositionSingularSubAttributesHelper;
|
import org.hibernate.persister.walking.internal.CompositionSingularSubAttributesHelper;
|
||||||
|
import org.hibernate.persister.walking.internal.StandardAnyTypeDefinition;
|
||||||
|
import org.hibernate.persister.walking.spi.AnyMappingDefinition;
|
||||||
import org.hibernate.persister.walking.spi.AttributeDefinition;
|
import org.hibernate.persister.walking.spi.AttributeDefinition;
|
||||||
import org.hibernate.persister.walking.spi.AttributeSource;
|
import org.hibernate.persister.walking.spi.AttributeSource;
|
||||||
import org.hibernate.persister.walking.spi.CollectionDefinition;
|
import org.hibernate.persister.walking.spi.CollectionDefinition;
|
||||||
|
@ -89,6 +91,7 @@ import org.hibernate.persister.walking.spi.CollectionIndexDefinition;
|
||||||
import org.hibernate.persister.walking.spi.CompositeCollectionElementDefinition;
|
import org.hibernate.persister.walking.spi.CompositeCollectionElementDefinition;
|
||||||
import org.hibernate.persister.walking.spi.CompositionDefinition;
|
import org.hibernate.persister.walking.spi.CompositionDefinition;
|
||||||
import org.hibernate.persister.walking.spi.EntityDefinition;
|
import org.hibernate.persister.walking.spi.EntityDefinition;
|
||||||
|
import org.hibernate.persister.walking.spi.WalkingException;
|
||||||
import org.hibernate.pretty.MessageHelper;
|
import org.hibernate.pretty.MessageHelper;
|
||||||
import org.hibernate.sql.Alias;
|
import org.hibernate.sql.Alias;
|
||||||
import org.hibernate.sql.SelectFragment;
|
import org.hibernate.sql.SelectFragment;
|
||||||
|
@ -100,6 +103,7 @@ import org.hibernate.sql.ordering.antlr.FormulaReference;
|
||||||
import org.hibernate.sql.ordering.antlr.OrderByAliasResolver;
|
import org.hibernate.sql.ordering.antlr.OrderByAliasResolver;
|
||||||
import org.hibernate.sql.ordering.antlr.OrderByTranslation;
|
import org.hibernate.sql.ordering.antlr.OrderByTranslation;
|
||||||
import org.hibernate.sql.ordering.antlr.SqlValueReference;
|
import org.hibernate.sql.ordering.antlr.SqlValueReference;
|
||||||
|
import org.hibernate.type.AnyType;
|
||||||
import org.hibernate.type.AssociationType;
|
import org.hibernate.type.AssociationType;
|
||||||
import org.hibernate.type.CollectionType;
|
import org.hibernate.type.CollectionType;
|
||||||
import org.hibernate.type.CompositeType;
|
import org.hibernate.type.CompositeType;
|
||||||
|
@ -2017,10 +2021,19 @@ public abstract class AbstractCollectionPersister
|
||||||
return getElementType();
|
return getElementType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnyMappingDefinition toAnyMappingDefinition() {
|
||||||
|
final Type type = getType();
|
||||||
|
if ( ! type.isAnyType() ) {
|
||||||
|
throw new WalkingException( "Cannot treat collection element type as ManyToAny" );
|
||||||
|
}
|
||||||
|
return new StandardAnyTypeDefinition( (AnyType) type, isLazy() || isExtraLazy() );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EntityDefinition toEntityDefinition() {
|
public EntityDefinition toEntityDefinition() {
|
||||||
if ( getType().isComponentType() ) {
|
if ( getType().isComponentType() ) {
|
||||||
throw new IllegalStateException( "Cannot treat composite collection element type as entity" );
|
throw new WalkingException( "Cannot treat composite collection element type as entity" );
|
||||||
}
|
}
|
||||||
return getElementPersister();
|
return getElementPersister();
|
||||||
}
|
}
|
||||||
|
@ -2029,7 +2042,7 @@ public abstract class AbstractCollectionPersister
|
||||||
public CompositeCollectionElementDefinition toCompositeElementDefinition() {
|
public CompositeCollectionElementDefinition toCompositeElementDefinition() {
|
||||||
|
|
||||||
if ( ! getType().isComponentType() ) {
|
if ( ! getType().isComponentType() ) {
|
||||||
throw new IllegalStateException( "Cannot treat entity collection element type as composite" );
|
throw new WalkingException( "Cannot treat entity collection element type as composite" );
|
||||||
}
|
}
|
||||||
|
|
||||||
return new CompositeCollectionElementDefinition() {
|
return new CompositeCollectionElementDefinition() {
|
||||||
|
@ -2043,6 +2056,11 @@ public abstract class AbstractCollectionPersister
|
||||||
return getElementType();
|
return getElementType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isNullable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AttributeSource getSource() {
|
public AttributeSource getSource() {
|
||||||
// TODO: what if this is a collection w/in an encapsulated composition attribute?
|
// TODO: what if this is a collection w/in an encapsulated composition attribute?
|
||||||
|
|
|
@ -29,6 +29,7 @@ import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -2495,6 +2496,15 @@ public abstract class AbstractEntityPersister
|
||||||
.buildLoader( this, batchSize, lockOptions, getFactory(), loadQueryInfluencers );
|
.buildLoader( this, batchSize, lockOptions, getFactory(), loadQueryInfluencers );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used internally to create static loaders. These are the default set of loaders used to handle get()/load()
|
||||||
|
* processing. lock() handling is done by the LockingStrategy instances (see {@link #getLocker})
|
||||||
|
*
|
||||||
|
* @param lockMode The lock mode to apply to the thing being loaded.
|
||||||
|
* @return
|
||||||
|
*
|
||||||
|
* @throws MappingException
|
||||||
|
*/
|
||||||
protected UniqueEntityLoader createEntityLoader(LockMode lockMode) throws MappingException {
|
protected UniqueEntityLoader createEntityLoader(LockMode lockMode) throws MappingException {
|
||||||
return createEntityLoader( lockMode, LoadQueryInfluencers.NONE );
|
return createEntityLoader( lockMode, LoadQueryInfluencers.NONE );
|
||||||
}
|
}
|
||||||
|
@ -3765,8 +3775,24 @@ public abstract class AbstractEntityPersister
|
||||||
return StringHelper.generateAlias( getEntityName() );
|
return StringHelper.generateAlias( getEntityName() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post-construct is a callback for AbstractEntityPersister subclasses to call after they are all done with their
|
||||||
|
* constructor processing. It allows AbstractEntityPersister to extend its construction after all subclass-specific
|
||||||
|
* details have been handled.
|
||||||
|
*
|
||||||
|
* @param mapping The mapping
|
||||||
|
*
|
||||||
|
* @throws MappingException Indicates a problem accessing the Mapping
|
||||||
|
*/
|
||||||
protected void postConstruct(Mapping mapping) throws MappingException {
|
protected void postConstruct(Mapping mapping) throws MappingException {
|
||||||
initPropertyPaths(mapping);
|
initPropertyPaths( mapping );
|
||||||
|
|
||||||
|
//doLateInit();
|
||||||
|
prepareEntityIdentifierDefinition();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doLateInit() {
|
||||||
|
generateEntityDefinition();
|
||||||
|
|
||||||
//insert/update/delete SQL
|
//insert/update/delete SQL
|
||||||
final int joinSpan = getTableSpan();
|
final int joinSpan = getTableSpan();
|
||||||
|
@ -3824,11 +3850,11 @@ public abstract class AbstractEntityPersister
|
||||||
}
|
}
|
||||||
|
|
||||||
logStaticSQL();
|
logStaticSQL();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void postInstantiate() throws MappingException {
|
public final void postInstantiate() throws MappingException {
|
||||||
generateEntityDefinition();
|
doLateInit();
|
||||||
|
// generateEntityDefinition();
|
||||||
|
|
||||||
createLoaders();
|
createLoaders();
|
||||||
createUniqueKeyLoaders();
|
createUniqueKeyLoaders();
|
||||||
|
@ -5112,6 +5138,9 @@ public abstract class AbstractEntityPersister
|
||||||
|
|
||||||
|
|
||||||
private void prepareEntityIdentifierDefinition() {
|
private void prepareEntityIdentifierDefinition() {
|
||||||
|
if ( entityIdentifierDefinition != null ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
final Type idType = getIdentifierType();
|
final Type idType = getIdentifierType();
|
||||||
|
|
||||||
if ( !idType.isComponentType() ) {
|
if ( !idType.isComponentType() ) {
|
||||||
|
@ -5131,35 +5160,119 @@ public abstract class AbstractEntityPersister
|
||||||
EntityIdentifierDefinitionHelper.buildNonEncapsulatedCompositeIdentifierDefinition( this );
|
EntityIdentifierDefinitionHelper.buildNonEncapsulatedCompositeIdentifierDefinition( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void collectAttributeDefinitions() {
|
private void collectAttributeDefinitions(List<AttributeDefinition> definitions, EntityMetamodel metamodel) {
|
||||||
// todo : leverage the attribute definitions housed on EntityMetamodel
|
for ( int i = 0; i < metamodel.getPropertySpan(); i++ ) {
|
||||||
// for that to work, we'd have to be able to walk our super entity persister(s)
|
definitions.add( metamodel.getProperties()[i] );
|
||||||
attributeDefinitions = new Iterable<AttributeDefinition>() {
|
}
|
||||||
@Override
|
|
||||||
public Iterator<AttributeDefinition> iterator() {
|
|
||||||
return new Iterator<AttributeDefinition>() {
|
|
||||||
// private final int numberOfAttributes = countSubclassProperties();
|
|
||||||
private final int numberOfAttributes = entityMetamodel.getPropertySpan();
|
|
||||||
private int currentAttributeNumber = 0;
|
|
||||||
|
|
||||||
@Override
|
// see if there are any subclass persisters...
|
||||||
public boolean hasNext() {
|
final Set<String> subClassEntityNames = metamodel.getSubclassEntityNames();
|
||||||
return currentAttributeNumber < numberOfAttributes;
|
if ( subClassEntityNames == null ) {
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
// see if we can find the persisters...
|
||||||
public AttributeDefinition next() {
|
for ( String subClassEntityName : subClassEntityNames ) {
|
||||||
final int attributeNumber = currentAttributeNumber;
|
if ( metamodel.getName().equals( subClassEntityName ) ) {
|
||||||
currentAttributeNumber++;
|
// skip it
|
||||||
return entityMetamodel.getProperties()[ attributeNumber ];
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void remove() {
|
|
||||||
throw new UnsupportedOperationException( "Remove operation not supported here" );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
};
|
try {
|
||||||
|
final EntityPersister subClassEntityPersister = factory.getEntityPersister( subClassEntityName );
|
||||||
|
collectAttributeDefinitions( definitions, subClassEntityPersister.getEntityMetamodel() );
|
||||||
|
}
|
||||||
|
catch (MappingException e) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
String.format(
|
||||||
|
"Could not locate subclass EntityPersister [%s] while processing EntityPersister [%s]",
|
||||||
|
subClassEntityName,
|
||||||
|
metamodel.getName()
|
||||||
|
),
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void collectAttributeDefinitions() {
|
||||||
|
// todo : I think this works purely based on luck atm
|
||||||
|
// specifically in terms of the sub/super class entity persister(s) being available. Bit of chicken-egg
|
||||||
|
// problem there:
|
||||||
|
// * If I do this during postConstruct (as it is now), it works as long as the
|
||||||
|
// super entity persister is already registered, but I don't think that is necessarily true.
|
||||||
|
// * If I do this during postInstantiate then lots of stuff in postConstruct breaks if we want
|
||||||
|
// to try and drive SQL generation on these (which we do ultimately). A possible solution there
|
||||||
|
// would be to delay all SQL generation until postInstantiate
|
||||||
|
|
||||||
|
List<AttributeDefinition> attributeDefinitions = new ArrayList<AttributeDefinition>();
|
||||||
|
collectAttributeDefinitions( attributeDefinitions, getEntityMetamodel() );
|
||||||
|
|
||||||
|
|
||||||
|
// EntityMetamodel currentEntityMetamodel = this.getEntityMetamodel();
|
||||||
|
// while ( currentEntityMetamodel != null ) {
|
||||||
|
// for ( int i = 0; i < currentEntityMetamodel.getPropertySpan(); i++ ) {
|
||||||
|
// attributeDefinitions.add( currentEntityMetamodel.getProperties()[i] );
|
||||||
|
// }
|
||||||
|
// // see if there is a super class EntityMetamodel
|
||||||
|
// final String superEntityName = currentEntityMetamodel.getSuperclass();
|
||||||
|
// if ( superEntityName != null ) {
|
||||||
|
// currentEntityMetamodel = factory.getEntityPersister( superEntityName ).getEntityMetamodel();
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
// currentEntityMetamodel = null;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
this.attributeDefinitions = Collections.unmodifiableList( attributeDefinitions );
|
||||||
|
// // todo : leverage the attribute definitions housed on EntityMetamodel
|
||||||
|
// // for that to work, we'd have to be able to walk our super entity persister(s)
|
||||||
|
// this.attributeDefinitions = new Iterable<AttributeDefinition>() {
|
||||||
|
// @Override
|
||||||
|
// public Iterator<AttributeDefinition> iterator() {
|
||||||
|
// return new Iterator<AttributeDefinition>() {
|
||||||
|
//// private final int numberOfAttributes = countSubclassProperties();
|
||||||
|
//// private final int numberOfAttributes = entityMetamodel.getPropertySpan();
|
||||||
|
//
|
||||||
|
// EntityMetamodel currentEntityMetamodel = entityMetamodel;
|
||||||
|
// int numberOfAttributesInCurrentEntityMetamodel = currentEntityMetamodel.getPropertySpan();
|
||||||
|
//
|
||||||
|
// private int currentAttributeNumber;
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public boolean hasNext() {
|
||||||
|
// return currentEntityMetamodel != null
|
||||||
|
// && currentAttributeNumber < numberOfAttributesInCurrentEntityMetamodel;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public AttributeDefinition next() {
|
||||||
|
// final int attributeNumber = currentAttributeNumber;
|
||||||
|
// currentAttributeNumber++;
|
||||||
|
// final AttributeDefinition next = currentEntityMetamodel.getProperties()[ attributeNumber ];
|
||||||
|
//
|
||||||
|
// if ( currentAttributeNumber >= numberOfAttributesInCurrentEntityMetamodel ) {
|
||||||
|
// // see if there is a super class EntityMetamodel
|
||||||
|
// final String superEntityName = currentEntityMetamodel.getSuperclass();
|
||||||
|
// if ( superEntityName != null ) {
|
||||||
|
// currentEntityMetamodel = factory.getEntityPersister( superEntityName ).getEntityMetamodel();
|
||||||
|
// if ( currentEntityMetamodel != null ) {
|
||||||
|
// numberOfAttributesInCurrentEntityMetamodel = currentEntityMetamodel.getPropertySpan();
|
||||||
|
// currentAttributeNumber = 0;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return next;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public void remove() {
|
||||||
|
// throw new UnsupportedOperationException( "Remove operation not supported here" );
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue