HHH-8276 - Integrate LoadPlanAdvisor into UniqueEntityLoader (PoC)
This commit is contained in:
parent
290133158c
commit
456d61bd4e
|
@ -48,7 +48,7 @@ public abstract class BatchingEntityLoaderBuilder {
|
|||
return DynamicBatchingEntityLoaderBuilder.INSTANCE;
|
||||
}
|
||||
default: {
|
||||
return LegacyBatchingEntityLoaderBuilder.INSTANCE;
|
||||
return org.hibernate.loader.entity.plan.LegacyBatchingEntityLoaderBuilder.INSTANCE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,769 @@
|
|||
package org.hibernate.loader.entity.plan;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.ScrollMode;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.dialect.pagination.LimitHandler;
|
||||
import org.hibernate.dialect.pagination.LimitHelper;
|
||||
import org.hibernate.dialect.pagination.NoopLimitHandler;
|
||||
import org.hibernate.engine.jdbc.ColumnNameCache;
|
||||
import org.hibernate.engine.spi.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.engine.spi.TypedValue;
|
||||
import org.hibernate.internal.CoreLogging;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.loader.entity.UniqueEntityLoader;
|
||||
import org.hibernate.loader.internal.EntityLoadQueryBuilderImpl;
|
||||
import org.hibernate.loader.internal.LoadQueryAliasResolutionContextImpl;
|
||||
import org.hibernate.loader.internal.ResultSetProcessorImpl;
|
||||
import org.hibernate.loader.plan.internal.SingleRootReturnLoadPlanBuilderStrategy;
|
||||
import org.hibernate.loader.plan.spi.LoadPlan;
|
||||
import org.hibernate.loader.plan.spi.build.LoadPlanBuilder;
|
||||
import org.hibernate.loader.spi.AfterLoadAction;
|
||||
import org.hibernate.loader.spi.LoadQueryAliasResolutionContext;
|
||||
import org.hibernate.loader.spi.NamedParameterContext;
|
||||
import org.hibernate.loader.spi.NoOpLoadPlanAdvisor;
|
||||
import org.hibernate.loader.spi.ResultSetProcessor;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
||||
import org.hibernate.pretty.MessageHelper;
|
||||
import org.hibernate.transform.ResultTransformer;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
/**
|
||||
* A UniqueEntityLoader implementation based on using LoadPlans
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityLoader {
|
||||
private static final CoreMessageLogger log = CoreLogging.messageLogger( AbstractLoadPlanBasedEntityLoader.class );
|
||||
|
||||
private final SessionFactoryImplementor factory;
|
||||
private final OuterJoinLoadable entityPersister;
|
||||
private final Type uniqueKeyType;
|
||||
private final String entityName;
|
||||
|
||||
private final LoadPlan plan;
|
||||
private final String staticSql;
|
||||
private final LoadQueryAliasResolutionContext staticAliasResolutionContext;
|
||||
private final ResultSetProcessor staticResultSetProcessor;
|
||||
|
||||
private ColumnNameCache columnNameCache;
|
||||
|
||||
public AbstractLoadPlanBasedEntityLoader(
|
||||
OuterJoinLoadable entityPersister,
|
||||
Type uniqueKeyType,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers loadQueryInfluencers) {
|
||||
this.factory = factory;
|
||||
this.uniqueKeyType = uniqueKeyType;
|
||||
this.entityName = entityPersister.getEntityName();
|
||||
this.entityPersister = entityPersister;
|
||||
|
||||
final SingleRootReturnLoadPlanBuilderStrategy strategy = new SingleRootReturnLoadPlanBuilderStrategy(
|
||||
factory,
|
||||
loadQueryInfluencers
|
||||
);
|
||||
|
||||
this.plan = LoadPlanBuilder.buildRootEntityLoadPlan( strategy, entityPersister );
|
||||
this.staticAliasResolutionContext = buildAliasResolutionContext( plan, factory );
|
||||
this.staticSql = generateStaticSql( plan, staticAliasResolutionContext, factory, loadQueryInfluencers );
|
||||
this.staticResultSetProcessor = generateStaticResultSetProcessor( plan );
|
||||
}
|
||||
|
||||
protected LoadQueryAliasResolutionContext buildAliasResolutionContext(LoadPlan plan, SessionFactoryImplementor factory) {
|
||||
return new LoadQueryAliasResolutionContextImpl(
|
||||
factory,
|
||||
0,
|
||||
Collections.singletonMap( plan.getReturns().get( 0 ), new String[] {"abc"} )
|
||||
);
|
||||
}
|
||||
|
||||
protected String generateStaticSql(
|
||||
LoadPlan plan,
|
||||
LoadQueryAliasResolutionContext aliasResolutionContext,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers loadQueryInfluencers) {
|
||||
return new EntityLoadQueryBuilderImpl( loadQueryInfluencers, plan ).generateSql(
|
||||
1,
|
||||
factory,
|
||||
aliasResolutionContext
|
||||
);
|
||||
}
|
||||
|
||||
protected ResultSetProcessor generateStaticResultSetProcessor(LoadPlan plan) {
|
||||
return new ResultSetProcessorImpl( plan );
|
||||
}
|
||||
|
||||
protected SessionFactoryImplementor getFactory() {
|
||||
return factory;
|
||||
}
|
||||
|
||||
protected String getSqlStatement() {
|
||||
return staticSql;
|
||||
}
|
||||
|
||||
protected String getEntityName() {
|
||||
return entityName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by wrappers that batch load entities
|
||||
* @param persister only needed for logging
|
||||
* @param lockOptions
|
||||
*/
|
||||
public final List loadEntityBatch(
|
||||
final SessionImplementor session,
|
||||
final Serializable[] ids,
|
||||
final Type idType,
|
||||
final Object optionalObject,
|
||||
final String optionalEntityName,
|
||||
final Serializable optionalId,
|
||||
final EntityPersister persister,
|
||||
LockOptions lockOptions) throws HibernateException {
|
||||
|
||||
if ( log.isDebugEnabled() ) {
|
||||
log.debugf( "Batch loading entity: %s", MessageHelper.infoString( persister, ids, getFactory() ) );
|
||||
}
|
||||
|
||||
final Type[] types = new Type[ids.length];
|
||||
Arrays.fill( types, idType );
|
||||
List result;
|
||||
try {
|
||||
final QueryParameters qp = new QueryParameters();
|
||||
qp.setPositionalParameterTypes( types );
|
||||
qp.setPositionalParameterValues( ids );
|
||||
qp.setOptionalObject( optionalObject );
|
||||
qp.setOptionalEntityName( optionalEntityName );
|
||||
qp.setOptionalId( optionalId );
|
||||
qp.setLockOptions( lockOptions );
|
||||
|
||||
result = executeLoad(
|
||||
session,
|
||||
qp,
|
||||
staticSql,
|
||||
staticResultSetProcessor,
|
||||
staticAliasResolutionContext,
|
||||
false,
|
||||
null
|
||||
);
|
||||
}
|
||||
catch ( SQLException sqle ) {
|
||||
throw factory.getSQLExceptionHelper().convert(
|
||||
sqle,
|
||||
"could not load an entity batch: " + MessageHelper.infoString( entityPersister, ids, getFactory() ),
|
||||
getSqlStatement()
|
||||
);
|
||||
}
|
||||
|
||||
log.debug( "Done entity batch load" );
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public Object load(Serializable id, Object optionalObject, SessionImplementor session) throws HibernateException {
|
||||
return load( id, optionalObject, session, LockOptions.NONE );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object load(Serializable id, Object optionalObject, SessionImplementor session, LockOptions lockOptions) {
|
||||
Object result = null;
|
||||
|
||||
try {
|
||||
final QueryParameters qp = new QueryParameters();
|
||||
qp.setPositionalParameterTypes( new Type[] { entityPersister.getIdentifierType() } );
|
||||
qp.setPositionalParameterValues( new Object[] { id } );
|
||||
qp.setOptionalObject( optionalObject );
|
||||
qp.setOptionalEntityName( entityPersister.getEntityName() );
|
||||
qp.setOptionalId( id );
|
||||
qp.setLockOptions( lockOptions );
|
||||
|
||||
final List results = executeLoad(
|
||||
session,
|
||||
qp,
|
||||
staticSql,
|
||||
staticResultSetProcessor,
|
||||
staticAliasResolutionContext,
|
||||
false,
|
||||
null
|
||||
);
|
||||
result = extractEntityResult( results );
|
||||
}
|
||||
catch ( SQLException sqle ) {
|
||||
throw factory.getSQLExceptionHelper().convert(
|
||||
sqle,
|
||||
"could not load an entity: " + MessageHelper.infoString(
|
||||
entityPersister,
|
||||
id,
|
||||
entityPersister.getIdentifierType(),
|
||||
factory
|
||||
),
|
||||
getSqlStatement()
|
||||
);
|
||||
}
|
||||
|
||||
log.debugf( "Done entity load : %s#%s", getEntityName(), id );
|
||||
return result;
|
||||
}
|
||||
|
||||
protected List executeLoad(
|
||||
SessionImplementor session,
|
||||
QueryParameters queryParameters,
|
||||
String sql,
|
||||
ResultSetProcessor resultSetProcessor,
|
||||
LoadQueryAliasResolutionContext aliasResolutionContext,
|
||||
boolean returnProxies,
|
||||
ResultTransformer forcedResultTransformer) throws SQLException {
|
||||
final List<AfterLoadAction> afterLoadActions = new ArrayList<AfterLoadAction>();
|
||||
return executeLoad(
|
||||
session,
|
||||
queryParameters,
|
||||
sql,
|
||||
resultSetProcessor,
|
||||
aliasResolutionContext,
|
||||
returnProxies,
|
||||
forcedResultTransformer,
|
||||
afterLoadActions
|
||||
);
|
||||
}
|
||||
|
||||
protected List executeLoad(
|
||||
SessionImplementor session,
|
||||
QueryParameters queryParameters,
|
||||
String sql,
|
||||
ResultSetProcessor resultSetProcessor,
|
||||
LoadQueryAliasResolutionContext aliasResolutionContext,
|
||||
boolean returnProxies,
|
||||
ResultTransformer forcedResultTransformer,
|
||||
List<AfterLoadAction> afterLoadActions) throws SQLException {
|
||||
final PersistenceContext persistenceContext = session.getPersistenceContext();
|
||||
final boolean defaultReadOnlyOrig = persistenceContext.isDefaultReadOnly();
|
||||
if ( queryParameters.isReadOnlyInitialized() ) {
|
||||
// The read-only/modifiable mode for the query was explicitly set.
|
||||
// Temporarily set the default read-only/modifiable setting to the query's setting.
|
||||
persistenceContext.setDefaultReadOnly( queryParameters.isReadOnly() );
|
||||
}
|
||||
else {
|
||||
// The read-only/modifiable setting for the query was not initialized.
|
||||
// Use the default read-only/modifiable from the persistence context instead.
|
||||
queryParameters.setReadOnly( persistenceContext.isDefaultReadOnly() );
|
||||
}
|
||||
persistenceContext.beforeLoad();
|
||||
try {
|
||||
List results;
|
||||
try {
|
||||
final SqlStatementWrapper wrapper = executeQueryStatement( sql, queryParameters, false, afterLoadActions, session );
|
||||
results = resultSetProcessor.extractResults(
|
||||
// todo : hook in the JPA 2.1 entity graph advisor
|
||||
NoOpLoadPlanAdvisor.INSTANCE,
|
||||
wrapper.getResultSet(),
|
||||
session,
|
||||
queryParameters,
|
||||
new NamedParameterContext() {
|
||||
@Override
|
||||
public int[] getNamedParameterLocations(String name) {
|
||||
return AbstractLoadPlanBasedEntityLoader.this.getNamedParameterLocs( name );
|
||||
}
|
||||
},
|
||||
aliasResolutionContext,
|
||||
returnProxies,
|
||||
queryParameters.isReadOnly(),
|
||||
forcedResultTransformer,
|
||||
afterLoadActions
|
||||
);
|
||||
}
|
||||
finally {
|
||||
persistenceContext.afterLoad();
|
||||
}
|
||||
persistenceContext.initializeNonLazyCollections();
|
||||
return results;
|
||||
}
|
||||
finally {
|
||||
// Restore the original default
|
||||
persistenceContext.setDefaultReadOnly( defaultReadOnlyOrig );
|
||||
}
|
||||
}
|
||||
|
||||
protected Object extractEntityResult(List results) {
|
||||
if ( results.size() == 0 ) {
|
||||
return null;
|
||||
}
|
||||
else if ( results.size() == 1 ) {
|
||||
return results.get( 0 );
|
||||
}
|
||||
else {
|
||||
final Object row = results.get( 0 );
|
||||
if ( row.getClass().isArray() ) {
|
||||
// the logical type of the result list is List<Object[]>. See if the contained
|
||||
// array contains just one element, and return that if so
|
||||
final Object[] rowArray = (Object[]) row;
|
||||
if ( rowArray.length == 1 ) {
|
||||
return rowArray[0];
|
||||
}
|
||||
}
|
||||
else {
|
||||
return row;
|
||||
}
|
||||
}
|
||||
|
||||
throw new HibernateException( "Unable to interpret given query results in terms of a load-entity query" );
|
||||
}
|
||||
|
||||
protected Object doQueryAndLoadEntity(
|
||||
SessionImplementor session,
|
||||
QueryParameters queryParameters,
|
||||
String sql,
|
||||
ResultSetProcessor resultSetProcessor,
|
||||
LoadQueryAliasResolutionContext aliasResolutionContext,
|
||||
boolean returnProxies,
|
||||
ResultTransformer forcedResultTransformer) throws SQLException {
|
||||
|
||||
final List<AfterLoadAction> afterLoadActions = new ArrayList<AfterLoadAction>();
|
||||
|
||||
final SqlStatementWrapper wrapper = executeQueryStatement( queryParameters, false, afterLoadActions, session );
|
||||
|
||||
try {
|
||||
final List results = resultSetProcessor.extractResults(
|
||||
NoOpLoadPlanAdvisor.INSTANCE,
|
||||
wrapper.getResultSet(),
|
||||
session,
|
||||
queryParameters,
|
||||
new NamedParameterContext() {
|
||||
@Override
|
||||
public int[] getNamedParameterLocations(String name) {
|
||||
return AbstractLoadPlanBasedEntityLoader.this.getNamedParameterLocs( name );
|
||||
}
|
||||
},
|
||||
staticAliasResolutionContext,
|
||||
returnProxies,
|
||||
queryParameters.isReadOnly(),
|
||||
forcedResultTransformer,
|
||||
afterLoadActions
|
||||
);
|
||||
|
||||
|
||||
if ( results.size() == 0 ) {
|
||||
return null;
|
||||
}
|
||||
else if ( results.size() == 1 ) {
|
||||
return results.get( 0 );
|
||||
}
|
||||
else {
|
||||
final Object row = results.get( 0 );
|
||||
if ( row.getClass().isArray() ) {
|
||||
// the logical type of the result list is List<Object[]>. See if the contained
|
||||
// array contains just one element, and return that if so
|
||||
final Object[] rowArray = (Object[]) row;
|
||||
if ( rowArray.length == 1 ) {
|
||||
return rowArray[0];
|
||||
}
|
||||
}
|
||||
else {
|
||||
return row;
|
||||
}
|
||||
}
|
||||
|
||||
throw new HibernateException( "Unable to interpret given query results in terms of a load-entity query" );
|
||||
}
|
||||
finally {
|
||||
session.getTransactionCoordinator().getJdbcCoordinator().release( wrapper.getStatement() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected SqlStatementWrapper executeQueryStatement(
|
||||
final QueryParameters queryParameters,
|
||||
final boolean scroll,
|
||||
List<AfterLoadAction> afterLoadActions,
|
||||
final SessionImplementor session) throws SQLException {
|
||||
return executeQueryStatement( getSqlStatement(), queryParameters, scroll, afterLoadActions, session );
|
||||
}
|
||||
|
||||
protected SqlStatementWrapper executeQueryStatement(
|
||||
String sqlStatement,
|
||||
QueryParameters queryParameters,
|
||||
boolean scroll,
|
||||
List<AfterLoadAction> afterLoadActions,
|
||||
SessionImplementor session) throws SQLException {
|
||||
|
||||
// Processing query filters.
|
||||
queryParameters.processFilters( sqlStatement, session );
|
||||
|
||||
// Applying LIMIT clause.
|
||||
final LimitHandler limitHandler = getLimitHandler(
|
||||
queryParameters.getFilteredSQL(),
|
||||
queryParameters.getRowSelection()
|
||||
);
|
||||
String sql = limitHandler.getProcessedSql();
|
||||
|
||||
// Adding locks and comments.
|
||||
sql = preprocessSQL( sql, queryParameters, getFactory().getDialect(), afterLoadActions );
|
||||
|
||||
final PreparedStatement st = prepareQueryStatement( sql, queryParameters, limitHandler, scroll, session );
|
||||
return new SqlStatementWrapper( st, getResultSet( st, queryParameters.getRowSelection(), limitHandler, queryParameters.hasAutoDiscoverScalarTypes(), session ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Build LIMIT clause handler applicable for given selection criteria. Returns {@link org.hibernate.dialect.pagination.NoopLimitHandler} delegate
|
||||
* if dialect does not support LIMIT expression or processed query does not use pagination.
|
||||
*
|
||||
* @param sql Query string.
|
||||
* @param selection Selection criteria.
|
||||
* @return LIMIT clause delegate.
|
||||
*/
|
||||
protected LimitHandler getLimitHandler(String sql, RowSelection selection) {
|
||||
final LimitHandler limitHandler = getFactory().getDialect().buildLimitHandler( sql, selection );
|
||||
return LimitHelper.useLimit( limitHandler, selection ) ? limitHandler : new NoopLimitHandler( sql, selection );
|
||||
}
|
||||
|
||||
private String preprocessSQL(
|
||||
String sql,
|
||||
QueryParameters queryParameters,
|
||||
Dialect dialect,
|
||||
List<AfterLoadAction> afterLoadActions) {
|
||||
return getFactory().getSettings().isCommentsEnabled()
|
||||
? prependComment( sql, queryParameters )
|
||||
: sql;
|
||||
}
|
||||
|
||||
private String prependComment(String sql, QueryParameters parameters) {
|
||||
final String comment = parameters.getComment();
|
||||
if ( comment == null ) {
|
||||
return sql;
|
||||
}
|
||||
else {
|
||||
return "/* " + comment + " */ " + sql;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a <tt>PreparedStatement</tt> with all parameters pre-bound.
|
||||
* Bind JDBC-style <tt>?</tt> parameters, named parameters, and
|
||||
* limit parameters.
|
||||
*/
|
||||
protected final PreparedStatement prepareQueryStatement(
|
||||
final String sql,
|
||||
final QueryParameters queryParameters,
|
||||
final LimitHandler limitHandler,
|
||||
final boolean scroll,
|
||||
final SessionImplementor session) throws SQLException, HibernateException {
|
||||
final Dialect dialect = getFactory().getDialect();
|
||||
final RowSelection selection = queryParameters.getRowSelection();
|
||||
final boolean useLimit = LimitHelper.useLimit( limitHandler, selection );
|
||||
final boolean hasFirstRow = LimitHelper.hasFirstRow( selection );
|
||||
final boolean useLimitOffset = hasFirstRow && useLimit && limitHandler.supportsLimitOffset();
|
||||
final boolean callable = queryParameters.isCallable();
|
||||
final ScrollMode scrollMode = getScrollMode( scroll, hasFirstRow, useLimitOffset, queryParameters );
|
||||
|
||||
final PreparedStatement st = session.getTransactionCoordinator().getJdbcCoordinator()
|
||||
.getStatementPreparer().prepareQueryStatement( sql, callable, scrollMode );
|
||||
|
||||
try {
|
||||
|
||||
int col = 1;
|
||||
//TODO: can we limit stored procedures ?!
|
||||
col += limitHandler.bindLimitParametersAtStartOfQuery( st, col );
|
||||
|
||||
if (callable) {
|
||||
col = dialect.registerResultSetOutParameter( (CallableStatement)st, col );
|
||||
}
|
||||
|
||||
col += bindParameterValues( st, queryParameters, col, session );
|
||||
|
||||
col += limitHandler.bindLimitParametersAtEndOfQuery( st, col );
|
||||
|
||||
limitHandler.setMaxRows( st );
|
||||
|
||||
if ( selection != null ) {
|
||||
if ( selection.getTimeout() != null ) {
|
||||
st.setQueryTimeout( selection.getTimeout() );
|
||||
}
|
||||
if ( selection.getFetchSize() != null ) {
|
||||
st.setFetchSize( selection.getFetchSize() );
|
||||
}
|
||||
}
|
||||
|
||||
// handle lock timeout...
|
||||
final LockOptions lockOptions = queryParameters.getLockOptions();
|
||||
if ( lockOptions != null ) {
|
||||
if ( lockOptions.getTimeOut() != LockOptions.WAIT_FOREVER ) {
|
||||
if ( !dialect.supportsLockTimeouts() ) {
|
||||
if ( log.isDebugEnabled() ) {
|
||||
log.debugf(
|
||||
"Lock timeout [%s] requested but dialect reported to not support lock timeouts",
|
||||
lockOptions.getTimeOut()
|
||||
);
|
||||
}
|
||||
}
|
||||
else if ( dialect.isLockTimeoutParameterized() ) {
|
||||
st.setInt( col++, lockOptions.getTimeOut() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( log.isTraceEnabled() ) {
|
||||
log.tracev( "Bound [{0}] parameters total", col );
|
||||
}
|
||||
}
|
||||
catch ( SQLException sqle ) {
|
||||
session.getTransactionCoordinator().getJdbcCoordinator().release( st );
|
||||
throw sqle;
|
||||
}
|
||||
catch ( HibernateException he ) {
|
||||
session.getTransactionCoordinator().getJdbcCoordinator().release( st );
|
||||
throw he;
|
||||
}
|
||||
|
||||
return st;
|
||||
}
|
||||
|
||||
protected ScrollMode getScrollMode(boolean scroll, boolean hasFirstRow, boolean useLimitOffSet, QueryParameters queryParameters) {
|
||||
final boolean canScroll = getFactory().getSettings().isScrollableResultSetsEnabled();
|
||||
if ( canScroll ) {
|
||||
if ( scroll ) {
|
||||
return queryParameters.getScrollMode();
|
||||
}
|
||||
if ( hasFirstRow && !useLimitOffSet ) {
|
||||
return ScrollMode.SCROLL_INSENSITIVE;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind all parameter values into the prepared statement in preparation
|
||||
* for execution.
|
||||
*
|
||||
* @param statement The JDBC prepared statement
|
||||
* @param queryParameters The encapsulation of the parameter values to be bound.
|
||||
* @param startIndex The position from which to start binding parameter values.
|
||||
* @param session The originating session.
|
||||
* @return The number of JDBC bind positions actually bound during this method execution.
|
||||
* @throws SQLException Indicates problems performing the binding.
|
||||
*/
|
||||
protected int bindParameterValues(
|
||||
PreparedStatement statement,
|
||||
QueryParameters queryParameters,
|
||||
int startIndex,
|
||||
SessionImplementor session) throws SQLException {
|
||||
int span = 0;
|
||||
span += bindPositionalParameters( statement, queryParameters, startIndex, session );
|
||||
span += bindNamedParameters( statement, queryParameters.getNamedParameters(), startIndex + span, session );
|
||||
return span;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind positional parameter values to the JDBC prepared statement.
|
||||
* <p/>
|
||||
* Positional parameters are those specified by JDBC-style ? parameters
|
||||
* in the source query. It is (currently) expected that these come
|
||||
* before any named parameters in the source query.
|
||||
*
|
||||
* @param statement The JDBC prepared statement
|
||||
* @param queryParameters The encapsulation of the parameter values to be bound.
|
||||
* @param startIndex The position from which to start binding parameter values.
|
||||
* @param session The originating session.
|
||||
* @return The number of JDBC bind positions actually bound during this method execution.
|
||||
* @throws SQLException Indicates problems performing the binding.
|
||||
* @throws org.hibernate.HibernateException Indicates problems delegating binding to the types.
|
||||
*/
|
||||
protected int bindPositionalParameters(
|
||||
final PreparedStatement statement,
|
||||
final QueryParameters queryParameters,
|
||||
final int startIndex,
|
||||
final SessionImplementor session) throws SQLException, HibernateException {
|
||||
final Object[] values = queryParameters.getFilteredPositionalParameterValues();
|
||||
final Type[] types = queryParameters.getFilteredPositionalParameterTypes();
|
||||
int span = 0;
|
||||
for ( int i = 0; i < values.length; i++ ) {
|
||||
types[i].nullSafeSet( statement, values[i], startIndex + span, session );
|
||||
span += types[i].getColumnSpan( getFactory() );
|
||||
}
|
||||
return span;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind named parameters to the JDBC prepared statement.
|
||||
* <p/>
|
||||
* This is a generic implementation, the problem being that in the
|
||||
* general case we do not know enough information about the named
|
||||
* parameters to perform this in a complete manner here. Thus this
|
||||
* is generally overridden on subclasses allowing named parameters to
|
||||
* apply the specific behavior. The most usual limitation here is that
|
||||
* we need to assume the type span is always one...
|
||||
*
|
||||
* @param statement The JDBC prepared statement
|
||||
* @param namedParams A map of parameter names to values
|
||||
* @param startIndex The position from which to start binding parameter values.
|
||||
* @param session The originating session.
|
||||
* @return The number of JDBC bind positions actually bound during this method execution.
|
||||
* @throws SQLException Indicates problems performing the binding.
|
||||
* @throws org.hibernate.HibernateException Indicates problems delegating binding to the types.
|
||||
*/
|
||||
protected int bindNamedParameters(
|
||||
final PreparedStatement statement,
|
||||
final Map namedParams,
|
||||
final int startIndex,
|
||||
final SessionImplementor session) throws SQLException, HibernateException {
|
||||
if ( namedParams != null ) {
|
||||
// assumes that types are all of span 1
|
||||
final Iterator itr = namedParams.entrySet().iterator();
|
||||
final boolean debugEnabled = log.isDebugEnabled();
|
||||
int result = 0;
|
||||
while ( itr.hasNext() ) {
|
||||
final Map.Entry e = (Map.Entry) itr.next();
|
||||
final String name = (String) e.getKey();
|
||||
final TypedValue typedval = (TypedValue) e.getValue();
|
||||
final int[] locs = getNamedParameterLocs( name );
|
||||
for ( int loc : locs ) {
|
||||
if ( debugEnabled ) {
|
||||
log.debugf(
|
||||
"bindNamedParameters() %s -> %s [%s]",
|
||||
typedval.getValue(),
|
||||
name,
|
||||
loc + startIndex
|
||||
);
|
||||
}
|
||||
typedval.getType().nullSafeSet( statement, typedval.getValue(), loc + startIndex, session );
|
||||
}
|
||||
result += locs.length;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public int[] getNamedParameterLocs(String name) {
|
||||
throw new AssertionFailure("no named parameters");
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute given <tt>PreparedStatement</tt>, advance to the first result and return SQL <tt>ResultSet</tt>.
|
||||
*/
|
||||
protected final ResultSet getResultSet(
|
||||
final PreparedStatement st,
|
||||
final RowSelection selection,
|
||||
final LimitHandler limitHandler,
|
||||
final boolean autodiscovertypes,
|
||||
final SessionImplementor session)
|
||||
throws SQLException, HibernateException {
|
||||
|
||||
try {
|
||||
ResultSet rs = session.getTransactionCoordinator().getJdbcCoordinator().getResultSetReturn().extract( st );
|
||||
rs = wrapResultSetIfEnabled( rs , session );
|
||||
|
||||
if ( !limitHandler.supportsLimitOffset() || !LimitHelper.useLimit( limitHandler, selection ) ) {
|
||||
advance( rs, selection );
|
||||
}
|
||||
|
||||
if ( autodiscovertypes ) {
|
||||
autoDiscoverTypes( rs );
|
||||
}
|
||||
return rs;
|
||||
}
|
||||
catch ( SQLException sqle ) {
|
||||
session.getTransactionCoordinator().getJdbcCoordinator().release( st );
|
||||
throw sqle;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Advance the cursor to the first required row of the <tt>ResultSet</tt>
|
||||
*/
|
||||
protected void advance(final ResultSet rs, final RowSelection selection) throws SQLException {
|
||||
final int firstRow = LimitHelper.getFirstRow( selection );
|
||||
if ( firstRow != 0 ) {
|
||||
if ( getFactory().getSettings().isScrollableResultSetsEnabled() ) {
|
||||
// we can go straight to the first required row
|
||||
rs.absolute( firstRow );
|
||||
}
|
||||
else {
|
||||
// we need to step through the rows one row at a time (slow)
|
||||
for ( int m = 0; m < firstRow; m++ ) {
|
||||
rs.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void autoDiscoverTypes(ResultSet rs) {
|
||||
throw new AssertionFailure("Auto discover types not supported in this loader");
|
||||
|
||||
}
|
||||
|
||||
private synchronized ResultSet wrapResultSetIfEnabled(final ResultSet rs, final SessionImplementor session) {
|
||||
// synchronized to avoid multi-thread access issues; defined as method synch to avoid
|
||||
// potential deadlock issues due to nature of code.
|
||||
if ( session.getFactory().getSettings().isWrapResultSetsEnabled() ) {
|
||||
try {
|
||||
if ( log.isDebugEnabled() ) {
|
||||
log.debugf( "Wrapping result set [%s]", rs );
|
||||
}
|
||||
return session.getFactory()
|
||||
.getJdbcServices()
|
||||
.getResultSetWrapper().wrap( rs, retreiveColumnNameToIndexCache( rs ) );
|
||||
}
|
||||
catch(SQLException e) {
|
||||
log.unableToWrapResultSet( e );
|
||||
return rs;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return rs;
|
||||
}
|
||||
}
|
||||
|
||||
private ColumnNameCache retreiveColumnNameToIndexCache(ResultSet rs) throws SQLException {
|
||||
if ( columnNameCache == null ) {
|
||||
log.trace( "Building columnName->columnIndex cache" );
|
||||
columnNameCache = new ColumnNameCache( rs.getMetaData().getColumnCount() );
|
||||
}
|
||||
|
||||
return columnNameCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper class for {@link java.sql.Statement} and associated {@link ResultSet}.
|
||||
*/
|
||||
protected static class SqlStatementWrapper {
|
||||
private final Statement statement;
|
||||
private final ResultSet resultSet;
|
||||
|
||||
private SqlStatementWrapper(Statement statement, ResultSet resultSet) {
|
||||
this.resultSet = resultSet;
|
||||
this.statement = statement;
|
||||
}
|
||||
|
||||
public ResultSet getResultSet() {
|
||||
return resultSet;
|
||||
}
|
||||
|
||||
public Statement getStatement() {
|
||||
return statement;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2008, 2012, Red Hat Inc. or third-party contributors as
|
||||
* indicated by the @author tags or express copyright attribution
|
||||
* statements applied by the authors. All third-party contributions are
|
||||
* distributed under license by Red Hat Inc.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
* Lesser General Public License, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this distribution; if not, write to:
|
||||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.loader.entity.plan;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.engine.spi.QueryParameters;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.loader.Loader;
|
||||
import org.hibernate.loader.entity.UniqueEntityLoader;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.pretty.MessageHelper;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
/**
|
||||
* The base contract for loaders capable of performing batch-fetch loading of entities using multiple primary key
|
||||
* values in the SQL <tt>WHERE</tt> clause.
|
||||
*
|
||||
* @author Gavin King
|
||||
* @author Steve Ebersole
|
||||
*
|
||||
* @see org.hibernate.loader.entity.BatchingEntityLoaderBuilder
|
||||
* @see org.hibernate.loader.entity.UniqueEntityLoader
|
||||
*/
|
||||
public abstract class BatchingEntityLoader implements UniqueEntityLoader {
|
||||
private static final Logger log = Logger.getLogger( BatchingEntityLoader.class );
|
||||
|
||||
private final EntityPersister persister;
|
||||
|
||||
public BatchingEntityLoader(EntityPersister persister) {
|
||||
this.persister = persister;
|
||||
}
|
||||
|
||||
public EntityPersister persister() {
|
||||
return persister;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public Object load(Serializable id, Object optionalObject, SessionImplementor session) {
|
||||
return load( id, optionalObject, session, LockOptions.NONE );
|
||||
}
|
||||
|
||||
protected QueryParameters buildQueryParameters(
|
||||
Serializable id,
|
||||
Serializable[] ids,
|
||||
Object optionalObject,
|
||||
LockOptions lockOptions) {
|
||||
Type[] types = new Type[ids.length];
|
||||
Arrays.fill( types, persister().getIdentifierType() );
|
||||
|
||||
QueryParameters qp = new QueryParameters();
|
||||
qp.setPositionalParameterTypes( types );
|
||||
qp.setPositionalParameterValues( ids );
|
||||
qp.setOptionalObject( optionalObject );
|
||||
qp.setOptionalEntityName( persister().getEntityName() );
|
||||
qp.setOptionalId( id );
|
||||
qp.setLockOptions( lockOptions );
|
||||
return qp;
|
||||
}
|
||||
|
||||
protected Object getObjectFromList(List results, Serializable id, SessionImplementor session) {
|
||||
for ( Object obj : results ) {
|
||||
final boolean equal = persister.getIdentifierType().isEqual(
|
||||
id,
|
||||
session.getContextEntityIdentifier( obj ),
|
||||
session.getFactory()
|
||||
);
|
||||
if ( equal ) {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Object doBatchLoad(
|
||||
Serializable id,
|
||||
Loader loaderToUse,
|
||||
SessionImplementor session,
|
||||
Serializable[] ids,
|
||||
Object optionalObject,
|
||||
LockOptions lockOptions) {
|
||||
if ( log.isDebugEnabled() ) {
|
||||
log.debugf( "Batch loading entity: %s", MessageHelper.infoString( persister, ids, session.getFactory() ) );
|
||||
}
|
||||
|
||||
QueryParameters qp = buildQueryParameters( id, ids, optionalObject, lockOptions );
|
||||
|
||||
try {
|
||||
final List results = loaderToUse.doQueryAndInitializeNonLazyCollections( session, qp, false );
|
||||
log.debug( "Done entity batch load" );
|
||||
return getObjectFromList(results, id, session);
|
||||
}
|
||||
catch ( SQLException sqle ) {
|
||||
throw session.getFactory().getSQLExceptionHelper().convert(
|
||||
sqle,
|
||||
"could not load an entity batch: " + MessageHelper.infoString( persister(), ids, session.getFactory() ),
|
||||
loaderToUse.getSQLString()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
|
||||
* indicated by the @author tags or express copyright attribution
|
||||
* statements applied by the authors. All third-party contributions are
|
||||
* distributed under license by Red Hat Middleware LLC.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
* Lesser General Public License, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this distribution; if not, write to:
|
||||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
package org.hibernate.loader.entity.plan;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.internal.CoreLogging;
|
||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
/**
|
||||
* Loads an entity instance using outerjoin fetching to fetch associated entities.
|
||||
* <br>
|
||||
* The <tt>EntityPersister</tt> must implement <tt>Loadable</tt>. For other entities,
|
||||
* create a customized subclass of <tt>Loader</tt>.
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class EntityLoader extends AbstractLoadPlanBasedEntityLoader {
|
||||
private static final Logger log = CoreLogging.logger( EntityLoader.class );
|
||||
|
||||
// private final boolean batchLoader;
|
||||
// private final int[][] compositeKeyManyToOneTargetIndices;
|
||||
//
|
||||
// public EntityLoader(
|
||||
// OuterJoinLoadable persister,
|
||||
// LockMode lockMode,
|
||||
// SessionFactoryImplementor factory,
|
||||
// LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||
// this( persister, 1, lockMode, factory, loadQueryInfluencers );
|
||||
// }
|
||||
//
|
||||
// public EntityLoader(
|
||||
// OuterJoinLoadable persister,
|
||||
// LockOptions lockOptions,
|
||||
// SessionFactoryImplementor factory,
|
||||
// LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||
// this( persister, 1, lockOptions, factory, loadQueryInfluencers );
|
||||
// }
|
||||
|
||||
public EntityLoader(
|
||||
OuterJoinLoadable persister,
|
||||
int batchSize,
|
||||
LockMode lockMode,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||
this(
|
||||
persister,
|
||||
persister.getIdentifierColumnNames(),
|
||||
persister.getIdentifierType(),
|
||||
batchSize,
|
||||
lockMode,
|
||||
factory,
|
||||
loadQueryInfluencers
|
||||
);
|
||||
}
|
||||
|
||||
public EntityLoader(
|
||||
OuterJoinLoadable persister,
|
||||
int batchSize,
|
||||
LockOptions lockOptions,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||
this(
|
||||
persister,
|
||||
persister.getIdentifierColumnNames(),
|
||||
persister.getIdentifierType(),
|
||||
batchSize,
|
||||
lockOptions,
|
||||
factory,
|
||||
loadQueryInfluencers
|
||||
);
|
||||
}
|
||||
|
||||
public EntityLoader(
|
||||
OuterJoinLoadable persister,
|
||||
String[] uniqueKey,
|
||||
Type uniqueKeyType,
|
||||
int batchSize,
|
||||
LockMode lockMode,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||
super( persister, uniqueKeyType, factory, loadQueryInfluencers );
|
||||
|
||||
// EntityJoinWalker walker = new EntityJoinWalker(
|
||||
// persister,
|
||||
// uniqueKey,
|
||||
// batchSize,
|
||||
// lockMode,
|
||||
// factory,
|
||||
// loadQueryInfluencers
|
||||
// );
|
||||
// initFromWalker( walker );
|
||||
// this.compositeKeyManyToOneTargetIndices = walker.getCompositeKeyManyToOneTargetIndices();
|
||||
// postInstantiate();
|
||||
//
|
||||
// batchLoader = batchSize > 1;
|
||||
//
|
||||
if ( log.isDebugEnabled() ) {
|
||||
log.debugf( "Static select for entity %s [%s]: %s", getEntityName(), lockMode, getSqlStatement() );
|
||||
}
|
||||
}
|
||||
|
||||
public EntityLoader(
|
||||
OuterJoinLoadable persister,
|
||||
String[] uniqueKey,
|
||||
Type uniqueKeyType,
|
||||
int batchSize,
|
||||
LockOptions lockOptions,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||
super( persister, uniqueKeyType, factory, loadQueryInfluencers );
|
||||
//
|
||||
// EntityJoinWalker walker = new EntityJoinWalker(
|
||||
// persister,
|
||||
// uniqueKey,
|
||||
// batchSize,
|
||||
// lockOptions,
|
||||
// factory,
|
||||
// loadQueryInfluencers
|
||||
// );
|
||||
// initFromWalker( walker );
|
||||
// this.compositeKeyManyToOneTargetIndices = walker.getCompositeKeyManyToOneTargetIndices();
|
||||
// postInstantiate();
|
||||
//
|
||||
// batchLoader = batchSize > 1;
|
||||
//
|
||||
if ( log.isDebugEnabled() ) {
|
||||
log.debugf(
|
||||
"Static select for entity %s [%s:%s]: %s",
|
||||
getEntityName(),
|
||||
lockOptions.getLockMode(),
|
||||
lockOptions.getTimeOut(),
|
||||
getSqlStatement()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// public Object loadByUniqueKey(SessionImplementor session,Object key) {
|
||||
// return load( session, key, null, null, LockOptions.NONE );
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// protected boolean isSingleRowLoader() {
|
||||
// return !batchLoader;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public int[][] getCompositeKeyManyToOneTargetIndices() {
|
||||
// return compositeKeyManyToOneTargetIndices;
|
||||
// }
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* 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.plan;
|
||||
|
||||
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.loader.entity.BatchingEntityLoader;
|
||||
import org.hibernate.loader.entity.BatchingEntityLoaderBuilder;
|
||||
import org.hibernate.loader.entity.UniqueEntityLoader;
|
||||
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 EntityLoader[] loaders;
|
||||
|
||||
public LegacyBatchingEntityLoader(
|
||||
OuterJoinLoadable persister,
|
||||
int maxBatchSize,
|
||||
LockMode lockMode,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers loadQueryInfluencers) {
|
||||
super( persister );
|
||||
this.batchSizes = ArrayHelper.getBatchSizes( maxBatchSize );
|
||||
this.loaders = new EntityLoader[ 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 EntityLoader[ 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
|
||||
);
|
||||
//EARLY EXIT
|
||||
return getObjectFromList( results, id, session );
|
||||
}
|
||||
}
|
||||
return ( loaders[batchSizes.length-1] ).load( id, optionalObject, session );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue