HHH-8629 : Integrate LoadPlans into collection initializers

This commit is contained in:
Gail Badner 2013-10-25 12:54:59 -07:00
parent 0e4c2a9ed5
commit e1eef18d62
44 changed files with 2321 additions and 1221 deletions

View File

@ -44,7 +44,8 @@ public abstract class BatchingCollectionInitializerBuilder {
return DynamicBatchingCollectionInitializerBuilder.INSTANCE; return DynamicBatchingCollectionInitializerBuilder.INSTANCE;
} }
default: { default: {
return LegacyBatchingCollectionInitializerBuilder.INSTANCE; return org.hibernate.loader.collection.plan.LegacyBatchingCollectionInitializerBuilder.INSTANCE;
//return LegacyBatchingCollectionInitializerBuilder.INSTANCE;
} }
} }
} }
@ -67,7 +68,7 @@ public abstract class BatchingCollectionInitializerBuilder {
LoadQueryInfluencers influencers) { LoadQueryInfluencers influencers) {
if ( maxBatchSize <= 1 ) { if ( maxBatchSize <= 1 ) {
// no batching // no batching
return new BasicCollectionLoader( persister, factory, influencers ); return buildNonBatchingLoader( persister, factory, influencers );
} }
return createRealBatchingCollectionInitializer( persister, maxBatchSize, factory, influencers ); return createRealBatchingCollectionInitializer( persister, maxBatchSize, factory, influencers );
@ -98,7 +99,7 @@ public abstract class BatchingCollectionInitializerBuilder {
LoadQueryInfluencers influencers) { LoadQueryInfluencers influencers) {
if ( maxBatchSize <= 1 ) { if ( maxBatchSize <= 1 ) {
// no batching // no batching
return new OneToManyLoader( persister, factory, influencers ); return buildNonBatchingLoader( persister, factory, influencers );
} }
return createRealBatchingOneToManyInitializer( persister, maxBatchSize, factory, influencers ); return createRealBatchingOneToManyInitializer( persister, maxBatchSize, factory, influencers );
@ -109,4 +110,13 @@ public abstract class BatchingCollectionInitializerBuilder {
int maxBatchSize, int maxBatchSize,
SessionFactoryImplementor factory, SessionFactoryImplementor factory,
LoadQueryInfluencers influencers); LoadQueryInfluencers influencers);
protected CollectionInitializer buildNonBatchingLoader(
QueryableCollection persister,
SessionFactoryImplementor factory,
LoadQueryInfluencers influencers) {
return persister.isOneToMany() ?
new OneToManyLoader( persister, factory, influencers ) :
new BasicCollectionLoader( persister, factory, influencers );
}
} }

View File

