HHH-7572 - Develop API for load-by-multiple-ids

This commit is contained in:
Steve Ebersole 2016-01-25 13:12:08 -06:00
parent 43db914f0d
commit 95ed41b445
9 changed files with 110 additions and 38 deletions

View File

@ -38,6 +38,9 @@ public interface MultiIdentifierLoadAccess<T> {
* to use a batch sizing strategy defined by the Dialect in use. Any greater-than-one * 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, * 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. * care should be taken to not exceed the capabilities of of the underlying database.
* <p/>
* Note that overall a batch-size is considered a hint. How the underlying loading
* mechanism interprets that is completely up to that underlying loading mechanism.
* *
* @param batchSize The batch size * @param batchSize The batch size
* *

View File

@ -133,6 +133,7 @@ import org.hibernate.loader.custom.CustomQuery;
import org.hibernate.loader.entity.DynamicBatchingEntityLoaderBuilder; import org.hibernate.loader.entity.DynamicBatchingEntityLoaderBuilder;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.MultiLoadOptions;
import org.hibernate.persister.entity.OuterJoinLoadable; import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.pretty.MessageHelper; import org.hibernate.pretty.MessageHelper;
import org.hibernate.procedure.ProcedureCall; import org.hibernate.procedure.ProcedureCall;
@ -2771,7 +2772,7 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc
} }
} }
private class MultiIdentifierLoadAccessImpl<T> implements MultiIdentifierLoadAccess<T> { private class MultiIdentifierLoadAccessImpl<T> implements MultiIdentifierLoadAccess<T>, MultiLoadOptions {
private final EntityPersister entityPersister; private final EntityPersister entityPersister;
private LockOptions lockOptions; private LockOptions lockOptions;
private CacheMode cacheMode; private CacheMode cacheMode;
@ -2782,6 +2783,11 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc
this.entityPersister = entityPersister; this.entityPersister = entityPersister;
} }
@Override
public LockOptions getLockOptions() {
return lockOptions;
}
@Override @Override
public final MultiIdentifierLoadAccessImpl<T> with(LockOptions lockOptions) { public final MultiIdentifierLoadAccessImpl<T> with(LockOptions lockOptions) {
this.lockOptions = lockOptions; this.lockOptions = lockOptions;
@ -2794,6 +2800,11 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc
return this; return this;
} }
@Override
public Integer getBatchSize() {
return batchSize;
}
@Override @Override
public MultiIdentifierLoadAccess<T> withBatchSize(int batchSize) { public MultiIdentifierLoadAccess<T> withBatchSize(int batchSize) {
if ( batchSize < 1 ) { if ( batchSize < 1 ) {
@ -2805,6 +2816,11 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc
return this; return this;
} }
@Override
public boolean isSessionCheckingEnabled() {
return sessionCheckingEnabled;
}
@Override @Override
public MultiIdentifierLoadAccess<T> enableSessionCheck(boolean enabled) { public MultiIdentifierLoadAccess<T> enableSessionCheck(boolean enabled) {
this.sessionCheckingEnabled = enabled; this.sessionCheckingEnabled = enabled;
@ -2812,6 +2828,7 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc
} }
@Override @Override
@SuppressWarnings("unchecked")
public <K extends Serializable> List<T> multiLoad(K... ids) { public <K extends Serializable> List<T> multiLoad(K... ids) {
CacheMode sessionCacheMode = getCacheMode(); CacheMode sessionCacheMode = getCacheMode();
boolean cacheModeChanged = false; boolean cacheModeChanged = false;
@ -2825,14 +2842,7 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc
} }
try { try {
return DynamicBatchingEntityLoaderBuilder.INSTANCE.multiLoad( return entityPersister.multiLoad( ids, SessionImpl.this, this );
(OuterJoinLoadable) entityPersister,
ids,
lockOptions,
batchSize,
sessionCheckingEnabled,
SessionImpl.this
);
} }
finally { finally {
if ( cacheModeChanged ) { if ( cacheModeChanged ) {
@ -2843,6 +2853,7 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc
} }
@Override @Override
@SuppressWarnings("unchecked")
public <K extends Serializable> List<T> multiLoad(List<K> ids) { public <K extends Serializable> List<T> multiLoad(List<K> ids) {
CacheMode sessionCacheMode = getCacheMode(); CacheMode sessionCacheMode = getCacheMode();
boolean cacheModeChanged = false; boolean cacheModeChanged = false;
@ -2856,14 +2867,7 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc
} }
try { try {
return DynamicBatchingEntityLoaderBuilder.INSTANCE.multiLoad( return entityPersister.multiLoad( ids.toArray( new Serializable[ ids.size() ] ), SessionImpl.this, this );
(OuterJoinLoadable) entityPersister,
ids.toArray( new Serializable[ ids.size() ] ),
lockOptions,
batchSize,
sessionCheckingEnabled,
SessionImpl.this
);
} }
finally { finally {
if ( cacheModeChanged ) { if ( cacheModeChanged ) {

View File

@ -29,6 +29,7 @@ import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.loader.spi.AfterLoadAction; import org.hibernate.loader.spi.AfterLoadAction;
import org.hibernate.persister.entity.MultiLoadOptions;
import org.hibernate.persister.entity.OuterJoinLoadable; import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.pretty.MessageHelper; import org.hibernate.pretty.MessageHelper;
import org.hibernate.type.Type; import org.hibernate.type.Type;
@ -47,26 +48,23 @@ public class DynamicBatchingEntityLoaderBuilder extends BatchingEntityLoaderBuil
public static final DynamicBatchingEntityLoaderBuilder INSTANCE = new DynamicBatchingEntityLoaderBuilder(); public static final DynamicBatchingEntityLoaderBuilder INSTANCE = new DynamicBatchingEntityLoaderBuilder();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T, K extends Serializable> List<T> multiLoad( public List multiLoad(
OuterJoinLoadable persister, OuterJoinLoadable persister,
K[] ids, Serializable[] ids, SessionImplementor session,
LockOptions lockOptions, MultiLoadOptions loadOptions) {
Integer explicitBatchSize, List result = CollectionHelper.arrayList( ids.length );
boolean sessionCheckingEnabled,
SessionImplementor session) {
List<T> result = CollectionHelper.arrayList( ids.length );
if ( sessionCheckingEnabled ) { if ( loadOptions.isSessionCheckingEnabled() ) {
// the user requested that we exclude ids corresponding to already managed // the user requested that we exclude ids corresponding to already managed
// entities from the generated load SQL. So here we will iterate all // entities from the generated load SQL. So here we will iterate all
// incoming id values and see whether it corresponds to an existing // 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 // 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. // list immediately and remove its id from the group of ids to load.
boolean foundAnyManagedEntities = false; boolean foundAnyManagedEntities = false;
final List<K> nonManagedIds = new ArrayList<K>(); final List<Serializable> nonManagedIds = new ArrayList<Serializable>();
for ( K id : ids ) { for ( Serializable id : ids ) {
final EntityKey entityKey = new EntityKey( id, persister ); final EntityKey entityKey = new EntityKey( id, persister );
final T managedEntity = (T) session.getPersistenceContext().getEntity( entityKey ); final Object managedEntity = session.getPersistenceContext().getEntity( entityKey );
if ( managedEntity != null ) { if ( managedEntity != null ) {
foundAnyManagedEntities = true; foundAnyManagedEntities = true;
result.add( managedEntity ); result.add( managedEntity );
@ -85,7 +83,7 @@ public class DynamicBatchingEntityLoaderBuilder extends BatchingEntityLoaderBuil
// over-write the ids to be loaded with the collection of // over-write the ids to be loaded with the collection of
// just non-managed ones // just non-managed ones
ids = nonManagedIds.toArray( ids = nonManagedIds.toArray(
(K[]) Array.newInstance( (Serializable[]) Array.newInstance(
ids.getClass().getComponentType(), ids.getClass().getComponentType(),
nonManagedIds.size() nonManagedIds.size()
) )
@ -94,16 +92,14 @@ public class DynamicBatchingEntityLoaderBuilder extends BatchingEntityLoaderBuil
} }
} }
final LockOptions lockOptions = (loadOptions.getLockOptions() == null)
if ( lockOptions == null ) { ? new LockOptions( LockMode.NONE )
lockOptions = new LockOptions( LockMode.NONE ); : loadOptions.getLockOptions();
}
int numberOfIdsLeft = ids.length; int numberOfIdsLeft = ids.length;
final int maxBatchSize; final int maxBatchSize;
if ( explicitBatchSize != null && explicitBatchSize > 0 ) { if ( loadOptions.getBatchSize() != null && loadOptions.getBatchSize() > 0 ) {
maxBatchSize = explicitBatchSize; maxBatchSize = loadOptions.getBatchSize();
} }
else { else {
maxBatchSize = session.getFactory().getDialect().getDefaultBatchLoadSizingStrategy().determineOptimalBatchLoadSize( maxBatchSize = session.getFactory().getDialect().getDefaultBatchLoadSizingStrategy().determineOptimalBatchLoadSize(

View File

@ -79,6 +79,7 @@ import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.FilterHelper; import org.hibernate.internal.FilterHelper;
import org.hibernate.internal.SessionImpl;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.jdbc.Expectation; import org.hibernate.jdbc.Expectation;
@ -86,6 +87,7 @@ import org.hibernate.jdbc.Expectations;
import org.hibernate.jdbc.TooManyRowsAffectedException; import org.hibernate.jdbc.TooManyRowsAffectedException;
import org.hibernate.loader.entity.BatchingEntityLoaderBuilder; import org.hibernate.loader.entity.BatchingEntityLoaderBuilder;
import org.hibernate.loader.entity.CascadeEntityLoader; import org.hibernate.loader.entity.CascadeEntityLoader;
import org.hibernate.loader.entity.DynamicBatchingEntityLoaderBuilder;
import org.hibernate.loader.entity.EntityLoader; import org.hibernate.loader.entity.EntityLoader;
import org.hibernate.loader.entity.UniqueEntityLoader; import org.hibernate.loader.entity.UniqueEntityLoader;
import org.hibernate.mapping.Column; import org.hibernate.mapping.Column;
@ -3989,6 +3991,16 @@ public abstract class AbstractEntityPersister
return loader.load( id, optionalObject, session, lockOptions ); return loader.load( id, optionalObject, session, lockOptions );
} }
@Override
public List multiLoad(Serializable[] ids, SessionImplementor session, MultiLoadOptions loadOptions) {
return DynamicBatchingEntityLoaderBuilder.INSTANCE.multiLoad(
this,
ids,
session,
loadOptions
);
}
public void registerAffectingFetchProfile(String fetchProfileName) { public void registerAffectingFetchProfile(String fetchProfileName) {
affectingFetchProfileNames.add( fetchProfileName ); affectingFetchProfileNames.add( fetchProfileName );
} }

View File

@ -7,8 +7,8 @@
package org.hibernate.persister.entity; package org.hibernate.persister.entity;
import java.io.Serializable; import java.io.Serializable;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.hibernate.EntityMode; import org.hibernate.EntityMode;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
@ -364,6 +364,17 @@ public interface EntityPersister extends OptimisticCacheSource, EntityDefinition
public Object load(Serializable id, Object optionalObject, LockOptions lockOptions, SessionImplementor session) public Object load(Serializable id, Object optionalObject, LockOptions lockOptions, SessionImplementor session)
throws HibernateException; throws HibernateException;
/**
* Performs a load of multiple entities (of this type) by identifier simultaneously.
*
* @param ids The identifiers to load
* @param session The originating Sesison
* @param loadOptions The options for loading
*
* @return The loaded, matching entities
*/
List multiLoad(Serializable[] ids, SessionImplementor session, MultiLoadOptions loadOptions);
/** /**
* Do a version check (optional operation) * Do a version check (optional operation)
*/ */

View File

@ -0,0 +1,22 @@
/*
* 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.persister.entity;
import org.hibernate.LockOptions;
/**
* Encapsulation of the options for performing a load by multiple identifiers.
*
* @author Steve Ebersole
*/
public interface MultiLoadOptions {
boolean isSessionCheckingEnabled();
LockOptions getLockOptions();
Integer getBatchSize();
}

View File

@ -9,7 +9,9 @@ package org.hibernate.test.cfg.persister;
import java.io.Serializable; import java.io.Serializable;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -40,6 +42,7 @@ import org.hibernate.metadata.ClassMetadata;
import org.hibernate.metadata.CollectionMetadata; import org.hibernate.metadata.CollectionMetadata;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.MultiLoadOptions;
import org.hibernate.persister.spi.PersisterClassResolver; import org.hibernate.persister.spi.PersisterClassResolver;
import org.hibernate.persister.spi.PersisterCreationContext; import org.hibernate.persister.spi.PersisterCreationContext;
import org.hibernate.persister.walking.spi.AttributeDefinition; import org.hibernate.persister.walking.spi.AttributeDefinition;
@ -267,6 +270,12 @@ public class GoofyPersisterClassProvider implements PersisterClassResolver {
return null; return null;
} }
@Override
public List multiLoad(
Serializable[] ids, SessionImplementor session, MultiLoadOptions loadOptions) {
return Collections.emptyList();
}
@Override @Override
public void lock(Serializable id, Object version, Object object, LockMode lockMode, SessionImplementor session) { public void lock(Serializable id, Object version, Object object, LockMode lockMode, SessionImplementor session) {
} }

View File

@ -7,10 +7,11 @@
package org.hibernate.test.legacy; package org.hibernate.test.legacy;
import java.io.Serializable; import java.io.Serializable;
import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.Hashtable; import java.util.Hashtable;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.hibernate.EntityMode; import org.hibernate.EntityMode;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
@ -43,6 +44,7 @@ import org.hibernate.internal.util.compare.EqualsHelper;
import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.PersistentClass;
import org.hibernate.metadata.ClassMetadata; import org.hibernate.metadata.ClassMetadata;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.MultiLoadOptions;
import org.hibernate.persister.spi.PersisterCreationContext; import org.hibernate.persister.spi.PersisterCreationContext;
import org.hibernate.persister.walking.spi.AttributeDefinition; import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.persister.walking.spi.EntityIdentifierDefinition; import org.hibernate.persister.walking.spi.EntityIdentifierDefinition;
@ -304,6 +306,11 @@ public class CustomPersister implements EntityPersister {
return load(id, optionalObject, lockOptions.getLockMode(), session); return load(id, optionalObject, lockOptions.getLockMode(), session);
} }
@Override
public List multiLoad(Serializable[] ids, SessionImplementor session, MultiLoadOptions loadOptions) {
return Collections.emptyList();
}
/** /**
* @see EntityPersister#load(Serializable, Object, LockMode, SessionImplementor) * @see EntityPersister#load(Serializable, Object, LockMode, SessionImplementor)
*/ */

View File

@ -8,7 +8,9 @@ package org.hibernate.jpa.test.ejb3configuration;
import java.io.Serializable; import java.io.Serializable;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.List;
import java.util.Map; import java.util.Map;
import javax.persistence.EntityManagerFactory; import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceException; import javax.persistence.PersistenceException;
@ -40,6 +42,7 @@ import org.hibernate.mapping.PersistentClass;
import org.hibernate.metadata.ClassMetadata; import org.hibernate.metadata.ClassMetadata;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.MultiLoadOptions;
import org.hibernate.persister.internal.PersisterClassResolverInitiator; import org.hibernate.persister.internal.PersisterClassResolverInitiator;
import org.hibernate.persister.spi.PersisterClassResolver; import org.hibernate.persister.spi.PersisterClassResolver;
import org.hibernate.persister.spi.PersisterCreationContext; import org.hibernate.persister.spi.PersisterCreationContext;
@ -303,6 +306,11 @@ public class PersisterClassProviderTest {
return null; return null;
} }
@Override
public List multiLoad(Serializable[] ids, SessionImplementor session, MultiLoadOptions loadOptions) {
return Collections.emptyList();
}
@Override @Override
public void lock(Serializable id, Object version, Object object, LockMode lockMode, SessionImplementor session) { public void lock(Serializable id, Object version, Object object, LockMode lockMode, SessionImplementor session) {
} }