HHH-7572 - Develop API for load-by-multiple-ids
This commit is contained in:
parent
2b563794c7
commit
134eb06fba
|
@ -22,7 +22,16 @@ public interface IdentifierLoadAccess<T> {
|
|||
*
|
||||
* @return {@code this}, for method chaining
|
||||
*/
|
||||
public IdentifierLoadAccess<T> with(LockOptions lockOptions);
|
||||
IdentifierLoadAccess<T> with(LockOptions lockOptions);
|
||||
|
||||
/**
|
||||
* Specify the {@link CacheMode} to use when retrieving the entity.
|
||||
*
|
||||
* @param cacheMode The CacheMode to use.
|
||||
*
|
||||
* @return {@code this}, for method chaining
|
||||
*/
|
||||
IdentifierLoadAccess<T> with(CacheMode cacheMode);
|
||||
|
||||
/**
|
||||
* Return the persistent instance with the given identifier, assuming that the instance exists. This method
|
||||
|
@ -36,7 +45,7 @@ public interface IdentifierLoadAccess<T> {
|
|||
*
|
||||
* @return the persistent instance or proxy
|
||||
*/
|
||||
public T getReference(Serializable id);
|
||||
T getReference(Serializable id);
|
||||
|
||||
/**
|
||||
* Return the persistent instance with the given identifier, or null if there is no such persistent instance.
|
||||
|
@ -47,5 +56,5 @@ public interface IdentifierLoadAccess<T> {
|
|||
*
|
||||
* @return The persistent instance or {@code null}
|
||||
*/
|
||||
public T load(Serializable id);
|
||||
T load(Serializable id);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Loads multiple entities at once by identifiers
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface MultiIdentifierLoadAccess<T> {
|
||||
/**
|
||||
* Specify the {@link LockOptions} to use when retrieving the entity.
|
||||
*
|
||||
* @param lockOptions The lock options to use.
|
||||
*
|
||||
* @return {@code this}, for method chaining
|
||||
*/
|
||||
MultiIdentifierLoadAccess<T> with(LockOptions lockOptions);
|
||||
|
||||
/**
|
||||
* Specify the {@link CacheMode} to use when retrieving the entity.
|
||||
*
|
||||
* @param cacheMode The CacheMode to use.
|
||||
*
|
||||
* @return {@code this}, for method chaining
|
||||
*/
|
||||
MultiIdentifierLoadAccess<T> with(CacheMode cacheMode);
|
||||
|
||||
/**
|
||||
* Specify a batch size for loading the entities (how many at a time). The default is
|
||||
* to use a batch sizing strategy defined by the Dialect in use. Any greater-than-one
|
||||
* value here will override that default behavior. If giving an explicit value here,
|
||||
* care should be taken to not exceed the capabilities of of the underlying database.
|
||||
*
|
||||
* @param batchSize The batch size
|
||||
*
|
||||
* @return {@code this}, for method chaining
|
||||
*/
|
||||
MultiIdentifierLoadAccess<T> withBatchSize(int batchSize);
|
||||
|
||||
/**
|
||||
* Should we check the Session to see whether it already contains any of the
|
||||
* entities to be loaded in a managed state <b>for the purpose of not including those
|
||||
* ids to the batch-load SQL</b>
|
||||
*
|
||||
* @param enabled {@code true} enables this checking; {@code false} disables it.
|
||||
*
|
||||
* @return {@code this}, for method chaining
|
||||
*/
|
||||
MultiIdentifierLoadAccess<T> enableSessionCheck(boolean enabled);
|
||||
|
||||
/**
|
||||
* Perform a load of multiple entities by identifiers
|
||||
*
|
||||
* @param ids The ids to load
|
||||
* @param <K> The identifier type
|
||||
*
|
||||
* @return The persistent entities.
|
||||
*/
|
||||
<K extends Serializable> List<T> multiLoad(K... ids);
|
||||
|
||||
/**
|
||||
* Perform a load of multiple entities by identifiers
|
||||
*
|
||||
* @param ids The ids to load
|
||||
* @param <K> The identifier type
|
||||
*
|
||||
* @return The persistent entities.
|
||||
*/
|
||||
<K extends Serializable> List<T> multiLoad(List<K> ids);
|
||||
}
|
|
@ -782,6 +782,30 @@ public interface Session extends SharedSessionContract, java.io.Closeable {
|
|||
*/
|
||||
IdentifierLoadAccess byId(String entityName);
|
||||
|
||||
/**
|
||||
* Create a {@link MultiIdentifierLoadAccess} instance to retrieve multiple entities at once
|
||||
* as specified by primary key values.
|
||||
*
|
||||
* @param entityClass The entity type to be retrieved
|
||||
*
|
||||
* @return load delegate for loading the specified entity type by primary key values
|
||||
*
|
||||
* @throws HibernateException If the specified Class cannot be resolved as a mapped entity
|
||||
*/
|
||||
<T> MultiIdentifierLoadAccess<T> byMultipleIds(Class<T> entityClass);
|
||||
|
||||
/**
|
||||
* Create a {@link MultiIdentifierLoadAccess} instance to retrieve multiple entities at once
|
||||
* as specified by primary key values.
|
||||
*
|
||||
* @param entityName The entity name of the entity type to be retrieved
|
||||
*
|
||||
* @return load delegate for loading the specified entity type by primary key values
|
||||
*
|
||||
* @throws HibernateException If the specified entity name cannot be resolved as an entity name
|
||||
*/
|
||||
MultiIdentifierLoadAccess byMultipleIds(String entityName);
|
||||
|
||||
/**
|
||||
* Create an {@link IdentifierLoadAccess} instance to retrieve the specified entity by
|
||||
* primary key.
|
||||
|
|
|
@ -77,6 +77,7 @@ import org.hibernate.internal.util.ReflectHelper;
|
|||
import org.hibernate.internal.util.StringHelper;
|
||||
import org.hibernate.internal.util.collections.ArrayHelper;
|
||||
import org.hibernate.internal.util.io.StreamCopier;
|
||||
import org.hibernate.loader.BatchLoadSizingStrategy;
|
||||
import org.hibernate.mapping.Column;
|
||||
import org.hibernate.mapping.Constraint;
|
||||
import org.hibernate.mapping.ForeignKey;
|
||||
|
@ -2813,4 +2814,15 @@ public abstract class Dialect implements ConversionContext {
|
|||
public NameQualifierSupport getNameQualifierSupport() {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected final BatchLoadSizingStrategy STANDARD_DEFAULT_BATCH_LOAD_SIZING_STRATEGY = new BatchLoadSizingStrategy() {
|
||||
@Override
|
||||
public int determineOptimalBatchLoadSize(int numberOfKeyColumns, int numberOfKeys) {
|
||||
return 50;
|
||||
}
|
||||
};
|
||||
|
||||
public BatchLoadSizingStrategy getDefaultBatchLoadSizingStrategy() {
|
||||
return STANDARD_DEFAULT_BATCH_LOAD_SIZING_STRATEGY;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import org.hibernate.Interceptor;
|
|||
import org.hibernate.LobHelper;
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.MultiIdentifierLoadAccess;
|
||||
import org.hibernate.NaturalIdLoadAccess;
|
||||
import org.hibernate.Query;
|
||||
import org.hibernate.ReplicationMode;
|
||||
|
@ -662,6 +663,16 @@ public class SessionDelegatorBaseImpl implements SessionImplementor, Session {
|
|||
return session.byId( entityName );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> MultiIdentifierLoadAccess<T> byMultipleIds(Class<T> entityClass) {
|
||||
return session.byMultipleIds( entityClass );
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiIdentifierLoadAccess byMultipleIds(String entityName) {
|
||||
return session.byMultipleIds( entityName );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> IdentifierLoadAccess<T> byId(Class<T> entityClass) {
|
||||
return session.byId( entityClass );
|
||||
|
|
|
@ -42,6 +42,7 @@ import org.hibernate.LobHelper;
|
|||
import org.hibernate.LockMode;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.MultiIdentifierLoadAccess;
|
||||
import org.hibernate.NaturalIdLoadAccess;
|
||||
import org.hibernate.ObjectDeletedException;
|
||||
import org.hibernate.ObjectNotFoundException;
|
||||
|
@ -129,6 +130,7 @@ import org.hibernate.jdbc.WorkExecutorVisitable;
|
|||
import org.hibernate.loader.criteria.CriteriaLoader;
|
||||
import org.hibernate.loader.custom.CustomLoader;
|
||||
import org.hibernate.loader.custom.CustomQuery;
|
||||
import org.hibernate.loader.entity.DynamicBatchingEntityLoaderBuilder;
|
||||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
||||
|
@ -1044,6 +1046,16 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc
|
|||
return new IdentifierLoadAccessImpl<T>( entityClass );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> MultiIdentifierLoadAccess<T> byMultipleIds(Class<T> entityClass) {
|
||||
return new MultiIdentifierLoadAccessImpl<T>( locateEntityPersister( entityClass ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiIdentifierLoadAccess byMultipleIds(String entityName) {
|
||||
return new MultiIdentifierLoadAccessImpl( locateEntityPersister( entityName ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public NaturalIdLoadAccess byNaturalId(String entityName) {
|
||||
return new NaturalIdLoadAccessImpl( entityName );
|
||||
|
@ -2577,6 +2589,7 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc
|
|||
private class IdentifierLoadAccessImpl<T> implements IdentifierLoadAccess<T> {
|
||||
private final EntityPersister entityPersister;
|
||||
private LockOptions lockOptions;
|
||||
private CacheMode cacheMode;
|
||||
|
||||
private IdentifierLoadAccessImpl(EntityPersister entityPersister) {
|
||||
this.entityPersister = entityPersister;
|
||||
|
@ -2597,8 +2610,37 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc
|
|||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public IdentifierLoadAccess<T> with(CacheMode cacheMode) {
|
||||
this.cacheMode = cacheMode;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final T getReference(Serializable id) {
|
||||
CacheMode sessionCacheMode = getCacheMode();
|
||||
boolean cacheModeChanged = false;
|
||||
if ( cacheMode != null ) {
|
||||
// naive check for now...
|
||||
// todo : account for "conceptually equal"
|
||||
if ( cacheMode != sessionCacheMode ) {
|
||||
setCacheMode( cacheMode );
|
||||
cacheModeChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return doGetReference( id );
|
||||
}
|
||||
finally {
|
||||
if ( cacheModeChanged ) {
|
||||
// change it back
|
||||
setCacheMode( sessionCacheMode );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected T doGetReference(Serializable id) {
|
||||
if ( this.lockOptions != null ) {
|
||||
LoadEvent event = new LoadEvent( id, entityPersister.getEntityName(), lockOptions, SessionImpl.this );
|
||||
fireLoad( event, LoadEventListener.LOAD );
|
||||
|
@ -2624,8 +2666,31 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc
|
|||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public final T load(Serializable id) {
|
||||
CacheMode sessionCacheMode = getCacheMode();
|
||||
boolean cacheModeChanged = false;
|
||||
if ( cacheMode != null ) {
|
||||
// naive check for now...
|
||||
// todo : account for "conceptually equal"
|
||||
if ( cacheMode != sessionCacheMode ) {
|
||||
setCacheMode( cacheMode );
|
||||
cacheModeChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return doLoad( id );
|
||||
}
|
||||
finally {
|
||||
if ( cacheModeChanged ) {
|
||||
// change it back
|
||||
setCacheMode( sessionCacheMode );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected final T doLoad(Serializable id) {
|
||||
if ( this.lockOptions != null ) {
|
||||
LoadEvent event = new LoadEvent( id, entityPersister.getEntityName(), lockOptions, SessionImpl.this );
|
||||
fireLoad( event, LoadEventListener.GET );
|
||||
|
@ -2648,6 +2713,109 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc
|
|||
}
|
||||
}
|
||||
|
||||
private class MultiIdentifierLoadAccessImpl<T> implements MultiIdentifierLoadAccess<T> {
|
||||
private final EntityPersister entityPersister;
|
||||
private LockOptions lockOptions;
|
||||
private CacheMode cacheMode;
|
||||
private Integer batchSize;
|
||||
private boolean sessionCheckingEnabled;
|
||||
|
||||
public MultiIdentifierLoadAccessImpl(EntityPersister entityPersister) {
|
||||
this.entityPersister = entityPersister;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final MultiIdentifierLoadAccessImpl<T> with(LockOptions lockOptions) {
|
||||
this.lockOptions = lockOptions;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiIdentifierLoadAccessImpl<T> with(CacheMode cacheMode) {
|
||||
this.cacheMode = cacheMode;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiIdentifierLoadAccess<T> withBatchSize(int batchSize) {
|
||||
if ( batchSize < 1 ) {
|
||||
this.batchSize = null;
|
||||
}
|
||||
else {
|
||||
this.batchSize = batchSize;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiIdentifierLoadAccess<T> enableSessionCheck(boolean enabled) {
|
||||
this.sessionCheckingEnabled = enabled;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <K extends Serializable> List<T> multiLoad(K... ids) {
|
||||
CacheMode sessionCacheMode = getCacheMode();
|
||||
boolean cacheModeChanged = false;
|
||||
if ( cacheMode != null ) {
|
||||
// naive check for now...
|
||||
// todo : account for "conceptually equal"
|
||||
if ( cacheMode != sessionCacheMode ) {
|
||||
setCacheMode( cacheMode );
|
||||
cacheModeChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return DynamicBatchingEntityLoaderBuilder.INSTANCE.multiLoad(
|
||||
(OuterJoinLoadable) entityPersister,
|
||||
ids,
|
||||
lockOptions,
|
||||
batchSize,
|
||||
sessionCheckingEnabled,
|
||||
SessionImpl.this
|
||||
);
|
||||
}
|
||||
finally {
|
||||
if ( cacheModeChanged ) {
|
||||
// change it back
|
||||
setCacheMode( sessionCacheMode );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <K extends Serializable> List<T> multiLoad(List<K> ids) {
|
||||
CacheMode sessionCacheMode = getCacheMode();
|
||||
boolean cacheModeChanged = false;
|
||||
if ( cacheMode != null ) {
|
||||
// naive check for now...
|
||||
// todo : account for "conceptually equal"
|
||||
if ( cacheMode != sessionCacheMode ) {
|
||||
setCacheMode( cacheMode );
|
||||
cacheModeChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return DynamicBatchingEntityLoaderBuilder.INSTANCE.multiLoad(
|
||||
(OuterJoinLoadable) entityPersister,
|
||||
ids.toArray( new Serializable[ ids.size() ] ),
|
||||
lockOptions,
|
||||
batchSize,
|
||||
sessionCheckingEnabled,
|
||||
SessionImpl.this
|
||||
);
|
||||
}
|
||||
finally {
|
||||
if ( cacheModeChanged ) {
|
||||
// change it back
|
||||
setCacheMode( sessionCacheMode );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private EntityPersister locateEntityPersister(Class entityClass) {
|
||||
return factory.locateEntityPersister( entityClass );
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Strategy (pluggable) for determining an optimal size for batch loads.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface BatchLoadSizingStrategy {
|
||||
int determineOptimalBatchLoadSize(int numberOfKeyColumns, int numberOfKeys);
|
||||
}
|
|
@ -7,15 +7,18 @@
|
|||
package org.hibernate.loader.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Array;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.dialect.pagination.LimitHelper;
|
||||
import org.hibernate.engine.spi.EntityKey;
|
||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||
import org.hibernate.engine.spi.PersistenceContext;
|
||||
import org.hibernate.engine.spi.QueryParameters;
|
||||
|
@ -24,9 +27,11 @@ 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.internal.util.collections.CollectionHelper;
|
||||
import org.hibernate.loader.spi.AfterLoadAction;
|
||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
||||
import org.hibernate.pretty.MessageHelper;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
|
@ -41,6 +46,114 @@ public class DynamicBatchingEntityLoaderBuilder extends BatchingEntityLoaderBuil
|
|||
|
||||
public static final DynamicBatchingEntityLoaderBuilder INSTANCE = new DynamicBatchingEntityLoaderBuilder();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T, K extends Serializable> List<T> multiLoad(
|
||||
OuterJoinLoadable persister,
|
||||
K[] ids,
|
||||
LockOptions lockOptions,
|
||||
Integer explicitBatchSize,
|
||||
boolean sessionCheckingEnabled,
|
||||
SessionImplementor session) {
|
||||
List<T> result = CollectionHelper.arrayList( ids.length );
|
||||
|
||||
if ( sessionCheckingEnabled ) {
|
||||
// the user requested that we exclude ids corresponding to already managed
|
||||
// entities from the generated load SQL. So here we will iterate all
|
||||
// incoming id values and see whether it corresponds to an existing
|
||||
// entity associated with the PC - if it does we add it to the result
|
||||
// list immediately and remove its id from the group of ids to load.
|
||||
boolean foundAnyManagedEntities = false;
|
||||
final List<K> nonManagedIds = new ArrayList<K>();
|
||||
for ( K id : ids ) {
|
||||
final EntityKey entityKey = new EntityKey( id, persister );
|
||||
final T managedEntity = (T) session.getPersistenceContext().getEntity( entityKey );
|
||||
if ( managedEntity != null ) {
|
||||
foundAnyManagedEntities = true;
|
||||
result.add( managedEntity );
|
||||
}
|
||||
else {
|
||||
nonManagedIds.add( id );
|
||||
}
|
||||
}
|
||||
|
||||
if ( foundAnyManagedEntities ) {
|
||||
if ( nonManagedIds.isEmpty() ) {
|
||||
// all of the given ids were already associated with the Session
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
// over-write the ids to be loaded with the collection of
|
||||
// just non-managed ones
|
||||
ids = nonManagedIds.toArray(
|
||||
(K[]) Array.newInstance(
|
||||
ids.getClass().getComponentType(),
|
||||
nonManagedIds.size()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( lockOptions == null ) {
|
||||
lockOptions = new LockOptions( LockMode.NONE );
|
||||
}
|
||||
|
||||
int numberOfIdsLeft = ids.length;
|
||||
|
||||
final int maxBatchSize;
|
||||
if ( explicitBatchSize != null && explicitBatchSize > 0 ) {
|
||||
maxBatchSize = explicitBatchSize;
|
||||
}
|
||||
else {
|
||||
maxBatchSize = session.getFactory().getDialect().getDefaultBatchLoadSizingStrategy().determineOptimalBatchLoadSize(
|
||||
persister.getIdentifierType().getColumnSpan( session.getFactory() ),
|
||||
numberOfIdsLeft
|
||||
);
|
||||
}
|
||||
|
||||
int idPosition = 0;
|
||||
while ( numberOfIdsLeft > 0 ) {
|
||||
int batchSize = Math.min( numberOfIdsLeft, maxBatchSize );
|
||||
final DynamicEntityLoader batchingLoader = new DynamicEntityLoader(
|
||||
persister,
|
||||
batchSize,
|
||||
lockOptions,
|
||||
session.getFactory(),
|
||||
session.getLoadQueryInfluencers()
|
||||
);
|
||||
|
||||
Serializable[] idsInBatch = new Serializable[batchSize];
|
||||
System.arraycopy( ids, idPosition, idsInBatch, 0, batchSize );
|
||||
|
||||
QueryParameters qp = buildMultiLoadQueryParameters( persister, idsInBatch, lockOptions );
|
||||
result.addAll( batchingLoader.doEntityBatchFetch( session, qp, idsInBatch ) );
|
||||
|
||||
numberOfIdsLeft = numberOfIdsLeft - batchSize;
|
||||
idPosition += batchSize;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static QueryParameters buildMultiLoadQueryParameters(
|
||||
OuterJoinLoadable persister,
|
||||
Serializable[] ids,
|
||||
LockOptions lockOptions) {
|
||||
Type[] types = new Type[ids.length];
|
||||
Arrays.fill( types, persister.getIdentifierType() );
|
||||
|
||||
QueryParameters qp = new QueryParameters();
|
||||
qp.setOptionalEntityName( persister.getEntityName() );
|
||||
qp.setPositionalParameterTypes( types );
|
||||
qp.setPositionalParameterValues( ids );
|
||||
qp.setLockOptions( lockOptions );
|
||||
qp.setOptionalObject( null );
|
||||
qp.setOptionalId( null );
|
||||
return qp;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected UniqueEntityLoader buildBatchingLoader(
|
||||
OuterJoinLoadable persister,
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* 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.test.ops.multiLoad;
|
||||
|
||||
import java.util.List;
|
||||
import javax.persistence.Cacheable;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.SharedCacheMode;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.CacheMode;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.boot.MetadataBuilder;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.cache.spi.access.AccessType;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class MultiLoadTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||
@Override
|
||||
protected Class[] getAnnotatedClasses() {
|
||||
return new Class[] { SimpleEntity.class };
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
|
||||
super.configureStandardServiceRegistryBuilder( ssrb );
|
||||
ssrb.applySetting( AvailableSettings.USE_SECOND_LEVEL_CACHE, true );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureMetadataBuilder(MetadataBuilder metadataBuilder) {
|
||||
super.configureMetadataBuilder( metadataBuilder );
|
||||
|
||||
metadataBuilder.applySharedCacheMode( SharedCacheMode.ENABLE_SELECTIVE );
|
||||
metadataBuilder.applyAccessType( AccessType.READ_WRITE );
|
||||
}
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
Session session = sessionFactory().openSession();
|
||||
session.getTransaction().begin();
|
||||
session.setCacheMode( CacheMode.IGNORE );
|
||||
for ( int i = 1; i <= 60; i++ ) {
|
||||
session.save( new SimpleEntity( i, "Entity #" + i ) );
|
||||
}
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
Session session = sessionFactory().openSession();
|
||||
session.getTransaction().begin();
|
||||
session.createQuery( "delete SimpleEntity" ).executeUpdate();
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicMultiLoad() {
|
||||
Session session = openSession();
|
||||
session.getTransaction().begin();
|
||||
List<SimpleEntity> list = session.byMultipleIds( SimpleEntity.class ).multiLoad( ids(56) );
|
||||
assertEquals( 56, list.size() );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicMultiLoadWithManagedAndNoChecking() {
|
||||
Session session = openSession();
|
||||
session.getTransaction().begin();
|
||||
SimpleEntity first = session.byId( SimpleEntity.class ).load( 1 );
|
||||
List<SimpleEntity> list = session.byMultipleIds( SimpleEntity.class ).multiLoad( ids(56) );
|
||||
assertEquals( 56, list.size() );
|
||||
// this check is HIGHLY specific to implementation in the batch loader
|
||||
// which puts existing managed entities first...
|
||||
assertSame( first, list.get( 0 ) );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicMultiLoadWithManagedAndChecking() {
|
||||
Session session = openSession();
|
||||
session.getTransaction().begin();
|
||||
SimpleEntity first = session.byId( SimpleEntity.class ).load( 1 );
|
||||
List<SimpleEntity> list = session.byMultipleIds( SimpleEntity.class ).enableSessionCheck( true ).multiLoad( ids(56) );
|
||||
assertEquals( 56, list.size() );
|
||||
// this check is HIGHLY specific to implementation in the batch loader
|
||||
// which puts existing managed entities first...
|
||||
assertSame( first, list.get( 0 ) );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiLoadWithCacheModeIgnore() {
|
||||
// do the multi-load, telling Hibernate to IGNORE the L2 cache -
|
||||
// the end result should be that the cache is (still) empty afterwards
|
||||
Session session = openSession();
|
||||
session.getTransaction().begin();
|
||||
List<SimpleEntity> list = session.byMultipleIds( SimpleEntity.class )
|
||||
.with( CacheMode.IGNORE )
|
||||
.multiLoad( ids(56) );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
|
||||
assertEquals( 56, list.size() );
|
||||
for ( SimpleEntity entity : list ) {
|
||||
assertFalse( sessionFactory().getCache().containsEntity( SimpleEntity.class, entity.getId() ) );
|
||||
}
|
||||
}
|
||||
|
||||
private Integer[] ids(int count) {
|
||||
Integer[] ids = new Integer[count];
|
||||
for ( int i = 1; i <= count; i++ ) {
|
||||
ids[i-1] = i;
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
@Entity( name = "SimpleEntity" )
|
||||
@Table( name = "SimpleEntity" )
|
||||
@Cacheable()
|
||||
public static class SimpleEntity {
|
||||
Integer id;
|
||||
String text;
|
||||
|
||||
public SimpleEntity() {
|
||||
}
|
||||
|
||||
public SimpleEntity(Integer id, String text) {
|
||||
this.id = id;
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
@Id
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue