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
* 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.
* <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
*

View File

@ -133,6 +133,7 @@ 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.MultiLoadOptions;
import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.pretty.MessageHelper;
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 LockOptions lockOptions;
private CacheMode cacheMode;
@ -2782,6 +2783,11 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc
this.entityPersister = entityPersister;
}
@Override
public LockOptions getLockOptions() {
return lockOptions;
}
@Override
public final MultiIdentifierLoadAccessImpl<T> with(LockOptions lockOptions) {
this.lockOptions = lockOptions;
@ -2794,6 +2800,11 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc
return this;
}
@Override
public Integer getBatchSize() {
return batchSize;
}
@Override
public MultiIdentifierLoadAccess<T> withBatchSize(int batchSize) {
if ( batchSize < 1 ) {
@ -2805,6 +2816,11 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc
return this;
}
@Override
public boolean isSessionCheckingEnabled() {
return sessionCheckingEnabled;
}
@Override
public MultiIdentifierLoadAccess<T> enableSessionCheck(boolean enabled) {
this.sessionCheckingEnabled = enabled;
@ -2812,6 +2828,7 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc
}
@Override
@SuppressWarnings("unchecked")
public <K extends Serializable> List<T> multiLoad(K... ids) {
CacheMode sessionCacheMode = getCacheMode();
boolean cacheModeChanged = false;
@ -2825,14 +2842,7 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc
}
try {
return DynamicBatchingEntityLoaderBuilder.INSTANCE.multiLoad(
(OuterJoinLoadable) entityPersister,
ids,
lockOptions,
batchSize,
sessionCheckingEnabled,
SessionImpl.this
);
return entityPersister.multiLoad( ids, SessionImpl.this, this );
}
finally {
if ( cacheModeChanged ) {
@ -2843,6 +2853,7 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc
}
@Override
@SuppressWarnings("unchecked")
public <K extends Serializable> List<T> multiLoad(List<K> ids) {
CacheMode sessionCacheMode = getCacheMode();
boolean cacheModeChanged = false;
@ -2856,14 +2867,7 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc
}
try {
return DynamicBatchingEntityLoaderBuilder.INSTANCE.multiLoad(
(OuterJoinLoadable) entityPersister,
ids.toArray( new Serializable[ ids.size() ] ),
lockOptions,
batchSize,
sessionCheckingEnabled,
SessionImpl.this
);
return entityPersister.multiLoad( ids.toArray( new Serializable[ ids.size() ] ), SessionImpl.this, this );
}
finally {
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.CollectionHelper;
import org.hibernate.loader.spi.AfterLoadAction;
import org.hibernate.persister.entity.MultiLoadOptions;
import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.pretty.MessageHelper;
import org.hibernate.type.Type;
@ -47,26 +48,23 @@ public class DynamicBatchingEntityLoaderBuilder extends BatchingEntityLoaderBuil
public static final DynamicBatchingEntityLoaderBuilder INSTANCE = new DynamicBatchingEntityLoaderBuilder();
@SuppressWarnings("unchecked")
public <T, K extends Serializable> List<T> multiLoad(
public List multiLoad(
OuterJoinLoadable persister,
K[] ids,
LockOptions lockOptions,
Integer explicitBatchSize,
boolean sessionCheckingEnabled,
SessionImplementor session) {
List<T> result = CollectionHelper.arrayList( ids.length );
Serializable[] ids, SessionImplementor session,
MultiLoadOptions loadOptions) {
List result = CollectionHelper.arrayList( ids.length );
if ( sessionCheckingEnabled ) {
if ( loadOptions.isSessionCheckingEnabled() ) {
// 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 List<Serializable> nonManagedIds = new ArrayList<Serializable>();
for ( Serializable id : ids ) {
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 ) {
foundAnyManagedEntities = true;
result.add( managedEntity );
@ -85,7 +83,7 @@ public class DynamicBatchingEntityLoaderBuilder extends BatchingEntityLoaderBuil
// over-write the ids to be loaded with the collection of
// just non-managed ones
ids = nonManagedIds.toArray(
(K[]) Array.newInstance(
(Serializable[]) Array.newInstance(
ids.getClass().getComponentType(),
nonManagedIds.size()
)
@ -94,16 +92,14 @@ public class DynamicBatchingEntityLoaderBuilder extends BatchingEntityLoaderBuil
}
}
if ( lockOptions == null ) {
lockOptions = new LockOptions( LockMode.NONE );
}
final LockOptions lockOptions = (loadOptions.getLockOptions() == null)
? new LockOptions( LockMode.NONE )
: loadOptions.getLockOptions();
int numberOfIdsLeft = ids.length;
final int maxBatchSize;
if ( explicitBatchSize != null && explicitBatchSize > 0 ) {
maxBatchSize = explicitBatchSize;
if ( loadOptions.getBatchSize() != null && loadOptions.getBatchSize() > 0 ) {
maxBatchSize = loadOptions.getBatchSize();
}
else {
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.CoreMessageLogger;
import org.hibernate.internal.FilterHelper;
import org.hibernate.internal.SessionImpl;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.jdbc.Expectation;
@ -86,6 +87,7 @@ import org.hibernate.jdbc.Expectations;
import org.hibernate.jdbc.TooManyRowsAffectedException;
import org.hibernate.loader.entity.BatchingEntityLoaderBuilder;
import org.hibernate.loader.entity.CascadeEntityLoader;
import org.hibernate.loader.entity.DynamicBatchingEntityLoaderBuilder;
import org.hibernate.loader.entity.EntityLoader;
import org.hibernate.loader.entity.UniqueEntityLoader;
import org.hibernate.mapping.Column;
@ -3989,6 +3991,16 @@ public abstract class AbstractEntityPersister
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) {
affectingFetchProfileNames.add( fetchProfileName );
}

View File

@ -7,8 +7,8 @@
package org.hibernate.persister.entity;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hibernate.EntityMode;
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)
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)
*/

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

View File

@ -7,10 +7,11 @@
package org.hibernate.test.legacy;
import java.io.Serializable;
import java.util.Collections;
import java.util.Comparator;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hibernate.EntityMode;
import org.hibernate.HibernateException;
@ -43,6 +44,7 @@ import org.hibernate.internal.util.compare.EqualsHelper;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.MultiLoadOptions;
import org.hibernate.persister.spi.PersisterCreationContext;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.persister.walking.spi.EntityIdentifierDefinition;
@ -304,6 +306,11 @@ public class CustomPersister implements EntityPersister {
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)
*/

View File

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