@ -41,7 +41,7 @@ public class LegacyBatchingCollectionInitializerBuilder extends BatchingCollecti
public static final LegacyBatchingCollectionInitializerBuilder INSTANCE = new LegacyBatchingCollectionInitializerBuilder(); public static final LegacyBatchingCollectionInitializerBuilder INSTANCE = new LegacyBatchingCollectionInitializerBuilder();
@Override @Override
public CollectionInitializer createRealBatchingCollectionInitializer( protected CollectionInitializer createRealBatchingCollectionInitializer(
QueryableCollection persister, QueryableCollection persister,
int maxBatchSize, int maxBatchSize,
SessionFactoryImplementor factory, SessionFactoryImplementor factory,
@ -55,7 +55,7 @@ public class LegacyBatchingCollectionInitializerBuilder extends BatchingCollecti
} }
@Override @Override
public CollectionInitializer createRealBatchingOneToManyInitializer( protected CollectionInitializer createRealBatchingOneToManyInitializer(
QueryableCollection persister, QueryableCollection persister,
int maxBatchSize, int maxBatchSize,
SessionFactoryImplementor factory, SessionFactoryImplementor factory,

View File

@ -0,0 +1,47 @@
/*
* 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.collection.plan;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.collection.BatchingCollectionInitializerBuilder;
import org.hibernate.loader.collection.CollectionInitializer;
import org.hibernate.persister.collection.QueryableCollection;
/**
* Base class for LoadPlan-based BatchingCollectionInitializerBuilder implementations. Mainly we handle the common
* "no batching" case here to use the LoadPlan-based CollectionLoader
*
* @author Gail Badner
*/
public abstract class AbstractBatchingCollectionInitializerBuilder extends BatchingCollectionInitializerBuilder {
@Override
protected CollectionInitializer buildNonBatchingLoader(
QueryableCollection persister,
SessionFactoryImplementor factory,
LoadQueryInfluencers influencers) {
return CollectionLoader.forCollection( persister ).withInfluencers( influencers ).byKey();
}
}

View File

@ -0,0 +1,147 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Middleware LLC 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 Middleware LLC.
*
* 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.collection.plan;
import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.hibernate.AssertionFailure;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.loader.collection.CollectionInitializer;
import org.hibernate.loader.plan2.build.internal.FetchStyleLoadPlanBuildingAssociationVisitationStrategy;
import org.hibernate.loader.plan2.build.spi.MetamodelDrivenLoadPlanBuilder;
import org.hibernate.loader.plan2.exec.internal.AbstractLoadPlanBasedLoader;
import org.hibernate.loader.plan2.exec.query.spi.QueryBuildingParameters;
import org.hibernate.loader.plan2.exec.spi.BasicCollectionLoadQueryDetails;
import org.hibernate.loader.plan2.exec.spi.CollectionLoadQueryDetails;
import org.hibernate.loader.plan2.exec.spi.OneToManyLoadQueryDetails;
import org.hibernate.loader.plan2.spi.LoadPlan;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.pretty.MessageHelper;
import org.hibernate.type.Type;
/**
* A CollectionInitializer implementation based on using LoadPlans
*
* @author Gail Badner
*/
public abstract class AbstractLoadPlanBasedCollectionInitializer
extends AbstractLoadPlanBasedLoader implements CollectionInitializer {
private static final CoreMessageLogger log = CoreLogging.messageLogger( AbstractLoadPlanBasedCollectionInitializer.class );
private final QueryableCollection collectionPersister;
private final LoadPlan plan;
private final CollectionLoadQueryDetails staticLoadQuery;
public AbstractLoadPlanBasedCollectionInitializer(
QueryableCollection collectionPersister,
QueryBuildingParameters buildingParameters) {
super( collectionPersister.getFactory() );
this.collectionPersister = collectionPersister;
final FetchStyleLoadPlanBuildingAssociationVisitationStrategy strategy =
new FetchStyleLoadPlanBuildingAssociationVisitationStrategy(
collectionPersister.getFactory(),
buildingParameters.getQueryInfluencers(),
buildingParameters.getLockMode() != null
? buildingParameters.getLockMode()
: buildingParameters.getLockOptions().getLockMode()
);
this.plan = MetamodelDrivenLoadPlanBuilder.buildRootCollectionLoadPlan( strategy, collectionPersister );
this.staticLoadQuery = collectionPersister.isOneToMany() ?
OneToManyLoadQueryDetails.makeForBatching(
plan,
buildingParameters,
collectionPersister.getFactory()
) :
BasicCollectionLoadQueryDetails.makeForBatching(
plan,
buildingParameters,
collectionPersister.getFactory()
);
}
@Override
public void initialize(Serializable id, SessionImplementor session)
throws HibernateException {
if ( log.isDebugEnabled() ) {
log.debugf( "Loading collection: %s",
MessageHelper.collectionInfoString( collectionPersister, id, getFactory() ) );
}
final Serializable[] ids = new Serializable[]{id};
try {
final QueryParameters qp = new QueryParameters();
qp.setPositionalParameterTypes( new Type[]{ collectionPersister.getKeyType() } );
qp.setPositionalParameterValues( ids );
qp.setCollectionKeys( ids );
executeLoad(
session,
qp,
staticLoadQuery,
true,
null
);
}
catch ( SQLException sqle ) {
throw getFactory().getSQLExceptionHelper().convert(
sqle,
"could not initialize a collection: " +
MessageHelper.collectionInfoString( collectionPersister, id, getFactory() ),
staticLoadQuery.getSqlStatement()
);
}
log.debug( "Done loading collection" );
}
protected QueryableCollection collectionPersister() {
return collectionPersister;
}
@Override
protected CollectionLoadQueryDetails getStaticLoadQuery() {
return staticLoadQuery;
}
@Override
protected int[] getNamedParameterLocs(String name) {
throw new AssertionFailure("no named parameters");
}
@Override
protected void autoDiscoverTypes(ResultSet rs) {
throw new AssertionFailure("Auto discover types not supported in this loader");
}
}

View File

@ -0,0 +1,51 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, 2012, 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.collection.plan;
import org.hibernate.loader.collection.CollectionInitializer;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.collection.QueryableCollection;
/**
* The base contract for loaders capable of performing batch-fetch loading of collections using multiple foreign key
* values in the SQL <tt>WHERE</tt> clause.
*
* @author Gavin King
* @author Steve Ebersole
*
* @see org.hibernate.loader.collection.BatchingCollectionInitializerBuilder
* @see org.hibernate.loader.collection.BasicCollectionLoader
* @see org.hibernate.loader.collection.OneToManyLoader
*/
public abstract class BatchingCollectionInitializer implements CollectionInitializer {
private final QueryableCollection collectionPersister;
public BatchingCollectionInitializer(QueryableCollection collectionPersister) {
this.collectionPersister = collectionPersister;
}
public CollectionPersister getCollectionPersister() {
return collectionPersister;
}
}

View File

@ -0,0 +1,129 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Middleware LLC 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 Middleware LLC.
*
* 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.collection.plan;
import java.sql.ResultSet;
import org.jboss.logging.Logger;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.internal.CoreLogging;
import org.hibernate.loader.plan2.exec.query.spi.QueryBuildingParameters;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.type.Type;
/**
* Superclass for loaders that initialize collections
*
* @see org.hibernate.loader.collection.OneToManyLoader
* @see org.hibernate.loader.collection.BasicCollectionLoader
* @author Gavin King
* @author Gail Badner
*/
public class CollectionLoader extends AbstractLoadPlanBasedCollectionInitializer {
private static final Logger log = CoreLogging.logger( CollectionLoader.class );
public static Builder forCollection(QueryableCollection collectionPersister) {
return new Builder( collectionPersister );
}
@Override
protected int[] getNamedParameterLocs(String name) {
return new int[0]; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
protected void autoDiscoverTypes(ResultSet rs) {
//To change body of implemented methods use File | Settings | File Templates.
}
protected static class Builder {
private final QueryableCollection collectionPersister;
private int batchSize = 1;
private LoadQueryInfluencers influencers = LoadQueryInfluencers.NONE;
private Builder(QueryableCollection collectionPersister) {
this.collectionPersister = collectionPersister;
}
public Builder withBatchSize(int batchSize) {
this.batchSize = batchSize;
return this;
}
public Builder withInfluencers(LoadQueryInfluencers influencers) {
this.influencers = influencers;
return this;
}
public CollectionLoader byKey() {
final QueryBuildingParameters buildingParameters = new QueryBuildingParameters() {
@Override
public LoadQueryInfluencers getQueryInfluencers() {
return influencers;
}
@Override
public int getBatchSize() {
return batchSize;
}
@Override
public LockMode getLockMode() {
return LockMode.NONE;
}
@Override
public LockOptions getLockOptions() {
return null;
}
};
return new CollectionLoader( collectionPersister, buildingParameters ) ;
}
}
public CollectionLoader(
QueryableCollection collectionPersister,
QueryBuildingParameters buildingParameters) {
super( collectionPersister, buildingParameters );
if ( log.isDebugEnabled() ) {
log.debugf(
"Static select for collection %s: %s",
collectionPersister.getRole(),
getStaticLoadQuery().getSqlStatement()
);
}
}
protected Type getKeyType() {
return collectionPersister().getKeyType();
}
public String toString() {
return getClass().getName() + '(' + collectionPersister().getRole() + ')';
}
}

View File

@ -0,0 +1,108 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, 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.collection.plan;
import java.io.Serializable;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.loader.Loader;
import org.hibernate.loader.collection.BasicCollectionLoader;
import org.hibernate.loader.collection.CollectionInitializer;
import org.hibernate.loader.collection.OneToManyLoader;
import org.hibernate.persister.collection.QueryableCollection;
/**
* LoadPlan-based implementation of the the legacy batch collection initializer.
*
* @author Steve Ebersole
*/
public class LegacyBatchingCollectionInitializerBuilder extends AbstractBatchingCollectionInitializerBuilder {
public static final LegacyBatchingCollectionInitializerBuilder INSTANCE = new LegacyBatchingCollectionInitializerBuilder();
@Override
public CollectionInitializer createRealBatchingCollectionInitializer(
QueryableCollection persister,
int maxBatchSize,
SessionFactoryImplementor factory,
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
int[] batchSizes = ArrayHelper.getBatchSizes( maxBatchSize );
Loader[] loaders = new Loader[ batchSizes.length ];
for ( int i = 0; i < batchSizes.length; i++ ) {
loaders[i] = new BasicCollectionLoader( persister, batchSizes[i], factory, loadQueryInfluencers );
}
return new LegacyBatchingCollectionInitializer( persister, batchSizes, loaders );
}
@Override
public CollectionInitializer createRealBatchingOneToManyInitializer(
QueryableCollection persister,
int maxBatchSize,
SessionFactoryImplementor factory,
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
final int[] batchSizes = ArrayHelper.getBatchSizes( maxBatchSize );
final Loader[] loaders = new Loader[ batchSizes.length ];
for ( int i = 0; i < batchSizes.length; i++ ) {
loaders[i] = new OneToManyLoader( persister, batchSizes[i], factory, loadQueryInfluencers );
}
return new LegacyBatchingCollectionInitializer( persister, batchSizes, loaders );
}
public static class LegacyBatchingCollectionInitializer extends BatchingCollectionInitializer {
private final int[] batchSizes;
private final Loader[] loaders;
public LegacyBatchingCollectionInitializer(
QueryableCollection persister,
int[] batchSizes,
Loader[] loaders) {
super( persister );
this.batchSizes = batchSizes;
this.loaders = loaders;
}
@Override
public void initialize(Serializable id, SessionImplementor session) throws HibernateException {
Serializable[] batch = session.getPersistenceContext().getBatchFetchQueue()
.getCollectionBatch( getCollectionPersister(), id, batchSizes[0] );
for ( int i=0; i<batchSizes.length-1; i++) {
final int smallBatchSize = batchSizes[i];
if ( batch[smallBatchSize-1]!=null ) {
Serializable[] smallBatch = new Serializable[smallBatchSize];
System.arraycopy(batch, 0, smallBatch, 0, smallBatchSize);
loaders[i].loadCollectionBatch( session, smallBatch, getCollectionPersister().getKeyType() );
return; //EARLY EXIT!
}
}
loaders[batchSizes.length-1].loadCollection( session, id, getCollectionPersister().getKeyType() );
}
}
}

View File

@ -1,37 +1,48 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Middleware LLC 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 Middleware LLC.
*
* 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; package org.hibernate.loader.entity.plan;
import java.io.Serializable; import java.io.Serializable;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map;
import org.hibernate.AssertionFailure; import org.hibernate.AssertionFailure;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.LockOptions; import org.hibernate.LockOptions;
import org.hibernate.ScrollMode;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.pagination.LimitHandler;
import org.hibernate.dialect.pagination.LimitHelper;
import org.hibernate.dialect.pagination.NoopLimitHandler;
import org.hibernate.engine.jdbc.ColumnNameCache;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.QueryParameters; import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.RowSelection;
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.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.plan2.build.internal.FetchStyleLoadPlanBuildingAssociationVisitationStrategy; import org.hibernate.loader.plan2.build.internal.FetchStyleLoadPlanBuildingAssociationVisitationStrategy;
import org.hibernate.loader.plan2.build.spi.MetamodelDrivenLoadPlanBuilder; import org.hibernate.loader.plan2.build.spi.MetamodelDrivenLoadPlanBuilder;
import org.hibernate.loader.plan2.exec.internal.AbstractLoadPlanBasedLoader;
import org.hibernate.loader.plan2.exec.query.spi.NamedParameterContext; import org.hibernate.loader.plan2.exec.query.spi.NamedParameterContext;
import org.hibernate.loader.plan2.exec.query.spi.QueryBuildingParameters; import org.hibernate.loader.plan2.exec.query.spi.QueryBuildingParameters;
import org.hibernate.loader.plan2.exec.spi.EntityLoadQueryDetails; import org.hibernate.loader.plan2.exec.spi.EntityLoadQueryDetails;
@ -48,10 +59,9 @@ import org.hibernate.type.Type;
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityLoader { public abstract class AbstractLoadPlanBasedEntityLoader extends AbstractLoadPlanBasedLoader implements UniqueEntityLoader {
private static final CoreMessageLogger log = CoreLogging.messageLogger( AbstractLoadPlanBasedEntityLoader.class ); private static final CoreMessageLogger log = CoreLogging.messageLogger( AbstractLoadPlanBasedEntityLoader.class );
private final SessionFactoryImplementor factory;
private final OuterJoinLoadable entityPersister; private final OuterJoinLoadable entityPersister;
private final Type uniqueKeyType; private final Type uniqueKeyType;
private final String entityName; private final String entityName;
@ -59,22 +69,21 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
private final LoadPlan plan; private final LoadPlan plan;
private final EntityLoadQueryDetails staticLoadQuery; private final EntityLoadQueryDetails staticLoadQuery;
private ColumnNameCache columnNameCache;
public AbstractLoadPlanBasedEntityLoader( public AbstractLoadPlanBasedEntityLoader(
OuterJoinLoadable entityPersister, OuterJoinLoadable entityPersister,
SessionFactoryImplementor factory, SessionFactoryImplementor factory,
String[] uniqueKeyColumnNames, String[] uniqueKeyColumnNames,
Type uniqueKeyType, Type uniqueKeyType,
QueryBuildingParameters buildingParameters) { QueryBuildingParameters buildingParameters) {
super( factory );
this.entityPersister = entityPersister; this.entityPersister = entityPersister;
this.factory = factory;
this.uniqueKeyType = uniqueKeyType; this.uniqueKeyType = uniqueKeyType;
this.entityName = entityPersister.getEntityName(); this.entityName = entityPersister.getEntityName();
final FetchStyleLoadPlanBuildingAssociationVisitationStrategy strategy = new FetchStyleLoadPlanBuildingAssociationVisitationStrategy( final FetchStyleLoadPlanBuildingAssociationVisitationStrategy strategy = new FetchStyleLoadPlanBuildingAssociationVisitationStrategy(
factory, factory,
buildingParameters.getQueryInfluencers() buildingParameters.getQueryInfluencers(),
buildingParameters.getLockMode()
); );
this.plan = MetamodelDrivenLoadPlanBuilder.buildRootEntityLoadPlan( strategy, entityPersister ); this.plan = MetamodelDrivenLoadPlanBuilder.buildRootEntityLoadPlan( strategy, entityPersister );
@ -86,10 +95,7 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
); );
} }
protected SessionFactoryImplementor getFactory() { @Override
return factory;
}
protected EntityLoadQueryDetails getStaticLoadQuery() { protected EntityLoadQueryDetails getStaticLoadQuery() {
return staticLoadQuery; return staticLoadQuery;
} }
@ -135,7 +141,7 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
); );
} }
catch ( SQLException sqle ) { catch ( SQLException sqle ) {
throw factory.getSQLExceptionHelper().convert( throw getFactory().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() ),
staticLoadQuery.getSqlStatement() staticLoadQuery.getSqlStatement()
@ -156,8 +162,8 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
@Override @Override
public Object load(Serializable id, Object optionalObject, SessionImplementor session, LockOptions lockOptions) { public Object load(Serializable id, Object optionalObject, SessionImplementor session, LockOptions lockOptions) {
Object result = null;
final Object result;
try { try {
final QueryParameters qp = new QueryParameters(); final QueryParameters qp = new QueryParameters();
qp.setPositionalParameterTypes( new Type[] { entityPersister.getIdentifierType() } ); qp.setPositionalParameterTypes( new Type[] { entityPersister.getIdentifierType() } );
@ -177,13 +183,13 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
result = extractEntityResult( results ); result = extractEntityResult( results );
} }
catch ( SQLException sqle ) { catch ( SQLException sqle ) {
throw factory.getSQLExceptionHelper().convert( throw getFactory().getSQLExceptionHelper().convert(
sqle, sqle,
"could not load an entity: " + MessageHelper.infoString( "could not load an entity: " + MessageHelper.infoString(
entityPersister, entityPersister,
id, id,
entityPersister.getIdentifierType(), entityPersister.getIdentifierType(),
factory getFactory()
), ),
staticLoadQuery.getSqlStatement() staticLoadQuery.getSqlStatement()
); );
@ -193,83 +199,6 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
return result; return result;
} }
protected List executeLoad(
SessionImplementor session,
QueryParameters queryParameters,
EntityLoadQueryDetails loadQueryDetails,
boolean returnProxies,
ResultTransformer forcedResultTransformer) throws SQLException {
final List<AfterLoadAction> afterLoadActions = new ArrayList<AfterLoadAction>();
return executeLoad(
session,
queryParameters,
loadQueryDetails,
returnProxies,
forcedResultTransformer,
afterLoadActions
);
}
protected List executeLoad(
SessionImplementor session,
QueryParameters queryParameters,
EntityLoadQueryDetails loadQueryDetails,
boolean returnProxies,
ResultTransformer forcedResultTransformer,
List<AfterLoadAction> afterLoadActions) throws SQLException {
final PersistenceContext persistenceContext = session.getPersistenceContext();
final boolean defaultReadOnlyOrig = persistenceContext.isDefaultReadOnly();
if ( queryParameters.isReadOnlyInitialized() ) {
// The read-only/modifiable mode for the query was explicitly set.
// Temporarily set the default read-only/modifiable setting to the query's setting.
persistenceContext.setDefaultReadOnly( queryParameters.isReadOnly() );
}
else {
// The read-only/modifiable setting for the query was not initialized.
// Use the default read-only/modifiable from the persistence context instead.
queryParameters.setReadOnly( persistenceContext.isDefaultReadOnly() );
}
persistenceContext.beforeLoad();
try {
List results = null;
final String sql = loadQueryDetails.getSqlStatement();
SqlStatementWrapper wrapper = null;
try {
wrapper = executeQueryStatement( sql, queryParameters, false, afterLoadActions, session );
results = loadQueryDetails.getResultSetProcessor().extractResults(
wrapper.getResultSet(),
session,
queryParameters,
new NamedParameterContext() {
@Override
public int[] getNamedParameterLocations(String name) {
return AbstractLoadPlanBasedEntityLoader.this.getNamedParameterLocs( name );
}
},
returnProxies,
queryParameters.isReadOnly(),
forcedResultTransformer,
afterLoadActions
);
}
finally {
if ( wrapper != null ) {
session.getTransactionCoordinator().getJdbcCoordinator().release(
wrapper.getResultSet(),
wrapper.getStatement()
);
}
persistenceContext.afterLoad();
}
persistenceContext.initializeNonLazyCollections();
return results;
}
finally {
// Restore the original default
persistenceContext.setDefaultReadOnly( defaultReadOnlyOrig );
}
}
protected Object extractEntityResult(List results) { protected Object extractEntityResult(List results) {
if ( results.size() == 0 ) { if ( results.size() == 0 ) {
return null; return null;
@ -352,378 +281,11 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
} }
} }
protected int[] getNamedParameterLocs(String name) {
protected SqlStatementWrapper executeQueryStatement(
final QueryParameters queryParameters,
final boolean scroll,
List<AfterLoadAction> afterLoadActions,
final SessionImplementor session) throws SQLException {
return executeQueryStatement( staticLoadQuery.getSqlStatement(), queryParameters, scroll, afterLoadActions, session );
}
protected SqlStatementWrapper executeQueryStatement(
String sqlStatement,
QueryParameters queryParameters,
boolean scroll,
List<AfterLoadAction> afterLoadActions,
SessionImplementor session) throws SQLException {
// Processing query filters.
queryParameters.processFilters( sqlStatement, session );
// Applying LIMIT clause.
final LimitHandler limitHandler = getLimitHandler(
queryParameters.getFilteredSQL(),
queryParameters.getRowSelection()
);
String sql = limitHandler.getProcessedSql();
// Adding locks and comments.
sql = preprocessSQL( sql, queryParameters, getFactory().getDialect(), afterLoadActions );
final PreparedStatement st = prepareQueryStatement( sql, queryParameters, limitHandler, scroll, session );
return new SqlStatementWrapper( st, getResultSet( st, queryParameters.getRowSelection(), limitHandler, queryParameters.hasAutoDiscoverScalarTypes(), session ) );
}
/**
* Build LIMIT clause handler applicable for given selection criteria. Returns {@link org.hibernate.dialect.pagination.NoopLimitHandler} delegate
* if dialect does not support LIMIT expression or processed query does not use pagination.
*
* @param sql Query string.
* @param selection Selection criteria.
* @return LIMIT clause delegate.
*/
protected LimitHandler getLimitHandler(String sql, RowSelection selection) {
final LimitHandler limitHandler = getFactory().getDialect().buildLimitHandler( sql, selection );
return LimitHelper.useLimit( limitHandler, selection ) ? limitHandler : new NoopLimitHandler( sql, selection );
}
private String preprocessSQL(
String sql,
QueryParameters queryParameters,
Dialect dialect,
List<AfterLoadAction> afterLoadActions) {
return getFactory().getSettings().isCommentsEnabled()
? prependComment( sql, queryParameters )
: sql;
}
private String prependComment(String sql, QueryParameters parameters) {
final String comment = parameters.getComment();
if ( comment == null ) {
return sql;
}
else {
return "/* " + comment + " */ " + sql;
}
}
/**
* Obtain a <tt>PreparedStatement</tt> with all parameters pre-bound.
* Bind JDBC-style <tt>?</tt> parameters, named parameters, and
* limit parameters.
*/
protected final PreparedStatement prepareQueryStatement(
final String sql,
final QueryParameters queryParameters,
final LimitHandler limitHandler,
final boolean scroll,
final SessionImplementor session) throws SQLException, HibernateException {
final Dialect dialect = getFactory().getDialect();
final RowSelection selection = queryParameters.getRowSelection();
final boolean useLimit = LimitHelper.useLimit( limitHandler, selection );
final boolean hasFirstRow = LimitHelper.hasFirstRow( selection );
final boolean useLimitOffset = hasFirstRow && useLimit && limitHandler.supportsLimitOffset();
final boolean callable = queryParameters.isCallable();
final ScrollMode scrollMode = getScrollMode( scroll, hasFirstRow, useLimitOffset, queryParameters );
final PreparedStatement st = session.getTransactionCoordinator().getJdbcCoordinator()
.getStatementPreparer().prepareQueryStatement( sql, callable, scrollMode );
try {
int col = 1;
//TODO: can we limit stored procedures ?!
col += limitHandler.bindLimitParametersAtStartOfQuery( st, col );
if (callable) {
col = dialect.registerResultSetOutParameter( (CallableStatement)st, col );
}
col += bindParameterValues( st, queryParameters, col, session );
col += limitHandler.bindLimitParametersAtEndOfQuery( st, col );
limitHandler.setMaxRows( st );
if ( selection != null ) {
if ( selection.getTimeout() != null ) {
st.setQueryTimeout( selection.getTimeout() );
}
if ( selection.getFetchSize() != null ) {
st.setFetchSize( selection.getFetchSize() );
}
}
// handle lock timeout...
final LockOptions lockOptions = queryParameters.getLockOptions();
if ( lockOptions != null ) {
if ( lockOptions.getTimeOut() != LockOptions.WAIT_FOREVER ) {
if ( !dialect.supportsLockTimeouts() ) {
if ( log.isDebugEnabled() ) {
log.debugf(
"Lock timeout [%s] requested but dialect reported to not support lock timeouts",
lockOptions.getTimeOut()
);
}
}
else if ( dialect.isLockTimeoutParameterized() ) {
st.setInt( col++, lockOptions.getTimeOut() );
}
}
}
if ( log.isTraceEnabled() ) {
log.tracev( "Bound [{0}] parameters total", col );
}
}
catch ( SQLException sqle ) {
session.getTransactionCoordinator().getJdbcCoordinator().release( st );
throw sqle;
}
catch ( HibernateException he ) {
session.getTransactionCoordinator().getJdbcCoordinator().release( st );
throw he;
}
return st;
}
protected ScrollMode getScrollMode(boolean scroll, boolean hasFirstRow, boolean useLimitOffSet, QueryParameters queryParameters) {
final boolean canScroll = getFactory().getSettings().isScrollableResultSetsEnabled();
if ( canScroll ) {
if ( scroll ) {
return queryParameters.getScrollMode();
}
if ( hasFirstRow && !useLimitOffSet ) {
return ScrollMode.SCROLL_INSENSITIVE;
}
}
return null;
}
/**
* Bind all parameter values into the prepared statement in preparation
* for execution.
*
* @param statement The JDBC prepared statement
* @param queryParameters The encapsulation of the parameter values to be bound.
* @param startIndex The position from which to start binding parameter values.
* @param session The originating session.
* @return The number of JDBC bind positions actually bound during this method execution.
* @throws SQLException Indicates problems performing the binding.
*/
protected int bindParameterValues(
PreparedStatement statement,
QueryParameters queryParameters,
int startIndex,
SessionImplementor session) throws SQLException {
int span = 0;
span += bindPositionalParameters( statement, queryParameters, startIndex, session );
span += bindNamedParameters( statement, queryParameters.getNamedParameters(), startIndex + span, session );
return span;
}
/**
* Bind positional parameter values to the JDBC prepared statement.
* <p/>
* Positional parameters are those specified by JDBC-style ? parameters
* in the source query. It is (currently) expected that these come
* before any named parameters in the source query.
*
* @param statement The JDBC prepared statement
* @param queryParameters The encapsulation of the parameter values to be bound.
* @param startIndex The position from which to start binding parameter values.
* @param session The originating session.
* @return The number of JDBC bind positions actually bound during this method execution.
* @throws SQLException Indicates problems performing the binding.
* @throws org.hibernate.HibernateException Indicates problems delegating binding to the types.
*/
protected int bindPositionalParameters(
final PreparedStatement statement,
final QueryParameters queryParameters,
final int startIndex,
final SessionImplementor session) throws SQLException, HibernateException {
final Object[] values = queryParameters.getFilteredPositionalParameterValues();
final Type[] types = queryParameters.getFilteredPositionalParameterTypes();
int span = 0;
for ( int i = 0; i < values.length; i++ ) {
types[i].nullSafeSet( statement, values[i], startIndex + span, session );
span += types[i].getColumnSpan( getFactory() );
}
return span;
}
/**
* Bind named parameters to the JDBC prepared statement.
* <p/>
* This is a generic implementation, the problem being that in the
* general case we do not know enough information about the named
* parameters to perform this in a complete manner here. Thus this
* is generally overridden on subclasses allowing named parameters to
* apply the specific behavior. The most usual limitation here is that
* we need to assume the type span is always one...
*
* @param statement The JDBC prepared statement
* @param namedParams A map of parameter names to values
* @param startIndex The position from which to start binding parameter values.
* @param session The originating session.
* @return The number of JDBC bind positions actually bound during this method execution.
* @throws SQLException Indicates problems performing the binding.
* @throws org.hibernate.HibernateException Indicates problems delegating binding to the types.
*/
protected int bindNamedParameters(
final PreparedStatement statement,
final Map namedParams,
final int startIndex,
final SessionImplementor session) throws SQLException, HibernateException {
if ( namedParams != null ) {
// assumes that types are all of span 1
final Iterator itr = namedParams.entrySet().iterator();
final boolean debugEnabled = log.isDebugEnabled();
int result = 0;
while ( itr.hasNext() ) {
final Map.Entry e = (Map.Entry) itr.next();
final String name = (String) e.getKey();
final TypedValue typedval = (TypedValue) e.getValue();
final int[] locs = getNamedParameterLocs( name );
for ( int loc : locs ) {
if ( debugEnabled ) {
log.debugf(
"bindNamedParameters() %s -> %s [%s]",
typedval.getValue(),
name,
loc + startIndex
);
}
typedval.getType().nullSafeSet( statement, typedval.getValue(), loc + startIndex, session );
}
result += locs.length;
}
return result;
}
else {
return 0;
}
}
public int[] getNamedParameterLocs(String name) {
throw new AssertionFailure("no named parameters"); throw new AssertionFailure("no named parameters");
} }
/**
* Execute given <tt>PreparedStatement</tt>, advance to the first result and return SQL <tt>ResultSet</tt>.
*/
protected final ResultSet getResultSet(
final PreparedStatement st,
final RowSelection selection,
final LimitHandler limitHandler,
final boolean autodiscovertypes,
final SessionImplementor session)
throws SQLException, HibernateException {
try {
ResultSet rs = session.getTransactionCoordinator().getJdbcCoordinator().getResultSetReturn().extract( st );
rs = wrapResultSetIfEnabled( rs , session );
if ( !limitHandler.supportsLimitOffset() || !LimitHelper.useLimit( limitHandler, selection ) ) {
advance( rs, selection );
}
if ( autodiscovertypes ) {
autoDiscoverTypes( rs );
}
return rs;
}
catch ( SQLException sqle ) {
session.getTransactionCoordinator().getJdbcCoordinator().release( st );
throw sqle;
}
}
/**
* Advance the cursor to the first required row of the <tt>ResultSet</tt>
*/
protected void advance(final ResultSet rs, final RowSelection selection) throws SQLException {
final int firstRow = LimitHelper.getFirstRow( selection );
if ( firstRow != 0 ) {
if ( getFactory().getSettings().isScrollableResultSetsEnabled() ) {
// we can go straight to the first required row
rs.absolute( firstRow );
}
else {
// we need to step through the rows one row at a time (slow)
for ( int m = 0; m < firstRow; m++ ) {
rs.next();
}
}
}
}
protected void autoDiscoverTypes(ResultSet rs) { protected void autoDiscoverTypes(ResultSet rs) {
throw new AssertionFailure("Auto discover types not supported in this loader"); throw new AssertionFailure("Auto discover types not supported in this loader");
}
private synchronized ResultSet wrapResultSetIfEnabled(final ResultSet rs, final SessionImplementor session) {
// synchronized to avoid multi-thread access issues; defined as method synch to avoid
// potential deadlock issues due to nature of code.
if ( session.getFactory().getSettings().isWrapResultSetsEnabled() ) {
try {
if ( log.isDebugEnabled() ) {
log.debugf( "Wrapping result set [%s]", rs );
}
return session.getFactory()
.getJdbcServices()
.getResultSetWrapper().wrap( rs, retreiveColumnNameToIndexCache( rs ) );
}
catch(SQLException e) {
log.unableToWrapResultSet( e );
return rs;
}
}
else {
return rs;
}
}
private ColumnNameCache retreiveColumnNameToIndexCache(ResultSet rs) throws SQLException {
if ( columnNameCache == null ) {
log.trace( "Building columnName->columnIndex cache" );
columnNameCache = new ColumnNameCache( rs.getMetaData().getColumnCount() );
}
return columnNameCache;
}
/**
* Wrapper class for {@link java.sql.Statement} and associated {@link ResultSet}.
*/
protected static class SqlStatementWrapper {
private final Statement statement;
private final ResultSet resultSet;
private SqlStatementWrapper(Statement statement, ResultSet resultSet) {
this.resultSet = resultSet;
this.statement = statement;
}
public ResultSet getResultSet() {
return resultSet;
}
public Statement getStatement() {
return statement;
}
} }
} }

View File

@ -42,7 +42,6 @@ import org.hibernate.type.Type;
* Can handle batch-loading as well as non-pk, unique-key loading, * Can handle batch-loading as well as non-pk, unique-key loading,
* <p/> * <p/>
* Much is ultimately delegated to its superclass, AbstractLoadPlanBasedEntityLoader. However: * 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>

View File

@ -23,6 +23,7 @@
*/ */
package org.hibernate.loader.plan2.build.internal; package org.hibernate.loader.plan2.build.internal;
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;
@ -47,8 +48,9 @@ public class CascadeStyleLoadPlanBuildingAssociationVisitationStrategy
public CascadeStyleLoadPlanBuildingAssociationVisitationStrategy( public CascadeStyleLoadPlanBuildingAssociationVisitationStrategy(
CascadingAction cascadeActionToMatch, CascadingAction cascadeActionToMatch,
SessionFactoryImplementor sessionFactory, SessionFactoryImplementor sessionFactory,
LoadQueryInfluencers loadQueryInfluencers) { LoadQueryInfluencers loadQueryInfluencers,
super( sessionFactory, loadQueryInfluencers ); LockMode lockMode) {
super( sessionFactory, loadQueryInfluencers, lockMode );
this.cascadeActionToMatch = cascadeActionToMatch; this.cascadeActionToMatch = cascadeActionToMatch;
} }

View File

@ -26,6 +26,7 @@ package org.hibernate.loader.plan2.build.internal;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
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;
@ -50,14 +51,17 @@ public class FetchStyleLoadPlanBuildingAssociationVisitationStrategy
private static final Logger log = CoreLogging.logger( FetchStyleLoadPlanBuildingAssociationVisitationStrategy.class ); private static final Logger log = CoreLogging.logger( FetchStyleLoadPlanBuildingAssociationVisitationStrategy.class );
private final LoadQueryInfluencers loadQueryInfluencers; private final LoadQueryInfluencers loadQueryInfluencers;
private final LockMode lockMode;
private Return rootReturn; private Return rootReturn;
public FetchStyleLoadPlanBuildingAssociationVisitationStrategy( public FetchStyleLoadPlanBuildingAssociationVisitationStrategy(
SessionFactoryImplementor sessionFactory, SessionFactoryImplementor sessionFactory,
LoadQueryInfluencers loadQueryInfluencers) { LoadQueryInfluencers loadQueryInfluencers,
LockMode lockMode) {
super( sessionFactory ); super( sessionFactory );
this.loadQueryInfluencers = loadQueryInfluencers; this.loadQueryInfluencers = loadQueryInfluencers;
this.lockMode = lockMode;
} }
@Override @Override
@ -106,6 +110,10 @@ public class FetchStyleLoadPlanBuildingAssociationVisitationStrategy
protected FetchStrategy adjustJoinFetchIfNeeded( protected FetchStrategy adjustJoinFetchIfNeeded(
AssociationAttributeDefinition attributeDefinition, AssociationAttributeDefinition attributeDefinition,
FetchStrategy fetchStrategy) { FetchStrategy fetchStrategy) {
if ( lockMode.greaterThan( LockMode.READ ) ) {
return new FetchStrategy( fetchStrategy.getTiming(), FetchStyle.SELECT );
}
final Integer maxFetchDepth = sessionFactory().getSettings().getMaximumFetchDepth(); final Integer maxFetchDepth = sessionFactory().getSettings().getMaximumFetchDepth();
if ( maxFetchDepth != null && currentDepth() > maxFetchDepth ) { if ( maxFetchDepth != null && currentDepth() > maxFetchDepth ) {
return new FetchStrategy( fetchStrategy.getTiming(), FetchStyle.SELECT ); return new FetchStrategy( fetchStrategy.getTiming(), FetchStyle.SELECT );
@ -121,7 +129,7 @@ public class FetchStyleLoadPlanBuildingAssociationVisitationStrategy
@Override @Override
protected boolean isTooManyCollections() { protected boolean isTooManyCollections() {
return false; return CollectionReturn.class.isInstance( rootReturn );
} }
// @Override // @Override

View File

@ -35,6 +35,7 @@ import org.hibernate.loader.plan2.spi.EntityReference;
import org.hibernate.loader.plan2.spi.FetchSource; import org.hibernate.loader.plan2.spi.FetchSource;
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.CompositionDefinition;
import org.hibernate.persister.walking.spi.WalkingException; import org.hibernate.persister.walking.spi.WalkingException;
import org.hibernate.type.CompositeType; import org.hibernate.type.CompositeType;
import org.hibernate.type.Type; import org.hibernate.type.Type;
@ -89,14 +90,14 @@ public abstract class AbstractCompositeFetch extends AbstractExpandingFetchSourc
} }
protected CompositeFetch createCompositeFetch( protected CompositeFetch createCompositeFetch(
CompositeType compositeType, CompositionDefinition compositionDefinition,
ExpandingCompositeQuerySpace compositeQuerySpace) { ExpandingCompositeQuerySpace compositeQuerySpace) {
return new NestedCompositeFetchImpl( return new NestedCompositeFetchImpl(
this, this,
compositeType, compositionDefinition.getType(),
compositeQuerySpace, compositeQuerySpace,
allowCollectionFetches, allowCollectionFetches,
getPropertyPath() getPropertyPath().append( compositionDefinition.getName() )
); );
} }

View File

@ -31,6 +31,7 @@ import org.hibernate.loader.plan2.spi.EntityIdentifierDescription;
import org.hibernate.loader.plan2.spi.EntityReference; import org.hibernate.loader.plan2.spi.EntityReference;
import org.hibernate.loader.plan2.spi.Join; import org.hibernate.loader.plan2.spi.Join;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.walking.spi.CompositionDefinition;
import org.hibernate.persister.walking.spi.EncapsulatedEntityIdentifierDefinition; import org.hibernate.persister.walking.spi.EncapsulatedEntityIdentifierDefinition;
import org.hibernate.persister.walking.spi.EntityIdentifierDefinition; import org.hibernate.persister.walking.spi.EntityIdentifierDefinition;
import org.hibernate.type.CompositeType; import org.hibernate.type.CompositeType;
@ -112,14 +113,14 @@ public abstract class AbstractEntityReference extends AbstractExpandingFetchSour
} }
protected CompositeFetch createCompositeFetch( protected CompositeFetch createCompositeFetch(
CompositeType compositeType, CompositionDefinition compositionDefinition,
ExpandingCompositeQuerySpace compositeQuerySpace) { ExpandingCompositeQuerySpace compositeQuerySpace) {
return new CompositeFetchImpl( return new CompositeFetchImpl(
this, this,
compositeType, compositionDefinition.getType(),
compositeQuerySpace, compositeQuerySpace,
true, true,
getPropertyPath() getPropertyPath().append( compositionDefinition.getName() )
); );
} }
} }

View File

@ -174,7 +174,7 @@ public abstract class AbstractExpandingFetchSource implements ExpandingFetchSour
} }
protected abstract CompositeFetch createCompositeFetch( protected abstract CompositeFetch createCompositeFetch(
CompositeType compositeType, CompositionDefinition compositeType,
ExpandingCompositeQuerySpace compositeQuerySpace); ExpandingCompositeQuerySpace compositeQuerySpace);
@Override @Override
@ -187,7 +187,7 @@ public abstract class AbstractExpandingFetchSource implements ExpandingFetchSour
loadPlanBuildingContext.getQuerySpaces().generateImplicitUid() loadPlanBuildingContext.getQuerySpaces().generateImplicitUid()
); );
final CompositeFetch fetch = createCompositeFetch( final CompositeFetch fetch = createCompositeFetch(
attributeDefinition.getType(), attributeDefinition,
(ExpandingCompositeQuerySpace) join.getRightHandSide() (ExpandingCompositeQuerySpace) join.getRightHandSide()
); );
addFetch( fetch ); addFetch( fetch );

View File

