HHH-8629 : Integrate LoadPlans into collection initializers
This commit is contained in:
parent
0e4c2a9ed5
commit
e1eef18d62
|
@ -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 );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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() + ')';
|
||||||
|
}
|
||||||
|
}
|
|
@ -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() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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() )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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() )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 );
|
||||||
|
|
|
@ -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 );
|
||||||
|
|
|
@ -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 + "]"
|
||||||
);
|
// );
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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 );
|
||||||
|
|
|
@ -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()
|
||||||
);
|
);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,
|
||||||
|
|
|
@ -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 );
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>() {
|
||||||
|
|
|
@ -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 );
|
||||||
|
|
|
@ -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() );
|
||||||
|
|
|
@ -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() ) );
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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 );
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue