HHH-14312 - entity graph is ignored for 'padded' and 'dynamic' batch style entity loader
This commit is contained in:
parent
c874618d30
commit
13fb23d44e
|
@ -329,4 +329,20 @@ public class LockOptions implements Serializable {
|
|||
destination.setFollowOnLocking( source.getFollowOnLocking() );
|
||||
return destination;
|
||||
}
|
||||
|
||||
public static LockOptions interpret(LockMode lockMode) {
|
||||
if ( lockMode == null || lockMode == LockMode.NONE ) {
|
||||
return NONE;
|
||||
}
|
||||
|
||||
if ( lockMode == LockMode.READ ) {
|
||||
return READ;
|
||||
}
|
||||
|
||||
if ( lockMode.greaterThan( LockMode.UPGRADE_NOWAIT ) ) {
|
||||
return UPGRADE;
|
||||
}
|
||||
|
||||
return new LockOptions( lockMode );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,9 @@ import org.hibernate.LockMode;
|
|||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.loader.entity.plan.DynamicBatchingEntityLoaderBuilder;
|
||||
import org.hibernate.loader.entity.plan.LegacyBatchingEntityLoaderBuilder;
|
||||
import org.hibernate.loader.entity.plan.PaddedBatchingEntityLoaderBuilder;
|
||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
||||
|
||||
/**
|
||||
|
@ -23,7 +26,7 @@ import org.hibernate.persister.entity.OuterJoinLoadable;
|
|||
*/
|
||||
public abstract class BatchingEntityLoaderBuilder {
|
||||
public static BatchingEntityLoaderBuilder getBuilder(SessionFactoryImplementor factory) {
|
||||
switch ( factory.getSettings().getBatchFetchStyle() ) {
|
||||
switch ( factory.getSessionFactoryOptions().getBatchFetchStyle() ) {
|
||||
case PADDED: {
|
||||
return PaddedBatchingEntityLoaderBuilder.INSTANCE;
|
||||
}
|
||||
|
@ -31,8 +34,7 @@ public abstract class BatchingEntityLoaderBuilder {
|
|||
return DynamicBatchingEntityLoaderBuilder.INSTANCE;
|
||||
}
|
||||
default: {
|
||||
return org.hibernate.loader.entity.plan.LegacyBatchingEntityLoaderBuilder.INSTANCE;
|
||||
// return LegacyBatchingEntityLoaderBuilder.INSTANCE;
|
||||
return LegacyBatchingEntityLoaderBuilder.INSTANCE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,132 +0,0 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
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.internal.BatchFetchQueueHelper;
|
||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.internal.util.collections.ArrayHelper;
|
||||
import org.hibernate.loader.Loader;
|
||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
||||
|
||||
/**
|
||||
* No longer used, see {@link org.hibernate.loader.entity.plan.LegacyBatchingEntityLoaderBuilder} instead.
|
||||
*
|
||||
* @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, SharedSessionContractImplementor session, LockOptions lockOptions) {
|
||||
return load( id, optionalObject, session, lockOptions, null );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object load(Serializable id, Object optionalObject, SharedSessionContractImplementor session, LockOptions lockOptions, Boolean readOnly) {
|
||||
final Serializable[] batch = session.getPersistenceContextInternal()
|
||||
.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,
|
||||
readOnly
|
||||
);
|
||||
// The EntityKey for any entity that is not found will remain in the batch.
|
||||
// Explicitly remove the EntityKeys for entities that were not found to
|
||||
// avoid including them in future batches that get executed.
|
||||
BatchFetchQueueHelper.removeNotFoundBatchLoadableEntityKeys(
|
||||
smallBatch,
|
||||
results,
|
||||
persister(),
|
||||
session
|
||||
);
|
||||
return getObjectFromList(results, id, session); //EARLY EXIT
|
||||
}
|
||||
}
|
||||
final Object result = ( (UniqueEntityLoader) loaders[batchSizes.length-1] ).load(id, optionalObject, session);
|
||||
if ( result == null ) {
|
||||
// There was no entity with the specified ID. Make sure the EntityKey does not remain
|
||||
// in the batch to avoid including it in future batches that get executed.
|
||||
BatchFetchQueueHelper.removeBatchLoadableEntityKey( id, persister(), session );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,135 +0,0 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
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.internal.BatchFetchQueueHelper;
|
||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
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, SharedSessionContractImplementor session, LockOptions lockOptions) {
|
||||
return load( id, optionalObject, session, lockOptions, null );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object load(Serializable id, Object optionalObject, SharedSessionContractImplementor session, LockOptions lockOptions, Boolean readOnly) {
|
||||
final Serializable[] batch = session.getPersistenceContextInternal()
|
||||
.getBatchFetchQueue()
|
||||
.getEntityBatch( persister(), id, batchSizes[0], persister().getEntityMode() );
|
||||
|
||||
final int numberOfIds = ArrayHelper.countNonNull( batch );
|
||||
if ( numberOfIds <= 1 ) {
|
||||
final Object result = ( (UniqueEntityLoader) loaders[batchSizes.length-1] ).load( id, optionalObject, session );
|
||||
if ( result == null ) {
|
||||
// There was no entity with the specified ID. Make sure the EntityKey does not remain
|
||||
// in the batch to avoid including it in future batches that get executed.
|
||||
BatchFetchQueueHelper.removeBatchLoadableEntityKey( id, persister(), session );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 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, readOnly );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.loader.entity.plan;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.engine.internal.BatchFetchQueueHelper;
|
||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||
import org.hibernate.engine.spi.QueryParameters;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.internal.util.collections.ArrayHelper;
|
||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
||||
import org.hibernate.pretty.MessageHelper;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class DynamicBatchingEntityLoader extends BatchingEntityLoader {
|
||||
private static final Logger log = Logger.getLogger( DynamicBatchingEntityLoader.class );
|
||||
|
||||
private final int maxBatchSize;
|
||||
private final EntityLoader.Builder entityLoaderBuilder;
|
||||
|
||||
public DynamicBatchingEntityLoader(
|
||||
OuterJoinLoadable persister,
|
||||
int maxBatchSize,
|
||||
LockOptions lockOptions,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers loadQueryInfluencers) {
|
||||
super( persister );
|
||||
this.maxBatchSize = maxBatchSize;
|
||||
|
||||
entityLoaderBuilder = EntityLoader.forEntity( persister )
|
||||
.withInfluencers( loadQueryInfluencers )
|
||||
.withLockOptions( lockOptions );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object load(
|
||||
Serializable id,
|
||||
Object optionalObject,
|
||||
SharedSessionContractImplementor session,
|
||||
LockOptions lockOptions) {
|
||||
return load( id, optionalObject, session, lockOptions, null );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object load(Serializable id, Object optionalObject, SharedSessionContractImplementor session, LockOptions lockOptions, Boolean readOnly) {
|
||||
final Serializable[] batch = session.getPersistenceContextInternal()
|
||||
.getBatchFetchQueue()
|
||||
.getEntityBatch( persister(), id, maxBatchSize, persister().getEntityMode() );
|
||||
|
||||
final int numberOfIds = ArrayHelper.countNonNull( batch );
|
||||
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() ) );
|
||||
}
|
||||
|
||||
final EntityLoader dynamicLoader = entityLoaderBuilder.withBatchSize( idsToLoad.length ).byPrimaryKey();
|
||||
final QueryParameters qp = buildQueryParameters( id, idsToLoad, optionalObject, lockOptions );
|
||||
|
||||
|
||||
|
||||
final List results = dynamicLoader.loadEntityBatch(
|
||||
session,
|
||||
idsToLoad,
|
||||
persister().getIdentifierType(),
|
||||
optionalObject,
|
||||
persister().getEntityName(),
|
||||
id,
|
||||
persister(),
|
||||
lockOptions,
|
||||
readOnly
|
||||
);
|
||||
|
||||
// The EntityKey for any entity that is not found will remain in the batch.
|
||||
// Explicitly remove the EntityKeys for entities that were not found to
|
||||
// avoid including them in future batches that get executed.
|
||||
BatchFetchQueueHelper.removeNotFoundBatchLoadableEntityKeys(
|
||||
idsToLoad,
|
||||
results,
|
||||
persister(),
|
||||
session
|
||||
);
|
||||
|
||||
return getObjectFromList( results, id, session );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.loader.entity.plan;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.loader.entity.UniqueEntityLoader;
|
||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class DynamicBatchingEntityLoaderBuilder extends AbstractBatchingEntityLoaderBuilder {
|
||||
/**
|
||||
* Singleton access
|
||||
*/
|
||||
public static final DynamicBatchingEntityLoaderBuilder INSTANCE = new DynamicBatchingEntityLoaderBuilder();
|
||||
|
||||
@Override
|
||||
protected UniqueEntityLoader buildBatchingLoader(
|
||||
OuterJoinLoadable persister,
|
||||
int batchSize,
|
||||
LockMode lockMode,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers influencers) {
|
||||
return buildBatchingLoader( persister, batchSize, LockOptions.interpret( 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 );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.loader.entity.plan;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.engine.internal.BatchFetchQueueHelper;
|
||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.internal.util.collections.ArrayHelper;
|
||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class PaddedBatchingEntityLoader extends BatchingEntityLoader {
|
||||
private final int[] batchSizes;
|
||||
private final EntityLoader[] loaders;
|
||||
|
||||
public PaddedBatchingEntityLoader(
|
||||
OuterJoinLoadable persister,
|
||||
int maxBatchSize,
|
||||
LockOptions lockOptions,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers loadQueryInfluencers) {
|
||||
super( persister );
|
||||
|
||||
this.batchSizes = ArrayHelper.getBatchSizes( maxBatchSize );
|
||||
this.loaders = new EntityLoader[ batchSizes.length ];
|
||||
final EntityLoader.Builder entityLoaderBuilder = EntityLoader.forEntity( persister )
|
||||
.withInfluencers( loadQueryInfluencers )
|
||||
.withLockOptions( lockOptions );
|
||||
|
||||
// we create a first entity loader to use it as a template for the others
|
||||
this.loaders[0] = entityLoaderBuilder.withBatchSize( batchSizes[0] ).byPrimaryKey();
|
||||
|
||||
for ( int i = 1; i < batchSizes.length; i++ ) {
|
||||
this.loaders[i] = entityLoaderBuilder.withEntityLoaderTemplate( this.loaders[0] ).withBatchSize( batchSizes[i] ).byPrimaryKey();
|
||||
}
|
||||
|
||||
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" );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object load(
|
||||
Serializable id,
|
||||
Object optionalObject,
|
||||
SharedSessionContractImplementor session,
|
||||
LockOptions lockOptions) {
|
||||
return load( id, optionalObject, session, lockOptions, null );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object load(Serializable id, Object optionalObject, SharedSessionContractImplementor session, LockOptions lockOptions, Boolean readOnly) {
|
||||
final Serializable[] batch = session.getPersistenceContextInternal()
|
||||
.getBatchFetchQueue()
|
||||
.getEntityBatch( persister(), id, batchSizes[0], persister().getEntityMode() );
|
||||
|
||||
final int numberOfIds = ArrayHelper.countNonNull( batch );
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
final List results = loaders[indexToUse].loadEntityBatch(
|
||||
session,
|
||||
idsToLoad,
|
||||
persister().getIdentifierType(),
|
||||
optionalObject,
|
||||
persister().getEntityName(),
|
||||
id,
|
||||
persister(),
|
||||
lockOptions,
|
||||
readOnly
|
||||
);
|
||||
|
||||
// The EntityKey for any entity that is not found will remain in the batch.
|
||||
// Explicitly remove the EntityKeys for entities that were not found to
|
||||
// avoid including them in future batches that get executed.
|
||||
BatchFetchQueueHelper.removeNotFoundBatchLoadableEntityKeys(
|
||||
idsToLoad,
|
||||
results,
|
||||
persister(),
|
||||
session
|
||||
);
|
||||
|
||||
return getObjectFromList( results, id, session );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.loader.entity.plan;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.loader.entity.UniqueEntityLoader;
|
||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class PaddedBatchingEntityLoaderBuilder extends AbstractBatchingEntityLoaderBuilder {
|
||||
/**
|
||||
* Singleton access
|
||||
*/
|
||||
public static final PaddedBatchingEntityLoaderBuilder INSTANCE = new PaddedBatchingEntityLoaderBuilder();
|
||||
|
||||
@Override
|
||||
protected UniqueEntityLoader buildBatchingLoader(
|
||||
OuterJoinLoadable persister,
|
||||
int batchSize,
|
||||
LockMode lockMode,
|
||||
SessionFactoryImplementor factory,
|
||||
LoadQueryInfluencers influencers) {
|
||||
return buildBatchingLoader( persister, batchSize, LockOptions.interpret( 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 );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Support for entity loaders built on top of the {@link org.hibernate.loader.plan}
|
||||
* API to apply entity-graphs
|
||||
*/
|
||||
package org.hibernate.loader.entity.plan;
|
|
@ -0,0 +1,143 @@
|
|||
package org.hibernate.graph;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
import org.hibernate.loader.BatchFetchStyle;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.hamcrest.CoreMatchers;
|
||||
|
||||
import static javax.persistence.CascadeType.MERGE;
|
||||
import static javax.persistence.CascadeType.PERSIST;
|
||||
import static javax.persistence.CascadeType.REMOVE;
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hibernate.testing.transaction.TransactionUtil2.inTransaction;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author David Hoffer
|
||||
* @author Nathan Xu
|
||||
*/
|
||||
@TestForIssue( jiraKey = "HHH-14312" )
|
||||
public class EntityGraphDynamicBatchStyleTest extends BaseEntityManagerFunctionalTestCase {
|
||||
private static final int BATCH_SIZE = 5;
|
||||
private static final int NUM_OF_LOCATIONS = (BATCH_SIZE * 2) + 1;
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] {
|
||||
Fruit.class,
|
||||
FruitLocation.class
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addConfigOptions(Map options) {
|
||||
options.put( AvailableSettings.BATCH_FETCH_STYLE, BatchFetchStyle.DYNAMIC );
|
||||
options.put( AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, BATCH_SIZE);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
inTransaction(
|
||||
entityManagerFactory(),
|
||||
entityManager -> {
|
||||
final Fruit fruit = new Fruit( 1, "Goji" );
|
||||
|
||||
for ( int i = 1; i <= NUM_OF_LOCATIONS; i++ ) {
|
||||
fruit.locations.add(
|
||||
new FruitLocation( i, "Goji location #" + i, fruit )
|
||||
);
|
||||
}
|
||||
|
||||
entityManager.persist( fruit );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEntityGraphSemantic() {
|
||||
inTransaction(
|
||||
entityManagerFactory(),
|
||||
entityManager -> {
|
||||
final Map<String, Object> hints = Collections.singletonMap(
|
||||
GraphSemantic.FETCH.getJpaHintName(),
|
||||
GraphParser.parse( Fruit.class, "locations", entityManager )
|
||||
);
|
||||
|
||||
final Fruit fruit = entityManager.find( Fruit.class, 1, hints );
|
||||
assertTrue( Hibernate.isInitialized( fruit.locations ) );
|
||||
assertThat( fruit.locations.size(), is( NUM_OF_LOCATIONS ) );
|
||||
|
||||
fruit.locations.forEach(
|
||||
fruitLocation -> {
|
||||
assertTrue( Hibernate.isInitialized( fruitLocation ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Entity(name = "Fruit")
|
||||
@Table(name = "Fruit")
|
||||
static class Fruit {
|
||||
|
||||
@Id
|
||||
Integer id;
|
||||
|
||||
@Column
|
||||
String name;
|
||||
|
||||
@OneToMany( mappedBy = "fruit", cascade = { PERSIST, MERGE, REMOVE } )
|
||||
Set<FruitLocation> locations = new HashSet<>();
|
||||
|
||||
public Fruit() {
|
||||
}
|
||||
|
||||
public Fruit(Integer id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "FruitLocation")
|
||||
@Table(name = "FruitLocation")
|
||||
static class FruitLocation {
|
||||
|
||||
@Id
|
||||
Integer id;
|
||||
|
||||
@Column
|
||||
String location;
|
||||
|
||||
@ManyToOne
|
||||
Fruit fruit;
|
||||
|
||||
public FruitLocation() {
|
||||
}
|
||||
|
||||
public FruitLocation(Integer id, String location, Fruit fruit) {
|
||||
this.id = id;
|
||||
this.location = location;
|
||||
this.fruit = fruit;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -14,7 +14,6 @@ import javax.persistence.Table;
|
|||
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.graph.spi.RootGraphImplementor;
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
import org.hibernate.loader.BatchFetchStyle;
|
||||
|
||||
|
@ -22,6 +21,12 @@ import org.hibernate.testing.TestForIssue;
|
|||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static javax.persistence.CascadeType.MERGE;
|
||||
import static javax.persistence.CascadeType.PERSIST;
|
||||
import static javax.persistence.CascadeType.REMOVE;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hibernate.loader.BatchFetchStyle.PADDED;
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
|
@ -31,6 +36,8 @@ import static org.junit.Assert.assertTrue;
|
|||
*/
|
||||
@TestForIssue( jiraKey = "HHH-14312" )
|
||||
public class EntityGraphPaddedBatchStyleTest extends BaseEntityManagerFunctionalTestCase {
|
||||
private static final int BATCH_SIZE = 5;
|
||||
private static final int NUM_OF_LOCATIONS = (BATCH_SIZE * 2) + 1;
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
|
@ -42,56 +49,77 @@ public class EntityGraphPaddedBatchStyleTest extends BaseEntityManagerFunctional
|
|||
|
||||
@Override
|
||||
protected void addConfigOptions(Map options) {
|
||||
options.put( AvailableSettings.BATCH_FETCH_STYLE, BatchFetchStyle.PADDED );
|
||||
options.put( AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, "50");
|
||||
options.put( AvailableSettings.BATCH_FETCH_STYLE, PADDED );
|
||||
options.put( AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, BATCH_SIZE );
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Fruit fruit = new Fruit();
|
||||
fruit.id = 1;
|
||||
FruitLocation location1 = new FruitLocation();
|
||||
location1.fruit = fruit;
|
||||
FruitLocation location2 = new FruitLocation();
|
||||
location2.fruit = fruit;
|
||||
fruit.locations.add( location1 );
|
||||
fruit.locations.add( location2 );
|
||||
entityManager.persist( fruit );
|
||||
entityManager.persist( location1 );
|
||||
entityManager.persist( location2 );
|
||||
} );
|
||||
doInJPA(
|
||||
this::entityManagerFactory,
|
||||
entityManager -> {
|
||||
final Fruit fruit = new Fruit( 1, "Goji" );
|
||||
|
||||
for ( int i = 1; i <= NUM_OF_LOCATIONS; i++ ) {
|
||||
fruit.locations.add(
|
||||
new FruitLocation( i, "Goji location #" + i, fruit )
|
||||
);
|
||||
}
|
||||
|
||||
entityManager.persist( fruit );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEntityGraphSemantic() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
final RootGraphImplementor<Fruit> graph = (RootGraphImplementor<Fruit>) GraphParser.parse( Fruit.class, "locations", entityManager );
|
||||
final Map<String, Object> hints = Collections.singletonMap( GraphSemantic.FETCH.getJpaHintName(), graph );
|
||||
final Fruit fruit = entityManager.find( Fruit.class, 1, hints );
|
||||
assertTrue( Hibernate.isInitialized( fruit.locations ) );
|
||||
} );
|
||||
doInJPA(
|
||||
this::entityManagerFactory,
|
||||
entityManager -> {
|
||||
final Map<String, Object> hints = Collections.singletonMap(
|
||||
GraphSemantic.FETCH.getJpaHintName(),
|
||||
GraphParser.parse( Fruit.class, "locations", entityManager )
|
||||
);
|
||||
|
||||
final Fruit fruit = entityManager.find( Fruit.class, 1, hints );
|
||||
assertTrue( Hibernate.isInitialized( fruit.locations ) );
|
||||
assertThat( fruit.locations.size(), is( NUM_OF_LOCATIONS ) );
|
||||
|
||||
fruit.locations.forEach(
|
||||
fruitLocation -> {
|
||||
assertTrue( Hibernate.isInitialized( fruitLocation ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Entity(name = "Fruit")
|
||||
@Table(name = "Fruit")
|
||||
static class Fruit {
|
||||
|
||||
@Id
|
||||
Integer id;
|
||||
|
||||
@Column
|
||||
String name;
|
||||
|
||||
@OneToMany(mappedBy = "fruit")
|
||||
@OneToMany( mappedBy = "fruit", cascade = { PERSIST, MERGE, REMOVE } )
|
||||
Set<FruitLocation> locations = new HashSet<>();
|
||||
|
||||
public Fruit() {
|
||||
}
|
||||
|
||||
public Fruit(Integer id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "FruitLocation")
|
||||
@Table(name = "FruitLocation")
|
||||
static class FruitLocation {
|
||||
|
||||
@Id @GeneratedValue
|
||||
@Id
|
||||
Integer id;
|
||||
|
||||
@Column
|
||||
|
@ -99,6 +127,15 @@ public class EntityGraphPaddedBatchStyleTest extends BaseEntityManagerFunctional
|
|||
|
||||
@ManyToOne
|
||||
Fruit fruit;
|
||||
|
||||
public FruitLocation() {
|
||||
}
|
||||
|
||||
public FruitLocation(Integer id, String location, Fruit fruit) {
|
||||
this.id = id;
|
||||
this.location = location;
|
||||
this.fruit = fruit;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue