Fix DynamicBatch loading

This commit is contained in:
Andrea Boriero 2021-10-26 19:28:23 +02:00 committed by Steve Ebersole
parent 29e22c68ac
commit 9b569932d2
36 changed files with 844 additions and 601 deletions

View File

@ -76,7 +76,7 @@ public class SingleIdEntityLoaderDynamicBatch<T> extends SingleIdEntityLoaderSup
if ( numberOfIds <= 1 ) { if ( numberOfIds <= 1 ) {
initializeSingleIdLoaderIfNeeded( session ); initializeSingleIdLoaderIfNeeded( session );
final T result = singleIdLoader.load( pkValue, lockOptions, readOnly, session ); final T result = singleIdLoader.load( pkValue, entityInstance, lockOptions, readOnly, session );
if ( result == null ) { if ( result == null ) {
// There was no entity with the specified ID. Make sure the EntityKey does not remain // 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. // in the batch to avoid including it in future batches that get executed.
@ -144,7 +144,14 @@ public class SingleIdEntityLoaderDynamicBatch<T> extends SingleIdEntityLoaderSup
JdbcSelectExecutorStandardImpl.INSTANCE.list( JdbcSelectExecutorStandardImpl.INSTANCE.list(
jdbcSelect, jdbcSelect,
jdbcParameterBindings, jdbcParameterBindings,
getExecutionContext( entityInstance, readOnly, session, subSelectFetchableKeysHandler ), getExecutionContext(
pkValue,
entityInstance,
readOnly,
lockOptions,
session,
subSelectFetchableKeysHandler
),
RowTransformerPassThruImpl.instance(), RowTransformerPassThruImpl.instance(),
ListResultsConsumer.UniqueSemantic.FILTER ListResultsConsumer.UniqueSemantic.FILTER
); );
@ -163,8 +170,10 @@ public class SingleIdEntityLoaderDynamicBatch<T> extends SingleIdEntityLoaderSup
} }
private ExecutionContext getExecutionContext( private ExecutionContext getExecutionContext(
Object entityId,
Object entityInstance, Object entityInstance,
Boolean readOnly, Boolean readOnly,
LockOptions lockOptions,
SharedSessionContractImplementor session, SharedSessionContractImplementor session,
SubselectFetch.RegistrationHandler subSelectFetchableKeysHandler) { SubselectFetch.RegistrationHandler subSelectFetchableKeysHandler) {
return new ExecutionContext() { return new ExecutionContext() {
@ -178,6 +187,11 @@ public class SingleIdEntityLoaderDynamicBatch<T> extends SingleIdEntityLoaderSup
return entityInstance; return entityInstance;
} }
@Override
public Object getEntityId() {
return entityId;
}
@Override @Override
public QueryOptions getQueryOptions() { public QueryOptions getQueryOptions() {
return new QueryOptionsAdapter() { return new QueryOptionsAdapter() {
@ -185,6 +199,11 @@ public class SingleIdEntityLoaderDynamicBatch<T> extends SingleIdEntityLoaderSup
public Boolean isReadOnly() { public Boolean isReadOnly() {
return readOnly; return readOnly;
} }
@Override
public LockOptions getLockOptions() {
return lockOptions;
}
}; };
} }

View File

@ -15,6 +15,7 @@ import java.util.function.Consumer;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
@ -604,6 +605,11 @@ public class CompoundNaturalIdMapping extends AbstractNaturalIdMapping implement
return navigablePath; return navigablePath;
} }
@Override
public EntityKey getEntityKey() {
return null;
}
@Override @Override
public ModelPart getInitializedPart() { public ModelPart getInitializedPart() {
return naturalIdMapping; return naturalIdMapping;

View File

@ -8,6 +8,7 @@ package org.hibernate.sql.results.graph;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.query.NavigablePath; import org.hibernate.query.NavigablePath;
/** /**
@ -26,6 +27,8 @@ public interface FetchParentAccess extends Initializer {
NavigablePath getNavigablePath(); NavigablePath getNavigablePath();
EntityKey getEntityKey();
/** /**
* Register a listener to be notified when the parent is "resolved" * Register a listener to be notified when the parent is "resolved"
* *

View File

@ -26,6 +26,7 @@ import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.FetchParentAccess; import org.hibernate.sql.results.graph.FetchParentAccess;
import org.hibernate.sql.results.graph.Initializer; import org.hibernate.sql.results.graph.Initializer;
import org.hibernate.sql.results.graph.collection.CollectionInitializer; import org.hibernate.sql.results.graph.collection.CollectionInitializer;
import org.hibernate.sql.results.graph.entity.AbstractEntityInitializer;
import org.hibernate.sql.results.graph.entity.EntityInitializer; import org.hibernate.sql.results.graph.entity.EntityInitializer;
import org.hibernate.sql.results.internal.NullValueAssembler; import org.hibernate.sql.results.internal.NullValueAssembler;
import org.hibernate.sql.results.jdbc.spi.RowProcessingState; import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
@ -165,8 +166,8 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
final PropertyAccess parentInjectionPropertyAccess = embeddedModelPartDescriptor.getParentInjectionAttributePropertyAccess(); final PropertyAccess parentInjectionPropertyAccess = embeddedModelPartDescriptor.getParentInjectionAttributePropertyAccess();
Initializer initializer = rowProcessingState.resolveInitializer( navigablePath.getParent() );
if ( parentInjectionPropertyAccess != null ) { if ( parentInjectionPropertyAccess != null ) {
Initializer initializer = rowProcessingState.resolveInitializer( navigablePath.getParent() );
final Object owner; final Object owner;
if ( initializer instanceof CollectionInitializer ) { if ( initializer instanceof CollectionInitializer ) {
owner = ( (CollectionInitializer) initializer ).getCollectionInstance().getOwner(); owner = ( (CollectionInitializer) initializer ).getCollectionInstance().getOwner();
@ -214,15 +215,27 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
else { else {
notifyParentResolutionListeners( compositeInstance ); notifyParentResolutionListeners( compositeInstance );
if ( compositeInstance instanceof HibernateProxy ) { if ( compositeInstance instanceof HibernateProxy ) {
Object target = embeddedModelPartDescriptor.getEmbeddableTypeDescriptor() if ( initializer != this ) {
.getRepresentationStrategy() ( (AbstractEntityInitializer) initializer ).registerResolutionListener(
.getInstantiator() entityInstance -> {
.instantiate( VALUE_ACCESS, rowProcessingState.getSession().getFactory() ); embeddedModelPartDescriptor.getEmbeddableTypeDescriptor().setPropertyValues(
embeddedModelPartDescriptor.getEmbeddableTypeDescriptor().setPropertyValues( entityInstance,
target, resolvedValues
resolvedValues );
); }
( (HibernateProxy) compositeInstance ).getHibernateLazyInitializer().setImplementation( target ); );
}
else {
Object target = embeddedModelPartDescriptor.getEmbeddableTypeDescriptor()
.getRepresentationStrategy()
.getInstantiator()
.instantiate( VALUE_ACCESS, rowProcessingState.getSession().getFactory() );
embeddedModelPartDescriptor.getEmbeddableTypeDescriptor().setPropertyValues(
target,
resolvedValues
);
( (HibernateProxy) compositeInstance ).getHibernateLazyInitializer().setImplementation( target );
}
} }
// At this point, createEmptyCompositesEnabled is always true. // At this point, createEmptyCompositesEnabled is always true.
// We can only set the property values on the compositeInstance though if there is at least one non null value. // We can only set the property values on the compositeInstance though if there is at least one non null value.

View File

@ -6,6 +6,7 @@
*/ */
package org.hibernate.sql.results.graph.embeddable.internal; package org.hibernate.sql.results.graph.embeddable.internal;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.sql.results.graph.AssemblerCreationState; import org.hibernate.sql.results.graph.AssemblerCreationState;
import org.hibernate.sql.results.graph.FetchParentAccess; import org.hibernate.sql.results.graph.FetchParentAccess;
import org.hibernate.sql.results.graph.embeddable.AbstractEmbeddableInitializer; import org.hibernate.sql.results.graph.embeddable.AbstractEmbeddableInitializer;
@ -25,6 +26,11 @@ public class EmbeddableFetchInitializer
@Override @Override
public Object getParentKey() { public Object getParentKey() {
return getFetchParentAccess().getParentKey(); return findFirstEntityDescriptorAccess().getParentKey();
}
@Override
public EntityKey getEntityKey() {
return findFirstEntityDescriptorAccess().getEntityKey();
} }
} }

View File

@ -6,6 +6,7 @@
*/ */
package org.hibernate.sql.results.graph.embeddable.internal; package org.hibernate.sql.results.graph.embeddable.internal;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.sql.results.graph.AssemblerCreationState; import org.hibernate.sql.results.graph.AssemblerCreationState;
import org.hibernate.sql.results.graph.embeddable.AbstractEmbeddableInitializer; import org.hibernate.sql.results.graph.embeddable.AbstractEmbeddableInitializer;
import org.hibernate.sql.results.graph.embeddable.EmbeddableResultGraphNode; import org.hibernate.sql.results.graph.embeddable.EmbeddableResultGraphNode;
@ -22,7 +23,12 @@ public class EmbeddableResultInitializer extends AbstractEmbeddableInitializer {
@Override @Override
public Object getParentKey() { public Object getParentKey() {
return null; return findFirstEntityDescriptorAccess().getParentKey();
}
@Override
public EntityKey getEntityKey() {
return findFirstEntityDescriptorAccess().getEntityKey();
} }
@Override @Override

View File

@ -581,6 +581,8 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
return existingLoadingEntry.getEntityInstance(); return existingLoadingEntry.getEntityInstance();
} }
assert existingLoadingEntry == null || existingLoadingEntry.getEntityInstance() == null;
Object instance = null; Object instance = null;
// this isEntityReturn bit is just for entity loaders, not hql/criteria // this isEntityReturn bit is just for entity loaders, not hql/criteria

View File

@ -0,0 +1,222 @@
/*
* 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.sql.results.graph.entity.internal;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Consumer;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.internal.log.LoggingHelper;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.persister.entity.AbstractEntityPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.UniqueKeyLoadable;
import org.hibernate.query.NavigablePath;
import org.hibernate.sql.exec.spi.ExecutionContext;
import org.hibernate.sql.results.graph.AbstractFetchParentAccess;
import org.hibernate.sql.results.graph.DomainResultAssembler;
import org.hibernate.sql.results.graph.FetchParentAccess;
import org.hibernate.sql.results.graph.Initializer;
import org.hibernate.sql.results.graph.entity.AbstractEntityInitializer;
import org.hibernate.sql.results.graph.entity.EntityInitializer;
import org.hibernate.sql.results.graph.entity.EntityLoadingLogger;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
import static org.hibernate.internal.log.LoggingHelper.toLoggableString;
public class BatchEntitySelectFetchInitializer extends AbstractFetchParentAccess implements EntityInitializer {
private static final String CONCRETE_NAME = BatchEntitySelectFetchInitializer.class.getSimpleName();
private FetchParentAccess parentAccess;
private final NavigablePath navigablePath;
protected final EntityPersister concreteDescriptor;
protected final DomainResultAssembler identifierAssembler;
private final ToOneAttributeMapping referencedModelPart;
protected Object entityInstance;
private EntityKey entityKey;
private Map<EntityKey, Object> toBatchLoad = new LinkedHashMap<>();
public BatchEntitySelectFetchInitializer(
FetchParentAccess parentAccess,
ToOneAttributeMapping referencedModelPart,
NavigablePath fetchedNavigable,
EntityPersister concreteDescriptor,
DomainResultAssembler identifierAssembler) {
this.parentAccess = parentAccess;
this.referencedModelPart = referencedModelPart;
this.navigablePath = fetchedNavigable;
this.concreteDescriptor = concreteDescriptor;
this.identifierAssembler = identifierAssembler;
}
public ModelPart getInitializedPart() {
return referencedModelPart;
}
@Override
public NavigablePath getNavigablePath() {
return navigablePath;
}
@Override
public void resolveKey(RowProcessingState rowProcessingState) {
}
@Override
public void resolveInstance(RowProcessingState rowProcessingState) {
}
@Override
public void initializeInstance(RowProcessingState rowProcessingState) {
final Object entityIdentifier = identifierAssembler.assemble( rowProcessingState );
if ( entityIdentifier == null ) {
return;
}
entityKey = new EntityKey( entityIdentifier, concreteDescriptor );
final PersistenceContext persistenceContext = rowProcessingState.getSession().getPersistenceContextInternal();
entityInstance = persistenceContext.getEntity( entityKey );
if ( entityInstance != null ) {
return;
}
Initializer initializer = rowProcessingState.getJdbcValuesSourceProcessingState()
.findInitializer( entityKey );
if ( initializer != null ) {
if ( EntityLoadingLogger.DEBUG_ENABLED ) {
EntityLoadingLogger.LOGGER.debugf(
"(%s) Found an initializer for entity (%s) : %s",
CONCRETE_NAME,
toLoggableString( getNavigablePath(), entityIdentifier ),
entityIdentifier
);
}
initializer.resolveInstance( rowProcessingState );
entityInstance = initializer.getInitializedInstance();
// EARLY EXIT!!!
return;
}
final LoadingEntityEntry existingLoadingEntry = rowProcessingState.getSession()
.getPersistenceContext()
.getLoadContexts()
.findLoadingEntityEntry( entityKey );
if ( existingLoadingEntry != null ) {
if ( existingLoadingEntry.getEntityInitializer() != this ) {
// the entity is already being loaded elsewhere
if ( EntityLoadingLogger.DEBUG_ENABLED ) {
EntityLoadingLogger.LOGGER.debugf(
"(%s) Entity [%s] being loaded by another initializer [%s] - skipping processing",
CONCRETE_NAME,
toLoggableString( getNavigablePath(), entityIdentifier ),
existingLoadingEntry.getEntityInitializer()
);
}
this.entityInstance = existingLoadingEntry.getEntityInstance();
// EARLY EXIT!!!
return;
}
}
persistenceContext.getBatchFetchQueue().addBatchLoadableEntityKey( entityKey );
toBatchLoad.put( entityKey, parentAccess.getInitializedInstance() );
}
@Override
public void finishUpRow(RowProcessingState rowProcessingState) {
entityInstance = null;
clearParentResolutionListeners();
}
@Override
public EntityPersister getEntityDescriptor() {
return concreteDescriptor;
}
@Override
public Object getEntityInstance() {
return entityInstance;
}
@Override
public EntityKey getEntityKey() {
return entityKey;
}
@Override
public Object getParentKey() {
throw new NotYetImplementedFor6Exception( getClass() );
}
@Override
public void registerResolutionListener(Consumer<Object> listener) {
if ( entityInstance != null ) {
listener.accept( entityInstance );
}
else {
super.registerResolutionListener( listener );
}
}
@Override
public EntityPersister getConcreteDescriptor() {
return concreteDescriptor;
}
@Override
public String toString() {
return "EntitySelectFetchInitializer(" + LoggingHelper.toLoggableString( getNavigablePath() ) + ")";
}
@Override
public void endLoading(ExecutionContext context) {
toBatchLoad.forEach(
(entityKey, parentInstance) -> {
final Object instance = context.getSession().internalLoad(
entityKey.getEntityName(),
entityKey.getIdentifier(),
true,
referencedModelPart.isNullable()
);
if ( instance != null ) {
( (AbstractEntityPersister) referencedModelPart.getDeclaringType() ).setPropertyValue(
parentInstance,
referencedModelPart.getPartName(),
instance
);
final EntityEntry entry = context.getSession()
.getPersistenceContext()
.getEntry( parentInstance );
final int propertyIndex = ( (UniqueKeyLoadable) ( (AbstractEntityInitializer) parentAccess ).getEntityDescriptor() ).getPropertyIndex(
referencedModelPart.getPartName() );
if ( entry != null ) {
final Object[] loadedState = entry.getLoadedState();
if ( loadedState != null ) {
loadedState[propertyIndex] = instance;
}
}
}
}
);
toBatchLoad = null;
parentAccess = null;
}
}

View File

@ -68,13 +68,24 @@ public class EntityFetchSelectImpl extends AbstractNonJoinedEntityFetch {
result.createResultAssembler( creationState ) result.createResultAssembler( creationState )
); );
} }
return new EntitySelectFetchInitializer( if ( entityPersister.isBatchLoadable() ) {
parentAccess, return new BatchEntitySelectFetchInitializer(
fetchedAttribute, parentAccess,
getNavigablePath(), fetchedAttribute,
entityPersister, getNavigablePath(),
result.createResultAssembler( creationState ) entityPersister,
); result.createResultAssembler( creationState )
);
}
else {
return new EntitySelectFetchInitializer(
parentAccess,
fetchedAttribute,
getNavigablePath(),
entityPersister,
result.createResultAssembler( creationState )
);
}
} }
); );

View File

@ -126,8 +126,8 @@ public class EntitySelectFetchInitializer extends AbstractFetchParentAccess impl
return; return;
} }
Initializer initializer = rowProcessingState.getJdbcValuesSourceProcessingState().findInitializer( Initializer initializer = rowProcessingState.getJdbcValuesSourceProcessingState()
entityKey ); .findInitializer( entityKey );
if ( initializer != null ) { if ( initializer != null ) {
if ( EntityLoadingLogger.DEBUG_ENABLED ) { if ( EntityLoadingLogger.DEBUG_ENABLED ) {

View File

@ -15,6 +15,7 @@ import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.MappingType; import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.NavigablePath; import org.hibernate.query.NavigablePath;
import org.hibernate.sql.results.graph.AssemblerCreationState; import org.hibernate.sql.results.graph.AssemblerCreationState;
import org.hibernate.sql.results.graph.BiDirectionalFetch; import org.hibernate.sql.results.graph.BiDirectionalFetch;
@ -27,6 +28,7 @@ import org.hibernate.sql.results.graph.FetchParent;
import org.hibernate.sql.results.graph.FetchParentAccess; import org.hibernate.sql.results.graph.FetchParentAccess;
import org.hibernate.sql.results.graph.Fetchable; import org.hibernate.sql.results.graph.Fetchable;
import org.hibernate.sql.results.graph.entity.EntityInitializer; import org.hibernate.sql.results.graph.entity.EntityInitializer;
import org.hibernate.sql.results.graph.entity.internal.BatchEntitySelectFetchInitializer;
import org.hibernate.sql.results.graph.entity.internal.EntityDelayedFetchInitializer; import org.hibernate.sql.results.graph.entity.internal.EntityDelayedFetchInitializer;
import org.hibernate.sql.results.graph.entity.internal.EntitySelectFetchByUniqueKeyInitializer; import org.hibernate.sql.results.graph.entity.internal.EntitySelectFetchByUniqueKeyInitializer;
import org.hibernate.sql.results.graph.entity.internal.EntitySelectFetchInitializer; import org.hibernate.sql.results.graph.entity.internal.EntitySelectFetchInitializer;
@ -116,13 +118,25 @@ public class CircularFetchImpl implements BiDirectionalFetch, Association {
); );
} }
if ( timing == FetchTiming.IMMEDIATE ) { if ( timing == FetchTiming.IMMEDIATE ) {
return new EntitySelectFetchInitializer( final EntityPersister entityPersister = entityMappingType.getEntityPersister();
parentAccess, if ( entityPersister.isBatchLoadable() ) {
(ToOneAttributeMapping) referencedModelPart, return new BatchEntitySelectFetchInitializer(
getReferencedPath(), parentAccess,
entityMappingType.getEntityPersister(), (ToOneAttributeMapping) referencedModelPart,
resultAssembler getReferencedPath(),
); entityPersister,
resultAssembler
);
}
else {
return new EntitySelectFetchInitializer(
parentAccess,
(ToOneAttributeMapping) referencedModelPart,
getReferencedPath(),
entityPersister,
resultAssembler
);
}
} }
else { else {
return new EntityDelayedFetchInitializer( return new EntityDelayedFetchInitializer(

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. * 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>. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/ */
package org.hibernate.test.batchfetch; package org.hibernate.orm.test.batchfetch;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.FetchType; import jakarta.persistence.FetchType;

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. * 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>. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/ */
package org.hibernate.test.batchfetch; package org.hibernate.orm.test.batchfetch;
import org.hibernate.annotations.BatchSize; import org.hibernate.annotations.BatchSize;

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. * 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>. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/ */
package org.hibernate.test.batchfetch; package org.hibernate.orm.test.batchfetch;
import java.io.Serializable; import java.io.Serializable;
@ -31,4 +31,8 @@ public class BId
this.idPart2 = idPart2; this.idPart2 = idPart2;
} }
@Override
public String toString() {
return "BId (" + idPart1 + ", " + idPart2 + ")";
}
} }

View File

@ -1,7 +1,16 @@
package org.hibernate.test.batchfetch; package org.hibernate.orm.test.batchfetch;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.Set; import java.util.Set;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.FetchType; import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue; import jakarta.persistence.GeneratedValue;
@ -9,33 +18,20 @@ import jakarta.persistence.Id;
import jakarta.persistence.ManyToMany; import jakarta.persistence.ManyToMany;
import jakarta.persistence.MappedSuperclass; import jakarta.persistence.MappedSuperclass;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; @DomainModel(
import org.junit.Test; annotatedClasses = {
BatchFetchBootstrapTest.JafSid.class, BatchFetchBootstrapTest.UserGroup.class
public class BatchFetchBootstrapTest extends BaseCoreFunctionalTestCase { }
)
@Override @SessionFactory
protected Class<?>[] getAnnotatedClasses() { @ServiceRegistry(
return new Class[] { settings = @Setting( name = AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, value = "30")
JafSid.class, UserGroup.class )
}; public class BatchFetchBootstrapTest {
}
@Override
protected void configure(Configuration configuration) {
configuration.setProperty(AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, "30");
}
@Override
protected void buildSessionFactory() {
}
@Test @Test
public void test() { public void test() {
super.buildSessionFactory();
} }

View File

@ -4,10 +4,28 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. * 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>. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/ */
package org.hibernate.test.batchfetch; package org.hibernate.orm.test.batchfetch;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.hibernate.Session;
import org.hibernate.annotations.BatchSize;
import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction;
import org.hibernate.engine.spi.BatchFetchQueue;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.resource.jdbc.spi.StatementInspector;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import jakarta.persistence.ConstraintMode; import jakarta.persistence.ConstraintMode;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.ForeignKey; import jakarta.persistence.ForeignKey;
@ -15,74 +33,54 @@ import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn; import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToOne; import jakarta.persistence.OneToOne;
import org.hibernate.Session;
import org.hibernate.annotations.BatchSize;
import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.engine.spi.BatchFetchQueue;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.resource.jdbc.spi.StatementInspector;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.Assert.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.Assert.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.Assert.assertNull;
/** /**
* @author Gail Badner * @author Gail Badner
* @author Stephen Fikes * @author Stephen Fikes
*/ */
public class BatchFetchNotFoundIgnoreDefaultStyleTest extends BaseCoreFunctionalTestCase { @DomainModel(
private static final AStatementInspector statementInspector = new AStatementInspector(); annotatedClasses = {
BatchFetchNotFoundIgnoreDynamicStyleTest.Employee.class,
BatchFetchNotFoundIgnoreDynamicStyleTest.Task.class
}
)
@SessionFactory(
statementInspectorClass = BatchFetchNotFoundIgnoreDynamicStyleTest.AStatementInspector.class
)
public class BatchFetchNotFoundIgnoreDynamicStyleTest {
private static final int NUMBER_OF_EMPLOYEES = 8; private static final int NUMBER_OF_EMPLOYEES = 8;
private List<Task> tasks = new ArrayList<>(); private List<Task> tasks = new ArrayList<>();
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { Employee.class, Task.class };
}
@Override @BeforeEach
protected void configure(Configuration configuration) { public void createData(SessionFactoryScope scope) {
super.configure( configuration );
configuration.getProperties().put( Environment.STATEMENT_INSPECTOR, statementInspector );
}
@Before
public void createData() {
tasks.clear(); tasks.clear();
tasks = doInHibernate( tasks = scope.fromTransaction(
this::sessionFactory, session -> { session -> {
for (int i = 0 ; i < NUMBER_OF_EMPLOYEES ; i++) { for ( int i = 0; i < NUMBER_OF_EMPLOYEES; i++ ) {
Task task = new Task(); Task task = new Task();
task.id = i; task.id = i;
tasks.add( task ); tasks.add( task );
session.persist( task ); session.persist( task );
Employee e = new Employee("employee0" + i); Employee e = new Employee( "employee0" + i );
e.task = task; e.task = task;
session.persist(e); session.persist( e );
} }
return tasks; return tasks;
} }
); );
} }
@After @AfterEach
public void deleteData() { public void deleteData(SessionFactoryScope scope) {
doInHibernate( scope.inTransaction(
this::sessionFactory, session -> { session -> {
session.createQuery( "delete from Task" ).executeUpdate(); session.createQuery( "delete from Task" ).executeUpdate();
session.createQuery( "delete from Employee" ).executeUpdate(); session.createQuery( "delete from Employee" ).executeUpdate();
} }
@ -90,10 +88,10 @@ public class BatchFetchNotFoundIgnoreDefaultStyleTest extends BaseCoreFunctional
} }
@Test @Test
public void testSeveralNotFoundFromQuery() { public void testSeveralNotFoundFromQuery(SessionFactoryScope scope) {
doInHibernate( scope.inTransaction(
this::sessionFactory, session -> { session -> {
// delete 2nd and 8th Task so that the non-found Task entities will be queried // delete 2nd and 8th Task so that the non-found Task entities will be queried
// in 2 different batches. // in 2 different batches.
session.delete( tasks.get( 1 ) ); session.delete( tasks.get( 1 ) );
@ -101,14 +99,15 @@ public class BatchFetchNotFoundIgnoreDefaultStyleTest extends BaseCoreFunctional
} }
); );
final AStatementInspector statementInspector = (AStatementInspector) scope.getStatementInspector();
statementInspector.clear(); statementInspector.clear();
final List<Employee> employees = doInHibernate( final List<Employee> employees = scope.fromTransaction(
this::sessionFactory, session -> { session -> {
List<Employee> results = List<Employee> results =
session.createQuery( "from Employee e order by e.id", Employee.class ).getResultList(); session.createQuery( "from Employee e order by e.id", Employee.class ).getResultList();
for ( int i = 0 ; i < tasks.size() ; i++ ) { for ( Task task : tasks ) {
checkInBatchFetchQueue( tasks.get( i ).id, session, false ); checkInBatchFetchQueue( task.id, session, false );
} }
return results; return results;
} }
@ -145,7 +144,7 @@ public class BatchFetchNotFoundIgnoreDefaultStyleTest extends BaseCoreFunctional
assertEquals( 1, paramterCounts.get( 3 ).intValue() ); assertEquals( 1, paramterCounts.get( 3 ).intValue() );
assertEquals( NUMBER_OF_EMPLOYEES, employees.size() ); assertEquals( NUMBER_OF_EMPLOYEES, employees.size() );
for ( int i = 0 ; i < NUMBER_OF_EMPLOYEES ; i++ ) { for ( int i = 0; i < NUMBER_OF_EMPLOYEES; i++ ) {
if ( i == 1 || i == 7 ) { if ( i == 1 || i == 7 ) {
assertNull( employees.get( i ).task ); assertNull( employees.get( i ).task );
} }
@ -156,10 +155,10 @@ public class BatchFetchNotFoundIgnoreDefaultStyleTest extends BaseCoreFunctional
} }
@Test @Test
public void testMostNotFoundFromQuery() { public void testMostNotFoundFromQuery(SessionFactoryScope scope) {
doInHibernate( scope.inTransaction(
this::sessionFactory, session -> { session -> {
// delete all but last Task entity // delete all but last Task entity
for ( int i = 0; i < 7; i++ ) { for ( int i = 0; i < 7; i++ ) {
session.delete( tasks.get( i ) ); session.delete( tasks.get( i ) );
@ -167,14 +166,15 @@ public class BatchFetchNotFoundIgnoreDefaultStyleTest extends BaseCoreFunctional
} }
); );
final AStatementInspector statementInspector = (AStatementInspector) scope.getStatementInspector();
statementInspector.clear(); statementInspector.clear();
final List<Employee> employees = doInHibernate( final List<Employee> employees = scope.fromTransaction(
this::sessionFactory, session -> { session -> {
List<Employee> results = List<Employee> results =
session.createQuery( "from Employee e order by e.id", Employee.class ).getResultList(); session.createQuery( "from Employee e order by e.id", Employee.class ).getResultList();
for ( int i = 0 ; i < tasks.size() ; i++ ) { for ( Task task : tasks ) {
checkInBatchFetchQueue( tasks.get( i ).id, session, false ); checkInBatchFetchQueue( task.id, session, false );
} }
return results; return results;
} }
@ -233,7 +233,7 @@ public class BatchFetchNotFoundIgnoreDefaultStyleTest extends BaseCoreFunctional
assertEquals( NUMBER_OF_EMPLOYEES, employees.size() ); assertEquals( NUMBER_OF_EMPLOYEES, employees.size() );
for ( int i = 0 ; i < NUMBER_OF_EMPLOYEES ; i++ ) { for ( int i = 0; i < NUMBER_OF_EMPLOYEES; i++ ) {
if ( i == 7 ) { if ( i == 7 ) {
assertEquals( tasks.get( i ).id, employees.get( i ).task.id ); assertEquals( tasks.get( i ).id, employees.get( i ).task.id );
} }
@ -244,19 +244,20 @@ public class BatchFetchNotFoundIgnoreDefaultStyleTest extends BaseCoreFunctional
} }
@Test @Test
public void testNotFoundFromGet() { public void testNotFoundFromGet(SessionFactoryScope scope) {
doInHibernate( scope.inTransaction(
this::sessionFactory, session -> { session -> {
// delete task so it is not found later when getting the Employee. // delete task so it is not found later when getting the Employee.
session.delete( tasks.get( 0 ) ); session.delete( tasks.get( 0 ) );
} }
); );
final AStatementInspector statementInspector = (AStatementInspector) scope.getStatementInspector();
statementInspector.clear(); statementInspector.clear();
doInHibernate( scope.inTransaction(
this::sessionFactory, session -> { session -> {
Employee employee = session.get( Employee.class, "employee00" ); Employee employee = session.get( Employee.class, "employee00" );
checkInBatchFetchQueue( tasks.get( 0 ).id, session, false ); checkInBatchFetchQueue( tasks.get( 0 ).id, session, false );
assertNotNull( employee ); assertNotNull( employee );
@ -266,18 +267,17 @@ public class BatchFetchNotFoundIgnoreDefaultStyleTest extends BaseCoreFunctional
final List<Integer> paramterCounts = statementInspector.parameterCounts; final List<Integer> paramterCounts = statementInspector.parameterCounts;
// there should be 2 SQL statements executed // there should be 1 SQL statements executed, we select the tasks with a join
// 1) query to load Employee entity by ID (associated Tasks is registered for batch loading) // 1) query to load Employee entity by ID and fetch the Tasks
// 2) batch will only contain the ID for the associated Task (which will not be found) assertEquals( 1, paramterCounts.size() );
assertEquals( 2, paramterCounts.size() );
// query loading Employee entities shouldn't have any parameters // query loading Employee entities shouldn't have any parameters
assertEquals( 1, paramterCounts.get( 0 ).intValue() ); assertEquals( 1, paramterCounts.get( 0 ).intValue() );
// Will result in just querying a single Task (because the batch is empty). // // Will result in just querying a single Task (because the batch is empty).
// query should have 1 parameter; // // query should have 1 parameter;
// Task won't be found. // // Task won't be found.
assertEquals( 1, paramterCounts.get( 1 ).intValue() ); // assertEquals( 1, paramterCounts.get( 1 ).intValue() );
} }
private static void checkInBatchFetchQueue(long id, Session session, boolean expected) { private static void checkInBatchFetchQueue(long id, Session session, boolean expected) {
@ -298,7 +298,7 @@ public class BatchFetchNotFoundIgnoreDefaultStyleTest extends BaseCoreFunctional
@Id @Id
private String name; private String name;
@OneToOne(optional = true) @OneToOne
@JoinColumn(foreignKey = @ForeignKey(value = ConstraintMode.NO_CONSTRAINT)) @JoinColumn(foreignKey = @ForeignKey(value = ConstraintMode.NO_CONSTRAINT))
@NotFound(action = NotFoundAction.IGNORE) @NotFound(action = NotFoundAction.IGNORE)
private Task task; private Task task;
@ -324,13 +324,18 @@ public class BatchFetchNotFoundIgnoreDefaultStyleTest extends BaseCoreFunctional
public static class AStatementInspector implements StatementInspector { public static class AStatementInspector implements StatementInspector {
private List<Integer> parameterCounts = new ArrayList<>(); private List<Integer> parameterCounts = new ArrayList<>();
public AStatementInspector() {
}
public String inspect(String sql) { public String inspect(String sql) {
parameterCounts.add( countParameters( sql ) ); parameterCounts.add( countParameters( sql ) );
return sql; return sql;
} }
private void clear() { private void clear() {
parameterCounts.clear(); parameterCounts.clear();
} }
private int countParameters(String sql) { private int countParameters(String sql) {
int count = 0; int count = 0;
int parameterIndex = sql.indexOf( '?' ); int parameterIndex = sql.indexOf( '?' );

View File

@ -4,13 +4,21 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. * 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>. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/ */
package org.hibernate.test.batchfetch; package org.hibernate.orm.test.batchfetch;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.util.List; import java.util.List;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.Test;
import jakarta.persistence.CascadeType; import jakarta.persistence.CascadeType;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
@ -21,34 +29,30 @@ import jakarta.persistence.OneToMany;
import jakarta.persistence.OrderBy; import jakarta.persistence.OrderBy;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import org.hibernate.cfg.AvailableSettings; import static org.junit.jupiter.api.Assertions.assertEquals;
import org.hibernate.cfg.Configuration; import static org.junit.jupiter.api.Assertions.assertNotNull;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Assert;
import org.junit.Test;
public class BatchFetchReferencedColumnNameTest extends BaseCoreFunctionalTestCase {
@Override @DomainModel(
protected Class<?>[] getAnnotatedClasses() { annotatedClasses = {
return new Class<?>[]{ Child.class, Parent.class }; BatchFetchReferencedColumnNameTest.Child.class,
} BatchFetchReferencedColumnNameTest.Parent.class
}
@Override )
protected void configure(Configuration configuration) { @SessionFactory
super.configure( configuration ); @ServiceRegistry(
settings = {
configuration.setProperty( AvailableSettings.SHOW_SQL, Boolean.TRUE.toString() ); @Setting(name = AvailableSettings.SHOW_SQL, value = "true"),
configuration.setProperty( AvailableSettings.FORMAT_SQL, Boolean.TRUE.toString() ); @Setting(name = AvailableSettings.FORMAT_SQL, value = "true"),
@Setting(name = AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, value = "64")
configuration.setProperty( AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, "64" ); }
} )
public class BatchFetchReferencedColumnNameTest {
@Test @Test
@TestForIssue(jiraKey = "HHH-13059") @TestForIssue(jiraKey = "HHH-13059")
public void test() throws Exception { public void test(SessionFactoryScope scope) throws Exception {
doInHibernate( this::sessionFactory, session -> { scope.inTransaction( session -> {
Parent p = new Parent(); Parent p = new Parent();
p.setId( 1L ); p.setId( 1L );
session.save( p ); session.save( p );
@ -66,11 +70,11 @@ public class BatchFetchReferencedColumnNameTest extends BaseCoreFunctionalTestCa
session.save( c2 ); session.save( c2 );
} ); } );
doInHibernate( this::sessionFactory, session -> { scope.inTransaction( session -> {
Parent p = session.get( Parent.class, 1L ); Parent p = session.get( Parent.class, 1L );
Assert.assertNotNull( p ); assertNotNull( p );
Assert.assertEquals( 2, p.getChildren().size() ); assertEquals( 2, p.getChildren().size() );
} ); } );
} }

View File

@ -4,11 +4,21 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. * 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>. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/ */
package org.hibernate.test.batchfetch; package org.hibernate.orm.test.batchfetch;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set; import java.util.Set;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.FetchType; import jakarta.persistence.FetchType;
@ -18,26 +28,29 @@ import jakarta.persistence.LockModeType;
import jakarta.persistence.ManyToOne; import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany; import jakarta.persistence.OneToMany;
import org.hibernate.cfg.AvailableSettings; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.*; @DomainModel(
annotatedClasses = {
public class BatchFetchRefreshTest extends BaseNonConfigCoreFunctionalTestCase { BatchFetchRefreshTest.Parent.class,
BatchFetchRefreshTest.Child.class
}
)
@SessionFactory
@ServiceRegistry(
settings = @Setting(name = AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, value = "8")
)
public class BatchFetchRefreshTest {
@Test @Test
public void testRefreshWithBatch(SessionFactoryScope scope) {
public void testRefreshWithBatch() { scope.inTransaction( session -> {
doInHibernate( this::sessionFactory, session -> {
// Retrieve one of the parents into the session. // Retrieve one of the parents into the session.
Parent parent = session.find(Parent.class, 1); Parent parent = session.find( Parent.class, 1 );
Assert.assertNotNull(parent); assertNotNull( parent );
// Retrieve children but keep their parents lazy! // Retrieve children but keep their parents lazy!
// This allows batch fetching to do its thing when we refresh below. // This allows batch fetching to do its thing when we refresh below.
@ -50,16 +63,16 @@ public class BatchFetchRefreshTest extends BaseNonConfigCoreFunctionalTestCase {
parent.getChildren().size(); parent.getChildren().size();
// Another interesting thing to note - em.getLockMode returns an incorrect value after the above refresh // Another interesting thing to note - em.getLockMode returns an incorrect value after the above refresh
Assert.assertEquals( LockModeType.PESSIMISTIC_WRITE, session.getLockMode( parent ) ); assertEquals( LockModeType.PESSIMISTIC_WRITE, session.getLockMode( parent ) );
}); } );
} }
@Before @BeforeEach
public void setupData() { public void setupData(SessionFactoryScope scope) {
final int numParents = 5; final int numParents = 5;
final int childrenPerParent = 2; final int childrenPerParent = 2;
doInHibernate( this::sessionFactory, session -> { scope.inTransaction( session -> {
int k = 1; int k = 1;
for ( int i = 1; i <= numParents; i++ ) { for ( int i = 1; i <= numParents; i++ ) {
Parent parent = new Parent(); Parent parent = new Parent();
@ -69,7 +82,7 @@ public class BatchFetchRefreshTest extends BaseNonConfigCoreFunctionalTestCase {
session.persist( parent ); session.persist( parent );
// Create some children for each parent... // Create some children for each parent...
for ( int j = 0; j < childrenPerParent; j++,k++ ) { for ( int j = 0; j < childrenPerParent; j++, k++ ) {
Child child = new Child(); Child child = new Child();
child.childId = k; child.childId = k;
child.name = "Child_" + i + "_" + j; child.name = "Child_" + i + "_" + j;
@ -79,24 +92,10 @@ public class BatchFetchRefreshTest extends BaseNonConfigCoreFunctionalTestCase {
session.persist( child ); session.persist( child );
} }
} }
}); } );
} }
@Override
protected Class[] getAnnotatedClasses() {
return new Class[] {
Parent.class,
Child.class
};
}
@Override
protected void addSettings(Map settings) {
super.addSettings( settings );
settings.put( AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, "8" );
}
@Entity(name = "Parent") @Entity(name = "Parent")
public static class Parent { public static class Parent {

View File

@ -0,0 +1,195 @@
/*
* 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.orm.test.batchfetch;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.hibernate.Hibernate;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* @author Gavin King
*/
@DomainModel(
xmlMappings = "org/hibernate/orm/test/batchfetch/ProductLine.hbm.xml",
annotatedClasses = BatchLoadableEntity.class
)
@SessionFactory(
generateStatistics = true
)
@ServiceRegistry(
settings = {
@Setting(name = AvailableSettings.USE_SECOND_LEVEL_CACHE, value = "false")
}
)
public class BatchFetchTest {
@SuppressWarnings({ "unchecked" })
@Test
public void testBatchFetch(SessionFactoryScope scope) {
ProductLine ossProductLine = new ProductLine();
Model hibernateModel = new Model( ossProductLine );
scope.inTransaction(
session -> {
ProductLine cars = new ProductLine();
cars.setDescription( "Cars" );
Model monaro = new Model( cars );
monaro.setName( "monaro" );
monaro.setDescription( "Holden Monaro" );
Model hsv = new Model( cars );
hsv.setName( "hsv" );
hsv.setDescription( "Holden Commodore HSV" );
session.save( cars );
ossProductLine.setDescription( "OSS" );
Model jboss = new Model( ossProductLine );
jboss.setName( "JBoss" );
jboss.setDescription( "JBoss Application Server" );
hibernateModel.setName( "Hibernate" );
hibernateModel.setDescription( "Hibernate" );
Model cache = new Model( ossProductLine );
cache.setName( "JBossCache" );
cache.setDescription( "JBoss TreeCache" );
session.save( ossProductLine );
}
);
scope.getSessionFactory().getCache().evictEntityRegion( Model.class );
scope.getSessionFactory().getCache().evictEntityRegion( ProductLine.class );
scope.inTransaction(
session -> {
List<ProductLine> list = session.createQuery( "from ProductLine pl order by pl.description" )
.list();
ProductLine cars = list.get( 0 );
ProductLine oss = list.get( 1 );
assertFalse( Hibernate.isInitialized( cars.getModels() ) );
assertFalse( Hibernate.isInitialized( oss.getModels() ) );
assertEquals( 2, cars.getModels().size() ); //fetch both collections
assertTrue( Hibernate.isInitialized( cars.getModels() ) );
assertTrue( Hibernate.isInitialized( oss.getModels() ) );
session.clear();
List<Model> models = session.createQuery( "from Model m" ).list();
Model hibernate = session.get( Model.class, hibernateModel.getId() );
hibernate.getProductLine().getId();
for ( Model aList : models ) {
assertFalse( Hibernate.isInitialized( aList.getProductLine() ) );
}
assertEquals( hibernate.getProductLine().getDescription(), "OSS" ); //fetch both productlines
session.clear();
Iterator<Model> iter = session.createQuery( "from Model" ).list().iterator();
models = new ArrayList();
while ( iter.hasNext() ) {
models.add( iter.next() );
}
Model m = models.get( 0 );
m.getDescription(); //fetch a batch of 4
session.clear();
list = session.createQuery( "from ProductLine" ).list();
ProductLine pl = list.get( 0 );
ProductLine pl2 = list.get( 1 );
session.evict( pl2 );
pl.getModels().size(); //fetch just one collection! (how can we write an assertion for that??)
}
);
scope.inTransaction(
session -> {
List<ProductLine> list = session.createQuery( "from ProductLine pl order by pl.description" )
.list();
ProductLine cars = list.get( 0 );
ProductLine oss = list.get( 1 );
assertEquals( cars.getModels().size(), 2 );
assertEquals( oss.getModels().size(), 3 );
session.delete( cars );
session.delete( oss );
}
);
}
@Test
@SuppressWarnings({ "unchecked" })
public void testBatchFetch2(SessionFactoryScope scope) {
int size = 32 + 14;
scope.inTransaction(
session -> {
for ( int i = 0; i < size; i++ ) {
session.save( new BatchLoadableEntity( i ) );
}
}
);
scope.inTransaction(
session -> {
// load them all as proxies
for ( int i = 0; i < size; i++ ) {
BatchLoadableEntity entity = session.load( BatchLoadableEntity.class, i );
assertFalse( Hibernate.isInitialized( entity ) );
}
scope.getSessionFactory().getStatistics().clear();
// now start initializing them...
for ( int i = 0; i < size; i++ ) {
BatchLoadableEntity entity = session.load( BatchLoadableEntity.class, i );
Hibernate.initialize( entity );
assertTrue( Hibernate.isInitialized( entity ) );
}
// so at this point, all entities are initialized. see how many fetches were performed.
final int expectedFetchCount;
// if ( sessionFactory().getSettings().getBatchFetchStyle() == BatchFetchStyle.LEGACY ) {
// expectedFetchCount = 3; // (32 + 10 + 4)
// }
// else if ( sessionFactory().getSettings().getBatchFetchStyle() == BatchFetchStyle.DYNAMIC ) {
// expectedFetchCount = 2; // (32 + 14) : because we limited batch-size to 32
// }
// else {
// PADDED
expectedFetchCount = 2; // (32 + 16*) with the 16 being padded
// }
assertEquals(
expectedFetchCount,
scope.getSessionFactory().getStatistics()
.getEntityStatistics( BatchLoadableEntity.class.getName() )
.getFetchCount()
);
}
);
}
@AfterEach
public void tearDown(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery( "delete BatchLoadableEntity" ).executeUpdate();
session.createQuery( "delete Model" ).executeUpdate();
session.createQuery( "delete ProductLine" ).executeUpdate();
}
);
}
}

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. * 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>. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/ */
package org.hibernate.test.batchfetch; package org.hibernate.orm.test.batchfetch;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.Id; import jakarta.persistence.Id;

View File

@ -1,9 +1,17 @@
package org.hibernate.orm.test.batchfetch; package org.hibernate.orm.test.batchfetch;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; import org.hibernate.LockMode;
import static org.junit.Assert.assertNotNull; import org.hibernate.LockOptions;
import org.hibernate.annotations.Fetch;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.spi.MetamodelImplementor;
import org.hibernate.persister.entity.EntityPersister;
import java.util.Map; import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.Jpa;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.FetchType; import jakarta.persistence.FetchType;
@ -11,38 +19,23 @@ import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id; import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne; import jakarta.persistence.ManyToOne;
import org.hibernate.LockMode; import static org.junit.jupiter.api.Assertions.assertNotNull;
import org.hibernate.LockOptions;
import org.hibernate.annotations.Fetch;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;
import org.hibernate.loader.BatchFetchStyle;
import org.hibernate.metamodel.spi.MetamodelImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.junit.Test;
public class BatchingEntityLoaderInitializationWithNoLockModeTest extends BaseEntityManagerFunctionalTestCase { @Jpa(
annotatedClasses = {
BatchingEntityLoaderInitializationWithNoLockModeTest.MainEntity.class,
BatchingEntityLoaderInitializationWithNoLockModeTest.SubEntity.class
},
properties = { @Setting(name = AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, value = "5") }
)
public class BatchingEntityLoaderInitializationWithNoLockModeTest {
private Long mainId; private Long mainId;
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] { MainEntity.class, SubEntity.class };
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
protected Map buildSettings() {
Map settings = super.buildSettings();
settings.put( AvailableSettings.BATCH_FETCH_STYLE, BatchFetchStyle.LEGACY );
settings.put( AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, 5 );
return settings;
}
@Test @Test
public void testJoin() { public void testJoin(EntityManagerFactoryScope scope) {
doInJPA( this::entityManagerFactory, em -> { scope.inTransaction( em -> {
SubEntity sub = new SubEntity(); SubEntity sub = new SubEntity();
em.persist( sub ); em.persist( sub );
@ -51,9 +44,9 @@ public class BatchingEntityLoaderInitializationWithNoLockModeTest extends BaseEn
em.persist( main ); em.persist( main );
this.mainId = main.getId(); this.mainId = main.getId();
}); } );
doInJPA( this::entityManagerFactory, em -> { scope.inTransaction( em -> {
EntityPersister entityPersister = ( (MetamodelImplementor) em.getMetamodel() ) EntityPersister entityPersister = ( (MetamodelImplementor) em.getMetamodel() )
.entityPersister( MainEntity.class ); .entityPersister( MainEntity.class );
@ -61,8 +54,9 @@ public class BatchingEntityLoaderInitializationWithNoLockModeTest extends BaseEn
LockOptions lockOptions = new LockOptions( LockMode.NONE ); LockOptions lockOptions = new LockOptions( LockMode.NONE );
lockOptions.setTimeOut( 10 ); lockOptions.setTimeOut( 10 );
MainEntity main = (MainEntity) entityPersister.load( this.mainId, null, lockOptions, MainEntity main = (MainEntity) entityPersister.
(SharedSessionContractImplementor) em ); load( this.mainId, null, lockOptions, (SharedSessionContractImplementor) em );
assertNotNull( main.getSub() ); assertNotNull( main.getSub() );
} ); } );
} }

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. * 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>. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/ */
package org.hibernate.test.batchfetch; package org.hibernate.orm.test.batchfetch;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. * 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>. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/ */
package org.hibernate.test.batchfetch; package org.hibernate.orm.test.batchfetch;
import java.util.List; import java.util.List;

View File

@ -0,0 +1,81 @@
/*
* 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.orm.test.batchfetch;
import java.util.List;
import org.hibernate.Hibernate;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@DomainModel(
annotatedClasses = { A.class, B.class }
)
@SessionFactory
@ServiceRegistry(
settings = @Setting(name = AvailableSettings.USE_SECOND_LEVEL_CACHE, value = "false")
)
public class DynamicBatchFetchTest {
private static int currentId = 1;
@Test
public void testDynamicBatchFetch(SessionFactoryScope scope) {
Integer aId1 = createAAndB( "foo_1", scope );
Integer aId2 = createAAndB( "foo_2", scope );
scope.inTransaction(
session -> {
List resultList = session.createQuery( "from A where id in (" + aId1 + "," + aId2 + ") order by id" )
.list();
A a1 = (A) resultList.get( 0 );
A a2 = (A) resultList.get( 1 );
assertEquals( aId1, a1.getId() );
assertEquals( aId2, a2.getId() );
assertFalse( Hibernate.isInitialized( a1.getB() ) );
assertFalse( Hibernate.isInitialized( a2.getB() ) );
B b = a1.getB();
assertFalse( Hibernate.isInitialized( b ) );
assertEquals( "foo_1", b.getOtherProperty() );
assertTrue( Hibernate.isInitialized( a1.getB() ) );
assertTrue( Hibernate.isInitialized( a2.getB() ) );
assertEquals( "foo_2", a2.getB().getOtherProperty() );
}
);
}
private int createAAndB(String otherProperty, SessionFactoryScope scope) {
scope.inTransaction(
session -> {
B b = new B();
b.setIdPart1( currentId );
b.setIdPart2( currentId );
b.setOtherProperty( otherProperty );
session.save( b );
A a = new A();
a.setId( currentId );
a.setB( b );
session.save( a );
}
);
currentId++;
return currentId - 1;
}
}

View File

@ -4,43 +4,40 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. * 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>. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/ */
package org.hibernate.test.batchfetch; package org.hibernate.orm.test.batchfetch;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertNotNull;
import java.util.List; import java.util.List;
import java.util.stream.IntStream; import java.util.stream.IntStream;
import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
import org.hibernate.loader.BatchFetchStyle;
import org.hibernate.testing.TestForIssue; import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.hibernate.testing.orm.junit.DomainModel;
import org.junit.Test; import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.Test;
public class PaddedBatchFetchTestCase extends BaseCoreFunctionalTestCase { import static org.junit.Assert.assertNotNull;
@Override @DomainModel(
protected Class<?>[] getAnnotatedClasses() { annotatedClasses = { Country.class, City.class }
return new Class<?>[]{ Country.class, City.class }; )
} @SessionFactory
@ServiceRegistry(
@Override settings = {
protected void configure(Configuration configuration) { @Setting(name = AvailableSettings.SHOW_SQL, value = "true"),
super.configure( configuration ); @Setting(name = AvailableSettings.FORMAT_SQL, value = "true"),
@Setting(name = AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, value = "15")
configuration.setProperty( AvailableSettings.SHOW_SQL, Boolean.TRUE.toString() ); }
configuration.setProperty( AvailableSettings.FORMAT_SQL, Boolean.TRUE.toString() ); )
public class DynamicBatchFetchTestCase {
configuration.setProperty( AvailableSettings.BATCH_FETCH_STYLE, BatchFetchStyle.PADDED.name() );
configuration.setProperty( AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, "15" );
}
@Test @Test
@TestForIssue(jiraKey = "HHH-12835") @TestForIssue(jiraKey = "HHH-12835")
public void paddedBatchFetchTest() throws Exception { public void batchFetchTest(SessionFactoryScope scope) {
doInHibernate( this::sessionFactory, session -> { scope.inTransaction( session -> {
// Having DEFAULT_BATCH_FETCH_SIZE=15 // Having DEFAULT_BATCH_FETCH_SIZE=15
// results in batchSizes = [15, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1] // results in batchSizes = [15, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
// Let's create 11 countries so batch size 15 will be used with padded values, // Let's create 11 countries so batch size 15 will be used with padded values,
@ -54,7 +51,7 @@ public class PaddedBatchFetchTestCase extends BaseCoreFunctionalTestCase {
} ); } );
} ); } );
doInHibernate( this::sessionFactory, session -> { scope.inTransaction( session -> {
List<City> allCities = session.createQuery( "from City", City.class ).list(); List<City> allCities = session.createQuery( "from City", City.class ).list();
// this triggers countries to be fetched in batch // this triggers countries to be fetched in batch

View File

@ -6,7 +6,7 @@
*/ */
//$Id: Model.java 4460 2004-08-29 12:04:14Z oneovthafew $ //$Id: Model.java 4460 2004-08-29 12:04:14Z oneovthafew $
package org.hibernate.test.batchfetch; package org.hibernate.orm.test.batchfetch;
/** /**

View File

@ -9,7 +9,7 @@
"-//Hibernate/Hibernate Mapping DTD 3.0//EN" "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.hibernate.test.batchfetch"> <hibernate-mapping package="org.hibernate.orm.test.batchfetch">
<!-- <!--

View File

@ -6,7 +6,7 @@
*/ */
//$Id: ProductLine.java 4460 2004-08-29 12:04:14Z oneovthafew $ //$Id: ProductLine.java 4460 2004-08-29 12:04:14Z oneovthafew $
package org.hibernate.test.batchfetch; package org.hibernate.orm.test.batchfetch;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;

View File

@ -1,23 +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.test.batchfetch;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.loader.BatchFetchStyle;
/**
* @author Gail Badner
*/
public class BatchFetchNotFoundIgnoreDynamicStyleTest extends BatchFetchNotFoundIgnoreDefaultStyleTest {
@Override
protected void configure(Configuration configuration) {
super.configure( configuration );
configuration.getProperties().put( Environment.BATCH_FETCH_STYLE, BatchFetchStyle.DYNAMIC );
}
}

View File

@ -1,24 +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.test.batchfetch;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.loader.BatchFetchStyle;
/**
* @author Gail Badner
*/
public class BatchFetchNotFoundIgnorePaddedStyleTest extends BatchFetchNotFoundIgnoreDefaultStyleTest {
@Override
protected void configure(Configuration configuration) {
super.configure( configuration );
configuration.getProperties().put( Environment.BATCH_FETCH_STYLE, BatchFetchStyle.PADDED );
}
}

View File

@ -1,187 +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.test.batchfetch;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.junit.Test;
import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
import org.hibernate.loader.BatchFetchStyle;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* @author Gavin King
*/
public class BatchFetchTest extends BaseCoreFunctionalTestCase {
@Override
public String[] getMappings() {
return new String[] { "batchfetch/ProductLine.hbm.xml" };
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { BatchLoadableEntity.class };
}
@Override
protected void configure(Configuration configuration) {
super.configure( configuration );
configuration.setProperty( AvailableSettings.GENERATE_STATISTICS, "true" );
configuration.setProperty( AvailableSettings.USE_SECOND_LEVEL_CACHE, "false" );
}
@SuppressWarnings( {"unchecked"})
@Test
public void testBatchFetch() {
Session s = openSession();
Transaction t = s.beginTransaction();
ProductLine cars = new ProductLine();
cars.setDescription( "Cars" );
Model monaro = new Model( cars );
monaro.setName( "monaro" );
monaro.setDescription( "Holden Monaro" );
Model hsv = new Model( cars );
hsv.setName( "hsv" );
hsv.setDescription( "Holden Commodore HSV" );
s.save( cars );
ProductLine oss = new ProductLine();
oss.setDescription( "OSS" );
Model jboss = new Model( oss );
jboss.setName( "JBoss" );
jboss.setDescription( "JBoss Application Server" );
Model hibernate = new Model( oss );
hibernate.setName( "Hibernate" );
hibernate.setDescription( "Hibernate" );
Model cache = new Model( oss );
cache.setName( "JBossCache" );
cache.setDescription( "JBoss TreeCache" );
s.save( oss );
t.commit();
s.close();
s.getSessionFactory().getCache().evictEntityRegion( Model.class );
s.getSessionFactory().getCache().evictEntityRegion( ProductLine.class );
s = openSession();
t = s.beginTransaction();
List list = s.createQuery( "from ProductLine pl order by pl.description" ).list();
cars = ( ProductLine ) list.get( 0 );
oss = ( ProductLine ) list.get( 1 );
assertFalse( Hibernate.isInitialized( cars.getModels() ) );
assertFalse( Hibernate.isInitialized( oss.getModels() ) );
assertEquals( cars.getModels().size(), 2 ); //fetch both collections
assertTrue( Hibernate.isInitialized( cars.getModels() ) );
assertTrue( Hibernate.isInitialized( oss.getModels() ) );
s.clear();
list = s.createQuery( "from Model m" ).list();
hibernate = ( Model ) s.get( Model.class, hibernate.getId() );
hibernate.getProductLine().getId();
for ( Object aList : list ) {
assertFalse( Hibernate.isInitialized( ((Model) aList).getProductLine() ) );
}
assertEquals( hibernate.getProductLine().getDescription(), "OSS" ); //fetch both productlines
s.clear();
Iterator iter = s.createQuery( "from Model" ).list().iterator();
list = new ArrayList();
while ( iter.hasNext() ) {
list.add( iter.next() );
}
Model m = ( Model ) list.get( 0 );
m.getDescription(); //fetch a batch of 4
s.clear();
list = s.createQuery( "from ProductLine" ).list();
ProductLine pl = ( ProductLine ) list.get( 0 );
ProductLine pl2 = ( ProductLine ) list.get( 1 );
s.evict( pl2 );
pl.getModels().size(); //fetch just one collection! (how can we write an assertion for that??)
t.commit();
s.close();
s = openSession();
t = s.beginTransaction();
list = s.createQuery( "from ProductLine pl order by pl.description" ).list();
cars = ( ProductLine ) list.get( 0 );
oss = ( ProductLine ) list.get( 1 );
assertEquals( cars.getModels().size(), 2 );
assertEquals( oss.getModels().size(), 3 );
s.delete( cars );
s.delete( oss );
t.commit();
s.close();
}
@Test
@SuppressWarnings( {"unchecked"})
public void testBatchFetch2() {
Session s = openSession();
s.beginTransaction();
int size = 32+14;
for ( int i = 0; i < size; i++ ) {
s.save( new BatchLoadableEntity( i ) );
}
s.getTransaction().commit();
s.close();
s = openSession();
s.beginTransaction();
// load them all as proxies
for ( int i = 0; i < size; i++ ) {
BatchLoadableEntity entity = (BatchLoadableEntity) s.load( BatchLoadableEntity.class, i );
assertFalse( Hibernate.isInitialized( entity ) );
}
sessionFactory().getStatistics().clear();
// now start initializing them...
for ( int i = 0; i < size; i++ ) {
BatchLoadableEntity entity = (BatchLoadableEntity) s.load( BatchLoadableEntity.class, i );
Hibernate.initialize( entity );
assertTrue( Hibernate.isInitialized( entity ) );
}
// so at this point, all entities are initialized. see how many fetches were performed.
final int expectedFetchCount;
if ( sessionFactory().getSettings().getBatchFetchStyle() == BatchFetchStyle.LEGACY ) {
expectedFetchCount = 3; // (32 + 10 + 4)
}
else if ( sessionFactory().getSettings().getBatchFetchStyle() == BatchFetchStyle.DYNAMIC ) {
expectedFetchCount = 2; // (32 + 14) : because we limited batch-size to 32
}
else {
// PADDED
expectedFetchCount = 2; // (32 + 16*) with the 16 being padded
}
assertEquals( expectedFetchCount, sessionFactory().getStatistics().getEntityStatistics( BatchLoadableEntity.class.getName() ).getFetchCount() );
s.getTransaction().commit();
s.close();
s = openSession();
s.beginTransaction();
s.createQuery( "delete BatchLoadableEntity" ).executeUpdate();
s.getTransaction().commit();
s.close();
}
}

View File

@ -1,98 +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.test.batchfetch;
import java.util.List;
import org.junit.Test;
import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class DynamicBatchFetchTest extends BaseCoreFunctionalTestCase {
private static int currentId = 1;
@Override
protected void configure(Configuration configuration) {
super.configure( configuration );
configuration.setProperty( AvailableSettings.BATCH_FETCH_STYLE, "DYNAMIC" );
super.configure( configuration );
configuration.setProperty( AvailableSettings.USE_SECOND_LEVEL_CACHE, "false" );
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { A.class, B.class };
}
@Test
public void testDynamicBatchFetch() {
Integer aId1 = createAAndB();
Integer aId2 = createAAndB();
Session s = openSession();
s.getTransaction().begin();
List resultList = s.createQuery("from A where id in (" + aId1 + "," + aId2 + ") order by id" ).list();
A a1 = (A) resultList.get(0);
A a2 = (A) resultList.get( 1 );
assertEquals( aId1, a1.getId() );
assertEquals( aId2, a2.getId() );
assertFalse( Hibernate.isInitialized( a1.getB() ) );
assertFalse( Hibernate.isInitialized( a2.getB() ) );
assertEquals( "foo", a1.getB().getOtherProperty() );
assertTrue( Hibernate.isInitialized( a1.getB() ) );
// a2.getB() is still uninitialized
assertFalse( Hibernate.isInitialized( a2.getB() ) );
// the B entity has been loaded, but is has not been made the target of a2.getB() yet.
assertTrue( ( (SessionImplementor) session ).getPersistenceContext().containsEntity(
new EntityKey(
( (SessionImplementor) session ).getContextEntityIdentifier( a2.getB() ),
( (SessionImplementor) session ).getFactory().getEntityPersister( B.class.getName() )
)
)
);
// a2.getB() is still uninitialized; getting the ID for a2.getB() did not initialize it.
assertFalse( Hibernate.isInitialized( a2.getB() ) );
assertEquals( "foo", a2.getB().getOtherProperty() );
// now it's initialized.
assertTrue( Hibernate.isInitialized( a2.getB() ) );
s.getTransaction().commit();
s.close();
}
private int createAAndB() {
Session s = openSession();
s.getTransaction().begin();
B b = new B();
b.setIdPart1( currentId );
b.setIdPart2( currentId);
b.setOtherProperty("foo");
s.save( b );
A a = new A();
a.setId( currentId );
a.setB( b );
s.save( a );
s.getTransaction().commit();
s.close();
currentId++;
return currentId - 1;
}
}

View File

@ -214,7 +214,7 @@ public class ASTParserLoadingTest extends BaseCoreFunctionalTestCase {
"/org/hibernate/test/hql/ComponentContainer.hbm.xml", "/org/hibernate/test/hql/ComponentContainer.hbm.xml",
"/org/hibernate/test/hql/VariousKeywordPropertyEntity.hbm.xml", "/org/hibernate/test/hql/VariousKeywordPropertyEntity.hbm.xml",
"/org/hibernate/test/hql/Constructor.hbm.xml", "/org/hibernate/test/hql/Constructor.hbm.xml",
"batchfetch/ProductLine.hbm.xml", "/org/hibernate/orm/test/batchfetch/ProductLine.hbm.xml",
"/org/hibernate/orm/test/cid/Customer.hbm.xml", "/org/hibernate/orm/test/cid/Customer.hbm.xml",
"/org/hibernate/orm/test/cid/Order.hbm.xml", "/org/hibernate/orm/test/cid/Order.hbm.xml",
"/org/hibernate/orm/test/cid/LineItem.hbm.xml", "/org/hibernate/orm/test/cid/LineItem.hbm.xml",

View File

@ -16,7 +16,6 @@ import org.hibernate.dialect.AbstractHANADialect;
import org.hibernate.dialect.SybaseASE15Dialect; import org.hibernate.dialect.SybaseASE15Dialect;
import org.hibernate.testing.DialectChecks; import org.hibernate.testing.DialectChecks;
import org.hibernate.testing.FailureExpected;
import org.hibernate.testing.RequiresDialectFeature; import org.hibernate.testing.RequiresDialectFeature;
import org.hibernate.testing.SkipForDialect; import org.hibernate.testing.SkipForDialect;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
@ -35,7 +34,6 @@ import static org.junit.Assert.fail;
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
@FailureExpected( jiraKey = "none", message = "Support for scrolling collection fetches not yet implemented" )
public class ScrollableCollectionFetchingTest extends BaseCoreFunctionalTestCase { public class ScrollableCollectionFetchingTest extends BaseCoreFunctionalTestCase {
public String[] getMappings() { public String[] getMappings() {
return new String[] { "hql/Animal.hbm.xml" }; return new String[] { "hql/Animal.hbm.xml" };

View File

@ -39,7 +39,7 @@ public class EJBQLTest extends BaseCoreFunctionalTestCase {
public String[] getMappings() { public String[] getMappings() {
return new String[]{ return new String[]{
"hql/Animal.hbm.xml", "hql/Animal.hbm.xml",
"batchfetch/ProductLine.hbm.xml", "/org/hibernate/orm/test/batchfetch/ProductLine.hbm.xml",
"cid/Customer.hbm.xml", "cid/Customer.hbm.xml",
"cid/Order.hbm.xml", "cid/Order.hbm.xml",
"cid/LineItem.hbm.xml", "cid/LineItem.hbm.xml",

View File

@ -42,7 +42,7 @@ public abstract class QueryTranslatorTestCase extends BaseCoreFunctionalTestCase
"hql/CrazyIdFieldNames.hbm.xml", "hql/CrazyIdFieldNames.hbm.xml",
"hql/SimpleEntityWithAssociation.hbm.xml", "hql/SimpleEntityWithAssociation.hbm.xml",
"hql/ComponentContainer.hbm.xml", "hql/ComponentContainer.hbm.xml",
"batchfetch/ProductLine.hbm.xml", "/org/hibernate/orm/test/batchfetch/ProductLine.hbm.xml",
"cid/Customer.hbm.xml", "cid/Customer.hbm.xml",
"cid/Order.hbm.xml", "cid/Order.hbm.xml",
"cid/LineItem.hbm.xml", "cid/LineItem.hbm.xml",