package org.hibernate.loader.entity;
import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
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.loader.spi.AfterLoadAction;
import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.pretty.MessageHelper;
import org.jboss.logging.Logger;
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 {
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() ) {
persistenceContext.setDefaultReadOnly( queryParameters.isReadOnly() );
}
else {
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 {
persistenceContext.setDefaultReadOnly( defaultReadOnlyOrig );
}
}
catch ( SQLException sqle ) {
throw session.getFactory().getSQLExceptionHelper().convert(
sqle,
"could not load an entity batch: " + MessageHelper.infoString(
getEntityPersisters()[0],
ids,
session.getFactory()
),
sql
);
}
}
private List doTheLoad(String sql, QueryParameters queryParameters, SessionImplementor session) throws SQLException {
final RowSelection selection = queryParameters.getRowSelection();
final int maxRows = LimitHelper.hasMaxRows( selection ) ?
selection.getMaxRows() :
Integer.MAX_VALUE;
final List<AfterLoadAction> afterLoadActions = new ArrayList<AfterLoadAction>();
final SqlStatementWrapper wrapper = executeQueryStatement( sql, queryParameters, false, afterLoadActions, session );
final ResultSet rs = wrapper.getResultSet();
final Statement st = wrapper.getStatement();
try {
return processResultSet( rs, queryParameters, session, false, null, maxRows, afterLoadActions );
}
finally {
session.getTransactionCoordinator().getJdbcCoordinator().release( st );
}
}
}
}