HHH-7746 - Investigate alternative batch loading algorithms
This commit is contained in:
parent
d41545e13b
commit
06b0faaf57
|
@ -579,4 +579,11 @@ public interface AvailableSettings {
|
||||||
public static final String ENABLE_LAZY_LOAD_NO_TRANS = "hibernate.enable_lazy_load_no_trans";
|
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";
|
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";
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ import org.hibernate.cache.spi.RegionFactory;
|
||||||
import org.hibernate.hql.spi.MultiTableBulkIdStrategy;
|
import org.hibernate.hql.spi.MultiTableBulkIdStrategy;
|
||||||
import org.hibernate.hql.spi.QueryTranslatorFactory;
|
import org.hibernate.hql.spi.QueryTranslatorFactory;
|
||||||
import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform;
|
import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform;
|
||||||
|
import org.hibernate.loader.BatchFetchStyle;
|
||||||
import org.hibernate.tuple.entity.EntityTuplizerFactory;
|
import org.hibernate.tuple.entity.EntityTuplizerFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -91,6 +92,7 @@ public final class Settings {
|
||||||
private JtaPlatform jtaPlatform;
|
private JtaPlatform jtaPlatform;
|
||||||
|
|
||||||
private MultiTableBulkIdStrategy multiTableBulkIdStrategy;
|
private MultiTableBulkIdStrategy multiTableBulkIdStrategy;
|
||||||
|
private BatchFetchStyle batchFetchStyle;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -480,4 +482,12 @@ public final class Settings {
|
||||||
void setMultiTableBulkIdStrategy(MultiTableBulkIdStrategy multiTableBulkIdStrategy) {
|
void setMultiTableBulkIdStrategy(MultiTableBulkIdStrategy multiTableBulkIdStrategy) {
|
||||||
this.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.CoreMessageLogger;
|
||||||
import org.hibernate.internal.util.StringHelper;
|
import org.hibernate.internal.util.StringHelper;
|
||||||
import org.hibernate.internal.util.config.ConfigurationHelper;
|
import org.hibernate.internal.util.config.ConfigurationHelper;
|
||||||
|
import org.hibernate.loader.BatchFetchStyle;
|
||||||
import org.hibernate.service.ServiceRegistry;
|
import org.hibernate.service.ServiceRegistry;
|
||||||
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
||||||
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
|
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
|
||||||
|
@ -199,6 +200,11 @@ public class SettingsFactory implements Serializable {
|
||||||
}
|
}
|
||||||
settings.setConnectionReleaseMode( releaseMode );
|
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:
|
//SQL Generation settings:
|
||||||
|
|
||||||
String defaultSchema = properties.getProperty( AvailableSettings.DEFAULT_SCHEMA );
|
String defaultSchema = properties.getProperty( AvailableSettings.DEFAULT_SCHEMA );
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.internal.util;
|
package org.hibernate.internal.util;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
@ -66,6 +67,17 @@ public final class StringHelper {
|
||||||
return buf.toString();
|
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) {
|
public static String join(String seperator, Iterator objects) {
|
||||||
StringBuilder buf = new StringBuilder();
|
StringBuilder buf = new StringBuilder();
|
||||||
if ( objects.hasNext() ) buf.append( objects.next() );
|
if ( objects.hasNext() ) buf.append( objects.next() );
|
||||||
|
@ -89,6 +101,15 @@ public final class StringHelper {
|
||||||
return buf.toString();
|
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) {
|
public static String repeat(char character, int times) {
|
||||||
char[] buffer = new char[times];
|
char[] buffer = new char[times];
|
||||||
Arrays.fill( buffer, character );
|
Arrays.fill( buffer, character );
|
||||||
|
@ -661,4 +682,69 @@ public final class StringHelper {
|
||||||
}
|
}
|
||||||
return unquoted;
|
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;
|
package org.hibernate.internal.util.collections;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -31,6 +32,7 @@ import java.util.Collection;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hibernate.HibernateException;
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.LockOptions;
|
import org.hibernate.LockOptions;
|
||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
|
@ -372,10 +374,43 @@ public final class ArrayHelper {
|
||||||
}
|
}
|
||||||
return true;
|
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();
|
public abstract String getComment();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isDuplicateAssociation(
|
protected boolean isDuplicateAssociation(final String foreignKeyTable, final String[] foreignKeyColumns) {
|
||||||
final String foreignKeyTable,
|
|
||||||
final String[] foreignKeyColumns
|
|
||||||
) {
|
|
||||||
//disable a join back to this same association
|
//disable a join back to this same association
|
||||||
final boolean isSameJoin =
|
final boolean isSameJoin =
|
||||||
persister.getTableName().equals( foreignKeyTable ) &&
|
persister.getTableName().equals( foreignKeyTable ) &&
|
||||||
|
@ -201,11 +198,11 @@ public abstract class AbstractEntityJoinWalker extends JoinWalker {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
protected final Loadable getPersister() {
|
public final Loadable getPersister() {
|
||||||
return persister;
|
return persister;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final String getAlias() {
|
public final String getAlias() {
|
||||||
return alias;
|
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.
|
||||||
|
}
|
||||||
|
}
|
|
@ -119,7 +119,7 @@ public abstract class Loader {
|
||||||
*
|
*
|
||||||
* @return The sql command this loader should use to get its {@link ResultSet}.
|
* @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;
|
* An array of persisters of entity classes contained in each row of results;
|
||||||
|
@ -256,7 +256,7 @@ public abstract class Loader {
|
||||||
* persister from each row of the <tt>ResultSet</tt>. If an object is supplied, will attempt to
|
* 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.
|
* initialize that object. If a collection is supplied, attempt to initialize that collection.
|
||||||
*/
|
*/
|
||||||
private List doQueryAndInitializeNonLazyCollections(
|
public List doQueryAndInitializeNonLazyCollections(
|
||||||
final SessionImplementor session,
|
final SessionImplementor session,
|
||||||
final QueryParameters queryParameters,
|
final QueryParameters queryParameters,
|
||||||
final boolean returnProxies) throws HibernateException, SQLException {
|
final boolean returnProxies) throws HibernateException, SQLException {
|
||||||
|
@ -268,7 +268,7 @@ public abstract class Loader {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List doQueryAndInitializeNonLazyCollections(
|
public List doQueryAndInitializeNonLazyCollections(
|
||||||
final SessionImplementor session,
|
final SessionImplementor session,
|
||||||
final QueryParameters queryParameters,
|
final QueryParameters queryParameters,
|
||||||
final boolean returnProxies,
|
final boolean returnProxies,
|
||||||
|
@ -1722,8 +1722,17 @@ public abstract class Loader {
|
||||||
final QueryParameters queryParameters,
|
final QueryParameters queryParameters,
|
||||||
final boolean scroll,
|
final boolean scroll,
|
||||||
final SessionImplementor session) throws SQLException {
|
final SessionImplementor session) throws SQLException {
|
||||||
|
return executeQueryStatement( getSQLString(), queryParameters, scroll, session );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ResultSet executeQueryStatement(
|
||||||
|
final String sqlStatement,
|
||||||
|
final QueryParameters queryParameters,
|
||||||
|
final boolean scroll,
|
||||||
|
final SessionImplementor session) throws SQLException {
|
||||||
|
|
||||||
// Processing query filters.
|
// Processing query filters.
|
||||||
queryParameters.processFilters( getSQLString(), session );
|
queryParameters.processFilters( sqlStatement, session );
|
||||||
|
|
||||||
// Applying LIMIT clause.
|
// Applying LIMIT clause.
|
||||||
final LimitHandler limitHandler = getLimitHandler(
|
final LimitHandler limitHandler = getLimitHandler(
|
||||||
|
|
|
@ -75,7 +75,8 @@ public abstract class OuterJoinLoader extends BasicLoader {
|
||||||
return collectionSuffixes;
|
return collectionSuffixes;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final String getSQLString() {
|
@Override
|
||||||
|
public final String getSQLString() {
|
||||||
return sql;
|
return sql;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
/*
|
/*
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
* 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
|
* indicated by the @author tags or express copyright attribution
|
||||||
* statements applied by the authors. All third-party contributions are
|
* 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,
|
* 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
|
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||||
|
@ -20,106 +20,35 @@
|
||||||
* Free Software Foundation, Inc.
|
* Free Software Foundation, Inc.
|
||||||
* 51 Franklin Street, Fifth Floor
|
* 51 Franklin Street, Fifth Floor
|
||||||
* Boston, MA 02110-1301 USA
|
* Boston, MA 02110-1301 USA
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
package org.hibernate.loader.collection;
|
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.CollectionPersister;
|
||||||
import org.hibernate.persister.collection.QueryableCollection;
|
import org.hibernate.persister.collection.QueryableCollection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* "Batch" loads collections, using multiple foreign key values in the
|
* The base contract for loaders capable of performing batch-fetch loading of collections using multiple foreign key
|
||||||
* SQL <tt>where</tt> clause.
|
* values in the SQL <tt>WHERE</tt> clause.
|
||||||
*
|
*
|
||||||
|
* @author Gavin King
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*
|
||||||
|
* @see BatchingCollectionInitializerBuilder
|
||||||
* @see BasicCollectionLoader
|
* @see BasicCollectionLoader
|
||||||
* @see OneToManyLoader
|
* @see OneToManyLoader
|
||||||
* @author Gavin King
|
|
||||||
*/
|
*/
|
||||||
public class BatchingCollectionInitializer implements CollectionInitializer {
|
public abstract class BatchingCollectionInitializer implements CollectionInitializer {
|
||||||
private final Loader[] loaders;
|
private final QueryableCollection collectionPersister;
|
||||||
private final int[] batchSizes;
|
|
||||||
private final CollectionPersister collectionPersister;
|
|
||||||
|
|
||||||
public BatchingCollectionInitializer(CollectionPersister collPersister, int[] batchSizes, Loader[] loaders) {
|
public BatchingCollectionInitializer(QueryableCollection collectionPersister) {
|
||||||
this.loaders = loaders;
|
this.collectionPersister = collectionPersister;
|
||||||
this.batchSizes = batchSizes;
|
|
||||||
this.collectionPersister = collPersister;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public CollectionPersister getCollectionPersister() {
|
public CollectionPersister getCollectionPersister() {
|
||||||
return collectionPersister;
|
return collectionPersister;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Loader[] getLoaders() {
|
public QueryableCollection collectionPersister() {
|
||||||
return loaders;
|
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) {
|
protected StringBuilder whereString(String alias, String[] columnNames, String subselect, int batchSize) {
|
||||||
if (subselect==null) {
|
if (subselect==null) {
|
||||||
return super.whereString(alias, columnNames, batchSize);
|
return whereString(alias, columnNames, batchSize);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
StringBuilder buf = new StringBuilder();
|
StringBuilder buf = new StringBuilder();
|
||||||
|
|
|
@ -52,6 +52,10 @@ public class CollectionLoader extends OuterJoinLoader implements CollectionIniti
|
||||||
this.collectionPersister = collectionPersister;
|
this.collectionPersister = collectionPersister;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected QueryableCollection collectionPersister() {
|
||||||
|
return collectionPersister;
|
||||||
|
}
|
||||||
|
|
||||||
protected boolean isSubselectLoadingEnabled() {
|
protected boolean isSubselectLoadingEnabled() {
|
||||||
return hasSubselectLoadableCollections();
|
return hasSubselectLoadableCollections();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,265 @@
|
||||||
|
/*
|
||||||
|
* 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 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 ResultSet rs = executeQueryStatement( sql, queryParameters, false, session );
|
||||||
|
final Statement st = rs.getStatement();
|
||||||
|
try {
|
||||||
|
processResultSet( rs, queryParameters, session, true, null, maxRows );
|
||||||
|
}
|
||||||
|
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() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -294,7 +294,7 @@ public class CustomLoader extends Loader {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getSQLString() {
|
public String getSQLString() {
|
||||||
return sql;
|
return sql;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
/*
|
/*
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
* 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
|
* indicated by the @author tags or express copyright attribution
|
||||||
* statements applied by the authors. All third-party contributions are
|
* 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,
|
* 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
|
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||||
|
@ -20,135 +20,109 @@
|
||||||
* Free Software Foundation, Inc.
|
* Free Software Foundation, Inc.
|
||||||
* 51 Franklin Street, Fifth Floor
|
* 51 Franklin Street, Fifth Floor
|
||||||
* Boston, MA 02110-1301 USA
|
* Boston, MA 02110-1301 USA
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
package org.hibernate.loader.entity;
|
package org.hibernate.loader.entity;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Iterator;
|
import java.sql.SQLException;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.hibernate.LockMode;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
import org.hibernate.LockOptions;
|
import org.hibernate.LockOptions;
|
||||||
import org.hibernate.MappingException;
|
import org.hibernate.engine.spi.QueryParameters;
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.engine.spi.SessionImplementor;
|
import org.hibernate.engine.spi.SessionImplementor;
|
||||||
import org.hibernate.internal.util.collections.ArrayHelper;
|
|
||||||
import org.hibernate.loader.Loader;
|
import org.hibernate.loader.Loader;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
import org.hibernate.pretty.MessageHelper;
|
||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* "Batch" loads entities, using multiple primary key values in the
|
* The base contract for loaders capable of performing batch-fetch loading of entities using multiple primary key
|
||||||
* SQL <tt>where</tt> clause.
|
* values in the SQL <tt>WHERE</tt> clause.
|
||||||
*
|
*
|
||||||
* @see EntityLoader
|
|
||||||
* @author Gavin King
|
* @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 EntityPersister persister;
|
||||||
private final Type idType;
|
|
||||||
|
|
||||||
public BatchingEntityLoader(EntityPersister persister, int[] batchSizes, Loader[] loaders) {
|
public BatchingEntityLoader(EntityPersister persister) {
|
||||||
this.batchSizes = batchSizes;
|
|
||||||
this.loaders = loaders;
|
|
||||||
this.persister = persister;
|
this.persister = persister;
|
||||||
idType = persister.getIdentifierType();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object getObjectFromList(List results, Serializable id, SessionImplementor session) {
|
public EntityPersister persister() {
|
||||||
// get the right object from the list ... would it be easier to just call getEntity() ??
|
return persister;
|
||||||
Iterator iter = results.iterator();
|
}
|
||||||
while ( iter.hasNext() ) {
|
|
||||||
Object obj = iter.next();
|
@Override
|
||||||
final boolean equal = idType.isEqual(
|
@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,
|
id,
|
||||||
session.getContextEntityIdentifier(obj),
|
session.getContextEntityIdentifier( obj ),
|
||||||
session.getFactory()
|
session.getFactory()
|
||||||
);
|
);
|
||||||
if ( equal ) return obj;
|
if ( equal ) {
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
protected Object doBatchLoad(
|
||||||
* {@inheritDoc}
|
Serializable id,
|
||||||
*/
|
Loader loaderToUse,
|
||||||
public Object load(Serializable id, Object optionalObject, SessionImplementor session) {
|
SessionImplementor session,
|
||||||
// this form is deprecated!
|
Serializable[] ids,
|
||||||
return load( id, optionalObject, session, LockOptions.NONE );
|
Object optionalObject,
|
||||||
}
|
LockOptions lockOptions) {
|
||||||
|
if ( log.isDebugEnabled() ) {
|
||||||
public Object load(Serializable id, Object optionalObject, SessionImplementor session, LockOptions lockOptions) {
|
log.debugf( "Batch loading entity: %s", MessageHelper.infoString( persister, ids, session.getFactory() ) );
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ( (UniqueEntityLoader) loaders[batchSizes.length-1] ).load(id, optionalObject, session);
|
QueryParameters qp = buildQueryParameters( id, ids, optionalObject, lockOptions );
|
||||||
|
|
||||||
}
|
try {
|
||||||
|
final List results = loaderToUse.doQueryAndInitializeNonLazyCollections( session, qp, false );
|
||||||
public static UniqueEntityLoader createBatchingEntityLoader(
|
log.debug( "Done entity batch load" );
|
||||||
final OuterJoinLoadable persister,
|
return getObjectFromList(results, id, session);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
else {
|
catch ( SQLException sqle ) {
|
||||||
return new EntityLoader(persister, lockMode, factory, loadQueryInfluencers);
|
throw session.getFactory().getSQLExceptionHelper().convert(
|
||||||
}
|
sqle,
|
||||||
}
|
"could not load an entity batch: " + MessageHelper.infoString( persister(), ids, session.getFactory() ),
|
||||||
|
loaderToUse.getSQLString()
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,266 @@
|
||||||
|
/*
|
||||||
|
* 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.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 ResultSet rs = executeQueryStatement( sql, queryParameters, false, session );
|
||||||
|
final Statement st = rs.getStatement();
|
||||||
|
try {
|
||||||
|
return processResultSet( rs, queryParameters, session, false, null, maxRows );
|
||||||
|
}
|
||||||
|
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.
|
* @deprecated use {@link #load(java.io.Serializable, Object, SessionImplementor, LockOptions)} instead.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings( {"JavaDoc"})
|
@SuppressWarnings( {"JavaDoc"})
|
||||||
|
@Deprecated
|
||||||
public Object load(Serializable id, Object optionalObject, SessionImplementor session) throws HibernateException;
|
public Object load(Serializable id, Object optionalObject, SessionImplementor session) throws HibernateException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -239,7 +239,7 @@ public class QueryLoader extends BasicLoader {
|
||||||
/**
|
/**
|
||||||
* The SQL query string to be called.
|
* The SQL query string to be called.
|
||||||
*/
|
*/
|
||||||
protected String getSQLString() {
|
public String getSQLString() {
|
||||||
return queryTranslator.getSQLString();
|
return queryTranslator.getSQLString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,7 @@ import org.hibernate.internal.util.collections.ArrayHelper;
|
||||||
import org.hibernate.jdbc.Expectation;
|
import org.hibernate.jdbc.Expectation;
|
||||||
import org.hibernate.jdbc.Expectations;
|
import org.hibernate.jdbc.Expectations;
|
||||||
import org.hibernate.loader.collection.BatchingCollectionInitializer;
|
import org.hibernate.loader.collection.BatchingCollectionInitializer;
|
||||||
|
import org.hibernate.loader.collection.BatchingCollectionInitializerBuilder;
|
||||||
import org.hibernate.loader.collection.CollectionInitializer;
|
import org.hibernate.loader.collection.CollectionInitializer;
|
||||||
import org.hibernate.loader.collection.SubselectCollectionLoader;
|
import org.hibernate.loader.collection.SubselectCollectionLoader;
|
||||||
import org.hibernate.mapping.Collection;
|
import org.hibernate.mapping.Collection;
|
||||||
|
@ -332,7 +333,8 @@ public class BasicCollectionPersister extends AbstractCollectionPersister {
|
||||||
@Override
|
@Override
|
||||||
protected CollectionInitializer createCollectionInitializer(LoadQueryInfluencers loadQueryInfluencers)
|
protected CollectionInitializer createCollectionInitializer(LoadQueryInfluencers loadQueryInfluencers)
|
||||||
throws MappingException {
|
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) {
|
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.Expectation;
|
||||||
import org.hibernate.jdbc.Expectations;
|
import org.hibernate.jdbc.Expectations;
|
||||||
import org.hibernate.loader.collection.BatchingCollectionInitializer;
|
import org.hibernate.loader.collection.BatchingCollectionInitializer;
|
||||||
|
import org.hibernate.loader.collection.BatchingCollectionInitializerBuilder;
|
||||||
import org.hibernate.loader.collection.CollectionInitializer;
|
import org.hibernate.loader.collection.CollectionInitializer;
|
||||||
import org.hibernate.loader.collection.SubselectOneToManyLoader;
|
import org.hibernate.loader.collection.SubselectOneToManyLoader;
|
||||||
import org.hibernate.loader.entity.CollectionElementLoader;
|
import org.hibernate.loader.entity.CollectionElementLoader;
|
||||||
|
@ -359,7 +360,8 @@ public class OneToManyPersister extends AbstractCollectionPersister {
|
||||||
@Override
|
@Override
|
||||||
protected CollectionInitializer createCollectionInitializer(LoadQueryInfluencers loadQueryInfluencers)
|
protected CollectionInitializer createCollectionInitializer(LoadQueryInfluencers loadQueryInfluencers)
|
||||||
throws MappingException {
|
throws MappingException {
|
||||||
return BatchingCollectionInitializer.createBatchingOneToManyInitializer( this, batchSize, getFactory(), loadQueryInfluencers );
|
return BatchingCollectionInitializerBuilder.getBuilder( getFactory() )
|
||||||
|
.createBatchingOneToManyInitializer( this, batchSize, getFactory(), loadQueryInfluencers );
|
||||||
}
|
}
|
||||||
|
|
||||||
public String fromJoinFragment(String alias,
|
public String fromJoinFragment(String alias,
|
||||||
|
|
|
@ -90,6 +90,7 @@ import org.hibernate.jdbc.Expectation;
|
||||||
import org.hibernate.jdbc.Expectations;
|
import org.hibernate.jdbc.Expectations;
|
||||||
import org.hibernate.jdbc.TooManyRowsAffectedException;
|
import org.hibernate.jdbc.TooManyRowsAffectedException;
|
||||||
import org.hibernate.loader.entity.BatchingEntityLoader;
|
import org.hibernate.loader.entity.BatchingEntityLoader;
|
||||||
|
import org.hibernate.loader.entity.BatchingEntityLoaderBuilder;
|
||||||
import org.hibernate.loader.entity.CascadeEntityLoader;
|
import org.hibernate.loader.entity.CascadeEntityLoader;
|
||||||
import org.hibernate.loader.entity.EntityLoader;
|
import org.hibernate.loader.entity.EntityLoader;
|
||||||
import org.hibernate.loader.entity.UniqueEntityLoader;
|
import org.hibernate.loader.entity.UniqueEntityLoader;
|
||||||
|
@ -2440,26 +2441,16 @@ public abstract class AbstractEntityPersister
|
||||||
LockMode lockMode,
|
LockMode lockMode,
|
||||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||||
//TODO: disable batch loading if lockMode > READ?
|
//TODO: disable batch loading if lockMode > READ?
|
||||||
return BatchingEntityLoader.createBatchingEntityLoader(
|
return BatchingEntityLoaderBuilder.getBuilder( getFactory() )
|
||||||
this,
|
.buildLoader( this, batchSize, lockMode, getFactory(), loadQueryInfluencers );
|
||||||
batchSize,
|
|
||||||
lockMode,
|
|
||||||
getFactory(),
|
|
||||||
loadQueryInfluencers
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected UniqueEntityLoader createEntityLoader(
|
protected UniqueEntityLoader createEntityLoader(
|
||||||
LockOptions lockOptions,
|
LockOptions lockOptions,
|
||||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||||
//TODO: disable batch loading if lockMode > READ?
|
//TODO: disable batch loading if lockMode > READ?
|
||||||
return BatchingEntityLoader.createBatchingEntityLoader(
|
return BatchingEntityLoaderBuilder.getBuilder( getFactory() )
|
||||||
this,
|
.buildLoader( this, batchSize, lockOptions, getFactory(), loadQueryInfluencers );
|
||||||
batchSize,
|
|
||||||
lockOptions,
|
|
||||||
getFactory(),
|
|
||||||
loadQueryInfluencers
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected UniqueEntityLoader createEntityLoader(LockMode lockMode) throws MappingException {
|
protected UniqueEntityLoader createEntityLoader(LockMode lockMode) throws MappingException {
|
||||||
|
|
|
@ -31,6 +31,10 @@ import org.junit.Test;
|
||||||
import org.hibernate.Hibernate;
|
import org.hibernate.Hibernate;
|
||||||
import org.hibernate.Session;
|
import org.hibernate.Session;
|
||||||
import org.hibernate.Transaction;
|
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 org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
@ -46,6 +50,18 @@ public class BatchFetchTest extends BaseCoreFunctionalTestCase {
|
||||||
return new String[] { "batchfetch/ProductLine.hbm.xml" };
|
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"})
|
@SuppressWarnings( {"unchecked"})
|
||||||
@Test
|
@Test
|
||||||
public void testBatchFetch() {
|
public void testBatchFetch() {
|
||||||
|
@ -136,5 +152,53 @@ public class BatchFetchTest extends BaseCoreFunctionalTestCase {
|
||||||
s.close();
|
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 java.util.List;
|
||||||
|
|
||||||
import junit.framework.Assert;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import org.hibernate.EmptyInterceptor;
|
import org.hibernate.EmptyInterceptor;
|
||||||
import org.hibernate.Hibernate;
|
import org.hibernate.Hibernate;
|
||||||
import org.hibernate.Interceptor;
|
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.Batch;
|
||||||
import org.hibernate.engine.jdbc.batch.spi.BatchKey;
|
import org.hibernate.engine.jdbc.batch.spi.BatchKey;
|
||||||
import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
|
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.hibernate.stat.CollectionStatistics;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import junit.framework.Assert;
|
||||||
|
|
||||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
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.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
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
|
@Test
|
||||||
public void testLoadingNonInverseSide() {
|
public void testLoadingNonInverseSide() {
|
||||||
prepareTestData();
|
prepareTestData();
|
||||||
|
|
Loading…
Reference in New Issue