HHH-7746 - Investigate alternative batch loading algorithms
(cherry picked from commit 06b0faaf57
)
Conflicts:
hibernate-core/src/main/java/org/hibernate/cfg/Settings.java
hibernate-core/src/main/java/org/hibernate/internal/util/collections/ArrayHelper.java
hibernate-core/src/main/java/org/hibernate/loader/Loader.java
This commit is contained in:
parent
30b3bd1564
commit
ff14688d7c
|
@ -596,4 +596,11 @@ public interface AvailableSettings {
|
|||
public static final String ENABLE_LAZY_LOAD_NO_TRANS = "hibernate.enable_lazy_load_no_trans";
|
||||
|
||||
public static final String HQL_BULK_ID_STRATEGY = "hibernate.hql.bulk_id_strategy";
|
||||
|
||||
/**
|
||||
* Names the {@link org.hibernate.loader.BatchFetchStyle} to use. Can specify either the
|
||||
* {@link org.hibernate.loader.BatchFetchStyle} name (insensitively), or a
|
||||
* {@link org.hibernate.loader.BatchFetchStyle} instance.
|
||||
*/
|
||||
public static final String BATCH_FETCH_STYLE = "hibernate.batch_fetch_style";
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.hibernate.cache.spi.QueryCacheFactory;
|
|||
import org.hibernate.cache.spi.RegionFactory;
|
||||
import org.hibernate.hql.spi.MultiTableBulkIdStrategy;
|
||||
import org.hibernate.hql.spi.QueryTranslatorFactory;
|
||||
import org.hibernate.loader.BatchFetchStyle;
|
||||
import org.hibernate.service.jta.platform.spi.JtaPlatform;
|
||||
import org.hibernate.tuple.entity.EntityTuplizerFactory;
|
||||
|
||||
|
@ -91,6 +92,7 @@ public final class Settings {
|
|||
private JtaPlatform jtaPlatform;
|
||||
|
||||
private MultiTableBulkIdStrategy multiTableBulkIdStrategy;
|
||||
private BatchFetchStyle batchFetchStyle;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -480,4 +482,12 @@ public final class Settings {
|
|||
void setMultiTableBulkIdStrategy(MultiTableBulkIdStrategy multiTableBulkIdStrategy) {
|
||||
this.multiTableBulkIdStrategy = multiTableBulkIdStrategy;
|
||||
}
|
||||
|
||||
public BatchFetchStyle getBatchFetchStyle() {
|
||||
return batchFetchStyle;
|
||||
}
|
||||
|
||||
void setBatchFetchStyle(BatchFetchStyle batchFetchStyle) {
|
||||
this.batchFetchStyle = batchFetchStyle;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,6 +49,7 @@ import org.hibernate.hql.spi.TemporaryTableBulkIdStrategy;
|
|||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
import org.hibernate.internal.util.config.ConfigurationHelper;
|
||||
import org.hibernate.loader.BatchFetchStyle;
|
||||
import org.hibernate.service.ServiceRegistry;
|
||||
import org.hibernate.service.classloading.spi.ClassLoaderService;
|
||||
import org.hibernate.service.jdbc.connections.spi.ConnectionProvider;
|
||||
|
@ -194,6 +195,11 @@ public class SettingsFactory implements Serializable {
|
|||
}
|
||||
settings.setConnectionReleaseMode( releaseMode );
|
||||
|
||||
final BatchFetchStyle batchFetchStyle = BatchFetchStyle.interpret( properties.get( AvailableSettings.BATCH_FETCH_STYLE ) );
|
||||
LOG.debugf( "Using BatchFetchStyle : " + batchFetchStyle.name() );
|
||||
settings.setBatchFetchStyle( batchFetchStyle );
|
||||
|
||||
|
||||
//SQL Generation settings:
|
||||
|
||||
String defaultSchema = properties.getProperty( AvailableSettings.DEFAULT_SCHEMA );
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
*/
|
||||
package org.hibernate.internal.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
|
@ -66,6 +67,17 @@ public final class StringHelper {
|
|||
return buf.toString();
|
||||
}
|
||||
|
||||
public static String joinWithQualifier(String[] values, String qualifier, String deliminator) {
|
||||
int length = values.length;
|
||||
if ( length == 0 ) return "";
|
||||
StringBuilder buf = new StringBuilder( length * values[0].length() )
|
||||
.append( qualify( qualifier, values[0] ) );
|
||||
for ( int i = 1; i < length; i++ ) {
|
||||
buf.append( deliminator ).append( qualify( qualifier, values[i] ) );
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public static String join(String seperator, Iterator objects) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
if ( objects.hasNext() ) buf.append( objects.next() );
|
||||
|
@ -89,6 +101,15 @@ public final class StringHelper {
|
|||
return buf.toString();
|
||||
}
|
||||
|
||||
public static String repeat(String string, int times, String deliminator) {
|
||||
StringBuilder buf = new StringBuilder( ( string.length() * times ) + ( deliminator.length() * (times-1) ) )
|
||||
.append( string );
|
||||
for ( int i = 1; i < times; i++ ) {
|
||||
buf.append( deliminator ).append( string );
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public static String repeat(char character, int times) {
|
||||
char[] buffer = new char[times];
|
||||
Arrays.fill( buffer, character );
|
||||
|
@ -661,4 +682,69 @@ public final class StringHelper {
|
|||
}
|
||||
return unquoted;
|
||||
}
|
||||
|
||||
|
||||
public static final String BATCH_ID_PLACEHOLDER = "$$BATCH_ID_PLACEHOLDER$$";
|
||||
|
||||
public static StringBuilder buildBatchFetchRestrictionFragment(
|
||||
String alias,
|
||||
String[] columnNames,
|
||||
Dialect dialect) {
|
||||
// the general idea here is to just insert a placeholder that we can easily find later...
|
||||
if ( columnNames.length == 1 ) {
|
||||
// non-composite key
|
||||
return new StringBuilder( StringHelper.qualify( alias, columnNames[0] ) )
|
||||
.append( " in (" ).append( BATCH_ID_PLACEHOLDER ).append( ")" );
|
||||
}
|
||||
else {
|
||||
// composite key - the form to use here depends on what the dialect supports.
|
||||
if ( dialect.supportsRowValueConstructorSyntaxInInList() ) {
|
||||
// use : (col1, col2) in ( (?,?), (?,?), ... )
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append( "(" );
|
||||
boolean firstPass = true;
|
||||
String deliminator = "";
|
||||
for ( String columnName : columnNames ) {
|
||||
builder.append( deliminator ).append( StringHelper.qualify( alias, columnName ) );
|
||||
if ( firstPass ) {
|
||||
firstPass = false;
|
||||
deliminator = ",";
|
||||
}
|
||||
}
|
||||
builder.append( ") in (" );
|
||||
builder.append( BATCH_ID_PLACEHOLDER );
|
||||
builder.append( ")" );
|
||||
return builder;
|
||||
}
|
||||
else {
|
||||
// use : ( (col1 = ? and col2 = ?) or (col1 = ? and col2 = ?) or ... )
|
||||
// unfortunately most of this building needs to be held off until we know
|
||||
// the exact number of ids :(
|
||||
return new StringBuilder( "(" ).append( BATCH_ID_PLACEHOLDER ).append( ")" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String expandBatchIdPlaceholder(
|
||||
String sql,
|
||||
Serializable[] ids,
|
||||
String alias,
|
||||
String[] keyColumnNames,
|
||||
Dialect dialect) {
|
||||
if ( keyColumnNames.length == 1 ) {
|
||||
// non-composite
|
||||
return StringHelper.replace( sql, BATCH_ID_PLACEHOLDER, repeat( "?", ids.length, "," ) );
|
||||
}
|
||||
else {
|
||||
// composite
|
||||
if ( dialect.supportsRowValueConstructorSyntaxInInList() ) {
|
||||
final String tuple = "(" + StringHelper.repeat( "?", keyColumnNames.length, "," );
|
||||
return StringHelper.replace( sql, BATCH_ID_PLACEHOLDER, repeat( tuple, ids.length, "," ) );
|
||||
}
|
||||
else {
|
||||
final String keyCheck = joinWithQualifier( keyColumnNames, alias, " and " );
|
||||
return replace( sql, BATCH_ID_PLACEHOLDER, repeat( keyCheck, ids.length, " or " ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
*/
|
||||
package org.hibernate.internal.util.collections;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
@ -31,6 +32,7 @@ import java.util.Collection;
|
|||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.type.Type;
|
||||
|
@ -373,11 +375,42 @@ public final class ArrayHelper {
|
|||
return true;
|
||||
}
|
||||
|
||||
public static Serializable[] extractNonNull(Serializable[] array) {
|
||||
final int nonNullCount = countNonNull( array );
|
||||
final Serializable[] result = new Serializable[nonNullCount];
|
||||
int i = 0;
|
||||
for ( Serializable element : array ) {
|
||||
if ( element != null ) {
|
||||
result[i++] = element;
|
||||
}
|
||||
}
|
||||
if ( i != nonNullCount ) {
|
||||
throw new HibernateException( "Number of non-null elements varied between iterations" );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int countNonNull(Serializable[] array) {
|
||||
int i = 0;
|
||||
for ( Serializable element : array ) {
|
||||
if ( element != null ) {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
public static void main(String... args) {
|
||||
int[] batchSizes = ArrayHelper.getBatchSizes( 32 );
|
||||
|
||||
System.out.println( "Forward ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" );
|
||||
for ( int i = 0; i < batchSizes.length; i++ ) {
|
||||
System.out.println( "[" + i + "] -> " + batchSizes[i] );
|
||||
}
|
||||
|
||||
System.out.println( "Backward ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" );
|
||||
for ( int i = batchSizes.length-1; i >= 0; i-- ) {
|
||||
System.out.println( "[" + i + "] -> " + batchSizes[i] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -187,10 +187,7 @@ public abstract class AbstractEntityJoinWalker extends JoinWalker {
|
|||
public abstract String getComment();
|
||||
|
||||
@Override
|
||||
protected boolean isDuplicateAssociation(
|
||||
final String foreignKeyTable,
|
||||
final String[] foreignKeyColumns
|
||||
) {
|
||||
protected boolean isDuplicateAssociation(final String foreignKeyTable, final String[] foreignKeyColumns) {
|
||||
//disable a join back to this same association
|
||||
final boolean isSameJoin =
|
||||
persister.getTableName().equals( foreignKeyTable ) &&
|
||||
|
@ -201,11 +198,11 @@ public abstract class AbstractEntityJoinWalker extends JoinWalker {
|
|||
|
||||
|
||||
|
||||
protected final Loadable getPersister() {
|
||||
public final Loadable getPersister() {
|
||||
return persister;
|
||||
}
|
||||
|
||||
protected final String getAlias() {
|
||||
public final String getAlias() {
|
||||
return alias;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Defines the style that should be used to perform batch loading. Which style to use is declared using
|
||||
* the "{@value org.hibernate.cfg.AvailableSettings#BATCH_FETCH_STYLE}"
|
||||
* ({@link org.hibernate.cfg.AvailableSettings#BATCH_FETCH_STYLE}) setting
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public enum BatchFetchStyle {
|
||||
/**
|
||||
* The legacy algorithm where we keep a set of pre-built batch sizes based on
|
||||
* {@link org.hibernate.internal.util.collections.ArrayHelper#getBatchSizes}. Batches are performed
|
||||
* using the next-smaller pre-built batch size from the number of existing batchable identifiers.
|
||||
* <p/>
|
||||
* For example, with a batch-size setting of 32 the pre-built batch sizes would be [32, 16, 10, 9, 8, 7, .., 1].
|
||||
* An attempt to batch load 31 identifiers would result in batches of 16, 10, and 5.
|
||||
*/
|
||||
LEGACY,
|
||||
/**
|
||||
* Still keeps the concept of pre-built batch sizes, but uses the next-bigger batch size and pads the extra
|
||||
* identifier placeholders.
|
||||
* <p/>
|
||||
* Using the same example of a batch-size setting of 32 the pre-built batch sizes would be the same. However, the
|
||||
* attempt to batch load 31 identifiers would result just a single batch of size 32. The identifiers to load would
|
||||
* be "padded" (aka, repeated) to make up the difference.
|
||||
*/
|
||||
PADDED,
|
||||
/**
|
||||
* Dynamically builds its SQL based on the actual number of available ids. Does still limit to the batch-size
|
||||
* defined on the entity/collection
|
||||
*/
|
||||
DYNAMIC;
|
||||
|
||||
private static final Logger log = Logger.getLogger( BatchFetchStyle.class );
|
||||
|
||||
public static BatchFetchStyle byName(String name) {
|
||||
return valueOf( name.toUpperCase() );
|
||||
}
|
||||
|
||||
public static BatchFetchStyle interpret(Object setting) {
|
||||
log.tracef( "Interpreting BatchFetchStyle from setting : %s", setting );
|
||||
|
||||
if ( setting == null ) {
|
||||
return LEGACY; // as default
|
||||
}
|
||||
|
||||
if ( BatchFetchStyle.class.isInstance( setting ) ) {
|
||||
return (BatchFetchStyle) setting;
|
||||
}
|
||||
|
||||
try {
|
||||
final BatchFetchStyle byName = byName( setting.toString() );
|
||||
if ( byName != null ) {
|
||||
return byName;
|
||||
}
|
||||
}
|
||||
catch (Exception ignore) {
|
||||
}
|
||||
|
||||
log.debugf( "Unable to interpret given setting [%s] as BatchFetchStyle", setting );
|
||||
|
||||
return LEGACY; // again as default.
|
||||
}
|
||||
}
|
|
@ -122,7 +122,7 @@ public abstract class Loader {
|
|||
*
|
||||
* @return The sql command this loader should use to get its {@link ResultSet}.
|
||||
*/
|
||||
protected abstract String getSQLString();
|
||||
public abstract String getSQLString();
|
||||
|
||||
/**
|
||||
* An array of persisters of entity classes contained in each row of results;
|
||||
|
@ -304,7 +304,7 @@ public abstract class Loader {
|
|||
* persister from each row of the <tt>ResultSet</tt>. If an object is supplied, will attempt to
|
||||
* initialize that object. If a collection is supplied, attempt to initialize that collection.
|
||||
*/
|
||||
private List doQueryAndInitializeNonLazyCollections(
|
||||
public List doQueryAndInitializeNonLazyCollections(
|
||||
final SessionImplementor session,
|
||||
final QueryParameters queryParameters,
|
||||
final boolean returnProxies) throws HibernateException, SQLException {
|
||||
|
@ -316,7 +316,7 @@ public abstract class Loader {
|
|||
);
|
||||
}
|
||||
|
||||
private List doQueryAndInitializeNonLazyCollections(
|
||||
public List doQueryAndInitializeNonLazyCollections(
|
||||
final SessionImplementor session,
|
||||
final QueryParameters queryParameters,
|
||||
final boolean returnProxies,
|
||||
|
@ -1819,7 +1819,7 @@ public abstract class Loader {
|
|||
final SessionImplementor session) throws SQLException {
|
||||
|
||||
// Processing query filters.
|
||||
queryParameters.processFilters( getSQLString(), session );
|
||||
queryParameters.processFilters( sqlStatement, session );
|
||||
|
||||
// Applying LIMIT clause.
|
||||
final LimitHandler limitHandler = getLimitHandler( queryParameters.getFilteredSQL(), queryParameters.getRowSelection() );
|
||||
|
|
|
@ -75,7 +75,8 @@ public abstract class OuterJoinLoader extends BasicLoader {
|
|||
return collectionSuffixes;
|
||||
}
|
||||
|
||||
protected final String getSQLString() {
|
||||
@Override
|
||||
public final String getSQLString() {
|
||||
return sql;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
|
||||
* 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 Middleware LLC.
|
||||
* 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
|
||||
|
@ -20,106 +20,35 @@
|
|||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
package org.hibernate.loader.collection;
|
||||
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.persister.collection.CollectionPersister;
|
||||
import org.hibernate.persister.collection.QueryableCollection;
|
||||
|
||||
/**
|
||||
* "Batch" loads collections, using multiple foreign key values in the
|
||||
* SQL <tt>where</tt> clause.
|
||||
* 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 BatchingCollectionInitializerBuilder
|
||||
* @see BasicCollectionLoader
|
||||
* @see OneToManyLoader
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class BatchingCollectionInitializer implements CollectionInitializer {
|
||||
private final Loader[] loaders;
|
||||
private final int[] batchSizes;
|
||||
private final CollectionPersister collectionPersister;
|
||||
public abstract class BatchingCollectionInitializer implements CollectionInitializer {
|
||||
private final QueryableCollection collectionPersister;
|
||||
|
||||
public BatchingCollectionInitializer(CollectionPersister collPersister, int[] batchSizes, Loader[] loaders) {
|
||||
this.loaders = loaders;
|
||||
this.batchSizes = batchSizes;
|
||||
this.collectionPersister = collPersister;
|
||||
public BatchingCollectionInitializer(QueryableCollection collectionPersister) {
|
||||
this.collectionPersister = collectionPersister;
|
||||
}
|
||||
|
||||
public CollectionPersister getCollectionPersister() {
|
||||
return collectionPersister;
|
||||
}
|
||||
|
||||
public Loader[] getLoaders() {
|
||||
return loaders;
|
||||
public QueryableCollection collectionPersister() {
|
||||
return collectionPersister;
|
||||
}
|
||||
|
||||
public int[] getBatchSizes() {
|
||||
return batchSizes;
|
||||
}
|
||||
|
||||
public void initialize(Serializable id, SessionImplementor session)
|
||||
throws HibernateException {
|
||||
|
||||
Serializable[] batch = session.getPersistenceContext().getBatchFetchQueue()
|
||||
.getCollectionBatch( collectionPersister, 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, collectionPersister.getKeyType() );
|
||||
return; //EARLY EXIT!
|
||||
}
|
||||
}
|
||||
|
||||
loaders[batchSizes.length-1].loadCollection( session, id, collectionPersister.getKeyType() );
|
||||
|
||||
}
|
||||
|
||||
public static CollectionInitializer createBatchingOneToManyInitializer(
|
||||
final QueryableCollection persister,
|
||||
final int maxBatchSize,
|
||||
final SessionFactoryImplementor factory,
|
||||
final LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||
if ( maxBatchSize > 1 ) {
|
||||
int[] batchSizesToCreate = ArrayHelper.getBatchSizes(maxBatchSize);
|
||||
Loader[] loadersToCreate = new Loader[ batchSizesToCreate.length ];
|
||||
for ( int i=0; i<batchSizesToCreate.length; i++ ) {
|
||||
loadersToCreate[i] = new OneToManyLoader( persister, batchSizesToCreate[i], factory, loadQueryInfluencers );
|
||||
}
|
||||
return new BatchingCollectionInitializer( persister, batchSizesToCreate, loadersToCreate );
|
||||
}
|
||||
else {
|
||||
return new OneToManyLoader( persister, factory, loadQueryInfluencers );
|
||||
}
|
||||
}
|
||||
|
||||
public static CollectionInitializer createBatchingCollectionInitializer(
|
||||
final QueryableCollection persister,
|
||||
final int maxBatchSize,
|
||||
final SessionFactoryImplementor factory,
|
||||
final LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||
if ( maxBatchSize > 1 ) {
|
||||
int[] batchSizesToCreate = ArrayHelper.getBatchSizes( maxBatchSize );
|
||||
Loader[] loadersToCreate = new Loader[ batchSizesToCreate.length ];
|
||||
for ( int i=0; i<batchSizesToCreate.length; i++ ) {
|
||||
loadersToCreate[i] = new BasicCollectionLoader( persister, batchSizesToCreate[i], factory, loadQueryInfluencers );
|
||||
}
|
||||
return new BatchingCollectionInitializer(persister, batchSizesToCreate, loadersToCreate);
|
||||
}
|
||||
else {
|
||||
return new BasicCollectionLoader( persister, factory, loadQueryInfluencers );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.persister.collection.QueryableCollection;
|
||||
|
||||
/**
|
||||
* Contract for building {@link CollectionInitializer} instances capable of performing batch-fetch loading.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*
|
||||
* @see org.hibernate.loader.BatchFetchStyle
|
||||
*/
|
||||
public abstract class BatchingCollectionInitializerBuilder {
|
||||
public static BatchingCollectionInitializerBuilder getBuilder(SessionFactoryImplementor factory) {
|
||||
switch ( factory.getSettings().getBatchFetchStyle() ) {
|
||||
case PADDED: {
|
||||
return PaddedBatchingCollectionInitializerBuilder.INSTANCE;
|
||||
}
|
||||
case DYNAMIC: {
|
||||
return DynamicBatchingCollectionInitializerBuilder.INSTANCE;
|
||||
}
|
||||
default: {
|
||||
return LegacyBatchingCollectionInitializerBuilder.INSTANCE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a batch-fetch capable CollectionInitializer for basic and many-to-many collections (collections with
|
||||
* a dedicated collection table).
|
||||
*
|
||||
* @param persister THe collection persister
|
||||
* @param maxBatchSize The maximum number of keys to batch-fetch together
|
||||
* @param factory The SessionFactory
|
||||
* @param influencers Any influencers that should affect the built query
|
||||
*
|
||||
* @return The batch-fetch capable collection initializer
|
||||
*/
|
||||
public CollectionInitializer createBatchingCollectionInitializer(
|
||||
QueryableCollection persister,
|
||||
int maxBatchSize,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers influencers) {
|
||||
if ( maxBatchSize <= 1 ) {
|
||||
// no batching
|
||||
return new BasicCollectionLoader( persister, factory, influencers );
|
||||
}
|
||||
|
||||
return createRealBatchingCollectionInitializer( persister, maxBatchSize, factory, influencers );
|
||||
}
|
||||
|
||||
protected abstract CollectionInitializer createRealBatchingCollectionInitializer(
|
||||
QueryableCollection persister,
|
||||
int maxBatchSize,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers influencers);
|
||||
|
||||
|
||||
/**
|
||||
* Builds a batch-fetch capable CollectionInitializer for one-to-many collections (collections without
|
||||
* a dedicated collection table).
|
||||
*
|
||||
* @param persister THe collection persister
|
||||
* @param maxBatchSize The maximum number of keys to batch-fetch together
|
||||
* @param factory The SessionFactory
|
||||
* @param influencers Any influencers that should affect the built query
|
||||
*
|
||||
* @return The batch-fetch capable collection initializer
|
||||
*/
|
||||
public CollectionInitializer createBatchingOneToManyInitializer(
|
||||
QueryableCollection persister,
|
||||
int maxBatchSize,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers influencers) {
|
||||
if ( maxBatchSize <= 1 ) {
|
||||
// no batching
|
||||
return new OneToManyLoader( persister, factory, influencers );
|
||||
}
|
||||
|
||||
return createRealBatchingOneToManyInitializer( persister, maxBatchSize, factory, influencers );
|
||||
}
|
||||
|
||||
protected abstract CollectionInitializer createRealBatchingOneToManyInitializer(
|
||||
QueryableCollection persister,
|
||||
int maxBatchSize,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers influencers);
|
||||
}
|
|
@ -44,7 +44,7 @@ public abstract class CollectionJoinWalker extends JoinWalker {
|
|||
|
||||
protected StringBuilder whereString(String alias, String[] columnNames, String subselect, int batchSize) {
|
||||
if (subselect==null) {
|
||||
return super.whereString(alias, columnNames, batchSize);
|
||||
return whereString(alias, columnNames, batchSize);
|
||||
}
|
||||
else {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
|
|
|
@ -52,6 +52,10 @@ public class CollectionLoader extends OuterJoinLoader implements CollectionIniti
|
|||
this.collectionPersister = collectionPersister;
|
||||
}
|
||||
|
||||
protected QueryableCollection collectionPersister() {
|
||||
return collectionPersister;
|
||||
}
|
||||
|
||||
protected boolean isSubselectLoadingEnabled() {
|
||||
return hasSubselectLoadableCollections();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,268 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.dialect.pagination.LimitHelper;
|
||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||
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.internal.util.StringHelper;
|
||||
import org.hibernate.internal.util.collections.ArrayHelper;
|
||||
import org.hibernate.loader.JoinWalker;
|
||||
import org.hibernate.loader.Loader;
|
||||
import org.hibernate.persister.collection.QueryableCollection;
|
||||
import org.hibernate.pretty.MessageHelper;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
/**
|
||||
* A BatchingCollectionInitializerBuilder that builds CollectionInitializer instances capable of dynamically building
|
||||
* its batch-fetch SQL based on the actual number of collections keys waiting to be fetched.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class DynamicBatchingCollectionInitializerBuilder extends BatchingCollectionInitializerBuilder {
|
||||
public static final DynamicBatchingCollectionInitializerBuilder INSTANCE = new DynamicBatchingCollectionInitializerBuilder();
|
||||
|
||||
@Override
|
||||
protected CollectionInitializer createRealBatchingCollectionInitializer(
|
||||
QueryableCollection persister,
|
||||
int maxBatchSize,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers influencers) {
|
||||
return new DynamicBatchingCollectionInitializer( persister, maxBatchSize, factory, influencers );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CollectionInitializer createRealBatchingOneToManyInitializer(
|
||||
QueryableCollection persister,
|
||||
int maxBatchSize,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers influencers) {
|
||||
return new DynamicBatchingCollectionInitializer( persister, maxBatchSize, factory, influencers );
|
||||
}
|
||||
|
||||
public static class DynamicBatchingCollectionInitializer extends BatchingCollectionInitializer {
|
||||
private final int maxBatchSize;
|
||||
private final Loader singleKeyLoader;
|
||||
private final DynamicBatchingCollectionLoader batchLoader;
|
||||
|
||||
public DynamicBatchingCollectionInitializer(
|
||||
QueryableCollection collectionPersister,
|
||||
int maxBatchSize,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers influencers) {
|
||||
super( collectionPersister );
|
||||
this.maxBatchSize = maxBatchSize;
|
||||
|
||||
if ( collectionPersister.isOneToMany() ) {
|
||||
this.singleKeyLoader = new OneToManyLoader( collectionPersister, 1, factory, influencers );
|
||||
}
|
||||
else {
|
||||
this.singleKeyLoader = new BasicCollectionLoader( collectionPersister, 1, factory, influencers );
|
||||
}
|
||||
|
||||
this.batchLoader = new DynamicBatchingCollectionLoader( collectionPersister, factory, influencers );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(Serializable id, SessionImplementor session) throws HibernateException {
|
||||
// first, figure out how many batchable ids we have...
|
||||
final Serializable[] batch = session.getPersistenceContext()
|
||||
.getBatchFetchQueue()
|
||||
.getCollectionBatch( collectionPersister(), id, maxBatchSize );
|
||||
final int numberOfIds = ArrayHelper.countNonNull( batch );
|
||||
if ( numberOfIds <= 1 ) {
|
||||
singleKeyLoader.loadCollection( session, id, collectionPersister().getKeyType() );
|
||||
return;
|
||||
}
|
||||
|
||||
final Serializable[] idsToLoad = new Serializable[numberOfIds];
|
||||
System.arraycopy( batch, 0, idsToLoad, 0, numberOfIds );
|
||||
|
||||
batchLoader.doBatchedCollectionLoad( session, idsToLoad, collectionPersister().getKeyType() );
|
||||
}
|
||||
}
|
||||
|
||||
private static class DynamicBatchingCollectionLoader extends CollectionLoader {
|
||||
// todo : this represents another case where the current Loader contract is unhelpful
|
||||
// the other recent case was stored procedure support. Really any place where the SQL
|
||||
// generation is dynamic but the "loading plan" remains constant. The long term plan
|
||||
// is to split Loader into (a) PreparedStatement generation/execution and (b) ResultSet
|
||||
// processing.
|
||||
//
|
||||
// Same holds true for org.hibernate.loader.entity.DynamicBatchingEntityLoaderBuilder.DynamicBatchingEntityLoader
|
||||
//
|
||||
// for now I will essentially semi-re-implement the collection loader contract here to be able to alter
|
||||
// the SQL (specifically to be able to dynamically build the WHERE-clause IN-condition) later, when
|
||||
// we actually know the ids to batch fetch
|
||||
|
||||
private final String sqlTemplate;
|
||||
private final String alias;
|
||||
|
||||
public DynamicBatchingCollectionLoader(
|
||||
QueryableCollection collectionPersister,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers influencers) {
|
||||
super( collectionPersister, factory, influencers );
|
||||
|
||||
JoinWalker walker = buildJoinWalker( collectionPersister, factory, influencers );
|
||||
initFromWalker( walker );
|
||||
this.sqlTemplate = walker.getSQLString();
|
||||
this.alias = StringHelper.generateAlias( collectionPersister.getRole(), 0 );
|
||||
postInstantiate();
|
||||
|
||||
if ( LOG.isDebugEnabled() ) {
|
||||
LOG.debugf(
|
||||
"SQL-template for dynamic collection [%s] batch-fetching : %s",
|
||||
collectionPersister.getRole(),
|
||||
sqlTemplate
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private JoinWalker buildJoinWalker(
|
||||
QueryableCollection collectionPersister,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers influencers) {
|
||||
|
||||
if ( collectionPersister.isOneToMany() ) {
|
||||
return new OneToManyJoinWalker( collectionPersister, -1, null, factory, influencers ) {
|
||||
@Override
|
||||
protected StringBuilder whereString(String alias, String[] columnNames, String subselect, int batchSize) {
|
||||
if ( subselect != null ) {
|
||||
return super.whereString( alias, columnNames, subselect, batchSize );
|
||||
}
|
||||
|
||||
return StringHelper.buildBatchFetchRestrictionFragment( alias, columnNames, getFactory().getDialect() );
|
||||
}
|
||||
};
|
||||
}
|
||||
else {
|
||||
return new BasicCollectionJoinWalker( collectionPersister, -1, null, factory, influencers ) {
|
||||
@Override
|
||||
protected StringBuilder whereString(String alias, String[] columnNames, String subselect, int batchSize) {
|
||||
if ( subselect != null ) {
|
||||
return super.whereString( alias, columnNames, subselect, batchSize );
|
||||
}
|
||||
|
||||
return StringHelper.buildBatchFetchRestrictionFragment( alias, columnNames, getFactory().getDialect() );
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public final void doBatchedCollectionLoad(
|
||||
final SessionImplementor session,
|
||||
final Serializable[] ids,
|
||||
final Type type) throws HibernateException {
|
||||
|
||||
if ( LOG.isDebugEnabled() )
|
||||
LOG.debugf( "Batch loading collection: %s",
|
||||
MessageHelper.collectionInfoString( getCollectionPersisters()[0], ids, getFactory() ) );
|
||||
|
||||
final Type[] idTypes = new Type[ids.length];
|
||||
Arrays.fill( idTypes, type );
|
||||
final QueryParameters queryParameters = new QueryParameters( idTypes, ids, ids );
|
||||
|
||||
final String sql = StringHelper.expandBatchIdPlaceholder(
|
||||
sqlTemplate,
|
||||
ids,
|
||||
alias,
|
||||
collectionPersister().getKeyColumnNames(),
|
||||
getFactory().getDialect()
|
||||
);
|
||||
|
||||
try {
|
||||
final PersistenceContext persistenceContext = session.getPersistenceContext();
|
||||
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 {
|
||||
try {
|
||||
doTheLoad( sql, queryParameters, session );
|
||||
}
|
||||
finally {
|
||||
persistenceContext.afterLoad();
|
||||
}
|
||||
persistenceContext.initializeNonLazyCollections();
|
||||
}
|
||||
finally {
|
||||
// Restore the original default
|
||||
persistenceContext.setDefaultReadOnly( defaultReadOnlyOrig );
|
||||
}
|
||||
}
|
||||
catch ( SQLException e ) {
|
||||
throw getFactory().getSQLExceptionHelper().convert(
|
||||
e,
|
||||
"could not initialize a collection batch: " +
|
||||
MessageHelper.collectionInfoString( collectionPersister(), ids, getFactory() ),
|
||||
sql
|
||||
);
|
||||
}
|
||||
|
||||
LOG.debug( "Done batch load" );
|
||||
|
||||
}
|
||||
|
||||
private void doTheLoad(String sql, QueryParameters queryParameters, SessionImplementor session) throws SQLException {
|
||||
final RowSelection selection = queryParameters.getRowSelection();
|
||||
final int maxRows = LimitHelper.hasMaxRows( selection ) ?
|
||||
selection.getMaxRows() :
|
||||
Integer.MAX_VALUE;
|
||||
|
||||
final List<AfterLoadAction> afterLoadActions = Collections.emptyList();
|
||||
final ResultSet rs = executeQueryStatement( sql, queryParameters, false, afterLoadActions, session );
|
||||
final Statement st = rs.getStatement();
|
||||
try {
|
||||
processResultSet( rs, queryParameters, session, true, null, maxRows, afterLoadActions );
|
||||
}
|
||||
finally {
|
||||
st.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
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.persister.collection.QueryableCollection;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class LegacyBatchingCollectionInitializerBuilder extends BatchingCollectionInitializerBuilder {
|
||||
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( collectionPersister(), 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, collectionPersister().getKeyType() );
|
||||
return; //EARLY EXIT!
|
||||
}
|
||||
}
|
||||
|
||||
loaders[batchSizes.length-1].loadCollection( session, id, collectionPersister().getKeyType() );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
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.persister.collection.QueryableCollection;
|
||||
|
||||
/**
|
||||
* A batch-fetch capable CollectionInitializer that performs batch-fetching using the padded style. See
|
||||
* {@link org.hibernate.loader.BatchFetchStyle} for a discussion of the different styles.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*
|
||||
* @see org.hibernate.loader.BatchFetchStyle#PADDED
|
||||
*/
|
||||
public class PaddedBatchingCollectionInitializerBuilder extends BatchingCollectionInitializerBuilder {
|
||||
public static final PaddedBatchingCollectionInitializerBuilder INSTANCE = new PaddedBatchingCollectionInitializerBuilder();
|
||||
|
||||
@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 PaddedBatchingCollectionInitializer( 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 PaddedBatchingCollectionInitializer( persister, batchSizes, loaders );
|
||||
}
|
||||
|
||||
|
||||
private static class PaddedBatchingCollectionInitializer extends BatchingCollectionInitializer {
|
||||
private final int[] batchSizes;
|
||||
private final Loader[] loaders;
|
||||
|
||||
public PaddedBatchingCollectionInitializer(QueryableCollection persister, int[] batchSizes, Loader[] loaders) {
|
||||
super( persister );
|
||||
|
||||
this.batchSizes = batchSizes;
|
||||
this.loaders = loaders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(Serializable id, SessionImplementor session) throws HibernateException {
|
||||
final Serializable[] batch = session.getPersistenceContext()
|
||||
.getBatchFetchQueue()
|
||||
.getCollectionBatch( collectionPersister(), id, batchSizes[0] );
|
||||
final int numberOfIds = ArrayHelper.countNonNull( batch );
|
||||
if ( numberOfIds <= 1 ) {
|
||||
loaders[batchSizes.length-1].loadCollection( session, id, collectionPersister().getKeyType() );
|
||||
return;
|
||||
}
|
||||
|
||||
// Uses the first batch-size bigger than the number of actual ids in the batch
|
||||
int indexToUse = batchSizes.length-1;
|
||||
for ( int i = 0; i < batchSizes.length-1; i++ ) {
|
||||
if ( batchSizes[i] >= numberOfIds ) {
|
||||
indexToUse = i;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
final Serializable[] idsToLoad = new Serializable[ batchSizes[indexToUse] ];
|
||||
System.arraycopy( batch, 0, idsToLoad, 0, numberOfIds );
|
||||
for ( int i = numberOfIds; i < batchSizes[indexToUse]; i++ ) {
|
||||
idsToLoad[i] = id;
|
||||
}
|
||||
|
||||
loaders[indexToUse].loadCollectionBatch( session, idsToLoad, collectionPersister().getKeyType() );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -301,7 +301,7 @@ public class CustomLoader extends Loader {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected String getSQLString() {
|
||||
public String getSQLString() {
|
||||
return sql;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
|
||||
* 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 Middleware LLC.
|
||||
* 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
|
||||
|
@ -20,135 +20,109 @@
|
|||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
package org.hibernate.loader.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Iterator;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.QueryParameters;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.internal.util.collections.ArrayHelper;
|
||||
import org.hibernate.loader.Loader;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
||||
import org.hibernate.pretty.MessageHelper;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
/**
|
||||
* "Batch" loads entities, using multiple primary key values in the
|
||||
* SQL <tt>where</tt> clause.
|
||||
* The base contract for loaders capable of performing batch-fetch loading of entities using multiple primary key
|
||||
* values in the SQL <tt>WHERE</tt> clause.
|
||||
*
|
||||
* @see EntityLoader
|
||||
* @author Gavin King
|
||||
* @author Steve Ebersole
|
||||
*
|
||||
* @see BatchingEntityLoaderBuilder
|
||||
* @see UniqueEntityLoader
|
||||
*/
|
||||
public class BatchingEntityLoader implements UniqueEntityLoader {
|
||||
public abstract class BatchingEntityLoader implements UniqueEntityLoader {
|
||||
private static final Logger log = Logger.getLogger( BatchingEntityLoader.class );
|
||||
|
||||
private final Loader[] loaders;
|
||||
private final int[] batchSizes;
|
||||
private final EntityPersister persister;
|
||||
private final Type idType;
|
||||
|
||||
public BatchingEntityLoader(EntityPersister persister, int[] batchSizes, Loader[] loaders) {
|
||||
this.batchSizes = batchSizes;
|
||||
this.loaders = loaders;
|
||||
public BatchingEntityLoader(EntityPersister persister) {
|
||||
this.persister = persister;
|
||||
idType = persister.getIdentifierType();
|
||||
}
|
||||
|
||||
private Object getObjectFromList(List results, Serializable id, SessionImplementor session) {
|
||||
// get the right object from the list ... would it be easier to just call getEntity() ??
|
||||
Iterator iter = results.iterator();
|
||||
while ( iter.hasNext() ) {
|
||||
Object obj = iter.next();
|
||||
final boolean equal = idType.isEqual(
|
||||
public EntityPersister persister() {
|
||||
return persister;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public Object load(Serializable id, Object optionalObject, SessionImplementor session) {
|
||||
return load( id, optionalObject, session, LockOptions.NONE );
|
||||
}
|
||||
|
||||
protected QueryParameters buildQueryParameters(
|
||||
Serializable id,
|
||||
Serializable[] ids,
|
||||
Object optionalObject,
|
||||
LockOptions lockOptions) {
|
||||
Type[] types = new Type[ids.length];
|
||||
Arrays.fill( types, persister().getIdentifierType() );
|
||||
|
||||
QueryParameters qp = new QueryParameters();
|
||||
qp.setPositionalParameterTypes( types );
|
||||
qp.setPositionalParameterValues( ids );
|
||||
qp.setOptionalObject( optionalObject );
|
||||
qp.setOptionalEntityName( persister().getEntityName() );
|
||||
qp.setOptionalId( id );
|
||||
qp.setLockOptions( lockOptions );
|
||||
return qp;
|
||||
}
|
||||
|
||||
protected Object getObjectFromList(List results, Serializable id, SessionImplementor session) {
|
||||
for ( Object obj : results ) {
|
||||
final boolean equal = persister.getIdentifierType().isEqual(
|
||||
id,
|
||||
session.getContextEntityIdentifier(obj),
|
||||
session.getContextEntityIdentifier( obj ),
|
||||
session.getFactory()
|
||||
);
|
||||
if ( equal ) return obj;
|
||||
if ( equal ) {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public Object load(Serializable id, Object optionalObject, SessionImplementor session) {
|
||||
// this form is deprecated!
|
||||
return load( id, optionalObject, session, LockOptions.NONE );
|
||||
}
|
||||
|
||||
public Object load(Serializable id, Object optionalObject, SessionImplementor session, LockOptions lockOptions) {
|
||||
Serializable[] batch = session.getPersistenceContext()
|
||||
.getBatchFetchQueue()
|
||||
.getEntityBatch( persister, id, batchSizes[0], persister.getEntityMode() );
|
||||
|
||||
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);
|
||||
final List results = loaders[i].loadEntityBatch(
|
||||
session,
|
||||
smallBatch,
|
||||
idType,
|
||||
optionalObject,
|
||||
persister.getEntityName(),
|
||||
id,
|
||||
persister,
|
||||
lockOptions
|
||||
);
|
||||
return getObjectFromList(results, id, session); //EARLY EXIT
|
||||
}
|
||||
protected Object doBatchLoad(
|
||||
Serializable id,
|
||||
Loader loaderToUse,
|
||||
SessionImplementor session,
|
||||
Serializable[] ids,
|
||||
Object optionalObject,
|
||||
LockOptions lockOptions) {
|
||||
if ( log.isDebugEnabled() ) {
|
||||
log.debugf( "Batch loading entity: %s", MessageHelper.infoString( persister, ids, session.getFactory() ) );
|
||||
}
|
||||
|
||||
return ( (UniqueEntityLoader) loaders[batchSizes.length-1] ).load(id, optionalObject, session);
|
||||
QueryParameters qp = buildQueryParameters( id, ids, optionalObject, lockOptions );
|
||||
|
||||
}
|
||||
|
||||
public static UniqueEntityLoader createBatchingEntityLoader(
|
||||
final OuterJoinLoadable persister,
|
||||
final int maxBatchSize,
|
||||
final LockMode lockMode,
|
||||
final SessionFactoryImplementor factory,
|
||||
final LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||
|
||||
if ( maxBatchSize>1 ) {
|
||||
int[] batchSizesToCreate = ArrayHelper.getBatchSizes(maxBatchSize);
|
||||
Loader[] loadersToCreate = new Loader[ batchSizesToCreate.length ];
|
||||
for ( int i=0; i<batchSizesToCreate.length; i++ ) {
|
||||
loadersToCreate[i] = new EntityLoader(persister, batchSizesToCreate[i], lockMode, factory, loadQueryInfluencers);
|
||||
}
|
||||
return new BatchingEntityLoader(persister, batchSizesToCreate, loadersToCreate);
|
||||
try {
|
||||
final List results = loaderToUse.doQueryAndInitializeNonLazyCollections( session, qp, false );
|
||||
log.debug( "Done entity batch load" );
|
||||
return getObjectFromList(results, id, session);
|
||||
}
|
||||
else {
|
||||
return new EntityLoader(persister, lockMode, factory, loadQueryInfluencers);
|
||||
}
|
||||
}
|
||||
|
||||
public static UniqueEntityLoader createBatchingEntityLoader(
|
||||
final OuterJoinLoadable persister,
|
||||
final int maxBatchSize,
|
||||
final LockOptions lockOptions,
|
||||
final SessionFactoryImplementor factory,
|
||||
final LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||
|
||||
if ( maxBatchSize>1 ) {
|
||||
int[] batchSizesToCreate = ArrayHelper.getBatchSizes(maxBatchSize);
|
||||
Loader[] loadersToCreate = new Loader[ batchSizesToCreate.length ];
|
||||
for ( int i=0; i<batchSizesToCreate.length; i++ ) {
|
||||
loadersToCreate[i] = new EntityLoader(persister, batchSizesToCreate[i], lockOptions, factory, loadQueryInfluencers);
|
||||
}
|
||||
return new BatchingEntityLoader(persister, batchSizesToCreate, loadersToCreate);
|
||||
}
|
||||
else {
|
||||
return new EntityLoader(persister, lockOptions, factory, loadQueryInfluencers);
|
||||
catch ( SQLException sqle ) {
|
||||
throw session.getFactory().getSQLExceptionHelper().convert(
|
||||
sqle,
|
||||
"could not load an entity batch: " + MessageHelper.infoString( persister(), ids, session.getFactory() ),
|
||||
loaderToUse.getSQLString()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* 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.entity;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
||||
|
||||
/**
|
||||
* The contract for building {@link UniqueEntityLoader} capable of performing batch-fetch loading. Intention
|
||||
* is to build these instances, by first calling the static {@link #getBuilder}, and then calling the appropriate
|
||||
* {@link #buildLoader} method.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*
|
||||
* @see org.hibernate.loader.BatchFetchStyle
|
||||
*/
|
||||
public abstract class BatchingEntityLoaderBuilder {
|
||||
public static BatchingEntityLoaderBuilder getBuilder(SessionFactoryImplementor factory) {
|
||||
switch ( factory.getSettings().getBatchFetchStyle() ) {
|
||||
case PADDED: {
|
||||
return PaddedBatchingEntityLoaderBuilder.INSTANCE;
|
||||
}
|
||||
case DYNAMIC: {
|
||||
return DynamicBatchingEntityLoaderBuilder.INSTANCE;
|
||||
}
|
||||
default: {
|
||||
return LegacyBatchingEntityLoaderBuilder.INSTANCE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a batch-fetch capable loader based on the given persister, lock-mode, etc.
|
||||
*
|
||||
* @param persister The entity persister
|
||||
* @param batchSize The maximum number of ids to batch-fetch at once
|
||||
* @param lockMode The lock mode
|
||||
* @param factory The SessionFactory
|
||||
* @param influencers Any influencers that should affect the built query
|
||||
*
|
||||
* @return The loader.
|
||||
*/
|
||||
public UniqueEntityLoader buildLoader(
|
||||
OuterJoinLoadable persister,
|
||||
int batchSize,
|
||||
LockMode lockMode,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers influencers) {
|
||||
if ( batchSize <= 1 ) {
|
||||
// no batching
|
||||
return new EntityLoader( persister, lockMode, factory, influencers );
|
||||
}
|
||||
return buildBatchingLoader( persister, batchSize, lockMode, factory, influencers );
|
||||
}
|
||||
|
||||
protected abstract UniqueEntityLoader buildBatchingLoader(
|
||||
OuterJoinLoadable persister,
|
||||
int batchSize,
|
||||
LockMode lockMode,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers influencers);
|
||||
|
||||
/**
|
||||
* Builds a batch-fetch capable loader based on the given persister, lock-options, etc.
|
||||
*
|
||||
* @param persister The entity persister
|
||||
* @param batchSize The maximum number of ids to batch-fetch at once
|
||||
* @param lockOptions The lock options
|
||||
* @param factory The SessionFactory
|
||||
* @param influencers Any influencers that should affect the built query
|
||||
*
|
||||
* @return The loader.
|
||||
*/
|
||||
public UniqueEntityLoader buildLoader(
|
||||
OuterJoinLoadable persister,
|
||||
int batchSize,
|
||||
LockOptions lockOptions,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers influencers) {
|
||||
if ( batchSize <= 1 ) {
|
||||
// no batching
|
||||
return new EntityLoader( persister, lockOptions, factory, influencers );
|
||||
}
|
||||
return buildBatchingLoader( persister, batchSize, lockOptions, factory, influencers );
|
||||
}
|
||||
|
||||
protected abstract UniqueEntityLoader buildBatchingLoader(
|
||||
OuterJoinLoadable persister,
|
||||
int batchSize,
|
||||
LockOptions lockOptions,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers influencers);
|
||||
}
|
|
@ -0,0 +1,268 @@
|
|||
/*
|
||||
* 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.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.dialect.pagination.LimitHelper;
|
||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||
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.internal.util.StringHelper;
|
||||
import org.hibernate.internal.util.collections.ArrayHelper;
|
||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
||||
import org.hibernate.pretty.MessageHelper;
|
||||
|
||||
/**
|
||||
* A BatchingEntityLoaderBuilder that builds UniqueEntityLoader instances capable of dynamically building
|
||||
* its batch-fetch SQL based on the actual number of entity ids waiting to be fetched.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class DynamicBatchingEntityLoaderBuilder extends BatchingEntityLoaderBuilder {
|
||||
private static final Logger log = Logger.getLogger( DynamicBatchingEntityLoaderBuilder.class );
|
||||
|
||||
public static final DynamicBatchingEntityLoaderBuilder INSTANCE = new DynamicBatchingEntityLoaderBuilder();
|
||||
|
||||
@Override
|
||||
protected UniqueEntityLoader buildBatchingLoader(
|
||||
OuterJoinLoadable persister,
|
||||
int batchSize,
|
||||
LockMode lockMode,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers influencers) {
|
||||
return new DynamicBatchingEntityLoader( persister, batchSize, lockMode, factory, influencers );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UniqueEntityLoader buildBatchingLoader(
|
||||
OuterJoinLoadable persister,
|
||||
int batchSize,
|
||||
LockOptions lockOptions,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers influencers) {
|
||||
return new DynamicBatchingEntityLoader( persister, batchSize, lockOptions, factory, influencers );
|
||||
}
|
||||
|
||||
public static class DynamicBatchingEntityLoader extends BatchingEntityLoader {
|
||||
private final int maxBatchSize;
|
||||
private final UniqueEntityLoader singleKeyLoader;
|
||||
private final DynamicEntityLoader dynamicLoader;
|
||||
|
||||
public DynamicBatchingEntityLoader(
|
||||
OuterJoinLoadable persister,
|
||||
int maxBatchSize,
|
||||
LockMode lockMode,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers loadQueryInfluencers) {
|
||||
super( persister );
|
||||
this.maxBatchSize = maxBatchSize;
|
||||
this.singleKeyLoader = new EntityLoader( persister, 1, lockMode, factory, loadQueryInfluencers );
|
||||
this.dynamicLoader = new DynamicEntityLoader( persister, maxBatchSize, lockMode, factory, loadQueryInfluencers );
|
||||
}
|
||||
|
||||
public DynamicBatchingEntityLoader(
|
||||
OuterJoinLoadable persister,
|
||||
int maxBatchSize,
|
||||
LockOptions lockOptions,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers loadQueryInfluencers) {
|
||||
super( persister );
|
||||
this.maxBatchSize = maxBatchSize;
|
||||
this.singleKeyLoader = new EntityLoader( persister, 1, lockOptions, factory, loadQueryInfluencers );
|
||||
this.dynamicLoader = new DynamicEntityLoader( persister, maxBatchSize, lockOptions, factory, loadQueryInfluencers );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object load(
|
||||
Serializable id,
|
||||
Object optionalObject,
|
||||
SessionImplementor session,
|
||||
LockOptions lockOptions) {
|
||||
final Serializable[] batch = session.getPersistenceContext()
|
||||
.getBatchFetchQueue()
|
||||
.getEntityBatch( persister(), id, maxBatchSize, persister().getEntityMode() );
|
||||
|
||||
final int numberOfIds = ArrayHelper.countNonNull( batch );
|
||||
if ( numberOfIds <= 1 ) {
|
||||
return singleKeyLoader.load( id, optionalObject, session );
|
||||
}
|
||||
|
||||
final Serializable[] idsToLoad = new Serializable[numberOfIds];
|
||||
System.arraycopy( batch, 0, idsToLoad, 0, numberOfIds );
|
||||
|
||||
if ( log.isDebugEnabled() ) {
|
||||
log.debugf( "Batch loading entity: %s", MessageHelper.infoString( persister(), idsToLoad, session.getFactory() ) );
|
||||
}
|
||||
|
||||
QueryParameters qp = buildQueryParameters( id, idsToLoad, optionalObject, lockOptions );
|
||||
List results = dynamicLoader.doEntityBatchFetch( session, qp, idsToLoad );
|
||||
return getObjectFromList( results, id, session );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class DynamicEntityLoader extends EntityLoader {
|
||||
// todo : see the discussion on org.hibernate.loader.collection.DynamicBatchingCollectionInitializerBuilder.DynamicBatchingCollectionLoader
|
||||
|
||||
private final String sqlTemplate;
|
||||
private final String alias;
|
||||
|
||||
public DynamicEntityLoader(
|
||||
OuterJoinLoadable persister,
|
||||
int maxBatchSize,
|
||||
LockOptions lockOptions,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers loadQueryInfluencers) {
|
||||
this( persister, maxBatchSize, lockOptions.getLockMode(), factory, loadQueryInfluencers );
|
||||
}
|
||||
|
||||
public DynamicEntityLoader(
|
||||
OuterJoinLoadable persister,
|
||||
int maxBatchSize,
|
||||
LockMode lockMode,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers loadQueryInfluencers) {
|
||||
super( persister, -1, lockMode, factory, loadQueryInfluencers );
|
||||
|
||||
EntityJoinWalker walker = new EntityJoinWalker(
|
||||
persister,
|
||||
persister.getIdentifierColumnNames(),
|
||||
-1,
|
||||
lockMode,
|
||||
factory,
|
||||
loadQueryInfluencers
|
||||
) {
|
||||
@Override
|
||||
protected StringBuilder whereString(String alias, String[] columnNames, int batchSize) {
|
||||
return StringHelper.buildBatchFetchRestrictionFragment( alias, columnNames, getFactory().getDialect() );
|
||||
}
|
||||
};
|
||||
|
||||
initFromWalker( walker );
|
||||
this.sqlTemplate = walker.getSQLString();
|
||||
this.alias = walker.getAlias();
|
||||
postInstantiate();
|
||||
|
||||
if ( LOG.isDebugEnabled() ) {
|
||||
LOG.debugf(
|
||||
"SQL-template for dynamic entity [%s] batch-fetching [%s] : %s",
|
||||
entityName,
|
||||
lockMode,
|
||||
sqlTemplate
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isSingleRowLoader() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public List doEntityBatchFetch(
|
||||
SessionImplementor session,
|
||||
QueryParameters queryParameters,
|
||||
Serializable[] ids) {
|
||||
final String sql = StringHelper.expandBatchIdPlaceholder(
|
||||
sqlTemplate,
|
||||
ids,
|
||||
alias,
|
||||
persister.getKeyColumnNames(),
|
||||
getFactory().getDialect()
|
||||
);
|
||||
|
||||
try {
|
||||
final PersistenceContext persistenceContext = session.getPersistenceContext();
|
||||
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();
|
||||
List results;
|
||||
try {
|
||||
try {
|
||||
results = doTheLoad( sql, queryParameters, session );
|
||||
}
|
||||
finally {
|
||||
persistenceContext.afterLoad();
|
||||
}
|
||||
persistenceContext.initializeNonLazyCollections();
|
||||
log.debug( "Done batch load" );
|
||||
return results;
|
||||
}
|
||||
finally {
|
||||
// Restore the original default
|
||||
persistenceContext.setDefaultReadOnly( defaultReadOnlyOrig );
|
||||
}
|
||||
}
|
||||
catch ( SQLException sqle ) {
|
||||
throw session.getFactory().getSQLExceptionHelper().convert(
|
||||
sqle,
|
||||
"could not load an entity batch: " + MessageHelper.infoString(
|
||||
getEntityPersisters()[0],
|
||||
ids,
|
||||
session.getFactory()
|
||||
),
|
||||
sql
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private List doTheLoad(String sql, QueryParameters queryParameters, SessionImplementor session) throws SQLException {
|
||||
final RowSelection selection = queryParameters.getRowSelection();
|
||||
final int maxRows = LimitHelper.hasMaxRows( selection ) ?
|
||||
selection.getMaxRows() :
|
||||
Integer.MAX_VALUE;
|
||||
|
||||
final List<AfterLoadAction> afterLoadActions = Collections.emptyList();
|
||||
final ResultSet rs = executeQueryStatement( sql, queryParameters, false, afterLoadActions, session );
|
||||
final Statement st = rs.getStatement();
|
||||
try {
|
||||
return processResultSet( rs, queryParameters, session, false, null, maxRows, afterLoadActions );
|
||||
}
|
||||
finally {
|
||||
st.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* 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.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.LockOptions;
|
||||
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.persister.entity.OuterJoinLoadable;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class LegacyBatchingEntityLoaderBuilder extends BatchingEntityLoaderBuilder {
|
||||
public static final LegacyBatchingEntityLoaderBuilder INSTANCE = new LegacyBatchingEntityLoaderBuilder();
|
||||
|
||||
@Override
|
||||
protected UniqueEntityLoader buildBatchingLoader(
|
||||
OuterJoinLoadable persister,
|
||||
int batchSize,
|
||||
LockMode lockMode,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers influencers) {
|
||||
return new LegacyBatchingEntityLoader( persister, batchSize, lockMode, factory, influencers );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UniqueEntityLoader buildBatchingLoader(
|
||||
OuterJoinLoadable persister,
|
||||
int batchSize,
|
||||
LockOptions lockOptions,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers influencers) {
|
||||
return new LegacyBatchingEntityLoader( persister, batchSize, lockOptions, factory, influencers );
|
||||
}
|
||||
|
||||
public static class LegacyBatchingEntityLoader extends BatchingEntityLoader implements UniqueEntityLoader {
|
||||
private final int[] batchSizes;
|
||||
private final Loader[] loaders;
|
||||
|
||||
public LegacyBatchingEntityLoader(
|
||||
OuterJoinLoadable persister,
|
||||
int maxBatchSize,
|
||||
LockMode lockMode,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers loadQueryInfluencers) {
|
||||
super( persister );
|
||||
this.batchSizes = ArrayHelper.getBatchSizes( maxBatchSize );
|
||||
this.loaders = new Loader[ batchSizes.length ];
|
||||
for ( int i = 0; i < batchSizes.length; i++ ) {
|
||||
this.loaders[i] = new EntityLoader( persister, batchSizes[i], lockMode, factory, loadQueryInfluencers);
|
||||
}
|
||||
}
|
||||
|
||||
public LegacyBatchingEntityLoader(
|
||||
OuterJoinLoadable persister,
|
||||
int maxBatchSize,
|
||||
LockOptions lockOptions,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers loadQueryInfluencers) {
|
||||
super( persister );
|
||||
this.batchSizes = ArrayHelper.getBatchSizes( maxBatchSize );
|
||||
this.loaders = new Loader[ batchSizes.length ];
|
||||
for ( int i = 0; i < batchSizes.length; i++ ) {
|
||||
this.loaders[i] = new EntityLoader( persister, batchSizes[i], lockOptions, factory, loadQueryInfluencers);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object load(Serializable id, Object optionalObject, SessionImplementor session, LockOptions lockOptions) {
|
||||
final Serializable[] batch = session.getPersistenceContext()
|
||||
.getBatchFetchQueue()
|
||||
.getEntityBatch( persister(), id, batchSizes[0], persister().getEntityMode() );
|
||||
|
||||
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);
|
||||
// for now...
|
||||
final List results = loaders[i].loadEntityBatch(
|
||||
session,
|
||||
smallBatch,
|
||||
persister().getIdentifierType(),
|
||||
optionalObject,
|
||||
persister().getEntityName(),
|
||||
id,
|
||||
persister(),
|
||||
lockOptions
|
||||
);
|
||||
return getObjectFromList(results, id, session); //EARLY EXIT
|
||||
}
|
||||
}
|
||||
return ( (UniqueEntityLoader) loaders[batchSizes.length-1] ).load(id, optionalObject, session);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* 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.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.LockOptions;
|
||||
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.persister.entity.OuterJoinLoadable;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
class PaddedBatchingEntityLoaderBuilder extends BatchingEntityLoaderBuilder {
|
||||
public static final PaddedBatchingEntityLoaderBuilder INSTANCE = new PaddedBatchingEntityLoaderBuilder();
|
||||
|
||||
@Override
|
||||
protected UniqueEntityLoader buildBatchingLoader(
|
||||
OuterJoinLoadable persister,
|
||||
int batchSize,
|
||||
LockMode lockMode,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers influencers) {
|
||||
return new PaddedBatchingEntityLoader( persister, batchSize, lockMode, factory, influencers );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UniqueEntityLoader buildBatchingLoader(
|
||||
OuterJoinLoadable persister,
|
||||
int batchSize,
|
||||
LockOptions lockOptions,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers influencers) {
|
||||
return new PaddedBatchingEntityLoader( persister, batchSize, lockOptions, factory, influencers );
|
||||
}
|
||||
|
||||
public static class PaddedBatchingEntityLoader extends BatchingEntityLoader {
|
||||
private final int[] batchSizes;
|
||||
private final Loader[] loaders;
|
||||
|
||||
public PaddedBatchingEntityLoader(
|
||||
OuterJoinLoadable persister,
|
||||
int maxBatchSize,
|
||||
LockMode lockMode,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers loadQueryInfluencers) {
|
||||
super( persister );
|
||||
this.batchSizes = ArrayHelper.getBatchSizes( maxBatchSize );
|
||||
this.loaders = new Loader[ batchSizes.length ];
|
||||
for ( int i = 0; i < batchSizes.length; i++ ) {
|
||||
this.loaders[i] = new EntityLoader( persister, batchSizes[i], lockMode, factory, loadQueryInfluencers);
|
||||
}
|
||||
validate( maxBatchSize );
|
||||
}
|
||||
|
||||
private void validate(int max) {
|
||||
// these are more indicative of internal problems then user error...
|
||||
if ( batchSizes[0] != max ) {
|
||||
throw new HibernateException( "Unexpected batch size spread" );
|
||||
}
|
||||
if ( batchSizes[batchSizes.length-1] != 1 ) {
|
||||
throw new HibernateException( "Unexpected batch size spread" );
|
||||
}
|
||||
}
|
||||
|
||||
public PaddedBatchingEntityLoader(
|
||||
OuterJoinLoadable persister,
|
||||
int maxBatchSize,
|
||||
LockOptions lockOptions,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers loadQueryInfluencers) {
|
||||
super( persister );
|
||||
this.batchSizes = ArrayHelper.getBatchSizes( maxBatchSize );
|
||||
this.loaders = new Loader[ batchSizes.length ];
|
||||
for ( int i = 0; i < batchSizes.length; i++ ) {
|
||||
this.loaders[i] = new EntityLoader( persister, batchSizes[i], lockOptions, factory, loadQueryInfluencers);
|
||||
}
|
||||
validate( maxBatchSize );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object load(Serializable id, Object optionalObject, SessionImplementor session, LockOptions lockOptions) {
|
||||
final Serializable[] batch = session.getPersistenceContext()
|
||||
.getBatchFetchQueue()
|
||||
.getEntityBatch( persister(), id, batchSizes[0], persister().getEntityMode() );
|
||||
|
||||
final int numberOfIds = ArrayHelper.countNonNull( batch );
|
||||
if ( numberOfIds <= 1 ) {
|
||||
return ( (UniqueEntityLoader) loaders[batchSizes.length-1] ).load( id, optionalObject, session );
|
||||
}
|
||||
|
||||
// Uses the first batch-size bigger than the number of actual ids in the batch
|
||||
int indexToUse = batchSizes.length-1;
|
||||
for ( int i = 0; i < batchSizes.length-1; i++ ) {
|
||||
if ( batchSizes[i] >= numberOfIds ) {
|
||||
indexToUse = i;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
final Serializable[] idsToLoad = new Serializable[ batchSizes[indexToUse] ];
|
||||
System.arraycopy( batch, 0, idsToLoad, 0, numberOfIds );
|
||||
for ( int i = numberOfIds; i < batchSizes[indexToUse]; i++ ) {
|
||||
idsToLoad[i] = id;
|
||||
}
|
||||
|
||||
return doBatchLoad( id, loaders[indexToUse], session, idsToLoad, optionalObject, lockOptions );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -43,6 +43,7 @@ public interface UniqueEntityLoader {
|
|||
* @deprecated use {@link #load(java.io.Serializable, Object, SessionImplementor, LockOptions)} instead.
|
||||
*/
|
||||
@SuppressWarnings( {"JavaDoc"})
|
||||
@Deprecated
|
||||
public Object load(Serializable id, Object optionalObject, SessionImplementor session) throws HibernateException;
|
||||
|
||||
/**
|
||||
|
|
|
@ -240,7 +240,7 @@ public class QueryLoader extends BasicLoader {
|
|||
/**
|
||||
* The SQL query string to be called.
|
||||
*/
|
||||
protected String getSQLString() {
|
||||
public String getSQLString() {
|
||||
return queryTranslator.getSQLString();
|
||||
}
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@ import org.hibernate.internal.util.collections.ArrayHelper;
|
|||
import org.hibernate.jdbc.Expectation;
|
||||
import org.hibernate.jdbc.Expectations;
|
||||
import org.hibernate.loader.collection.BatchingCollectionInitializer;
|
||||
import org.hibernate.loader.collection.BatchingCollectionInitializerBuilder;
|
||||
import org.hibernate.loader.collection.CollectionInitializer;
|
||||
import org.hibernate.loader.collection.SubselectCollectionLoader;
|
||||
import org.hibernate.mapping.Collection;
|
||||
|
@ -332,7 +333,8 @@ public class BasicCollectionPersister extends AbstractCollectionPersister {
|
|||
@Override
|
||||
protected CollectionInitializer createCollectionInitializer(LoadQueryInfluencers loadQueryInfluencers)
|
||||
throws MappingException {
|
||||
return BatchingCollectionInitializer.createBatchingCollectionInitializer( this, batchSize, getFactory(), loadQueryInfluencers );
|
||||
return BatchingCollectionInitializerBuilder.getBuilder( getFactory() )
|
||||
.createBatchingCollectionInitializer( this, batchSize, getFactory(), loadQueryInfluencers );
|
||||
}
|
||||
|
||||
public String fromJoinFragment(String alias, boolean innerJoin, boolean includeSubclasses) {
|
||||
|
|
|
@ -44,6 +44,7 @@ import org.hibernate.internal.util.collections.ArrayHelper;
|
|||
import org.hibernate.jdbc.Expectation;
|
||||
import org.hibernate.jdbc.Expectations;
|
||||
import org.hibernate.loader.collection.BatchingCollectionInitializer;
|
||||
import org.hibernate.loader.collection.BatchingCollectionInitializerBuilder;
|
||||
import org.hibernate.loader.collection.CollectionInitializer;
|
||||
import org.hibernate.loader.collection.SubselectOneToManyLoader;
|
||||
import org.hibernate.loader.entity.CollectionElementLoader;
|
||||
|
@ -359,7 +360,8 @@ public class OneToManyPersister extends AbstractCollectionPersister {
|
|||
@Override
|
||||
protected CollectionInitializer createCollectionInitializer(LoadQueryInfluencers loadQueryInfluencers)
|
||||
throws MappingException {
|
||||
return BatchingCollectionInitializer.createBatchingOneToManyInitializer( this, batchSize, getFactory(), loadQueryInfluencers );
|
||||
return BatchingCollectionInitializerBuilder.getBuilder( getFactory() )
|
||||
.createBatchingOneToManyInitializer( this, batchSize, getFactory(), loadQueryInfluencers );
|
||||
}
|
||||
|
||||
public String fromJoinFragment(String alias,
|
||||
|
|
|
@ -90,6 +90,7 @@ import org.hibernate.jdbc.Expectation;
|
|||
import org.hibernate.jdbc.Expectations;
|
||||
import org.hibernate.jdbc.TooManyRowsAffectedException;
|
||||
import org.hibernate.loader.entity.BatchingEntityLoader;
|
||||
import org.hibernate.loader.entity.BatchingEntityLoaderBuilder;
|
||||
import org.hibernate.loader.entity.CascadeEntityLoader;
|
||||
import org.hibernate.loader.entity.EntityLoader;
|
||||
import org.hibernate.loader.entity.UniqueEntityLoader;
|
||||
|
@ -2440,26 +2441,16 @@ public abstract class AbstractEntityPersister
|
|||
LockMode lockMode,
|
||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||
//TODO: disable batch loading if lockMode > READ?
|
||||
return BatchingEntityLoader.createBatchingEntityLoader(
|
||||
this,
|
||||
batchSize,
|
||||
lockMode,
|
||||
getFactory(),
|
||||
loadQueryInfluencers
|
||||
);
|
||||
return BatchingEntityLoaderBuilder.getBuilder( getFactory() )
|
||||
.buildLoader( this, batchSize, lockMode, getFactory(), loadQueryInfluencers );
|
||||
}
|
||||
|
||||
protected UniqueEntityLoader createEntityLoader(
|
||||
LockOptions lockOptions,
|
||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||
//TODO: disable batch loading if lockMode > READ?
|
||||
return BatchingEntityLoader.createBatchingEntityLoader(
|
||||
this,
|
||||
batchSize,
|
||||
lockOptions,
|
||||
getFactory(),
|
||||
loadQueryInfluencers
|
||||
);
|
||||
return BatchingEntityLoaderBuilder.getBuilder( getFactory() )
|
||||
.buildLoader( this, batchSize, lockOptions, getFactory(), loadQueryInfluencers );
|
||||
}
|
||||
|
||||
protected UniqueEntityLoader createEntityLoader(LockMode lockMode) throws MappingException {
|
||||
|
|
|
@ -31,6 +31,10 @@ import org.junit.Test;
|
|||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.Transaction;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.loader.BatchFetchStyle;
|
||||
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
@ -46,6 +50,18 @@ public class BatchFetchTest extends BaseCoreFunctionalTestCase {
|
|||
return new String[] { "batchfetch/ProductLine.hbm.xml" };
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class[] { BatchLoadableEntity.class };
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(Configuration configuration) {
|
||||
super.configure( configuration );
|
||||
configuration.setProperty( AvailableSettings.GENERATE_STATISTICS, "true" );
|
||||
configuration.setProperty( AvailableSettings.USE_SECOND_LEVEL_CACHE, "false" );
|
||||
}
|
||||
|
||||
@SuppressWarnings( {"unchecked"})
|
||||
@Test
|
||||
public void testBatchFetch() {
|
||||
|
@ -136,5 +152,53 @@ public class BatchFetchTest extends BaseCoreFunctionalTestCase {
|
|||
s.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings( {"unchecked"})
|
||||
public void testBatchFetch2() {
|
||||
Session s = openSession();
|
||||
s.beginTransaction();
|
||||
int size = 32+14;
|
||||
for ( int i = 0; i < size; i++ ) {
|
||||
s.save( new BatchLoadableEntity( i ) );
|
||||
}
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
// load them all as proxies
|
||||
for ( int i = 0; i < size; i++ ) {
|
||||
BatchLoadableEntity entity = (BatchLoadableEntity) s.load( BatchLoadableEntity.class, i );
|
||||
assertFalse( Hibernate.isInitialized( entity ) );
|
||||
}
|
||||
sessionFactory().getStatistics().clear();
|
||||
// now start initializing them...
|
||||
for ( int i = 0; i < size; i++ ) {
|
||||
BatchLoadableEntity entity = (BatchLoadableEntity) s.load( BatchLoadableEntity.class, i );
|
||||
Hibernate.initialize( entity );
|
||||
assertTrue( Hibernate.isInitialized( entity ) );
|
||||
}
|
||||
// so at this point, all entities are initialized. see how many fetches were performed.
|
||||
final int expectedFetchCount;
|
||||
if ( sessionFactory().getSettings().getBatchFetchStyle() == BatchFetchStyle.LEGACY ) {
|
||||
expectedFetchCount = 3; // (32 + 10 + 4)
|
||||
}
|
||||
else if ( sessionFactory().getSettings().getBatchFetchStyle() == BatchFetchStyle.DYNAMIC ) {
|
||||
expectedFetchCount = 2; // (32 + 14) : because we limited batch-size to 32
|
||||
}
|
||||
else {
|
||||
// PADDED
|
||||
expectedFetchCount = 2; // (32 + 16*) with the 16 being padded
|
||||
}
|
||||
assertEquals( expectedFetchCount, sessionFactory().getStatistics().getEntityStatistics( BatchLoadableEntity.class.getName() ).getFetchCount() );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
s.createQuery( "delete BatchLoadableEntity" ).executeUpdate();
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* 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.test.batchfetch;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
|
||||
import org.hibernate.annotations.BatchSize;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@Entity
|
||||
@BatchSize( size = 32 )
|
||||
public class BatchLoadableEntity {
|
||||
private Integer id;
|
||||
private String name;
|
||||
|
||||
public BatchLoadableEntity() {
|
||||
}
|
||||
|
||||
public BatchLoadableEntity(int id) {
|
||||
this.id = id;
|
||||
this.name = "Entity #" + id;
|
||||
}
|
||||
|
||||
@Id
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
|
@ -25,9 +25,6 @@ package org.hibernate.test.manytomany.batchload;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import junit.framework.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.hibernate.EmptyInterceptor;
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.Interceptor;
|
||||
|
@ -39,12 +36,13 @@ import org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch;
|
|||
import org.hibernate.engine.jdbc.batch.spi.Batch;
|
||||
import org.hibernate.engine.jdbc.batch.spi.BatchKey;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
|
||||
import org.hibernate.loader.collection.BatchingCollectionInitializer;
|
||||
import org.hibernate.persister.collection.AbstractCollectionPersister;
|
||||
import org.hibernate.stat.CollectionStatistics;
|
||||
|
||||
import org.junit.Test;
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
|
||||
import static org.hibernate.testing.junit4.ExtraAssertions.assertClassAssignability;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
|
@ -80,25 +78,6 @@ public class BatchedManyToManyTest extends BaseCoreFunctionalTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProperLoaderSetup() {
|
||||
AbstractCollectionPersister cp = ( AbstractCollectionPersister )
|
||||
sessionFactory().getCollectionPersister( User.class.getName() + ".groups" );
|
||||
assertClassAssignability( BatchingCollectionInitializer.class, cp.getInitializer().getClass() );
|
||||
BatchingCollectionInitializer initializer = ( BatchingCollectionInitializer ) cp.getInitializer();
|
||||
assertEquals( 50, findMaxBatchSize( initializer.getBatchSizes() ) );
|
||||
}
|
||||
|
||||
private int findMaxBatchSize(int[] batchSizes) {
|
||||
int max = 0;
|
||||
for ( int size : batchSizes ) {
|
||||
if ( size > max ) {
|
||||
max = size;
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadingNonInverseSide() {
|
||||
prepareTestData();
|
||||
|
|
Loading…
Reference in New Issue