@ -23,22 +23,28 @@
*/ */
package org.hibernate.loader.plan2.build.internal.spaces; package org.hibernate.loader.plan2.build.internal.spaces;
import org.hibernate.engine.internal.JoinHelper;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.plan2.build.spi.AbstractQuerySpace; import org.hibernate.loader.plan2.build.spi.AbstractQuerySpace;
import org.hibernate.loader.plan2.build.spi.LoadPlanBuildingContext; import org.hibernate.loader.plan2.build.spi.LoadPlanBuildingContext;
import org.hibernate.loader.plan2.spi.CollectionQuerySpace; import org.hibernate.loader.plan2.spi.CollectionQuerySpace;
import org.hibernate.loader.plan2.spi.Join; import org.hibernate.loader.plan2.spi.Join;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.collection.CollectionPropertyMapping;
import org.hibernate.persister.collection.CollectionPropertyNames;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
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.type.CompositeType; import org.hibernate.type.CompositeType;
import org.hibernate.type.EntityType;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class CollectionQuerySpaceImpl extends AbstractQuerySpace implements CollectionQuerySpace { public class CollectionQuerySpaceImpl extends AbstractQuerySpace implements CollectionQuerySpace {
private final CollectionPersister persister; private final CollectionPersister persister;
private final CollectionPropertyMapping propertyMapping;
public CollectionQuerySpaceImpl( public CollectionQuerySpaceImpl(
CollectionPersister persister, CollectionPersister persister,
@ -48,6 +54,7 @@ public class CollectionQuerySpaceImpl extends AbstractQuerySpace implements Coll
SessionFactoryImplementor sessionFactory) { SessionFactoryImplementor sessionFactory) {
super( uid, Disposition.COLLECTION, querySpaces, canJoinsBeRequired, sessionFactory ); super( uid, Disposition.COLLECTION, querySpaces, canJoinsBeRequired, sessionFactory );
this.persister = persister; this.persister = persister;
this.propertyMapping = new CollectionPropertyMapping( (QueryableCollection) persister );
} }
@Override @Override
@ -57,7 +64,7 @@ public class CollectionQuerySpaceImpl extends AbstractQuerySpace implements Coll
@Override @Override
public PropertyMapping getPropertyMapping() { public PropertyMapping getPropertyMapping() {
return (PropertyMapping) persister; return propertyMapping;
} }
public JoinImpl addIndexEntityJoin( public JoinImpl addIndexEntityJoin(
@ -76,11 +83,10 @@ public class CollectionQuerySpaceImpl extends AbstractQuerySpace implements Coll
final JoinImpl join = new JoinImpl( final JoinImpl join = new JoinImpl(
this, this,
"index", CollectionPropertyNames.COLLECTION_INDICES,
entityQuerySpace, entityQuerySpace,
// not sure this 'rhsColumnNames' bit is correct... Helper.INSTANCE.determineRhsColumnNames( (EntityType) persister.getIndexType(), sessionFactory() ),
( (Queryable) indexPersister ).getKeyColumnNames(), persister.getIndexType(),
null,
required required
); );
internalGetJoins().add( join ); internalGetJoins().add( join );
@ -107,10 +113,10 @@ public class CollectionQuerySpaceImpl extends AbstractQuerySpace implements Coll
final JoinImpl join = new JoinImpl( final JoinImpl join = new JoinImpl(
this, this,
"index", CollectionPropertyNames.COLLECTION_INDICES,
compositeQuerySpace, compositeQuerySpace,
null, ( (QueryableCollection) persister ).getIndexColumnNames(),
null, persister.getIndexType(),
canJoinsBeRequired() canJoinsBeRequired()
); );
internalGetJoins().add( join ); internalGetJoins().add( join );
@ -134,10 +140,10 @@ public class CollectionQuerySpaceImpl extends AbstractQuerySpace implements Coll
final JoinImpl join = new JoinImpl( final JoinImpl join = new JoinImpl(
this, this,
// collection persister maps its elements (through its PropertyMapping contract) as non-prefixed // collection persister maps its elements (through its PropertyMapping contract) as non-prefixed
"id", CollectionPropertyNames.COLLECTION_ELEMENTS,
entityQuerySpace, entityQuerySpace,
( (Queryable) elementPersister ).getKeyColumnNames(), Helper.INSTANCE.determineRhsColumnNames( (EntityType) persister.getElementType(), sessionFactory() ),
null, persister.getElementType(),
canJoinsBeRequired() canJoinsBeRequired()
); );
internalGetJoins().add( join ); internalGetJoins().add( join );
@ -166,10 +172,10 @@ public class CollectionQuerySpaceImpl extends AbstractQuerySpace implements Coll
final JoinImpl join = new JoinImpl( final JoinImpl join = new JoinImpl(
this, this,
// collection persister maps its elements (through its PropertyMapping contract) as non-prefixed // collection persister maps its elements (through its PropertyMapping contract) as non-prefixed
"elements", CollectionPropertyNames.COLLECTION_ELEMENTS,
compositeQuerySpace, compositeQuerySpace,
null, ( (QueryableCollection) persister ).getElementColumnNames(),
null, compositeType,
canJoinsBeRequired() canJoinsBeRequired()
); );
internalGetJoins().add( join ); internalGetJoins().add( join );

View File

@ -91,11 +91,11 @@ public class CompositePropertyMapping implements PropertyMapping {
throw new NullPointerException( "Provided property name cannot be null" ); throw new NullPointerException( "Provided property name cannot be null" );
} }
if ( propertyName.contains( "." ) ) { //if ( propertyName.contains( "." ) ) {
throw new IllegalArgumentException( // throw new IllegalArgumentException(
"Provided property name cannot contain paths (dots) [" + propertyName + "]" // "Provided property name cannot contain paths (dots) [" + propertyName + "]"
); // );
} //}
} }
/** /**

View File

@ -78,7 +78,11 @@ public class CompositeQuerySpaceImpl extends AbstractQuerySpace implements Expan
final boolean required = canJoinsBeRequired() && !compositionDefinition.isNullable(); final boolean required = canJoinsBeRequired() && !compositionDefinition.isNullable();
final CompositeQuerySpaceImpl rhs = new CompositeQuerySpaceImpl( final CompositeQuerySpaceImpl rhs = new CompositeQuerySpaceImpl(
compositeSubPropertyMapping, new CompositePropertyMapping(
compositionDefinition.getType(),
compositeSubPropertyMapping,
compositionDefinition.getName()
),
querySpaceUid, querySpaceUid,
getQuerySpaces(), getQuerySpaces(),
required, required,
@ -90,8 +94,8 @@ public class CompositeQuerySpaceImpl extends AbstractQuerySpace implements Expan
this, this,
propertyPath, propertyPath,
rhs, rhs,
null, getPropertyMapping().toColumns( compositionDefinition.getName() ),
null, compositionDefinition.getType(),
required required
); );
internalGetJoins().add( join ); internalGetJoins().add( join );
@ -123,11 +127,8 @@ public class CompositeQuerySpaceImpl extends AbstractQuerySpace implements Expan
this, this,
propertyPath, propertyPath,
rhs, rhs,
Helper.INSTANCE.determineRhsColumnNames( Helper.INSTANCE.determineRhsColumnNames( (EntityType) attributeDefinition.getType(), sessionFactory() ),
(EntityType) attributeDefinition.getType(), attributeDefinition.getType(),
sessionFactory()
),
(EntityType) attributeDefinition.getType(),
required required
); );
internalGetJoins().add( join ); internalGetJoins().add( join );
@ -156,7 +157,7 @@ public class CompositeQuerySpaceImpl extends AbstractQuerySpace implements Expan
attributeDefinition.getName(), attributeDefinition.getName(),
rhs, rhs,
( (CollectionType) attributeDefinition.getType() ).getAssociatedJoinable( sessionFactory() ).getKeyColumnNames(), ( (CollectionType) attributeDefinition.getType() ).getAssociatedJoinable( sessionFactory() ).getKeyColumnNames(),
(AssociationType) attributeDefinition.getType(), attributeDefinition.getType(),
required required
); );
internalGetJoins().add( join ); internalGetJoins().add( join );

View File

@ -29,6 +29,7 @@ import org.hibernate.loader.plan2.build.spi.ExpandingEntityQuerySpace;
import org.hibernate.loader.plan2.spi.Join; import org.hibernate.loader.plan2.spi.Join;
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.PropertyMapping; import org.hibernate.persister.entity.PropertyMapping;
import org.hibernate.persister.entity.Queryable; import org.hibernate.persister.entity.Queryable;
import org.hibernate.persister.walking.spi.AttributeDefinition; import org.hibernate.persister.walking.spi.AttributeDefinition;
@ -92,8 +93,8 @@ public class EntityQuerySpaceImpl extends AbstractQuerySpace implements Expandin
this, this,
compositionDefinition.getName(), compositionDefinition.getName(),
rhs, rhs,
null, ( (PropertyMapping) persister ).toColumns( compositionDefinition.getName() ),
null, compositionDefinition.getType(),
required required
); );
internalGetJoins().add( join ); internalGetJoins().add( join );
@ -127,7 +128,7 @@ public class EntityQuerySpaceImpl extends AbstractQuerySpace implements Expandin
(EntityType) attribute.getType(), (EntityType) attribute.getType(),
sessionFactory() sessionFactory()
), ),
(AssociationType) attribute.getType(), attribute.getType(),
required required
); );
internalGetJoins().add( join ); internalGetJoins().add( join );
@ -156,7 +157,7 @@ public class EntityQuerySpaceImpl extends AbstractQuerySpace implements Expandin
attributeDefinition.getName(), attributeDefinition.getName(),
rhs, rhs,
( (CollectionType) attributeDefinition.getType() ).getAssociatedJoinable( sessionFactory() ).getKeyColumnNames(), ( (CollectionType) attributeDefinition.getType() ).getAssociatedJoinable( sessionFactory() ).getKeyColumnNames(),
(AssociationType) attributeDefinition.getType(), attributeDefinition.getType(),
required required
); );
internalGetJoins().add( join ); internalGetJoins().add( join );
@ -181,9 +182,9 @@ public class EntityQuerySpaceImpl extends AbstractQuerySpace implements Expandin
final JoinImpl join = new JoinImpl( final JoinImpl join = new JoinImpl(
this, this,
"id", EntityPersister.ENTITY_ID,
rhs, rhs,
null, ( (Loadable) persister ).getIdentifierColumnNames(),
null, null,
canJoinsBeRequired() canJoinsBeRequired()
); );

View File

@ -26,7 +26,7 @@ package org.hibernate.loader.plan2.build.internal.spaces;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.loader.plan2.spi.JoinDefinedByMetadata; import org.hibernate.loader.plan2.spi.JoinDefinedByMetadata;
import org.hibernate.loader.plan2.spi.QuerySpace; import org.hibernate.loader.plan2.spi.QuerySpace;
import org.hibernate.type.AssociationType; import org.hibernate.type.Type;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
@ -38,22 +38,22 @@ public class JoinImpl implements JoinDefinedByMetadata {
private final String lhsPropertyName; private final String lhsPropertyName;
private final String[] rhsColumnNames; private final String[] rhsColumnNames;
private final boolean rightHandSideOptional; private final boolean rightHandSideRequired;
private final AssociationType joinedAssociationPropertyType; private final Type joinedPropertyType;
public JoinImpl( public JoinImpl(
QuerySpace leftHandSide, QuerySpace leftHandSide,
String lhsPropertyName, String lhsPropertyName,
QuerySpace rightHandSide, QuerySpace rightHandSide,
String[] rhsColumnNames, String[] rhsColumnNames,
AssociationType attributeType, Type propertyType,
boolean rightHandSideOptional) { boolean rightHandSideRequired) {
this.leftHandSide = leftHandSide; this.leftHandSide = leftHandSide;
this.lhsPropertyName = lhsPropertyName; this.lhsPropertyName = lhsPropertyName;
this.rightHandSide = rightHandSide; this.rightHandSide = rightHandSide;
this.rhsColumnNames = rhsColumnNames; this.rhsColumnNames = rhsColumnNames;
this.rightHandSideOptional = rightHandSideOptional; this.rightHandSideRequired = rightHandSideRequired;
this.joinedAssociationPropertyType = attributeType; this.joinedPropertyType = propertyType;
if ( StringHelper.isEmpty( lhsPropertyName ) ) { if ( StringHelper.isEmpty( lhsPropertyName ) ) {
throw new IllegalArgumentException( "Incoming 'lhsPropertyName' parameter was empty" ); throw new IllegalArgumentException( "Incoming 'lhsPropertyName' parameter was empty" );
} }
@ -71,12 +71,12 @@ public class JoinImpl implements JoinDefinedByMetadata {
@Override @Override
public boolean isRightHandSideRequired() { public boolean isRightHandSideRequired() {
return rightHandSideOptional; return rightHandSideRequired;
} }
@Override @Override
public String[] resolveAliasedLeftHandSideJoinConditionColumns(String leftHandSideTableAlias) { public String[] resolveAliasedLeftHandSideJoinConditionColumns(String leftHandSideTableAlias) {
return getLeftHandSide().getPropertyMapping().toColumns( leftHandSideTableAlias, getJoinedAssociationPropertyName() ); return getLeftHandSide().getPropertyMapping().toColumns( leftHandSideTableAlias, getJoinedPropertyName() );
} }
@Override @Override
@ -99,12 +99,12 @@ public class JoinImpl implements JoinDefinedByMetadata {
} }
@Override @Override
public String getJoinedAssociationPropertyName() { public String getJoinedPropertyName() {
return lhsPropertyName; return lhsPropertyName;
} }
@Override @Override
public AssociationType getJoinedAssociationPropertyType() { public Type getJoinedPropertyType() {
return joinedAssociationPropertyType; return joinedPropertyType;
} }
} }

View File

@ -339,8 +339,18 @@ public abstract class AbstractLoadPlanBuildingAssociationVisitationStrategy
pushToCollectionStack( collectionReturn ); pushToCollectionStack( collectionReturn );
addRootReturn( collectionReturn ); addRootReturn( collectionReturn );
//if ( collectionDefinition.getCollectionPersister().isOneToMany() ) {
associationKeyRegistered(
new AssociationKey(
( (Joinable) collectionDefinition.getCollectionPersister() ).getTableName(),
( (Joinable) collectionDefinition.getCollectionPersister() ).getKeyColumnNames()
)
);
//}
// also add an AssociationKey for the root so we can later on recognize circular references back to the root. // also add an AssociationKey for the root so we can later on recognize circular references back to the root.
// for a collection, the circularity would always be to an entity element... // for a collection, the circularity would always be to an entity element...
/*
if ( collectionReturn.getElementGraph() != null ) { if ( collectionReturn.getElementGraph() != null ) {
if ( EntityReference.class.isInstance( collectionReturn.getElementGraph() ) ) { if ( EntityReference.class.isInstance( collectionReturn.getElementGraph() ) ) {
final EntityReference entityReference = (EntityReference) collectionReturn.getElementGraph(); final EntityReference entityReference = (EntityReference) collectionReturn.getElementGraph();
@ -350,6 +360,7 @@ public abstract class AbstractLoadPlanBuildingAssociationVisitationStrategy
); );
} }
} }
*/
} }
protected boolean supportsRootCollectionReturns() { protected boolean supportsRootCollectionReturns() {
@ -375,7 +386,7 @@ public abstract class AbstractLoadPlanBuildingAssociationVisitationStrategy
public void startingCollectionIndex(CollectionIndexDefinition indexDefinition) { public void startingCollectionIndex(CollectionIndexDefinition indexDefinition) {
final Type indexType = indexDefinition.getType(); final Type indexType = indexDefinition.getType();
if ( indexType.isAnyType() ) { if ( indexType.isAnyType() ) {
throw new WalkingException( "CollectionIndexDefinition reported any-type mapping as map index" ); return;
} }
log.tracef( log.tracef(
@ -409,6 +420,11 @@ public abstract class AbstractLoadPlanBuildingAssociationVisitationStrategy
@Override @Override
public void finishingCollectionIndex(CollectionIndexDefinition indexDefinition) { public void finishingCollectionIndex(CollectionIndexDefinition indexDefinition) {
final Type indexType = indexDefinition.getType(); final Type indexType = indexDefinition.getType();
if ( indexType.isAnyType() ) {
return;
}
if ( indexType.isComponentType() ) { if ( indexType.isComponentType() ) {
// todo : validate the stack? // todo : validate the stack?
popFromStack(); popFromStack();
@ -463,6 +479,10 @@ public abstract class AbstractLoadPlanBuildingAssociationVisitationStrategy
@Override @Override
public void finishingCollectionElements(CollectionElementDefinition elementDefinition) { public void finishingCollectionElements(CollectionElementDefinition elementDefinition) {
final Type elementType = elementDefinition.getType(); final Type elementType = elementDefinition.getType();
if ( elementType.isAnyType() ) {
return;
}
if ( elementType.isComponentType() ) { if ( elementType.isComponentType() ) {
// pop it from the stack // pop it from the stack
final ExpandingFetchSource popped = popFromStack(); final ExpandingFetchSource popped = popFromStack();
@ -597,7 +617,8 @@ public abstract class AbstractLoadPlanBuildingAssociationVisitationStrategy
// TODO: AFAICT, to avoid an overflow, the associated entity must already be loaded into the session, or // TODO: AFAICT, to avoid an overflow, the associated entity must already be loaded into the session, or
// it must be loaded when the ID for the dependent entity is resolved. Is there some other way to // it must be loaded when the ID for the dependent entity is resolved. Is there some other way to
// deal with this??? // deal with this???
if ( ! associationKey.equals( currentEntityReferenceAssociationKey ) ) { final FetchSource registeredFetchSource = registeredFetchSource( associationKey );
if ( registeredFetchSource != null && ! associationKey.equals( currentEntityReferenceAssociationKey ) ) {
currentSource().buildBidirectionalEntityReference( currentSource().buildBidirectionalEntityReference(
attributeDefinition, attributeDefinition,
fetchStrategy, fetchStrategy,

View File

@ -220,7 +220,7 @@ public class QuerySpaceTreePrinter {
private String determineJoinType(Join join) { private String determineJoinType(Join join) {
if ( JoinDefinedByMetadata.class.isInstance( join ) ) { if ( JoinDefinedByMetadata.class.isInstance( join ) ) {
return "JoinDefinedByMetadata(" + ( (JoinDefinedByMetadata) join ).getJoinedAssociationPropertyName() + ")"; return "JoinDefinedByMetadata(" + ( (JoinDefinedByMetadata) join ).getJoinedPropertyName() + ")";
} }
return join.getClass().getSimpleName(); return join.getClass().getSimpleName();

View File

@ -0,0 +1,527 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Middleware LLC 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 Middleware LLC.
*
* 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.plan2.exec.internal;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.hibernate.HibernateException;
import org.hibernate.LockOptions;
import org.hibernate.ScrollMode;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.pagination.LimitHandler;
import org.hibernate.dialect.pagination.LimitHelper;
import org.hibernate.dialect.pagination.NoopLimitHandler;
import org.hibernate.engine.jdbc.ColumnNameCache;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.RowSelection;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.spi.TypedValue;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.loader.plan2.exec.query.spi.NamedParameterContext;
import org.hibernate.loader.plan2.exec.spi.LoadQueryDetails;
import org.hibernate.loader.spi.AfterLoadAction;
import org.hibernate.transform.ResultTransformer;
import org.hibernate.type.Type;
/**
* todo How much of AbstractLoadPlanBasedEntityLoader is actually needed?
* @author Gail Badner
*/
public abstract class AbstractLoadPlanBasedLoader {
private static final CoreMessageLogger log = CoreLogging.messageLogger( AbstractLoadPlanBasedLoader.class );
private final SessionFactoryImplementor factory;
private ColumnNameCache columnNameCache;
public AbstractLoadPlanBasedLoader(
SessionFactoryImplementor factory) {
this.factory = factory;
}
protected SessionFactoryImplementor getFactory() {
return factory;
}
protected abstract LoadQueryDetails getStaticLoadQuery();
protected abstract int[] getNamedParameterLocs(String name);
protected abstract void autoDiscoverTypes(ResultSet rs);
protected List executeLoad(
SessionImplementor session,
QueryParameters queryParameters,
LoadQueryDetails loadQueryDetails,
boolean returnProxies,
ResultTransformer forcedResultTransformer) throws SQLException {
final List<AfterLoadAction> afterLoadActions = new ArrayList<AfterLoadAction>();
return executeLoad(
session,
queryParameters,
loadQueryDetails,
returnProxies,
forcedResultTransformer,
afterLoadActions
);
}
protected List executeLoad(
SessionImplementor session,
QueryParameters queryParameters,
LoadQueryDetails loadQueryDetails,
boolean returnProxies,
ResultTransformer forcedResultTransformer,
List<AfterLoadAction> afterLoadActions) throws SQLException {
final PersistenceContext persistenceContext = session.getPersistenceContext();
final boolean defaultReadOnlyOrig = persistenceContext.isDefaultReadOnly();
if ( queryParameters.isReadOnlyInitialized() ) {
// The read-only/modifiable mode for the query was explicitly set.
// Temporarily set the default read-only/modifiable setting to the query's setting.
persistenceContext.setDefaultReadOnly( queryParameters.isReadOnly() );
}
else {
// The read-only/modifiable setting for the query was not initialized.
// Use the default read-only/modifiable from the persistence context instead.
queryParameters.setReadOnly( persistenceContext.isDefaultReadOnly() );
}
persistenceContext.beforeLoad();
try {
List results = null;
final String sql = loadQueryDetails.getSqlStatement();
SqlStatementWrapper wrapper = null;
try {
wrapper = executeQueryStatement( sql, queryParameters, false, afterLoadActions, session );
results = loadQueryDetails.getResultSetProcessor().extractResults(
wrapper.getResultSet(),
session,
queryParameters,
new NamedParameterContext() {
@Override
public int[] getNamedParameterLocations(String name) {
return AbstractLoadPlanBasedLoader.this.getNamedParameterLocs( name );
}
},
returnProxies,
queryParameters.isReadOnly(),
forcedResultTransformer,
afterLoadActions
);
}
finally {
if ( wrapper != null ) {
session.getTransactionCoordinator().getJdbcCoordinator().release(
wrapper.getResultSet(),
wrapper.getStatement()
);
}
persistenceContext.afterLoad();
}
persistenceContext.initializeNonLazyCollections();
return results;
}
finally {
// Restore the original default
persistenceContext.setDefaultReadOnly( defaultReadOnlyOrig );
}
}
protected SqlStatementWrapper executeQueryStatement(
final QueryParameters queryParameters,
final boolean scroll,
List<AfterLoadAction> afterLoadActions,
final SessionImplementor session) throws SQLException {
return executeQueryStatement( getStaticLoadQuery().getSqlStatement(), queryParameters, scroll, afterLoadActions, session );
}
protected SqlStatementWrapper executeQueryStatement(
String sqlStatement,
QueryParameters queryParameters,
boolean scroll,
List<AfterLoadAction> afterLoadActions,
SessionImplementor session) throws SQLException {
// Processing query filters.
queryParameters.processFilters( sqlStatement, session );
// Applying LIMIT clause.
final LimitHandler limitHandler = getLimitHandler(
queryParameters.getFilteredSQL(),
queryParameters.getRowSelection()
);
String sql = limitHandler.getProcessedSql();
// Adding locks and comments.
sql = preprocessSQL( sql, queryParameters, getFactory().getDialect(), afterLoadActions );
final PreparedStatement st = prepareQueryStatement( sql, queryParameters, limitHandler, scroll, session );
return new SqlStatementWrapper( st, getResultSet( st, queryParameters.getRowSelection(), limitHandler, queryParameters.hasAutoDiscoverScalarTypes(), session ) );
}
/**
* Build LIMIT clause handler applicable for given selection criteria. Returns {@link org.hibernate.dialect.pagination.NoopLimitHandler} delegate
* if dialect does not support LIMIT expression or processed query does not use pagination.
*
* @param sql Query string.
* @param selection Selection criteria.
* @return LIMIT clause delegate.
*/
protected LimitHandler getLimitHandler(String sql, RowSelection selection) {
final LimitHandler limitHandler = getFactory().getDialect().buildLimitHandler( sql, selection );
return LimitHelper.useLimit( limitHandler, selection ) ? limitHandler : new NoopLimitHandler( sql, selection );
}
private String preprocessSQL(
String sql,
QueryParameters queryParameters,
Dialect dialect,
List<AfterLoadAction> afterLoadActions) {
return getFactory().getSettings().isCommentsEnabled()
? prependComment( sql, queryParameters )
: sql;
}
private String prependComment(String sql, QueryParameters parameters) {
final String comment = parameters.getComment();
if ( comment == null ) {
return sql;
}
else {
return "/* " + comment + " */ " + sql;
}
}
/**
* Obtain a <tt>PreparedStatement</tt> with all parameters pre-bound.
* Bind JDBC-style <tt>?</tt> parameters, named parameters, and
* limit parameters.
*/
protected final PreparedStatement prepareQueryStatement(
final String sql,
final QueryParameters queryParameters,
final LimitHandler limitHandler,
final boolean scroll,
final SessionImplementor session) throws SQLException, HibernateException {
final Dialect dialect = getFactory().getDialect();
final RowSelection selection = queryParameters.getRowSelection();
final boolean useLimit = LimitHelper.useLimit( limitHandler, selection );
final boolean hasFirstRow = LimitHelper.hasFirstRow( selection );
final boolean useLimitOffset = hasFirstRow && useLimit && limitHandler.supportsLimitOffset();
final boolean callable = queryParameters.isCallable();
final ScrollMode scrollMode = getScrollMode( scroll, hasFirstRow, useLimitOffset, queryParameters );
final PreparedStatement st = session.getTransactionCoordinator().getJdbcCoordinator()
.getStatementPreparer().prepareQueryStatement( sql, callable, scrollMode );
try {
int col = 1;
//TODO: can we limit stored procedures ?!
col += limitHandler.bindLimitParametersAtStartOfQuery( st, col );
if (callable) {
col = dialect.registerResultSetOutParameter( (CallableStatement)st, col );
}
col += bindParameterValues( st, queryParameters, col, session );
col += limitHandler.bindLimitParametersAtEndOfQuery( st, col );
limitHandler.setMaxRows( st );
if ( selection != null ) {
if ( selection.getTimeout() != null ) {
st.setQueryTimeout( selection.getTimeout() );
}
if ( selection.getFetchSize() != null ) {
st.setFetchSize( selection.getFetchSize() );
}
}
// handle lock timeout...
final LockOptions lockOptions = queryParameters.getLockOptions();
if ( lockOptions != null ) {
if ( lockOptions.getTimeOut() != LockOptions.WAIT_FOREVER ) {
if ( !dialect.supportsLockTimeouts() ) {
if ( log.isDebugEnabled() ) {
log.debugf(
"Lock timeout [%s] requested but dialect reported to not support lock timeouts",
lockOptions.getTimeOut()
);
}
}
else if ( dialect.isLockTimeoutParameterized() ) {
st.setInt( col++, lockOptions.getTimeOut() );
}
}
}
if ( log.isTraceEnabled() ) {
log.tracev( "Bound [{0}] parameters total", col );
}
}
catch ( SQLException sqle ) {
session.getTransactionCoordinator().getJdbcCoordinator().release( st );
throw sqle;
}
catch ( HibernateException he ) {
session.getTransactionCoordinator().getJdbcCoordinator().release( st );
throw he;
}
return st;
}
protected ScrollMode getScrollMode(boolean scroll, boolean hasFirstRow, boolean useLimitOffSet, QueryParameters queryParameters) {
final boolean canScroll = getFactory().getSettings().isScrollableResultSetsEnabled();
if ( canScroll ) {
if ( scroll ) {
return queryParameters.getScrollMode();
}
if ( hasFirstRow && !useLimitOffSet ) {
return ScrollMode.SCROLL_INSENSITIVE;
}
}
return null;
}
/**
* Bind all parameter values into the prepared statement in preparation
* for execution.
*
* @param statement The JDBC prepared statement
* @param queryParameters The encapsulation of the parameter values to be bound.
* @param startIndex The position from which to start binding parameter values.
* @param session The originating session.
* @return The number of JDBC bind positions actually bound during this method execution.
* @throws SQLException Indicates problems performing the binding.
*/
protected int bindParameterValues(
PreparedStatement statement,
QueryParameters queryParameters,
int startIndex,
SessionImplementor session) throws SQLException {
int span = 0;
span += bindPositionalParameters( statement, queryParameters, startIndex, session );
span += bindNamedParameters( statement, queryParameters.getNamedParameters(), startIndex + span, session );
return span;
}
/**
* Bind positional parameter values to the JDBC prepared statement.
* <p/>
* Positional parameters are those specified by JDBC-style ? parameters
* in the source query. It is (currently) expected that these come
* before any named parameters in the source query.
*
* @param statement The JDBC prepared statement
* @param queryParameters The encapsulation of the parameter values to be bound.
* @param startIndex The position from which to start binding parameter values.
* @param session The originating session.
* @return The number of JDBC bind positions actually bound during this method execution.
* @throws SQLException Indicates problems performing the binding.
* @throws org.hibernate.HibernateException Indicates problems delegating binding to the types.
*/
protected int bindPositionalParameters(
final PreparedStatement statement,
final QueryParameters queryParameters,
final int startIndex,
final SessionImplementor session) throws SQLException, HibernateException {
final Object[] values = queryParameters.getFilteredPositionalParameterValues();
final Type[] types = queryParameters.getFilteredPositionalParameterTypes();
int span = 0;
for ( int i = 0; i < values.length; i++ ) {
types[i].nullSafeSet( statement, values[i], startIndex + span, session );
span += types[i].getColumnSpan( getFactory() );
}
return span;
}
/**
* Bind named parameters to the JDBC prepared statement.
* <p/>
* This is a generic implementation, the problem being that in the
* general case we do not know enough information about the named
* parameters to perform this in a complete manner here. Thus this
* is generally overridden on subclasses allowing named parameters to
* apply the specific behavior. The most usual limitation here is that
* we need to assume the type span is always one...
*
* @param statement The JDBC prepared statement
* @param namedParams A map of parameter names to values
* @param startIndex The position from which to start binding parameter values.
* @param session The originating session.
* @return The number of JDBC bind positions actually bound during this method execution.
* @throws SQLException Indicates problems performing the binding.
* @throws org.hibernate.HibernateException Indicates problems delegating binding to the types.
*/
protected int bindNamedParameters(
final PreparedStatement statement,
final Map namedParams,
final int startIndex,
final SessionImplementor session) throws SQLException, HibernateException {
if ( namedParams != null ) {
// assumes that types are all of span 1
final Iterator itr = namedParams.entrySet().iterator();
final boolean debugEnabled = log.isDebugEnabled();
int result = 0;
while ( itr.hasNext() ) {
final Map.Entry e = (Map.Entry) itr.next();
final String name = (String) e.getKey();
final TypedValue typedval = (TypedValue) e.getValue();
final int[] locs = getNamedParameterLocs( name );
for ( int loc : locs ) {
if ( debugEnabled ) {
log.debugf(
"bindNamedParameters() %s -> %s [%s]",
typedval.getValue(),
name,
loc + startIndex
);
}
typedval.getType().nullSafeSet( statement, typedval.getValue(), loc + startIndex, session );
}
result += locs.length;
}
return result;
}
else {
return 0;
}
}
/**
* Execute given <tt>PreparedStatement</tt>, advance to the first result and return SQL <tt>ResultSet</tt>.
*/
protected final ResultSet getResultSet(
final PreparedStatement st,
final RowSelection selection,
final LimitHandler limitHandler,
final boolean autodiscovertypes,
final SessionImplementor session)
throws SQLException, HibernateException {
try {
ResultSet rs = session.getTransactionCoordinator().getJdbcCoordinator().getResultSetReturn().extract( st );
rs = wrapResultSetIfEnabled( rs , session );
if ( !limitHandler.supportsLimitOffset() || !LimitHelper.useLimit( limitHandler, selection ) ) {
advance( rs, selection );
}
if ( autodiscovertypes ) {
autoDiscoverTypes( rs );
}
return rs;
}
catch ( SQLException sqle ) {
session.getTransactionCoordinator().getJdbcCoordinator().release( st );
throw sqle;
}
}
/**
* Advance the cursor to the first required row of the <tt>ResultSet</tt>
*/
protected void advance(final ResultSet rs, final RowSelection selection) throws SQLException {
final int firstRow = LimitHelper.getFirstRow( selection );
if ( firstRow != 0 ) {
if ( getFactory().getSettings().isScrollableResultSetsEnabled() ) {
// we can go straight to the first required row
rs.absolute( firstRow );
}
else {
// we need to step through the rows one row at a time (slow)
for ( int m = 0; m < firstRow; m++ ) {
rs.next();
}
}
}
}
private synchronized ResultSet wrapResultSetIfEnabled(final ResultSet rs, final SessionImplementor session) {
// synchronized to avoid multi-thread access issues; defined as method synch to avoid
// potential deadlock issues due to nature of code.
if ( session.getFactory().getSettings().isWrapResultSetsEnabled() ) {
try {
if ( log.isDebugEnabled() ) {
log.debugf( "Wrapping result set [%s]", rs );
}
return session.getFactory()
.getJdbcServices()
.getResultSetWrapper().wrap( rs, retreiveColumnNameToIndexCache( rs ) );
}
catch(SQLException e) {
log.unableToWrapResultSet( e );
return rs;
}
}
else {
return rs;
}
}
private ColumnNameCache retreiveColumnNameToIndexCache(ResultSet rs) throws SQLException {
if ( columnNameCache == null ) {
log.trace( "Building columnName->columnIndex cache" );
columnNameCache = new ColumnNameCache( rs.getMetaData().getColumnCount() );
}
return columnNameCache;
}
/**
* Wrapper class for {@link java.sql.Statement} and associated {@link java.sql.ResultSet}.
*/
protected static class SqlStatementWrapper {
private final Statement statement;
private final ResultSet resultSet;
private SqlStatementWrapper(Statement statement, ResultSet resultSet) {
this.resultSet = resultSet;
this.statement = statement;
}
public ResultSet getResultSet() {
return resultSet;
}
public Statement getStatement() {
return statement;
}
}
}

View File

@ -129,8 +129,17 @@ public class AliasResolutionContextImpl implements AliasResolutionContext {
} }
public CollectionReferenceAliases generateCollectionReferenceAliases(String uid, CollectionPersister persister) { public CollectionReferenceAliases generateCollectionReferenceAliases(String uid, CollectionPersister persister) {
final String manyToManyTableAlias = persister.isManyToMany()? createTableAlias( persister.getRole() ) : null; final String manyToManyTableAlias;
final String tableAlias = createTableAlias( persister.getRole() ); final String tableAlias;
if ( persister.isManyToMany() ) {
manyToManyTableAlias = createTableAlias( persister.getRole() );
tableAlias = createTableAlias( persister.getElementDefinition().toEntityDefinition().getEntityPersister() );
}
else {
manyToManyTableAlias = null;
tableAlias = createTableAlias( persister.getRole() );
}
final CollectionReferenceAliasesImpl aliases = new CollectionReferenceAliasesImpl( final CollectionReferenceAliasesImpl aliases = new CollectionReferenceAliasesImpl(
tableAlias, tableAlias,
manyToManyTableAlias, manyToManyTableAlias,

View File

@ -30,12 +30,12 @@ import org.hibernate.engine.FetchTiming;
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.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.loader.EntityAliases;
import org.hibernate.loader.plan2.exec.process.internal.CollectionReferenceInitializerImpl; import org.hibernate.loader.plan2.exec.process.internal.CollectionReferenceInitializerImpl;
import org.hibernate.loader.plan2.exec.process.internal.EntityReferenceInitializerImpl; import org.hibernate.loader.plan2.exec.process.internal.EntityReferenceInitializerImpl;
import org.hibernate.loader.plan2.exec.process.spi.ReaderCollector; import org.hibernate.loader.plan2.exec.process.spi.ReaderCollector;
import org.hibernate.loader.plan2.exec.query.internal.SelectStatementBuilder; import org.hibernate.loader.plan2.exec.query.internal.SelectStatementBuilder;
import org.hibernate.loader.plan2.exec.query.spi.QueryBuildingParameters; import org.hibernate.loader.plan2.exec.query.spi.QueryBuildingParameters;
import org.hibernate.loader.plan2.exec.spi.AliasResolutionContext;
import org.hibernate.loader.plan2.exec.spi.CollectionReferenceAliases; import org.hibernate.loader.plan2.exec.spi.CollectionReferenceAliases;
import org.hibernate.loader.plan2.exec.spi.EntityReferenceAliases; import org.hibernate.loader.plan2.exec.spi.EntityReferenceAliases;
import org.hibernate.loader.plan2.spi.CollectionFetch; import org.hibernate.loader.plan2.spi.CollectionFetch;
@ -50,13 +50,16 @@ import org.hibernate.loader.plan2.spi.Join;
import org.hibernate.loader.plan2.spi.JoinDefinedByMetadata; import org.hibernate.loader.plan2.spi.JoinDefinedByMetadata;
import org.hibernate.loader.plan2.spi.QuerySpace; import org.hibernate.loader.plan2.spi.QuerySpace;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.collection.CollectionPropertyNames;
import org.hibernate.persister.collection.QueryableCollection; import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Joinable; import org.hibernate.persister.entity.Joinable;
import org.hibernate.persister.entity.OuterJoinLoadable; import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.persister.walking.internal.FetchStrategyHelper; import org.hibernate.persister.walking.internal.FetchStrategyHelper;
import org.hibernate.sql.JoinFragment; import org.hibernate.sql.JoinFragment;
import org.hibernate.sql.JoinType; import org.hibernate.sql.JoinType;
import org.hibernate.type.AssociationType; import org.hibernate.type.AssociationType;
import org.hibernate.type.Type;
/** /**
* Helper for implementors of entity and collection based query building based on LoadPlans providing common * Helper for implementors of entity and collection based query building based on LoadPlans providing common
@ -64,7 +67,7 @@ import org.hibernate.type.AssociationType;
* <p/> * <p/>
* Exposes 2 main methods:<ol> * Exposes 2 main methods:<ol>
* <li>{@link #processQuerySpaceJoins(QuerySpace, SelectStatementBuilder)}</li> * <li>{@link #processQuerySpaceJoins(QuerySpace, SelectStatementBuilder)}</li>
* <li>{@link #processFetches(FetchSource, SelectStatementBuilder, ReaderCollector)}li> * <li>{@link #processFetches(FetchSource, SelectStatementBuilder, org.hibernate.loader.plan2.exec.process.spi.ReaderCollector)}li>
* </ol> * </ol>
* *
* @author Steve Ebersole * @author Steve Ebersole
@ -92,6 +95,18 @@ public class LoadQueryJoinAndFetchProcessor {
this.factory = factory; this.factory = factory;
} }
public AliasResolutionContext getAliasResolutionContext() {
return aliasResolutionContext;
}
public QueryBuildingParameters getQueryBuildingParameters() {
return buildingParameters;
}
public SessionFactoryImplementor getSessionFactory() {
return factory;
}
public void processQuerySpaceJoins(QuerySpace querySpace, SelectStatementBuilder selectStatementBuilder) { public void processQuerySpaceJoins(QuerySpace querySpace, SelectStatementBuilder selectStatementBuilder) {
LOG.debug( "processing queryspace " + querySpace.getUid() ); LOG.debug( "processing queryspace " + querySpace.getUid() );
final JoinFragment joinFragment = factory.getDialect().createOuterJoinFragment(); final JoinFragment joinFragment = factory.getDialect().createOuterJoinFragment();
@ -130,17 +145,16 @@ public class LoadQueryJoinAndFetchProcessor {
else if ( EntityQuerySpace.class.isInstance( join.getRightHandSide() ) ) { else if ( EntityQuerySpace.class.isInstance( join.getRightHandSide() ) ) {
// do not render the entity join for a one-to-many association, since the collection join // do not render the entity join for a one-to-many association, since the collection join
// already joins to the associated entity table (see doc in renderCollectionJoin()). // already joins to the associated entity table (see doc in renderCollectionJoin()).
if ( join.getLeftHandSide().getDisposition() == QuerySpace.Disposition.COLLECTION && if ( join.getLeftHandSide().getDisposition() == QuerySpace.Disposition.COLLECTION ) {
CollectionQuerySpace.class.cast( join.getLeftHandSide() ).getCollectionPersister().isManyToMany() ) { if ( CollectionQuerySpace.class.cast( join.getLeftHandSide() ).getCollectionPersister().isManyToMany() ) {
final CollectionQuerySpace leftHandSide = (CollectionQuerySpace) join.getLeftHandSide(); renderManyToManyJoin( join, joinFragment );
final CollectionReferenceAliases aliases = aliasResolutionContext.resolveCollectionReferenceAliases( }
leftHandSide.getUid() else if ( JoinDefinedByMetadata.class.isInstance( join ) &&
); CollectionPropertyNames.COLLECTION_INDICES.equals( JoinDefinedByMetadata.class.cast( join ).getJoinedPropertyName() ) ) {
renderManyToManyJoin( join, joinFragment );
renderManyToManyJoin(aliases, leftHandSide, join, joinFragment ); }
} }
else if ( join.getLeftHandSide().getDisposition() != QuerySpace.Disposition.COLLECTION || else {
! CollectionQuerySpace.class.cast( join.getLeftHandSide() ).getCollectionPersister().isOneToMany() ) {
renderEntityJoin( join, joinFragment ); renderEntityJoin( join, joinFragment );
} }
} }
@ -164,47 +178,36 @@ public class LoadQueryJoinAndFetchProcessor {
} }
private void renderEntityJoin(Join join, JoinFragment joinFragment) { private void renderEntityJoin(Join join, JoinFragment joinFragment) {
final String leftHandSideUid = join.getLeftHandSide().getUid();
final String leftHandSideTableAlias = aliasResolutionContext.resolveSqlTableAliasFromQuerySpaceUid( leftHandSideUid );
if ( leftHandSideTableAlias == null ) {
throw new IllegalStateException( "QuerySpace with that UID was not yet registered in the AliasResolutionContext" );
}
final String[] aliasedLhsColumnNames = join.resolveAliasedLeftHandSideJoinConditionColumns( leftHandSideTableAlias );
final EntityQuerySpace rightHandSide = (EntityQuerySpace) join.getRightHandSide(); final EntityQuerySpace rightHandSide = (EntityQuerySpace) join.getRightHandSide();
// see if there is already aliases registered for this entity query space (collection joins) // see if there is already aliases registered for this entity query space (collection joins)
EntityReferenceAliases aliases = aliasResolutionContext.resolveEntityReferenceAliases( rightHandSide.getUid() ); EntityReferenceAliases aliases = aliasResolutionContext.resolveEntityReferenceAliases( rightHandSide.getUid() );
if ( aliases == null ) { if ( aliases == null ) {
aliases = aliasResolutionContext.generateEntityReferenceAliases( aliasResolutionContext.generateEntityReferenceAliases(
rightHandSide.getUid(), rightHandSide.getUid(),
rightHandSide.getEntityPersister() rightHandSide.getEntityPersister()
); );
} }
final String[] rhsColumnNames = join.resolveNonAliasedRightHandSideJoinConditionColumns();
final String rhsTableAlias = aliases.getTableAlias();
final AssociationType associationType = join instanceof JoinDefinedByMetadata ? ((JoinDefinedByMetadata)join).getJoinedAssociationPropertyType() : null;
final String additionalJoinConditions = resolveAdditionalJoinCondition(
rhsTableAlias,
join.getAnyAdditionalJoinConditions( rhsTableAlias ),
(Joinable) rightHandSide.getEntityPersister(),
associationType
);
final Joinable joinable = (Joinable) rightHandSide.getEntityPersister(); final Joinable joinable = (Joinable) rightHandSide.getEntityPersister();
addJoins( addJoins(
join,
joinFragment, joinFragment,
joinable, joinable
join.isRightHandSideRequired() ? JoinType.INNER_JOIN : JoinType.LEFT_OUTER_JOIN,
aliases.getTableAlias(),
rhsColumnNames,
aliasedLhsColumnNames,
additionalJoinConditions
); );
} }
private AssociationType getJoinedAssociationTypeOrNull(Join join) {
if ( !JoinDefinedByMetadata.class.isInstance( join ) ) {
return null;
}
final Type joinedType = ( (JoinDefinedByMetadata) join ).getJoinedPropertyType();
return joinedType.isAssociationType()
? (AssociationType) joinedType
: null;
}
private String resolveAdditionalJoinCondition(String rhsTableAlias, String withClause, Joinable joinable, AssociationType associationType) { private String resolveAdditionalJoinCondition(String rhsTableAlias, String withClause, Joinable joinable, AssociationType associationType) {
// turns out that the call to AssociationType#getOnCondition in the initial code really just translates to // turns out that the call to AssociationType#getOnCondition in the initial code really just translates to
// calls to the Joinable.filterFragment() method where the Joinable is either the entity or // calls to the Joinable.filterFragment() method where the Joinable is either the entity or
@ -228,41 +231,48 @@ public class LoadQueryJoinAndFetchProcessor {
} }
} }
private static void addJoins( private void addJoins(
Join join,
JoinFragment joinFragment, JoinFragment joinFragment,
Joinable joinable, Joinable joinable) {
JoinType joinType,
String rhsAlias, final String rhsTableAlias = aliasResolutionContext.resolveSqlTableAliasFromQuerySpaceUid(
String[] rhsColumnNames, join.getRightHandSide().getUid()
String[] aliasedLhsColumnNames, );
String additionalJoinConditions) { if ( StringHelper.isEmpty( rhsTableAlias ) ) {
// somewhere, one of these being empty is causing trouble...
if ( StringHelper.isEmpty( rhsAlias ) ) {
throw new IllegalStateException( "Join's RHS table alias cannot be empty" ); throw new IllegalStateException( "Join's RHS table alias cannot be empty" );
} }
final String lhsTableAlias = aliasResolutionContext.resolveSqlTableAliasFromQuerySpaceUid(
join.getLeftHandSide().getUid()
);
if ( lhsTableAlias == null ) {
throw new IllegalStateException( "QuerySpace with that UID was not yet registered in the AliasResolutionContext" );
}
// add join fragments from the collection table -> element entity table ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
final String additionalJoinConditions = resolveAdditionalJoinCondition(
rhsTableAlias,
join.getAnyAdditionalJoinConditions( rhsTableAlias ),
joinable,
getJoinedAssociationTypeOrNull( join )
);
joinFragment.addJoin( joinFragment.addJoin(
joinable.getTableName(), joinable.getTableName(),
rhsAlias, rhsTableAlias,
aliasedLhsColumnNames, join.resolveAliasedLeftHandSideJoinConditionColumns( lhsTableAlias ),
rhsColumnNames, join.resolveNonAliasedRightHandSideJoinConditionColumns(),
joinType, join.isRightHandSideRequired() ? JoinType.INNER_JOIN : JoinType.LEFT_OUTER_JOIN,
additionalJoinConditions additionalJoinConditions
); );
joinFragment.addJoins( joinFragment.addJoins(
joinable.fromJoinFragment( rhsAlias, false, true ), joinable.fromJoinFragment( rhsTableAlias, false, true ),
joinable.whereJoinFragment( rhsAlias, false, true ) joinable.whereJoinFragment( rhsTableAlias, false, true )
); );
} }
private void renderCollectionJoin(Join join, JoinFragment joinFragment) { private void renderCollectionJoin(Join join, JoinFragment joinFragment) {
final String leftHandSideUid = join.getLeftHandSide().getUid();
final String leftHandSideTableAlias = aliasResolutionContext.resolveSqlTableAliasFromQuerySpaceUid( leftHandSideUid );
if ( leftHandSideTableAlias == null ) {
throw new IllegalStateException( "QuerySpace with that UID was not yet registered in the AliasResolutionContext" );
}
final String[] aliasedLhsColumnNames = join.resolveAliasedLeftHandSideJoinConditionColumns( leftHandSideTableAlias );
final CollectionQuerySpace rightHandSide = (CollectionQuerySpace) join.getRightHandSide(); final CollectionQuerySpace rightHandSide = (CollectionQuerySpace) join.getRightHandSide();
final CollectionReferenceAliases aliases = aliasResolutionContext.generateCollectionReferenceAliases( final CollectionReferenceAliases aliases = aliasResolutionContext.generateCollectionReferenceAliases(
rightHandSide.getUid(), rightHandSide.getUid(),
@ -318,82 +328,16 @@ public class LoadQueryJoinAndFetchProcessor {
); );
} }
renderSqlJoinToCollectionTable( addJoins(
aliases,
rightHandSide,
aliasedLhsColumnNames,
join, join,
joinFragment joinFragment,
); (Joinable) rightHandSide.getCollectionPersister()
// if ( rightHandSide.getCollectionPersister().isManyToMany() ) {
// renderManyToManyJoin(
// aliases,
// rightHandSide,
// aliasedLhsColumnNames,
// join,
// joinFragment
// );
// }
// else if ( rightHandSide.getCollectionPersister().isOneToMany() ) {
// renderOneToManyJoin(
// aliases,
// rightHandSide,
// aliasedLhsColumnNames,
// join,
// joinFragment
// );
// }
// else {
// renderBasicCollectionJoin(
// aliases,
// rightHandSide,
// aliasedLhsColumnNames,
// join,
// joinFragment
// );
// }
}
private void renderSqlJoinToCollectionTable(
CollectionReferenceAliases aliases,
CollectionQuerySpace rightHandSide,
String[] aliasedLhsColumnNames,
Join join,
JoinFragment joinFragment) {
final String collectionTableAlias = aliases.getCollectionTableAlias();
final CollectionPersister persister = rightHandSide.getCollectionPersister();
final QueryableCollection queryableCollection = (QueryableCollection) persister;
// add join fragments from the owner table -> collection table ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
final String filterFragment = queryableCollection.filterFragment(
collectionTableAlias,
buildingParameters.getQueryInfluencers().getEnabledFilters()
);
joinFragment.addJoin(
queryableCollection.getTableName(),
collectionTableAlias,
aliasedLhsColumnNames,
queryableCollection.getKeyColumnNames(),
JoinType.LEFT_OUTER_JOIN,
filterFragment
);
joinFragment.addJoins(
queryableCollection.fromJoinFragment( collectionTableAlias, false, true ),
queryableCollection.whereJoinFragment( collectionTableAlias, false, true )
); );
} }
private void renderManyToManyJoin( private void renderManyToManyJoin(
CollectionReferenceAliases aliases,
CollectionQuerySpace rightHandSide,
// String[] aliasedLhsColumnNames,
Join join, Join join,
JoinFragment joinFragment) { JoinFragment joinFragment) {
final CollectionPersister persister = rightHandSide.getCollectionPersister();
final QueryableCollection queryableCollection = (QueryableCollection) persister;
// for many-to-many we have 3 table aliases. By way of example, consider a normal m-n: User<->Role // 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: // where User is the FetchOwner and Role (User.roles) is the Fetch. We'd have:
@ -402,142 +346,33 @@ public class LoadQueryJoinAndFetchProcessor {
// columns here (aliasedLhsColumnNames) // columns here (aliasedLhsColumnNames)
//final String ownerTableAlias = ...; //final String ownerTableAlias = ...;
// 2) the m-n table : user_role // 2) the m-n table : user_role
final String collectionTableAlias = aliases.getCollectionTableAlias();
// 3) the element table : role // 3) the element table : role
final String elementTableAlias = aliases.getElementTableAlias(); final EntityPersister entityPersister = ( (EntityQuerySpace) join.getRightHandSide() ).getEntityPersister();
final String entityTableAlias = aliasResolutionContext.resolveSqlTableAliasFromQuerySpaceUid(
join.getRightHandSide().getUid()
);
// somewhere, one of these being empty is causing trouble... if ( StringHelper.isEmpty( entityTableAlias ) ) {
if ( StringHelper.isEmpty( collectionTableAlias ) ) {
throw new IllegalStateException( "Collection table alias cannot be empty" );
}
if ( StringHelper.isEmpty( elementTableAlias ) ) {
throw new IllegalStateException( "Collection element (many-to-many) table alias cannot be empty" ); throw new IllegalStateException( "Collection element (many-to-many) table alias cannot be empty" );
} }
if ( JoinDefinedByMetadata.class.isInstance( join ) &&
// CollectionPropertyNames.COLLECTION_ELEMENTS.equals( ( (JoinDefinedByMetadata) join ).getJoinedPropertyName() ) ) {
// { final CollectionQuerySpace leftHandSide = (CollectionQuerySpace) join.getLeftHandSide();
// // add join fragments from the owner table -> collection table ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ final CollectionPersister persister = leftHandSide.getCollectionPersister();
// final String filterFragment = queryableCollection.filterFragment(
// collectionTableAlias,
// buildingParameters.getQueryInfluencers().getEnabledFilters()
// );
//
// joinFragment.addJoin(
// queryableCollection.getTableName(),
// collectionTableAlias,
// aliasedLhsColumnNames,
// queryableCollection.getKeyColumnNames(),
// JoinType.LEFT_OUTER_JOIN,
// filterFragment
// );
// joinFragment.addJoins(
// queryableCollection.fromJoinFragment( collectionTableAlias, false, true ),
// queryableCollection.whereJoinFragment( collectionTableAlias, false, true )
// );
// }
{
final AssociationType associationType = join instanceof JoinDefinedByMetadata ? ((JoinDefinedByMetadata)join).getJoinedAssociationPropertyType() : null;
// add join fragments from the collection table -> element entity table ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
final String additionalJoinConditions = resolveAdditionalJoinCondition(
elementTableAlias,
join.getAnyAdditionalJoinConditions( elementTableAlias ),
queryableCollection,
associationType
);
final String manyToManyFilter = persister.getManyToManyFilterFragment( final String manyToManyFilter = persister.getManyToManyFilterFragment(
elementTableAlias, entityTableAlias,
buildingParameters.getQueryInfluencers().getEnabledFilters() buildingParameters.getQueryInfluencers().getEnabledFilters()
); );
joinFragment.addCondition( manyToManyFilter );
final String condition;
if ( StringHelper.isEmpty( manyToManyFilter ) ) {
condition = additionalJoinConditions;
}
else if ( StringHelper.isEmpty( additionalJoinConditions ) ) {
condition = manyToManyFilter;
}
else {
condition = additionalJoinConditions + " and " + manyToManyFilter;
}
final OuterJoinLoadable elementPersister = (OuterJoinLoadable) queryableCollection.getElementPersister();
addJoins(
joinFragment,
elementPersister,
JoinType.LEFT_OUTER_JOIN,
elementTableAlias,
elementPersister.getIdentifierColumnNames(),
StringHelper.qualify( collectionTableAlias, queryableCollection.getElementColumnNames() ),
condition
);
} }
addJoins(
join,
joinFragment,
(Joinable) entityPersister
);
} }
// private void renderOneToManyJoin(
// CollectionReferenceAliases aliases,
// CollectionQuerySpace rightHandSide,
// String[] aliasedLhsColumnNames,
// Join join,
// JoinFragment joinFragment) {
// final QueryableCollection queryableCollection = (QueryableCollection) rightHandSide.getCollectionPersister();
//
// final String rhsTableAlias = aliases.getElementTableAlias();
// final String[] rhsColumnNames = join.resolveNonAliasedRightHandSideJoinConditionColumns();
//
// final String on = resolveAdditionalJoinCondition(
// rhsTableAlias,
// join.getAnyAdditionalJoinConditions( rhsTableAlias ),
// queryableCollection
// );
//
// addJoins(
// joinFragment,
// queryableCollection,
// JoinType.LEFT_OUTER_JOIN,
// rhsTableAlias,
// rhsColumnNames,
// aliasedLhsColumnNames,
// on
// );
// }
//
// private void renderBasicCollectionJoin(
// CollectionReferenceAliases aliases,
// CollectionQuerySpace rightHandSide,
// String[] aliasedLhsColumnNames,
// Join join,
// JoinFragment joinFragment) {
// final QueryableCollection queryableCollection = (QueryableCollection) rightHandSide.getCollectionPersister();
//
// final String rhsTableAlias = aliases.getElementTableAlias();
// final String[] rhsColumnNames = join.resolveNonAliasedRightHandSideJoinConditionColumns();
//
// final String on = resolveAdditionalJoinCondition(
// rhsTableAlias,
// join.getAnyAdditionalJoinConditions( rhsTableAlias ),
// queryableCollection
// );
//
// addJoins(
// joinFragment,
// queryableCollection,
// JoinType.LEFT_OUTER_JOIN,
// rhsTableAlias,
// rhsColumnNames,
// aliasedLhsColumnNames,
// on
// );
// }
public FetchStats processFetches( public FetchStats processFetches(
FetchSource fetchSource, FetchSource fetchSource,
SelectStatementBuilder selectStatementBuilder, SelectStatementBuilder selectStatementBuilder,
@ -644,9 +479,10 @@ public class LoadQueryJoinAndFetchProcessor {
// First write out the SQL SELECT fragments // First write out the SQL SELECT fragments
final Joinable joinable = (Joinable) fetch.getEntityPersister(); final Joinable joinable = (Joinable) fetch.getEntityPersister();
final EntityReferenceAliases aliases = aliasResolutionContext.resolveEntityReferenceAliases( EntityReferenceAliases aliases = aliasResolutionContext.resolveEntityReferenceAliases(
fetch.getQuerySpaceUid() fetch.getQuerySpaceUid()
); );
// the null arguments here relate to many-to-many fetches // the null arguments here relate to many-to-many fetches
selectStatementBuilder.appendSelectClauseFragment( selectStatementBuilder.appendSelectClauseFragment(
joinable.selectFragment( joinable.selectFragment(
@ -659,22 +495,19 @@ public class LoadQueryJoinAndFetchProcessor {
) )
); );
// // process its identifier fetches first (building EntityReferenceInitializers for them if needed) // process its identifier fetches first (building EntityReferenceInitializers for them if needed)
// if ( EntityReference.class.isInstance( fetchSource ) ) { if ( fetch.getIdentifierDescription().hasFetches() ) {
// final EntityReference fetchOwnerAsEntityReference = (EntityReference) fetchSource; final FetchSource entityIdentifierAsFetchSource = (FetchSource) fetch.getIdentifierDescription();
// if ( fetchOwnerAsEntityReference.getIdentifierDescription().hasFetches() ) { for ( Fetch identifierFetch : entityIdentifierAsFetchSource.getFetches() ) {
// final FetchSource entityIdentifierAsFetchSource = (FetchSource) fetchOwnerAsEntityReference.getIdentifierDescription(); processFetch(
// for ( Fetch identifierFetch : entityIdentifierAsFetchSource.getFetches() ) { selectStatementBuilder,
// processFetch( fetch,
// selectStatementBuilder, identifierFetch,
// fetchSource, readerCollector,
// identifierFetch, fetchStats
// readerCollector, );
// fetchStats }
// ); }
// }
// }
// }
// build an EntityReferenceInitializers for the incoming fetch itself // build an EntityReferenceInitializers for the incoming fetch itself
readerCollector.add( new EntityReferenceInitializerImpl( fetch, aliases ) ); readerCollector.add( new EntityReferenceInitializerImpl( fetch, aliases ) );
@ -691,7 +524,9 @@ public class LoadQueryJoinAndFetchProcessor {
FetchStatsImpl fetchStats) { FetchStatsImpl fetchStats) {
fetchStats.processingFetch( fetch ); fetchStats.processingFetch( fetch );
final CollectionReferenceAliases aliases = aliasResolutionContext.resolveCollectionReferenceAliases( fetch.getQuerySpaceUid() ); final CollectionReferenceAliases aliases = aliasResolutionContext.resolveCollectionReferenceAliases(
fetch.getQuerySpaceUid()
);
final QueryableCollection queryableCollection = (QueryableCollection) fetch.getCollectionPersister(); final QueryableCollection queryableCollection = (QueryableCollection) fetch.getCollectionPersister();
final Joinable joinableCollection = (Joinable) fetch.getCollectionPersister(); final Joinable joinableCollection = (Joinable) fetch.getCollectionPersister();
@ -746,17 +581,10 @@ public class LoadQueryJoinAndFetchProcessor {
} }
// add an EntityReferenceInitializer for the collection elements (keys also?) // add an EntityReferenceInitializer for the collection elements (keys also?)
final EntityReferenceAliases entityReferenceAliases = new EntityReferenceAliases() { final EntityReferenceAliases entityReferenceAliases = new EntityReferenceAliasesImpl(
@Override aliases.getCollectionTableAlias(),
public String getTableAlias() { aliases.getEntityElementColumnAliases()
return aliases.getCollectionTableAlias(); );
}
@Override
public EntityAliases getColumnAliases() {
return aliases.getEntityElementColumnAliases();
}
};
aliasResolutionContext.registerQuerySpaceAliases( fetch.getQuerySpaceUid(), entityReferenceAliases ); aliasResolutionContext.registerQuerySpaceAliases( fetch.getQuerySpaceUid(), entityReferenceAliases );
readerCollector.add( readerCollector.add(
new EntityReferenceInitializerImpl( new EntityReferenceInitializerImpl(
@ -766,12 +594,10 @@ public class LoadQueryJoinAndFetchProcessor {
); );
} }
else { else {
final String rhsTableAlias = aliases.getElementTableAlias();
// select the "collection columns" // select the "collection columns"
selectStatementBuilder.appendSelectClauseFragment( selectStatementBuilder.appendSelectClauseFragment(
queryableCollection.selectFragment( queryableCollection.selectFragment(
rhsTableAlias, aliases.getElementTableAlias(),
aliases.getCollectionColumnAliases().getSuffix() aliases.getCollectionColumnAliases().getSuffix()
) )
); );
@ -785,17 +611,10 @@ public class LoadQueryJoinAndFetchProcessor {
aliases.getEntityElementColumnAliases().getSuffix() aliases.getEntityElementColumnAliases().getSuffix()
) )
); );
final EntityReferenceAliases entityReferenceAliases = new EntityReferenceAliases() { final EntityReferenceAliases entityReferenceAliases = new EntityReferenceAliasesImpl(
@Override aliases.getElementTableAlias(),
public String getTableAlias() { aliases.getEntityElementColumnAliases()
return aliases.getElementTableAlias(); );
}
@Override
public EntityAliases getColumnAliases() {
return aliases.getEntityElementColumnAliases();
}
};
aliasResolutionContext.registerQuerySpaceAliases( fetch.getQuerySpaceUid(), entityReferenceAliases ); aliasResolutionContext.registerQuerySpaceAliases( fetch.getQuerySpaceUid(), entityReferenceAliases );
readerCollector.add( readerCollector.add(
new EntityReferenceInitializerImpl( new EntityReferenceInitializerImpl(
@ -805,7 +624,7 @@ public class LoadQueryJoinAndFetchProcessor {
); );
} }
final String ordering = queryableCollection.getSQLOrderByString( rhsTableAlias ); final String ordering = queryableCollection.getSQLOrderByString( aliases.getElementTableAlias() );
if ( StringHelper.isNotEmpty( ordering ) ) { if ( StringHelper.isNotEmpty( ordering ) ) {
selectStatementBuilder.appendOrderByFragment( ordering ); selectStatementBuilder.appendOrderByFragment( ordering );
} }

View File

@ -0,0 +1,47 @@
/*
* 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.plan2.exec.process.internal;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.hibernate.loader.plan2.exec.process.spi.ResultSetProcessingContext;
import org.hibernate.loader.plan2.exec.process.spi.ReturnReader;
import org.hibernate.loader.plan2.spi.CollectionReturn;
/**
* @author Steve Ebersole
*/
public class CollectionReturnReader implements ReturnReader {
private final CollectionReturn collectionReturn;
public CollectionReturnReader(CollectionReturn collectionReturn) {
this.collectionReturn = collectionReturn;
}
@Override
public Object read(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
}

View File

@ -210,6 +210,7 @@ public class EntityReferenceInitializerImpl implements EntityReferenceInitialize
// use the existing association as the hydrated state // use the existing association as the hydrated state
processingState.registerEntityInstance( existing ); processingState.registerEntityInstance( existing );
//context.registerHydratedEntity( entityReference, entityKey, existing );
return; return;
} }

View File

@ -30,7 +30,6 @@ import org.hibernate.AssertionFailure;
import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.EntityKey;
import org.hibernate.loader.plan2.exec.process.spi.ResultSetProcessingContext; import org.hibernate.loader.plan2.exec.process.spi.ResultSetProcessingContext;
import org.hibernate.loader.plan2.exec.process.spi.ReturnReader; import org.hibernate.loader.plan2.exec.process.spi.ReturnReader;
import org.hibernate.loader.plan2.exec.spi.EntityReferenceAliases;
import org.hibernate.loader.plan2.spi.EntityReturn; import org.hibernate.loader.plan2.spi.EntityReturn;
import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.HibernateProxy;
@ -41,11 +40,9 @@ import static org.hibernate.loader.plan2.exec.process.spi.ResultSetProcessingCon
*/ */
public class EntityReturnReader implements ReturnReader { public class EntityReturnReader implements ReturnReader {
private final EntityReturn entityReturn; private final EntityReturn entityReturn;
private final EntityReferenceAliases aliases;
public EntityReturnReader(EntityReturn entityReturn, EntityReferenceAliases aliases) { public EntityReturnReader(EntityReturn entityReturn) {
this.entityReturn = entityReturn; this.entityReturn = entityReturn;
this.aliases = aliases;
} }
public EntityReferenceProcessingState getIdentifierResolutionContext(ResultSetProcessingContext context) { public EntityReferenceProcessingState getIdentifierResolutionContext(ResultSetProcessingContext context) {

View File

@ -287,6 +287,9 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
*/ */
void finishUpRow() { void finishUpRow() {
if ( currentRowHydratedEntityRegistrationList == null ) { if ( currentRowHydratedEntityRegistrationList == null ) {
if ( identifierResolutionContextMap != null ) {
identifierResolutionContextMap.clear();
}
return; return;
} }

View File

@ -169,11 +169,11 @@ public class ResultSetProcessorImpl implements ResultSetProcessor {
"Preparing collection intializer : %s", "Preparing collection intializer : %s",
MessageHelper.collectionInfoString( persister, key, session.getFactory() ) MessageHelper.collectionInfoString( persister, key, session.getFactory() )
); );
session.getPersistenceContext()
.getLoadContexts()
.getCollectionLoadContext( resultSet )
.getLoadingCollection( persister, key );
} }
session.getPersistenceContext()
.getLoadContexts()
.getCollectionLoadContext( resultSet )
.getLoadingCollection( persister, key );
} }
} }

View File

@ -25,6 +25,8 @@ package org.hibernate.loader.plan2.exec.process.spi;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -55,18 +57,27 @@ import org.hibernate.persister.entity.Loadable;
public abstract class AbstractRowReader implements RowReader { public abstract class AbstractRowReader implements RowReader {
private static final Logger log = CoreLogging.logger( AbstractRowReader.class ); private static final Logger log = CoreLogging.logger( AbstractRowReader.class );
protected abstract List<EntityReferenceInitializer> getEntityReferenceInitializers(); private final List<EntityReferenceInitializer> entityReferenceInitializers;
protected abstract List<CollectionReferenceInitializer> getArrayReferenceInitializers(); private final List<CollectionReferenceInitializer> arrayReferenceInitializers;
protected abstract List<CollectionReferenceInitializer> getCollectionReferenceInitializers(); private final List<CollectionReferenceInitializer> collectionReferenceInitializers;
public AbstractRowReader(ReaderCollector readerCollector) {
this.entityReferenceInitializers = readerCollector.getEntityReferenceInitializers() != null
? new ArrayList<EntityReferenceInitializer>( readerCollector.getEntityReferenceInitializers() )
: Collections.<EntityReferenceInitializer>emptyList();
this.arrayReferenceInitializers = readerCollector.getArrayReferenceInitializers() != null
? new ArrayList<CollectionReferenceInitializer>( readerCollector.getArrayReferenceInitializers() )
: Collections.<CollectionReferenceInitializer>emptyList();
this.collectionReferenceInitializers = readerCollector.getNonArrayCollectionReferenceInitializers() != null
? new ArrayList<CollectionReferenceInitializer>( readerCollector.getNonArrayCollectionReferenceInitializers() )
: Collections.<CollectionReferenceInitializer>emptyList();
}
protected abstract Object readLogicalRow(ResultSet resultSet, ResultSetProcessingContextImpl context) protected abstract Object readLogicalRow(ResultSet resultSet, ResultSetProcessingContextImpl context)
throws SQLException; throws SQLException;
@Override @Override
public Object readRow(ResultSet resultSet, ResultSetProcessingContextImpl context) throws SQLException { public Object readRow(ResultSet resultSet, ResultSetProcessingContextImpl context) throws SQLException {
final List<EntityReferenceInitializer> entityReferenceInitializers = getEntityReferenceInitializers();
final List<CollectionReferenceInitializer> arrayReferenceInitializers = getArrayReferenceInitializers();
final List<CollectionReferenceInitializer> collectionReferenceInitializers = getCollectionReferenceInitializers();
final boolean hasEntityReferenceInitializers = CollectionHelper.isNotEmpty( entityReferenceInitializers ); final boolean hasEntityReferenceInitializers = CollectionHelper.isNotEmpty( entityReferenceInitializers );
@ -209,11 +220,8 @@ public abstract class AbstractRowReader implements RowReader {
} }
private void finishLoadingArrays(ResultSetProcessingContextImpl context) { private void finishLoadingArrays(ResultSetProcessingContextImpl context) {
final List<CollectionReferenceInitializer> arrayReferenceInitializers = getArrayReferenceInitializers(); for ( CollectionReferenceInitializer arrayReferenceInitializer : arrayReferenceInitializers ) {
if ( arrayReferenceInitializers != null ) { arrayReferenceInitializer.endLoading( context );
for ( CollectionReferenceInitializer arrayReferenceInitializer : arrayReferenceInitializers ) {
arrayReferenceInitializer.endLoading( context );
}
} }
} }
@ -241,11 +249,8 @@ public abstract class AbstractRowReader implements RowReader {
} }
private void finishLoadingCollections(ResultSetProcessingContextImpl context) { private void finishLoadingCollections(ResultSetProcessingContextImpl context) {
final List<CollectionReferenceInitializer> collectionReferenceInitializers = getCollectionReferenceInitializers(); for ( CollectionReferenceInitializer collectionReferenceInitializer : collectionReferenceInitializers ) {
if ( collectionReferenceInitializers != null ) { collectionReferenceInitializer.endLoading( context );
for ( CollectionReferenceInitializer arrayReferenceInitializer : collectionReferenceInitializers ) {
arrayReferenceInitializer.endLoading( context );
}
} }
} }

View File

@ -23,13 +23,23 @@
*/ */
package org.hibernate.loader.plan2.exec.process.spi; package org.hibernate.loader.plan2.exec.process.spi;
import java.util.List;
/** /**
* Used as a callback mechanism while building the SQL statement to collect the needed ResultSet readers * Used as a callback mechanism while building the SQL statement to collect the needed ResultSet initializers.
* *
* @author Steve Ebersole * @author Steve Ebersole
* @author Gail Badner
*/ */
public interface ReaderCollector { public interface ReaderCollector {
public ReturnReader getReturnReader();
public void add(CollectionReferenceInitializer collectionReferenceInitializer); public void add(CollectionReferenceInitializer collectionReferenceInitializer);
public List<CollectionReferenceInitializer> getArrayReferenceInitializers();
public List<CollectionReferenceInitializer> getNonArrayCollectionReferenceInitializers();
public void add(EntityReferenceInitializer entityReferenceInitializer); public void add(EntityReferenceInitializer entityReferenceInitializer);
public List<EntityReferenceInitializer> getEntityReferenceInitializers();
public RowReader buildRowReader();
} }

View File

@ -0,0 +1,292 @@
/*
* 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.plan2.exec.spi;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.plan2.build.spi.LoadPlanTreePrinter;
import org.hibernate.loader.plan2.exec.internal.AliasResolutionContextImpl;
import org.hibernate.loader.plan2.exec.internal.FetchStats;
import org.hibernate.loader.plan2.exec.internal.LoadQueryJoinAndFetchProcessor;
import org.hibernate.loader.plan2.exec.process.internal.ResultSetProcessorImpl;
import org.hibernate.loader.plan2.exec.process.spi.CollectionReferenceInitializer;
import org.hibernate.loader.plan2.exec.process.spi.EntityReferenceInitializer;
import org.hibernate.loader.plan2.exec.process.spi.ReaderCollector;
import org.hibernate.loader.plan2.exec.process.spi.ResultSetProcessor;
import org.hibernate.loader.plan2.exec.query.internal.SelectStatementBuilder;
import org.hibernate.loader.plan2.exec.query.spi.QueryBuildingParameters;
import org.hibernate.loader.plan2.spi.CollectionReturn;
import org.hibernate.loader.plan2.spi.FetchSource;
import org.hibernate.loader.plan2.spi.LoadPlan;
import org.hibernate.loader.plan2.spi.QuerySpace;
import org.hibernate.loader.plan2.spi.Return;
import org.hibernate.sql.ConditionFragment;
import org.hibernate.sql.DisjunctionFragment;
import org.hibernate.sql.InFragment;
/**
* @author Gail Badner
*/
public abstract class AbstractLoadQueryDetails implements LoadQueryDetails {
private final LoadPlan loadPlan;
private final String[] keyColumnNames;
private final Return rootReturn;
private final LoadQueryJoinAndFetchProcessor queryProcessor;
private String sqlStatement;
private ResultSetProcessor resultSetProcessor;
/**
* @param rootReturn The root return reference we are processing
* @param select The SelectStatementBuilder
* @param helper The Join/Fetch helper
* @param factory The SessionFactory
* @param buildingParameters The query building context
* @param rootAlias The table alias to use
* @param rootLoadable The persister
* @param readerCollector Collector for EntityReferenceInitializer and CollectionReferenceInitializer references
*/
protected AbstractLoadQueryDetails(
LoadPlan loadPlan,
AliasResolutionContextImpl aliasResolutionContext,
QueryBuildingParameters buildingParameters,
String[] keyColumnNames,
Return rootReturn,
SessionFactoryImplementor factory) {
this.keyColumnNames = keyColumnNames;
this.rootReturn = rootReturn;
this.loadPlan = loadPlan;
this.queryProcessor = new LoadQueryJoinAndFetchProcessor( aliasResolutionContext, buildingParameters, factory );
}
protected QuerySpace getQuerySpace(String querySpaceUid) {
return loadPlan.getQuerySpaces().getQuerySpaceByUid( querySpaceUid );
}
@Override
public String getSqlStatement() {
return sqlStatement;
}
@Override
public ResultSetProcessor getResultSetProcessor() {
return resultSetProcessor;
}
protected final Return getRootReturn() {
return rootReturn;
}
protected final AliasResolutionContext getAliasResolutionContext() {
return queryProcessor.getAliasResolutionContext();
}
protected final QueryBuildingParameters getQueryBuildingParameters() {
return queryProcessor.getQueryBuildingParameters();
}
protected final SessionFactoryImplementor getSessionFactory() {
return queryProcessor.getSessionFactory();
}
/**
* Main entry point for properly handling the FROM clause and and joins and restrictions
*
*/
protected void generate() {
// There are 2 high-level requirements to perform here:
// 1) Determine the SQL required to carry out the given LoadPlan (and fulfill
// {@code LoadQueryDetails#getSqlStatement()}). SelectStatementBuilder collects the ongoing efforts to
// build the needed SQL.
// 2) Determine how to read information out of the ResultSet resulting from executing the indicated SQL
// (the SQL aliases). ReaderCollector and friends are where this work happens, ultimately
// producing a ResultSetProcessor
final SelectStatementBuilder select = new SelectStatementBuilder( queryProcessor.getSessionFactory().getDialect() );
// LoadPlan is broken down into 2 high-level pieces that we need to process here.
//
// First is the QuerySpaces, which roughly equates to the SQL FROM-clause. We'll cycle through
// those first, generating aliases into the AliasContext in addition to writing SQL FROM-clause information
// into SelectStatementBuilder. The AliasContext is populated here and the reused while process the SQL
// SELECT-clause into the SelectStatementBuilder and then again also to build the ResultSetProcessor
applyRootReturnTableFragments( select );
if ( shouldApplyRootReturnFilterBeforeKeyRestriction() ) {
applyRootReturnFilterRestrictions( select );
// add restrictions...
// first, the load key restrictions (which entity(s)/collection(s) do we want to load?)
applyKeyRestriction(
select,
getRootTableAlias(),
keyColumnNames,
getQueryBuildingParameters().getBatchSize()
);
}
else {
// add restrictions...
// first, the load key restrictions (which entity(s)/collection(s) do we want to load?)
applyKeyRestriction(
select,
getRootTableAlias(),
keyColumnNames,
getQueryBuildingParameters().getBatchSize()
);
applyRootReturnFilterRestrictions( select );
}
applyRootReturnWhereJoinRestrictions( select );
applyRootReturnOrderByFragments( select );
// then move on to joins...
applyRootReturnSelectFragments( select );
queryProcessor.processQuerySpaceJoins( getRootQuerySpace(), select );
// Next, we process the Returns and Fetches building the SELECT clause and at the same time building
// Readers for reading the described results out of a SQL ResultSet
FetchStats fetchStats = null;
if ( FetchSource.class.isInstance( rootReturn ) ) {
fetchStats = queryProcessor.processFetches(
(FetchSource) rootReturn,
select,
getReaderCollector()
);
}
else if ( CollectionReturn.class.isInstance( rootReturn ) ) {
final CollectionReturn collectionReturn = (CollectionReturn) rootReturn;
if ( collectionReturn.getElementGraph() != null ) {
fetchStats = queryProcessor.processFetches(
collectionReturn.getElementGraph(),
select,
getReaderCollector()
);
}
// TODO: what about index???
}
LoadPlanTreePrinter.INSTANCE.logTree( loadPlan, queryProcessor.getAliasResolutionContext() );
this.sqlStatement = select.toStatementString();
this.resultSetProcessor = new ResultSetProcessorImpl(
loadPlan,
getReaderCollector().buildRowReader(),
fetchStats != null && fetchStats.hasSubselectFetches()
);
}
protected abstract ReaderCollector getReaderCollector();
protected abstract QuerySpace getRootQuerySpace();
protected abstract String getRootTableAlias();
protected abstract boolean shouldApplyRootReturnFilterBeforeKeyRestriction();
protected abstract void applyRootReturnSelectFragments(SelectStatementBuilder selectStatementBuilder );
protected abstract void applyRootReturnTableFragments(SelectStatementBuilder selectStatementBuilder);
protected abstract void applyRootReturnFilterRestrictions(SelectStatementBuilder selectStatementBuilder);
protected abstract void applyRootReturnWhereJoinRestrictions(SelectStatementBuilder selectStatementBuilder);
protected abstract void applyRootReturnOrderByFragments(SelectStatementBuilder selectStatementBuilder);
private static 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() );
}
}
protected abstract static class ReaderCollectorImpl implements ReaderCollector {
private final List<EntityReferenceInitializer> entityReferenceInitializers = new ArrayList<EntityReferenceInitializer>();
private List<CollectionReferenceInitializer> arrayReferenceInitializers;
private List<CollectionReferenceInitializer> collectionReferenceInitializers;
@Override
public void add(CollectionReferenceInitializer collectionReferenceInitializer) {
if ( collectionReferenceInitializer.getCollectionReference().getCollectionPersister().isArray() ) {
if ( arrayReferenceInitializers == null ) {
arrayReferenceInitializers = new ArrayList<CollectionReferenceInitializer>();
}
arrayReferenceInitializers.add( collectionReferenceInitializer );
}
else {
if ( collectionReferenceInitializers == null ) {
collectionReferenceInitializers = new ArrayList<CollectionReferenceInitializer>();
}
collectionReferenceInitializers.add( collectionReferenceInitializer );
}
}
@Override
public void add(EntityReferenceInitializer entityReferenceInitializer) {
entityReferenceInitializers.add( entityReferenceInitializer );
}
public final List<EntityReferenceInitializer> getEntityReferenceInitializers() {
return entityReferenceInitializers;
}
public List<CollectionReferenceInitializer> getArrayReferenceInitializers() {
return arrayReferenceInitializers;
}
public List<CollectionReferenceInitializer> getNonArrayCollectionReferenceInitializers() {
return collectionReferenceInitializers;
}
}
}

View File

@ -0,0 +1,130 @@
/*
* 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.plan2.exec.spi;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.loader.plan2.exec.internal.AliasResolutionContextImpl;
import org.hibernate.loader.plan2.exec.internal.Helper;
import org.hibernate.loader.plan2.exec.query.internal.SelectStatementBuilder;
import org.hibernate.loader.plan2.exec.query.spi.QueryBuildingParameters;
import org.hibernate.loader.plan2.spi.CollectionQuerySpace;
import org.hibernate.loader.plan2.spi.CollectionReturn;
import org.hibernate.loader.plan2.spi.EntityReference;
import org.hibernate.loader.plan2.spi.JoinDefinedByMetadata;
import org.hibernate.loader.plan2.spi.LoadPlan;
import org.hibernate.persister.collection.CollectionPropertyNames;
import org.hibernate.persister.entity.OuterJoinLoadable;
/**
* @author Gail Badner
*/
public class BasicCollectionLoadQueryDetails extends CollectionLoadQueryDetails {
/**
* Constructs a EntityLoadQueryDetails object from the given inputs.
*
* @param loadPlan The load plan
* @param buildingParameters And influencers that would affect the generated SQL (mostly we are concerned with those
* that add additional joins here)
* @param factory The SessionFactory
*
* @return The EntityLoadQueryDetails
*/
public static CollectionLoadQueryDetails makeForBatching(
LoadPlan loadPlan,
QueryBuildingParameters buildingParameters,
SessionFactoryImplementor factory) {
final CollectionReturn rootReturn = Helper.INSTANCE.extractRootReturn( loadPlan, CollectionReturn.class );
final AliasResolutionContextImpl aliasResolutionContext = new AliasResolutionContextImpl( factory );
return new BasicCollectionLoadQueryDetails(
loadPlan,
aliasResolutionContext,
rootReturn,
buildingParameters,
factory
);
}
protected BasicCollectionLoadQueryDetails(
LoadPlan loadPlan,
AliasResolutionContextImpl aliasResolutionContext,
CollectionReturn rootReturn,
QueryBuildingParameters buildingParameters,
SessionFactoryImplementor factory) {
super(
loadPlan,
aliasResolutionContext,
rootReturn,
buildingParameters,
factory
);
generate();
}
@Override
protected String getRootTableAlias() {
return getCollectionReferenceAliases().getCollectionTableAlias();
}
@Override
protected void applyRootReturnSelectFragments(SelectStatementBuilder selectStatementBuilder) {
selectStatementBuilder.appendSelectClauseFragment(
getQueryableCollection().selectFragment(
getCollectionReferenceAliases().getCollectionTableAlias(),
getCollectionReferenceAliases().getCollectionColumnAliases().getSuffix()
)
);
if ( getQueryableCollection().isManyToMany() ) {
final OuterJoinLoadable elementPersister = (OuterJoinLoadable) getQueryableCollection().getElementPersister();
selectStatementBuilder.appendSelectClauseFragment(
elementPersister.selectFragment(
getCollectionReferenceAliases().getElementTableAlias(),
getCollectionReferenceAliases().getEntityElementColumnAliases().getSuffix()
)
);
}
super.applyRootReturnSelectFragments( selectStatementBuilder );
}
@Override
protected void applyRootReturnTableFragments(SelectStatementBuilder selectStatementBuilder) {
selectStatementBuilder.appendFromClauseFragment(
getQueryableCollection().getTableName(),
getCollectionReferenceAliases().getCollectionTableAlias()
);
}
@Override
protected void applyRootReturnOrderByFragments(SelectStatementBuilder selectStatementBuilder) {
final String manyToManyOrdering = getQueryableCollection().getManyToManyOrderByString(
getCollectionReferenceAliases().getElementTableAlias()
);
if ( StringHelper.isNotEmpty( manyToManyOrdering ) ) {
selectStatementBuilder.appendOrderByFragment( manyToManyOrdering );
}
super.applyRootReturnOrderByFragments( selectStatementBuilder );
}
}

View File

@ -0,0 +1,215 @@
/*
* 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.plan2.exec.spi;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.loader.plan2.exec.internal.AliasResolutionContextImpl;
import org.hibernate.loader.plan2.exec.internal.EntityReferenceAliasesImpl;
import org.hibernate.loader.plan2.exec.process.internal.CollectionReferenceInitializerImpl;
import org.hibernate.loader.plan2.exec.process.internal.CollectionReturnReader;
import org.hibernate.loader.plan2.exec.process.internal.EntityReferenceInitializerImpl;
import org.hibernate.loader.plan2.exec.process.internal.ResultSetProcessingContextImpl;
import org.hibernate.loader.plan2.exec.process.spi.AbstractRowReader;
import org.hibernate.loader.plan2.exec.process.spi.CollectionReferenceInitializer;
import org.hibernate.loader.plan2.exec.process.spi.ReaderCollector;
import org.hibernate.loader.plan2.exec.process.spi.RowReader;
import org.hibernate.loader.plan2.exec.query.internal.SelectStatementBuilder;
import org.hibernate.loader.plan2.exec.query.spi.QueryBuildingParameters;
import org.hibernate.loader.plan2.spi.CollectionQuerySpace;
import org.hibernate.loader.plan2.spi.CollectionReturn;
import org.hibernate.loader.plan2.spi.EntityReference;
import org.hibernate.loader.plan2.spi.LoadPlan;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.OuterJoinLoadable;
/**
* Handles interpreting a LoadPlan (for loading of a collection) by:<ul>
* <li>generating the SQL query to perform</li>
* <li>creating the readers needed to read the results from the SQL's ResultSet</li>
* </ul>
*
* @author Gail Badner
*/
public abstract class CollectionLoadQueryDetails extends AbstractLoadQueryDetails {
private final CollectionReferenceAliases collectionReferenceAliases;
private final ReaderCollector readerCollector;
protected CollectionLoadQueryDetails(
LoadPlan loadPlan,
AliasResolutionContextImpl aliasResolutionContext,
CollectionReturn rootReturn,
QueryBuildingParameters buildingParameters,
SessionFactoryImplementor factory) {
super(
loadPlan,
aliasResolutionContext,
buildingParameters,
( (QueryableCollection) rootReturn.getCollectionPersister() ).getKeyColumnNames(),
rootReturn,
// collectionReferenceAliases.getCollectionTableAlias(),
// collectionReferenceAliases.getCollectionColumnAliases().getSuffix(),
// loadPlan.getQuerySpaces().getQuerySpaceByUid( rootReturn.getQuerySpaceUid() ),
// (OuterJoinLoadable) rootReturn.getCollectionPersister(),
factory
);
this.collectionReferenceAliases = aliasResolutionContext.generateCollectionReferenceAliases(
rootReturn.getQuerySpaceUid(),
rootReturn.getCollectionPersister()
);
this.readerCollector = new CollectionLoaderReaderCollectorImpl(
new CollectionReturnReader( rootReturn ),
new CollectionReferenceInitializerImpl( rootReturn, collectionReferenceAliases )
);
if ( rootReturn.getCollectionPersister().getElementType().isEntityType() ) {
final EntityReference elementEntityReference = rootReturn.getElementGraph().resolveEntityReference();
final EntityReferenceAliases elementEntityReferenceAliases = new EntityReferenceAliasesImpl(
collectionReferenceAliases.getElementTableAlias(),
collectionReferenceAliases.getEntityElementColumnAliases()
);
aliasResolutionContext.registerQuerySpaceAliases(
elementEntityReference.getQuerySpaceUid(),
elementEntityReferenceAliases
);
readerCollector.add(
new EntityReferenceInitializerImpl( elementEntityReference, elementEntityReferenceAliases )
);
}
if ( rootReturn.getCollectionPersister().hasIndex() &&
rootReturn.getCollectionPersister().getIndexType().isEntityType() ) {
final EntityReference indexEntityReference = rootReturn.getIndexGraph().resolveEntityReference();
final EntityReferenceAliases indexEntityReferenceAliases = aliasResolutionContext.generateEntityReferenceAliases(
indexEntityReference.getQuerySpaceUid(),
indexEntityReference.getEntityPersister()
);
readerCollector.add(
new EntityReferenceInitializerImpl( indexEntityReference, indexEntityReferenceAliases )
);
}
}
protected CollectionReturn getRootCollectionReturn() {
return (CollectionReturn) getRootReturn();
}
@Override
protected ReaderCollector getReaderCollector() {
return readerCollector;
}
@Override
protected CollectionQuerySpace getRootQuerySpace() {
return (CollectionQuerySpace) getQuerySpace( getRootCollectionReturn().getQuerySpaceUid() );
}
protected CollectionReferenceAliases getCollectionReferenceAliases() {
return collectionReferenceAliases;
}
protected QueryableCollection getQueryableCollection() {
return (QueryableCollection) getRootCollectionReturn().getCollectionPersister();
}
@Override
protected boolean shouldApplyRootReturnFilterBeforeKeyRestriction() {
return true;
}
@Override
protected void applyRootReturnSelectFragments(SelectStatementBuilder selectStatementBuilder) {
if ( getQueryableCollection().hasIndex() &&
getQueryableCollection().getIndexType().isEntityType() ) {
final EntityReference indexEntityReference = getRootCollectionReturn().getIndexGraph().resolveEntityReference();
final EntityReferenceAliases indexEntityReferenceAliases = getAliasResolutionContext().resolveEntityReferenceAliases(
indexEntityReference.getQuerySpaceUid()
);
selectStatementBuilder.appendSelectClauseFragment(
( (OuterJoinLoadable) indexEntityReference.getEntityPersister() ).selectFragment(
indexEntityReferenceAliases.getTableAlias(),
indexEntityReferenceAliases.getColumnAliases().getSuffix()
)
);
}
}
@Override
protected void applyRootReturnFilterRestrictions(SelectStatementBuilder selectStatementBuilder) {
selectStatementBuilder.appendRestrictions(
getQueryableCollection().filterFragment(
getRootTableAlias(),
getQueryBuildingParameters().getQueryInfluencers().getEnabledFilters()
)
);
}
@Override
protected void applyRootReturnWhereJoinRestrictions(SelectStatementBuilder selectStatementBuilder) {
}
@Override
protected void applyRootReturnOrderByFragments(SelectStatementBuilder selectStatementBuilder) {
final String ordering = getQueryableCollection().getSQLOrderByString( getRootTableAlias() );
if ( StringHelper.isNotEmpty( ordering ) ) {
selectStatementBuilder.appendOrderByFragment( ordering );
}
}
private static class CollectionLoaderReaderCollectorImpl extends ReaderCollectorImpl {
private final CollectionReturnReader collectionReturnReader;
public CollectionLoaderReaderCollectorImpl(
CollectionReturnReader collectionReturnReader,
CollectionReferenceInitializer collectionReferenceInitializer) {
this.collectionReturnReader = collectionReturnReader;
add( collectionReferenceInitializer );
}
@Override
public RowReader buildRowReader() {
return new CollectionLoaderRowReader( this );
}
@Override
public CollectionReturnReader getReturnReader() {
return collectionReturnReader;
}
}
public static class CollectionLoaderRowReader extends AbstractRowReader {
private final CollectionReturnReader rootReturnReader;
public CollectionLoaderRowReader(CollectionLoaderReaderCollectorImpl collectionLoaderReaderCollector) {
super( collectionLoaderReaderCollector );
this.rootReturnReader = collectionLoaderReaderCollector.getReturnReader();
}
@Override
protected Object readLogicalRow(ResultSet resultSet, ResultSetProcessingContextImpl context) throws SQLException {
return rootReturnReader.read( resultSet, context );
}
}
}

View File

@ -25,9 +25,7 @@ package org.hibernate.loader.plan2.exec.spi;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
@ -36,35 +34,26 @@ import org.hibernate.Session;
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.CoreLogging; import org.hibernate.internal.CoreLogging;
import org.hibernate.loader.plan2.build.spi.LoadPlanTreePrinter;
import org.hibernate.loader.plan2.exec.internal.AliasResolutionContextImpl; import org.hibernate.loader.plan2.exec.internal.AliasResolutionContextImpl;
import org.hibernate.loader.plan2.exec.internal.FetchStats;
import org.hibernate.loader.plan2.exec.internal.Helper; import org.hibernate.loader.plan2.exec.internal.Helper;
import org.hibernate.loader.plan2.exec.internal.LoadQueryJoinAndFetchProcessor;
import org.hibernate.loader.plan2.exec.process.internal.EntityReferenceInitializerImpl; import org.hibernate.loader.plan2.exec.process.internal.EntityReferenceInitializerImpl;
import org.hibernate.loader.plan2.exec.process.internal.EntityReturnReader; import org.hibernate.loader.plan2.exec.process.internal.EntityReturnReader;
import org.hibernate.loader.plan2.exec.process.internal.ResultSetProcessingContextImpl; import org.hibernate.loader.plan2.exec.process.internal.ResultSetProcessingContextImpl;
import org.hibernate.loader.plan2.exec.process.internal.ResultSetProcessorHelper; import org.hibernate.loader.plan2.exec.process.internal.ResultSetProcessorHelper;
import org.hibernate.loader.plan2.exec.process.internal.ResultSetProcessorImpl;
import org.hibernate.loader.plan2.exec.process.spi.AbstractRowReader; import org.hibernate.loader.plan2.exec.process.spi.AbstractRowReader;
import org.hibernate.loader.plan2.exec.process.spi.CollectionReferenceInitializer;
import org.hibernate.loader.plan2.exec.process.spi.EntityReferenceInitializer; import org.hibernate.loader.plan2.exec.process.spi.EntityReferenceInitializer;
import org.hibernate.loader.plan2.exec.process.spi.ReaderCollector; import org.hibernate.loader.plan2.exec.process.spi.ReaderCollector;
import org.hibernate.loader.plan2.exec.process.spi.ResultSetProcessingContext; import org.hibernate.loader.plan2.exec.process.spi.ResultSetProcessingContext;
import org.hibernate.loader.plan2.exec.process.spi.ResultSetProcessor;
import org.hibernate.loader.plan2.exec.process.spi.RowReader; import org.hibernate.loader.plan2.exec.process.spi.RowReader;
import org.hibernate.loader.plan2.exec.query.internal.SelectStatementBuilder; import org.hibernate.loader.plan2.exec.query.internal.SelectStatementBuilder;
import org.hibernate.loader.plan2.exec.query.spi.QueryBuildingParameters; import org.hibernate.loader.plan2.exec.query.spi.QueryBuildingParameters;
import org.hibernate.loader.plan2.spi.EntityQuerySpace;
import org.hibernate.loader.plan2.spi.EntityReturn; import org.hibernate.loader.plan2.spi.EntityReturn;
import org.hibernate.loader.plan2.spi.LoadPlan; import org.hibernate.loader.plan2.spi.LoadPlan;
import org.hibernate.loader.plan2.spi.QuerySpaces; import org.hibernate.loader.plan2.spi.QuerySpace;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Joinable;
import org.hibernate.persister.entity.OuterJoinLoadable; import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.persister.entity.Queryable; import org.hibernate.persister.entity.Queryable;
import org.hibernate.sql.ConditionFragment;
import org.hibernate.sql.DisjunctionFragment;
import org.hibernate.sql.InFragment;
import org.hibernate.type.ComponentType; import org.hibernate.type.ComponentType;
import org.hibernate.type.Type; import org.hibernate.type.Type;
@ -76,24 +65,9 @@ import org.hibernate.type.Type;
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class EntityLoadQueryDetails implements LoadQueryDetails { public class EntityLoadQueryDetails extends AbstractLoadQueryDetails {
private static final Logger log = CoreLogging.logger( EntityLoadQueryDetails.class ); private static final Logger log = CoreLogging.logger( EntityLoadQueryDetails.class );
private final LoadPlan loadPlan;
private final String sqlStatement;
private final ResultSetProcessor resultSetProcessor;
@Override
public String getSqlStatement() {
return sqlStatement;
}
@Override
public ResultSetProcessor getResultSetProcessor() {
return resultSetProcessor;
}
/** /**
* Constructs a EntityLoadQueryDetails object from the given inputs. * Constructs a EntityLoadQueryDetails object from the given inputs.
* *
@ -113,309 +87,171 @@ public class EntityLoadQueryDetails implements LoadQueryDetails {
final int batchSize = buildingParameters.getBatchSize(); final int batchSize = buildingParameters.getBatchSize();
final boolean shouldUseOptionalEntityInformation = batchSize == 1; final boolean shouldUseOptionalEntityInformation = batchSize == 1;
final EntityReturn rootReturn = Helper.INSTANCE.extractRootReturn( loadPlan, EntityReturn.class );
final String[] keyColumnNamesToUse = keyColumnNames != null
? keyColumnNames
: ( (Queryable) rootReturn.getEntityPersister() ).getIdentifierColumnNames();
// Should be just one querySpace (of type EntityQuerySpace) in querySpaces. Should we validate that?
// Should we make it a util method on Helper like we do for extractRootReturn ?
final AliasResolutionContextImpl aliasResolutionContext = new AliasResolutionContextImpl( factory );
return new EntityLoadQueryDetails( return new EntityLoadQueryDetails(
loadPlan, loadPlan,
keyColumnNames, keyColumnNamesToUse,
shouldUseOptionalEntityInformation, aliasResolutionContext,
rootReturn,
buildingParameters, buildingParameters,
factory factory
); );
} }
private final EntityReferenceAliases entityReferenceAliases;
private final ReaderCollector readerCollector;
protected EntityLoadQueryDetails( protected EntityLoadQueryDetails(
LoadPlan loadPlan, LoadPlan loadPlan,
String[] keyColumnNames, String[] keyColumnNames,
boolean shouldUseOptionalEntityInformation, AliasResolutionContextImpl aliasResolutionContext,
EntityReturn rootReturn,
QueryBuildingParameters buildingParameters, QueryBuildingParameters buildingParameters,
SessionFactoryImplementor factory) { SessionFactoryImplementor factory) {
this.loadPlan = loadPlan; super(
final AliasResolutionContextImpl aliasResolutionContext = new AliasResolutionContextImpl( factory ); loadPlan,
// LoadPlanTreePrinter.INSTANCE.logTree( loadPlan, aliasResolutionContext );
// if ( log.isDebugEnabled() ) {
// log.debug( LoadPlanTreePrinter.INSTANCE.toString( loadPlan ) );
// }
// There are 2 high-level requirements to perform here:
// 1) Determine the SQL required to carry out the given LoadPlan (and fulfill
// {@code LoadQueryDetails#getSqlStatement()}). SelectStatementBuilder collects the ongoing efforts to
// build the needed SQL.
// 2) Determine how to read information out of the ResultSet resulting from executing the indicated SQL
// (the SQL aliases). ReaderCollector and friends are where this work happens, ultimately
// producing a ResultSetProcessor
final SelectStatementBuilder select = new SelectStatementBuilder( factory.getDialect() );
final EntityReturn rootReturn = Helper.INSTANCE.extractRootReturn( loadPlan, EntityReturn.class );
final ReaderCollectorImpl readerCollector = new ReaderCollectorImpl();
final LoadQueryJoinAndFetchProcessor helper = new LoadQueryJoinAndFetchProcessor( aliasResolutionContext , buildingParameters, factory );
final String[] keyColumnNamesToUse = keyColumnNames != null
? keyColumnNames
: ( (Queryable) rootReturn.getEntityPersister() ).getIdentifierColumnNames();
// LoadPlan is broken down into 2 high-level pieces that we need to process here.
//
// First is the QuerySpaces, which roughly equates to the SQL FROM-clause. We'll cycle through
// those first, generating aliases into the AliasContext in addition to writing SQL FROM-clause information
// into SelectStatementBuilder. The AliasContext is populated here and the reused while process the SQL
// SELECT-clause into the SelectStatementBuilder and then again also to build the ResultSetProcessor
processQuerySpaces(
loadPlan.getQuerySpaces(),
select,
keyColumnNamesToUse,
helper,
aliasResolutionContext, aliasResolutionContext,
buildingParameters, buildingParameters,
keyColumnNames,
rootReturn,
factory factory
); );
this.entityReferenceAliases = aliasResolutionContext.generateEntityReferenceAliases(
// Next, we process the Returns and Fetches building the SELECT clause and at the same time building rootReturn.getQuerySpaceUid(),
// Readers for reading the described results out of a SQL ResultSet rootReturn.getEntityPersister()
final FetchStats fetchStats = processReturnAndFetches(
rootReturn,
select,
helper,
readerCollector,
aliasResolutionContext
); );
this.readerCollector = new EntityLoaderReaderCollectorImpl(
LoadPlanTreePrinter.INSTANCE.logTree( loadPlan, aliasResolutionContext ); new EntityReturnReader( rootReturn ),
new EntityReferenceInitializerImpl( rootReturn, entityReferenceAliases, true )
this.sqlStatement = select.toStatementString();
this.resultSetProcessor = new ResultSetProcessorImpl(
loadPlan,
readerCollector.buildRowReader(),
fetchStats.hasSubselectFetches()
); );
generate();
} }
/** private EntityReturn getRootEntityReturn() {
* Main entry point for handling building the SQL SELECT clause and the corresponding Readers, return (EntityReturn) getRootReturn();
*
* @param rootReturn The root return reference we are processing
* @param select The SelectStatementBuilder
* @param helper The Join/Fetch helper
* @param readerCollector Collector for EntityReferenceInitializer and CollectionReferenceInitializer references
* @param aliasResolutionContext The alias resolution context
*
* @return Stats about the processed fetches
*/
private FetchStats processReturnAndFetches(
EntityReturn rootReturn,
SelectStatementBuilder select,
LoadQueryJoinAndFetchProcessor helper,
ReaderCollectorImpl readerCollector,
AliasResolutionContextImpl aliasResolutionContext) {
final EntityReferenceAliases entityReferenceAliases = aliasResolutionContext.resolveEntityReferenceAliases(
rootReturn.getQuerySpaceUid()
);
final OuterJoinLoadable rootLoadable = (OuterJoinLoadable) rootReturn.getEntityPersister();
// add the root persister SELECT fragments...
select.appendSelectClauseFragment(
rootLoadable.selectFragment(
entityReferenceAliases.getTableAlias(),
entityReferenceAliases.getColumnAliases().getSuffix()
)
);
final FetchStats fetchStats = helper.processFetches(
rootReturn,
select,
readerCollector
);
readerCollector.setRootReturnReader( new EntityReturnReader( rootReturn, entityReferenceAliases ) );
readerCollector.add( new EntityReferenceInitializerImpl( rootReturn, entityReferenceAliases, true ) );
return fetchStats;
} }
/**
* Main entry point for properly handling the FROM clause and and joins and restrictions
*
* @param querySpaces The QuerySpaces
* @param select The SelectStatementBuilder
* @param keyColumnNamesToUse The column names to use from the entity table (space) in crafting the entity restriction
* (which entity/entities are we interested in?)
* @param helper The Join/Fetch helper
* @param aliasResolutionContext yadda
* @param buildingParameters yadda
* @param factory yadda
*/
private void processQuerySpaces(
QuerySpaces querySpaces,
SelectStatementBuilder select,
String[] keyColumnNamesToUse,
LoadQueryJoinAndFetchProcessor helper,
AliasResolutionContextImpl aliasResolutionContext,
QueryBuildingParameters buildingParameters,
SessionFactoryImplementor factory) {
// Should be just one querySpace (of type EntityQuerySpace) in querySpaces. Should we validate that?
// Should we make it a util method on Helper like we do for extractRootReturn ?
final EntityQuerySpace rootQuerySpace = Helper.INSTANCE.extractRootQuerySpace(
querySpaces,
EntityQuerySpace.class
);
final EntityReferenceAliases entityReferenceAliases = aliasResolutionContext.generateEntityReferenceAliases(
rootQuerySpace.getUid(),
rootQuerySpace.getEntityPersister()
);
final String rootTableAlias = entityReferenceAliases.getTableAlias();
applyTableFragments(
select,
factory,
buildingParameters,
rootTableAlias,
(OuterJoinLoadable) rootQuerySpace.getEntityPersister()
);
// add restrictions...
// first, the load key restrictions (which entity(s) do we want to load?)
applyKeyRestriction(
select,
entityReferenceAliases.getTableAlias(),
keyColumnNamesToUse,
buildingParameters.getBatchSize()
);
// don't quite remember why these 2 anymore, todo : research that and document this code or remove it etc..
final OuterJoinLoadable rootLoadable = (OuterJoinLoadable) rootQuerySpace.getEntityPersister();
final Queryable rootQueryable = (Queryable) rootQuerySpace.getEntityPersister();
select.appendRestrictions(
rootQueryable.filterFragment(
entityReferenceAliases.getTableAlias(),
Collections.emptyMap()
)
);
select.appendRestrictions(
rootLoadable.whereJoinFragment(
entityReferenceAliases.getTableAlias(),
true,
true
)
);
// then move on to joins...
helper.processQuerySpaceJoins( rootQuerySpace, select );
}
/** /**
* Applies "table fragments" to the FROM-CLAUSE of the given SelectStatementBuilder for the given Loadable * Applies "table fragments" to the FROM-CLAUSE of the given SelectStatementBuilder for the given Loadable
* *
* @param select The SELECT statement builder * @param select The SELECT statement builder
* @param factory The SessionFactory
* @param buildingParameters The query building context
* @param rootAlias The table alias to use
* @param rootLoadable The persister
* *
* @see org.hibernate.persister.entity.OuterJoinLoadable#fromTableFragment(java.lang.String) * @see org.hibernate.persister.entity.OuterJoinLoadable#fromTableFragment(java.lang.String)
* @see org.hibernate.persister.entity.Joinable#fromJoinFragment(java.lang.String, boolean, boolean) * @see org.hibernate.persister.entity.Joinable#fromJoinFragment(java.lang.String, boolean, boolean)
*/ */
private void applyTableFragments( protected void applyRootReturnTableFragments(SelectStatementBuilder select) {
SelectStatementBuilder select,
SessionFactoryImplementor factory,
QueryBuildingParameters buildingParameters,
String rootAlias,
OuterJoinLoadable rootLoadable) {
final String fromTableFragment; final String fromTableFragment;
if ( buildingParameters.getLockOptions() != null ) { final String rootAlias = entityReferenceAliases.getTableAlias();
fromTableFragment = factory.getDialect().appendLockHint( final OuterJoinLoadable outerJoinLoadable = (OuterJoinLoadable) getRootEntityReturn().getEntityPersister();
buildingParameters.getLockOptions(), if ( getQueryBuildingParameters().getLockOptions() != null ) {
rootLoadable.fromTableFragment( rootAlias ) fromTableFragment = getSessionFactory().getDialect().appendLockHint(
getQueryBuildingParameters().getLockOptions(),
outerJoinLoadable.fromTableFragment( rootAlias )
); );
select.setLockOptions( buildingParameters.getLockOptions() ); select.setLockOptions( getQueryBuildingParameters().getLockOptions() );
} }
else if ( buildingParameters.getLockMode() != null ) { else if ( getQueryBuildingParameters().getLockMode() != null ) {
fromTableFragment = factory.getDialect().appendLockHint( fromTableFragment = getSessionFactory().getDialect().appendLockHint(
buildingParameters.getLockMode(), getQueryBuildingParameters().getLockMode(),
rootLoadable.fromTableFragment( rootAlias ) outerJoinLoadable.fromTableFragment( rootAlias )
); );
select.setLockMode( buildingParameters.getLockMode() ); select.setLockMode( getQueryBuildingParameters().getLockMode() );
} }
else { else {
fromTableFragment = rootLoadable.fromTableFragment( rootAlias ); fromTableFragment = outerJoinLoadable.fromTableFragment( rootAlias );
} }
select.appendFromClauseFragment( fromTableFragment + rootLoadable.fromJoinFragment( rootAlias, true, true ) ); select.appendFromClauseFragment( fromTableFragment + outerJoinLoadable.fromJoinFragment( rootAlias, true, true ) );
} }
private static class ReaderCollectorImpl implements ReaderCollector { protected void applyRootReturnFilterRestrictions(SelectStatementBuilder selectStatementBuilder) {
private EntityReturnReader rootReturnReader; final Queryable rootQueryable = (Queryable) getRootEntityReturn().getEntityPersister();
private final List<EntityReferenceInitializer> entityReferenceInitializers = new ArrayList<EntityReferenceInitializer>(); selectStatementBuilder.appendRestrictions(
private List<CollectionReferenceInitializer> arrayReferenceInitializers; rootQueryable.filterFragment(
private List<CollectionReferenceInitializer> collectionReferenceInitializers; entityReferenceAliases.getTableAlias(),
Collections.emptyMap()
)
);
}
@Override protected void applyRootReturnWhereJoinRestrictions(SelectStatementBuilder selectStatementBuilder) {
public void add(CollectionReferenceInitializer collectionReferenceInitializer) { final Joinable joinable = (OuterJoinLoadable) getRootEntityReturn().getEntityPersister();
if ( collectionReferenceInitializer.getCollectionReference().getCollectionPersister().isArray() ) { selectStatementBuilder.appendRestrictions(
if ( arrayReferenceInitializers == null ) { joinable.whereJoinFragment(
arrayReferenceInitializers = new ArrayList<CollectionReferenceInitializer>(); entityReferenceAliases.getTableAlias(),
} true,
arrayReferenceInitializers.add( collectionReferenceInitializer ); true
} )
else { );
if ( collectionReferenceInitializers == null ) { }
collectionReferenceInitializers = new ArrayList<CollectionReferenceInitializer>();
} @Override
collectionReferenceInitializers.add( collectionReferenceInitializer ); protected void applyRootReturnOrderByFragments(SelectStatementBuilder selectStatementBuilder) {
} }
@Override
protected ReaderCollector getReaderCollector() {
return readerCollector;
}
@Override
protected QuerySpace getRootQuerySpace() {
return getQuerySpace( getRootEntityReturn().getQuerySpaceUid() );
}
@Override
protected String getRootTableAlias() {
return entityReferenceAliases.getTableAlias();
}
@Override
protected boolean shouldApplyRootReturnFilterBeforeKeyRestriction() {
return false;
}
protected void applyRootReturnSelectFragments(SelectStatementBuilder selectStatementBuilder) {
final OuterJoinLoadable outerJoinLoadable = (OuterJoinLoadable) getRootEntityReturn().getEntityPersister();
selectStatementBuilder.appendSelectClauseFragment(
outerJoinLoadable.selectFragment(
entityReferenceAliases.getTableAlias(),
entityReferenceAliases.getColumnAliases().getSuffix()
)
);
}
private static class EntityLoaderReaderCollectorImpl extends ReaderCollectorImpl {
private final EntityReturnReader entityReturnReader;
public EntityLoaderReaderCollectorImpl(
EntityReturnReader entityReturnReader,
EntityReferenceInitializer entityReferenceInitializer) {
this.entityReturnReader = entityReturnReader;
add( entityReferenceInitializer );
} }
@Override @Override
public void add(EntityReferenceInitializer entityReferenceInitializer) {
if ( EntityReturnReader.class.isInstance( entityReferenceInitializer ) ) {
setRootReturnReader( (EntityReturnReader) entityReferenceInitializer );
}
entityReferenceInitializers.add( entityReferenceInitializer );
}
public RowReader buildRowReader() { public RowReader buildRowReader() {
return new EntityLoaderRowReader( return new EntityLoaderRowReader( this );
rootReturnReader,
entityReferenceInitializers,
arrayReferenceInitializers,
collectionReferenceInitializers
);
} }
public void setRootReturnReader(EntityReturnReader entityReturnReader) { @Override
if ( rootReturnReader != null ) { public EntityReturnReader getReturnReader() {
throw new IllegalStateException( "Root return reader already set" ); return entityReturnReader;
}
rootReturnReader = entityReturnReader;
} }
} }
public static class EntityLoaderRowReader extends AbstractRowReader { public static class EntityLoaderRowReader extends AbstractRowReader {
private final EntityReturnReader rootReturnReader; private final EntityReturnReader rootReturnReader;
private final List<EntityReferenceInitializer> entityReferenceInitializers;
private final List<CollectionReferenceInitializer> arrayReferenceInitializers;
private final List<CollectionReferenceInitializer> collectionReferenceInitializers;
public EntityLoaderRowReader( public EntityLoaderRowReader(EntityLoaderReaderCollectorImpl entityLoaderReaderCollector) {
EntityReturnReader rootReturnReader, super( entityLoaderReaderCollector );
List<EntityReferenceInitializer> entityReferenceInitializers, this.rootReturnReader = entityLoaderReaderCollector.getReturnReader();
List<CollectionReferenceInitializer> arrayReferenceInitializers,
List<CollectionReferenceInitializer> collectionReferenceInitializers) {
this.rootReturnReader = rootReturnReader;
this.entityReferenceInitializers = entityReferenceInitializers != null
? entityReferenceInitializers
: Collections.<EntityReferenceInitializer>emptyList();
this.arrayReferenceInitializers = arrayReferenceInitializers != null
? arrayReferenceInitializers
: Collections.<CollectionReferenceInitializer>emptyList();
this.collectionReferenceInitializers = collectionReferenceInitializers != null
? collectionReferenceInitializers
: Collections.<CollectionReferenceInitializer>emptyList();
} }
@Override @Override
@ -459,62 +295,9 @@ public class EntityLoadQueryDetails implements LoadQueryDetails {
} }
} }
@Override
protected List<EntityReferenceInitializer> getEntityReferenceInitializers() {
return entityReferenceInitializers;
}
@Override
protected List<CollectionReferenceInitializer> getCollectionReferenceInitializers() {
return collectionReferenceInitializers;
}
@Override
protected List<CollectionReferenceInitializer> getArrayReferenceInitializers() {
return arrayReferenceInitializers;
}
@Override @Override
protected Object readLogicalRow(ResultSet resultSet, ResultSetProcessingContextImpl context) throws SQLException { protected Object readLogicalRow(ResultSet resultSet, ResultSetProcessingContextImpl context) throws SQLException {
return rootReturnReader.read( resultSet, context ); return rootReturnReader.read( resultSet, context );
} }
} }
private static void applyKeyRestriction(SelectStatementBuilder select, String alias, String[] keyColumnNames, int batchSize) {
if ( keyColumnNames.length==1 ) {
// NOT A COMPOSITE KEY
// for batching, use "foo in (?, ?, ?)" for batching
// for no batching, use "foo = ?"
// (that distinction is handled inside InFragment)
final InFragment in = new InFragment().setColumn( alias, keyColumnNames[0] );
for ( int i = 0; i < batchSize; i++ ) {
in.addValue( "?" );
}
select.appendRestrictions( in.toFragmentString() );
}
else {
// A COMPOSITE KEY...
final ConditionFragment keyRestrictionBuilder = new ConditionFragment()
.setTableAlias( alias )
.setCondition( keyColumnNames, "?" );
final String keyRestrictionFragment = keyRestrictionBuilder.toFragmentString();
StringBuilder restrictions = new StringBuilder();
if ( batchSize==1 ) {
// for no batching, use "foo = ? and bar = ?"
restrictions.append( keyRestrictionFragment );
}
else {
// for batching, use "( (foo = ? and bar = ?) or (foo = ? and bar = ?) )"
restrictions.append( '(' );
DisjunctionFragment df = new DisjunctionFragment();
for ( int i=0; i<batchSize; i++ ) {
df.addCondition( keyRestrictionFragment );
}
restrictions.append( df.toFragmentString() );
restrictions.append( ')' );
}
select.appendRestrictions( restrictions.toString() );
}
}
} }

View File

@ -0,0 +1,124 @@
/*
* 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.plan2.exec.spi;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.plan2.exec.internal.AliasResolutionContextImpl;
import org.hibernate.loader.plan2.exec.internal.Helper;
import org.hibernate.loader.plan2.exec.query.internal.SelectStatementBuilder;
import org.hibernate.loader.plan2.exec.query.spi.QueryBuildingParameters;
import org.hibernate.loader.plan2.spi.CollectionReturn;
import org.hibernate.loader.plan2.spi.EntityReference;
import org.hibernate.loader.plan2.spi.LoadPlan;
import org.hibernate.persister.entity.OuterJoinLoadable;
/**
* @author Gail Badner
*/
public class OneToManyLoadQueryDetails extends CollectionLoadQueryDetails {
/**
* Constructs a EntityLoadQueryDetails object from the given inputs.
*
* @param loadPlan The load plan
* @param buildingParameters And influencers that would affect the generated SQL (mostly we are concerned with those
* that add additional joins here)
* @param factory The SessionFactory
*
* @return The EntityLoadQueryDetails
*/
public static CollectionLoadQueryDetails makeForBatching(
LoadPlan loadPlan,
QueryBuildingParameters buildingParameters,
SessionFactoryImplementor factory) {
final CollectionReturn rootReturn = Helper.INSTANCE.extractRootReturn( loadPlan, CollectionReturn.class );
final AliasResolutionContextImpl aliasResolutionContext = new AliasResolutionContextImpl( factory );
return new OneToManyLoadQueryDetails(
loadPlan,
aliasResolutionContext,
rootReturn,
buildingParameters,
factory
);
}
protected OneToManyLoadQueryDetails(
LoadPlan loadPlan,
AliasResolutionContextImpl aliasResolutionContext,
CollectionReturn rootReturn,
QueryBuildingParameters buildingParameters,
SessionFactoryImplementor factory) {
super(
loadPlan,
aliasResolutionContext,
rootReturn,
buildingParameters,
factory
);
generate();
}
@Override
protected String getRootTableAlias() {
return getElementEntityReferenceAliases().getTableAlias();
}
@Override
protected void applyRootReturnSelectFragments(SelectStatementBuilder selectStatementBuilder) {
selectStatementBuilder.appendSelectClauseFragment(
getQueryableCollection().selectFragment(
null,
null,
//getCollectionReferenceAliases().getCollectionTableAlias(),
getElementEntityReferenceAliases().getTableAlias(),
getElementEntityReferenceAliases().getColumnAliases().getSuffix(),
getCollectionReferenceAliases().getCollectionColumnAliases().getSuffix(),
true
)
);
super.applyRootReturnSelectFragments( selectStatementBuilder );
}
@Override
protected void applyRootReturnTableFragments(SelectStatementBuilder selectStatementBuilder) {
final OuterJoinLoadable elementOuterJoinLoadable =
(OuterJoinLoadable) getElementEntityReference().getEntityPersister();
//final String tableAlias = getCollectionReferenceAliases().getCollectionTableAlias();
final String tableAlias = getElementEntityReferenceAliases().getTableAlias();
final String fragment =
elementOuterJoinLoadable.fromTableFragment( tableAlias ) +
elementOuterJoinLoadable.fromJoinFragment( tableAlias, true, true);
selectStatementBuilder.appendFromClauseFragment( fragment );
}
private EntityReference getElementEntityReference() {
return getRootCollectionReturn().getElementGraph().resolveEntityReference();
}
private EntityReferenceAliases getElementEntityReferenceAliases() {
return getAliasResolutionContext().resolveEntityReferenceAliases( getElementEntityReference().getQuerySpaceUid() );
}
}

View File

@ -33,7 +33,7 @@ import org.hibernate.persister.entity.EntityPersister;
public interface EntityReference extends FetchSource { public interface EntityReference extends FetchSource {
/** /**
* Obtain the UID of the QuerySpace (specifically a {@link CollectionQuerySpace}) that this CollectionReference * Obtain the UID of the QuerySpace (specifically a {@link EntityQuerySpace}) that this EntityReference
* refers to. * refers to.
* *
* @return The UID * @return The UID

View File

@ -24,6 +24,7 @@
package org.hibernate.loader.plan2.spi; package org.hibernate.loader.plan2.spi;
import org.hibernate.type.AssociationType; import org.hibernate.type.AssociationType;
import org.hibernate.type.Type;
/** /**
* Specialization of a Join that is defined by the metadata. * Specialization of a Join that is defined by the metadata.
@ -38,6 +39,6 @@ public interface JoinDefinedByMetadata extends Join {
* *
* @return The property name * @return The property name
*/ */
public String getJoinedAssociationPropertyName(); public String getJoinedPropertyName();
public AssociationType getJoinedAssociationPropertyType(); public Type getJoinedPropertyType();
} }

View File

@ -34,6 +34,7 @@ import java.util.Comparator;
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.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -5240,9 +5241,28 @@ public abstract class AbstractEntityPersister
EntityIdentifierDefinitionHelper.buildNonEncapsulatedCompositeIdentifierDefinition( this ); EntityIdentifierDefinitionHelper.buildNonEncapsulatedCompositeIdentifierDefinition( this );
} }
private void collectAttributeDefinitions(List<AttributeDefinition> definitions, EntityMetamodel metamodel) { private void collectAttributeDefinitions(
Map<String,AttributeDefinition> attributeDefinitionsByName,
EntityMetamodel metamodel) {
for ( int i = 0; i < metamodel.getPropertySpan(); i++ ) { for ( int i = 0; i < metamodel.getPropertySpan(); i++ ) {
definitions.add( metamodel.getProperties()[i] ); final AttributeDefinition attributeDefinition = metamodel.getProperties()[i];
// Don't replace an attribute definition if it is already in attributeDefinitionsByName
// because the new value will be from a subclass.
final AttributeDefinition oldAttributeDefinition = attributeDefinitionsByName.get(
attributeDefinition.getName()
);
if ( oldAttributeDefinition != null ) {
if ( LOG.isTraceEnabled() ) {
LOG.tracef(
"Ignoring subclass attribute definition [%s.%s] because it is defined in a superclass ",
entityMetamodel.getName(),
attributeDefinition.getName()
);
}
}
else {
attributeDefinitionsByName.put( attributeDefinition.getName(), attributeDefinition );
}
} }
// see if there are any subclass persisters... // see if there are any subclass persisters...
@ -5259,7 +5279,7 @@ public abstract class AbstractEntityPersister
} }
try { try {
final EntityPersister subClassEntityPersister = factory.getEntityPersister( subClassEntityName ); final EntityPersister subClassEntityPersister = factory.getEntityPersister( subClassEntityName );
collectAttributeDefinitions( definitions, subClassEntityPersister.getEntityMetamodel() ); collectAttributeDefinitions( attributeDefinitionsByName, subClassEntityPersister.getEntityMetamodel() );
} }
catch (MappingException e) { catch (MappingException e) {
throw new IllegalStateException( throw new IllegalStateException(
@ -5284,8 +5304,8 @@ public abstract class AbstractEntityPersister
// to try and drive SQL generation on these (which we do ultimately). A possible solution there // 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 // would be to delay all SQL generation until postInstantiate
List<AttributeDefinition> attributeDefinitions = new ArrayList<AttributeDefinition>(); Map<String,AttributeDefinition> attributeDefinitionsByName = new LinkedHashMap<String,AttributeDefinition>();
collectAttributeDefinitions( attributeDefinitions, getEntityMetamodel() ); collectAttributeDefinitions( attributeDefinitionsByName, getEntityMetamodel() );
// EntityMetamodel currentEntityMetamodel = this.getEntityMetamodel(); // EntityMetamodel currentEntityMetamodel = this.getEntityMetamodel();
@ -5303,7 +5323,9 @@ public abstract class AbstractEntityPersister
// } // }
// } // }
this.attributeDefinitions = Collections.unmodifiableList( attributeDefinitions ); this.attributeDefinitions = Collections.unmodifiableList(
new ArrayList<AttributeDefinition>( attributeDefinitionsByName.values() )
);
// // todo : leverage the attribute definitions housed on EntityMetamodel // // 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) // // for that to work, we'd have to be able to walk our super entity persister(s)
// this.attributeDefinitions = new Iterable<AttributeDefinition>() { // this.attributeDefinitions = new Iterable<AttributeDefinition>() {

View File

@ -37,6 +37,7 @@ import org.hibernate.persister.collection.CollectionPersister;
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.type.AssociationType; import org.hibernate.type.AssociationType;
import org.hibernate.type.EntityType;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
@ -104,11 +105,18 @@ public class FetchStrategyHelper {
return FetchStyle.JOIN; return FetchStyle.JOIN;
} }
if ( mappingFetchMode == FetchMode.SELECT ) {
return FetchStyle.SELECT;
}
if ( type.isEntityType() ) { if ( type.isEntityType() ) {
EntityPersister persister = (EntityPersister) type.getAssociatedJoinable( sessionFactory ); EntityPersister persister = (EntityPersister) type.getAssociatedJoinable( sessionFactory );
if ( persister.isBatchLoadable() ) { if ( persister.isBatchLoadable() ) {
return FetchStyle.BATCH; return FetchStyle.BATCH;
} }
else if ( !persister.hasProxy() ) {
return FetchStyle.JOIN;
}
} }
else { else {
CollectionPersister persister = (CollectionPersister) type.getAssociatedJoinable( sessionFactory ); CollectionPersister persister = (CollectionPersister) type.getAssociatedJoinable( sessionFactory );

View File

@ -241,7 +241,7 @@ public class MetamodelGraphWalker {
try { try {
final Type collectionIndexType = collectionIndexDefinition.getType(); final Type collectionIndexType = collectionIndexDefinition.getType();
if ( collectionIndexType.isComponentType() ) { if ( collectionIndexType.isComponentType() ) {
visitCompositeDefinition( collectionIndexDefinition.toCompositeDefinition() ); visitAttributes( collectionIndexDefinition.toCompositeDefinition() );
} }
else if ( collectionIndexType.isAssociationType() ) { else if ( collectionIndexType.isAssociationType() ) {
visitEntityDefinition( collectionIndexDefinition.toEntityDefinition() ); visitEntityDefinition( collectionIndexDefinition.toEntityDefinition() );

View File

@ -31,6 +31,7 @@ import javax.persistence.ManyToOne;
import javax.persistence.OneToMany; import javax.persistence.OneToMany;
import java.util.List; import java.util.List;
import org.hibernate.LockMode;
import org.hibernate.engine.spi.CascadingActions; import org.hibernate.engine.spi.CascadingActions;
import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.loader.plan2.build.internal.CascadeStyleLoadPlanBuildingAssociationVisitationStrategy; import org.hibernate.loader.plan2.build.internal.CascadeStyleLoadPlanBuildingAssociationVisitationStrategy;
@ -40,6 +41,7 @@ import org.hibernate.loader.plan2.build.spi.MetamodelDrivenLoadPlanBuilder;
import org.hibernate.loader.plan2.exec.internal.AliasResolutionContextImpl; import org.hibernate.loader.plan2.exec.internal.AliasResolutionContextImpl;
import org.hibernate.loader.plan2.spi.CollectionReturn; import org.hibernate.loader.plan2.spi.CollectionReturn;
import org.hibernate.loader.plan2.spi.EntityFetch; import org.hibernate.loader.plan2.spi.EntityFetch;
import org.hibernate.loader.plan2.spi.EntityReference;
import org.hibernate.loader.plan2.spi.EntityReturn; import org.hibernate.loader.plan2.spi.EntityReturn;
import org.hibernate.loader.plan2.spi.Fetch; import org.hibernate.loader.plan2.spi.Fetch;
import org.hibernate.loader.plan2.spi.LoadPlan; import org.hibernate.loader.plan2.spi.LoadPlan;
@ -70,7 +72,8 @@ public class LoadPlanBuilderTest extends BaseCoreFunctionalTestCase {
EntityPersister ep = (EntityPersister) sessionFactory().getClassMetadata(Message.class); EntityPersister ep = (EntityPersister) sessionFactory().getClassMetadata(Message.class);
FetchStyleLoadPlanBuildingAssociationVisitationStrategy strategy = new FetchStyleLoadPlanBuildingAssociationVisitationStrategy( FetchStyleLoadPlanBuildingAssociationVisitationStrategy strategy = new FetchStyleLoadPlanBuildingAssociationVisitationStrategy(
sessionFactory(), sessionFactory(),
LoadQueryInfluencers.NONE LoadQueryInfluencers.NONE,
LockMode.NONE
); );
LoadPlan plan = MetamodelDrivenLoadPlanBuilder.buildRootEntityLoadPlan( strategy, ep ); LoadPlan plan = MetamodelDrivenLoadPlanBuilder.buildRootEntityLoadPlan( strategy, ep );
assertFalse( plan.hasAnyScalarReturns() ); assertFalse( plan.hasAnyScalarReturns() );
@ -93,7 +96,8 @@ public class LoadPlanBuilderTest extends BaseCoreFunctionalTestCase {
CascadeStyleLoadPlanBuildingAssociationVisitationStrategy strategy = new CascadeStyleLoadPlanBuildingAssociationVisitationStrategy( CascadeStyleLoadPlanBuildingAssociationVisitationStrategy strategy = new CascadeStyleLoadPlanBuildingAssociationVisitationStrategy(
CascadingActions.MERGE, CascadingActions.MERGE,
sessionFactory(), sessionFactory(),
LoadQueryInfluencers.NONE LoadQueryInfluencers.NONE,
LockMode.NONE
); );
LoadPlan plan = MetamodelDrivenLoadPlanBuilder.buildRootEntityLoadPlan( strategy, ep ); LoadPlan plan = MetamodelDrivenLoadPlanBuilder.buildRootEntityLoadPlan( strategy, ep );
assertFalse( plan.hasAnyScalarReturns() ); assertFalse( plan.hasAnyScalarReturns() );
@ -115,7 +119,8 @@ public class LoadPlanBuilderTest extends BaseCoreFunctionalTestCase {
CollectionPersister cp = sessionFactory().getCollectionPersister( Poster.class.getName() + ".messages" ); CollectionPersister cp = sessionFactory().getCollectionPersister( Poster.class.getName() + ".messages" );
FetchStyleLoadPlanBuildingAssociationVisitationStrategy strategy = new FetchStyleLoadPlanBuildingAssociationVisitationStrategy( FetchStyleLoadPlanBuildingAssociationVisitationStrategy strategy = new FetchStyleLoadPlanBuildingAssociationVisitationStrategy(
sessionFactory(), sessionFactory(),
LoadQueryInfluencers.NONE LoadQueryInfluencers.NONE,
LockMode.NONE
); );
LoadPlan plan = MetamodelDrivenLoadPlanBuilder.buildRootCollectionLoadPlan( strategy, cp ); LoadPlan plan = MetamodelDrivenLoadPlanBuilder.buildRootCollectionLoadPlan( strategy, cp );
assertFalse( plan.hasAnyScalarReturns() ); assertFalse( plan.hasAnyScalarReturns() );
@ -123,12 +128,14 @@ public class LoadPlanBuilderTest extends BaseCoreFunctionalTestCase {
Return rtn = plan.getReturns().get( 0 ); Return rtn = plan.getReturns().get( 0 );
CollectionReturn collectionReturn = ExtraAssertions.assertTyping( CollectionReturn.class, rtn ); CollectionReturn collectionReturn = ExtraAssertions.assertTyping( CollectionReturn.class, rtn );
assertNotNull( collectionReturn.getElementGraph() );
assertNotNull( collectionReturn.getElementGraph().getFetches() ); assertNotNull( collectionReturn.getElementGraph().getFetches() );
assertEquals( 1, collectionReturn.getElementGraph().getFetches().length ); // the collection elements are fetched // the collection Message elements are fetched, but Message.poster is not fetched
Fetch fetch = collectionReturn.getElementGraph().getFetches()[0]; // (because that collection is owned by that Poster)
EntityFetch entityFetch = ExtraAssertions.assertTyping( EntityFetch.class, fetch ); assertEquals( 0, collectionReturn.getElementGraph().getFetches().length );
assertNotNull( entityFetch.getFetches() ); EntityReference entityReference = ExtraAssertions.assertTyping( EntityReference.class, collectionReturn.getElementGraph() );
assertEquals( 0, entityFetch.getFetches().length ); assertNotNull( entityReference.getFetches() );
assertEquals( 0, entityReference.getFetches().length );
LoadPlanTreePrinter.INSTANCE.logTree( plan, new AliasResolutionContextImpl( sessionFactory() ) ); LoadPlanTreePrinter.INSTANCE.logTree( plan, new AliasResolutionContextImpl( sessionFactory() ) );
} }

View File

@ -78,7 +78,7 @@ public class LoadPlanStructureAssertionHelper {
); );
// final EntityLoader loader = new EntityLoader( persister, lockMode, sf, influencers ); // final EntityLoader loader = new EntityLoader( persister, lockMode, sf, influencers );
LoadPlan plan = buildLoadPlan( sf, persister, influencers ); LoadPlan plan = buildLoadPlan( sf, persister, influencers, lockMode );
EntityLoadQueryDetails details = EntityLoadQueryDetails.makeForBatching( EntityLoadQueryDetails details = EntityLoadQueryDetails.makeForBatching(
plan, persister.getKeyColumnNames(), plan, persister.getKeyColumnNames(),
new QueryBuildingParameters() { new QueryBuildingParameters() {
@ -110,13 +110,18 @@ public class LoadPlanStructureAssertionHelper {
public LoadPlan buildLoadPlan( public LoadPlan buildLoadPlan(
SessionFactoryImplementor sf, SessionFactoryImplementor sf,
OuterJoinLoadable persister, OuterJoinLoadable persister,
LoadQueryInfluencers influencers) { LoadQueryInfluencers influencers,
FetchStyleLoadPlanBuildingAssociationVisitationStrategy strategy = new FetchStyleLoadPlanBuildingAssociationVisitationStrategy( sf, influencers ); LockMode lockMode) {
FetchStyleLoadPlanBuildingAssociationVisitationStrategy strategy = new FetchStyleLoadPlanBuildingAssociationVisitationStrategy(
sf,
influencers,
lockMode
);
return MetamodelDrivenLoadPlanBuilder.buildRootEntityLoadPlan( strategy, persister ); return MetamodelDrivenLoadPlanBuilder.buildRootEntityLoadPlan( strategy, persister );
} }
public LoadPlan buildLoadPlan(SessionFactoryImplementor sf, OuterJoinLoadable persister) { public LoadPlan buildLoadPlan(SessionFactoryImplementor sf, OuterJoinLoadable persister) {
return buildLoadPlan( sf, persister, LoadQueryInfluencers.NONE ); return buildLoadPlan( sf, persister, LoadQueryInfluencers.NONE, LockMode.NONE );
} }
private void compare(JoinWalker walker, EntityLoadQueryDetails details) { private void compare(JoinWalker walker, EntityLoadQueryDetails details) {

View File

@ -49,7 +49,8 @@ public class Helper implements QueryBuildingParameters {
public LoadPlan buildLoadPlan(SessionFactoryImplementor sf, EntityPersister entityPersister) { public LoadPlan buildLoadPlan(SessionFactoryImplementor sf, EntityPersister entityPersister) {
final FetchStyleLoadPlanBuildingAssociationVisitationStrategy strategy = new FetchStyleLoadPlanBuildingAssociationVisitationStrategy( final FetchStyleLoadPlanBuildingAssociationVisitationStrategy strategy = new FetchStyleLoadPlanBuildingAssociationVisitationStrategy(
sf, sf,
LoadQueryInfluencers.NONE LoadQueryInfluencers.NONE,
LockMode.NONE
); );
return MetamodelDrivenLoadPlanBuilder.buildRootEntityLoadPlan( strategy, entityPersister ); return MetamodelDrivenLoadPlanBuilder.buildRootEntityLoadPlan( strategy, entityPersister );
} }