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

View File

@ -8,6 +8,7 @@ package org.hibernate.sql.results.graph;
import java.util.function.Consumer;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.query.NavigablePath;
/**
@ -26,6 +27,8 @@ public interface FetchParentAccess extends Initializer {
NavigablePath getNavigablePath();
EntityKey getEntityKey();
/**
* 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.Initializer;
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.internal.NullValueAssembler;
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
@ -165,8 +166,8 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
final PropertyAccess parentInjectionPropertyAccess = embeddedModelPartDescriptor.getParentInjectionAttributePropertyAccess();
Initializer initializer = rowProcessingState.resolveInitializer( navigablePath.getParent() );
if ( parentInjectionPropertyAccess != null ) {
Initializer initializer = rowProcessingState.resolveInitializer( navigablePath.getParent() );
final Object owner;
if ( initializer instanceof CollectionInitializer ) {
owner = ( (CollectionInitializer) initializer ).getCollectionInstance().getOwner();
@ -214,15 +215,27 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
else {
notifyParentResolutionListeners( compositeInstance );
if ( compositeInstance instanceof HibernateProxy ) {
Object target = embeddedModelPartDescriptor.getEmbeddableTypeDescriptor()
.getRepresentationStrategy()
.getInstantiator()
.instantiate( VALUE_ACCESS, rowProcessingState.getSession().getFactory() );
embeddedModelPartDescriptor.getEmbeddableTypeDescriptor().setPropertyValues(
target,
resolvedValues
);
( (HibernateProxy) compositeInstance ).getHibernateLazyInitializer().setImplementation( target );
if ( initializer != this ) {
( (AbstractEntityInitializer) initializer ).registerResolutionListener(
entityInstance -> {
embeddedModelPartDescriptor.getEmbeddableTypeDescriptor().setPropertyValues(
entityInstance,
resolvedValues
);
}
);
}
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.
// 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;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.sql.results.graph.AssemblerCreationState;
import org.hibernate.sql.results.graph.FetchParentAccess;
import org.hibernate.sql.results.graph.embeddable.AbstractEmbeddableInitializer;
@ -25,6 +26,11 @@ public class EmbeddableFetchInitializer
@Override
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;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.sql.results.graph.AssemblerCreationState;
import org.hibernate.sql.results.graph.embeddable.AbstractEmbeddableInitializer;
import org.hibernate.sql.results.graph.embeddable.EmbeddableResultGraphNode;
@ -22,7 +23,12 @@ public class EmbeddableResultInitializer extends AbstractEmbeddableInitializer {
@Override
public Object getParentKey() {
return null;
return findFirstEntityDescriptorAccess().getParentKey();
}
@Override
public EntityKey getEntityKey() {
return findFirstEntityDescriptorAccess().getEntityKey();
}
@Override

View File

@ -581,6 +581,8 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
return existingLoadingEntry.getEntityInstance();
}
assert existingLoadingEntry == null || existingLoadingEntry.getEntityInstance() == null;
Object instance = null;
// 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 )
);
}
return new EntitySelectFetchInitializer(
parentAccess,
fetchedAttribute,
getNavigablePath(),
entityPersister,
result.createResultAssembler( creationState )
);
if ( entityPersister.isBatchLoadable() ) {
return new BatchEntitySelectFetchInitializer(
parentAccess,
fetchedAttribute,
getNavigablePath(),
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;
}
Initializer initializer = rowProcessingState.getJdbcValuesSourceProcessingState().findInitializer(
entityKey );
Initializer initializer = rowProcessingState.getJdbcValuesSourceProcessingState()
.findInitializer( entityKey );
if ( initializer != null ) {
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.internal.ToOneAttributeMapping;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.NavigablePath;
import org.hibernate.sql.results.graph.AssemblerCreationState;
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.Fetchable;
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.EntitySelectFetchByUniqueKeyInitializer;
import org.hibernate.sql.results.graph.entity.internal.EntitySelectFetchInitializer;
@ -116,13 +118,25 @@ public class CircularFetchImpl implements BiDirectionalFetch, Association {
);
}
if ( timing == FetchTiming.IMMEDIATE ) {
return new EntitySelectFetchInitializer(
parentAccess,
(ToOneAttributeMapping) referencedModelPart,
getReferencedPath(),
entityMappingType.getEntityPersister(),
resultAssembler
);
final EntityPersister entityPersister = entityMappingType.getEntityPersister();
if ( entityPersister.isBatchLoadable() ) {
return new BatchEntitySelectFetchInitializer(
parentAccess,
(ToOneAttributeMapping) referencedModelPart,
getReferencedPath(),
entityPersister,
resultAssembler
);
}
else {
return new EntitySelectFetchInitializer(
parentAccess,
(ToOneAttributeMapping) referencedModelPart,
getReferencedPath(),
entityPersister,
resultAssembler
);
}
}
else {
return new EntityDelayedFetchInitializer(

View File

@ -4,7 +4,7 @@
* 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;
package org.hibernate.orm.test.batchfetch;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;

View File

@ -4,7 +4,7 @@
* 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;
package org.hibernate.orm.test.batchfetch;
import org.hibernate.annotations.BatchSize;

View File

@ -4,7 +4,7 @@
* 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;
package org.hibernate.orm.test.batchfetch;
import java.io.Serializable;
@ -31,4 +31,8 @@ public class BId
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.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.FetchType;
import jakarta.persistence.GeneratedValue;
@ -9,33 +18,20 @@ import jakarta.persistence.Id;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.MappedSuperclass;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
public class BatchFetchBootstrapTest extends BaseCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] {
JafSid.class, UserGroup.class
};
}
@Override
protected void configure(Configuration configuration) {
configuration.setProperty(AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, "30");
}
@Override
protected void buildSessionFactory() {
}
@DomainModel(
annotatedClasses = {
BatchFetchBootstrapTest.JafSid.class, BatchFetchBootstrapTest.UserGroup.class
}
)
@SessionFactory
@ServiceRegistry(
settings = @Setting( name = AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, value = "30")
)
public class BatchFetchBootstrapTest {
@Test
public void test() {
super.buildSessionFactory();
}

View File

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

View File

@ -4,13 +4,21 @@
* 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 static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
package org.hibernate.orm.test.batchfetch;
import java.time.ZonedDateTime;
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.Column;
import jakarta.persistence.Entity;
@ -21,34 +29,30 @@ import jakarta.persistence.OneToMany;
import jakarta.persistence.OrderBy;
import jakarta.persistence.Table;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Assert;
import org.junit.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
public class BatchFetchReferencedColumnNameTest extends BaseCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[]{ Child.class, Parent.class };
}
@Override
protected void configure(Configuration configuration) {
super.configure( configuration );
configuration.setProperty( AvailableSettings.SHOW_SQL, Boolean.TRUE.toString() );
configuration.setProperty( AvailableSettings.FORMAT_SQL, Boolean.TRUE.toString() );
configuration.setProperty( AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, "64" );
}
@DomainModel(
annotatedClasses = {
BatchFetchReferencedColumnNameTest.Child.class,
BatchFetchReferencedColumnNameTest.Parent.class
}
)
@SessionFactory
@ServiceRegistry(
settings = {
@Setting(name = AvailableSettings.SHOW_SQL, value = "true"),
@Setting(name = AvailableSettings.FORMAT_SQL, value = "true"),
@Setting(name = AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, value = "64")
}
)
public class BatchFetchReferencedColumnNameTest {
@Test
@TestForIssue(jiraKey = "HHH-13059")
public void test() throws Exception {
doInHibernate( this::sessionFactory, session -> {
public void test(SessionFactoryScope scope) throws Exception {
scope.inTransaction( session -> {
Parent p = new Parent();
p.setId( 1L );
session.save( p );
@ -66,11 +70,11 @@ public class BatchFetchReferencedColumnNameTest extends BaseCoreFunctionalTestCa
session.save( c2 );
} );
doInHibernate( this::sessionFactory, session -> {
scope.inTransaction( session -> {
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.
* 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.Map;
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.Entity;
import jakarta.persistence.FetchType;
@ -18,26 +28,29 @@ import jakarta.persistence.LockModeType;
import jakarta.persistence.ManyToOne;
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.*;
public class BatchFetchRefreshTest extends BaseNonConfigCoreFunctionalTestCase {
@DomainModel(
annotatedClasses = {
BatchFetchRefreshTest.Parent.class,
BatchFetchRefreshTest.Child.class
}
)
@SessionFactory
@ServiceRegistry(
settings = @Setting(name = AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, value = "8")
)
public class BatchFetchRefreshTest {
@Test
public void testRefreshWithBatch() {
doInHibernate( this::sessionFactory, session -> {
public void testRefreshWithBatch(SessionFactoryScope scope) {
scope.inTransaction( session -> {
// Retrieve one of the parents into the session.
Parent parent = session.find(Parent.class, 1);
Assert.assertNotNull(parent);
Parent parent = session.find( Parent.class, 1 );
assertNotNull( parent );
// Retrieve children but keep their parents lazy!
// This allows batch fetching to do its thing when we refresh below.
@ -50,16 +63,16 @@ public class BatchFetchRefreshTest extends BaseNonConfigCoreFunctionalTestCase {
parent.getChildren().size();
// 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
public void setupData() {
@BeforeEach
public void setupData(SessionFactoryScope scope) {
final int numParents = 5;
final int childrenPerParent = 2;
doInHibernate( this::sessionFactory, session -> {
scope.inTransaction( session -> {
int k = 1;
for ( int i = 1; i <= numParents; i++ ) {
Parent parent = new Parent();
@ -69,7 +82,7 @@ public class BatchFetchRefreshTest extends BaseNonConfigCoreFunctionalTestCase {
session.persist( 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.childId = k;
child.name = "Child_" + i + "_" + j;
@ -79,24 +92,10 @@ public class BatchFetchRefreshTest extends BaseNonConfigCoreFunctionalTestCase {
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")
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.
* 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.Id;

View File

@ -1,9 +1,17 @@
package org.hibernate.orm.test.batchfetch;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.junit.Assert.assertNotNull;
import org.hibernate.LockMode;
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.FetchType;
@ -11,38 +19,23 @@ import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import org.hibernate.LockMode;
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;
import static org.junit.jupiter.api.Assertions.assertNotNull;
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;
@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
public void testJoin() {
doInJPA( this::entityManagerFactory, em -> {
public void testJoin(EntityManagerFactoryScope scope) {
scope.inTransaction( em -> {
SubEntity sub = new SubEntity();
em.persist( sub );
@ -51,9 +44,9 @@ public class BatchingEntityLoaderInitializationWithNoLockModeTest extends BaseEn
em.persist( main );
this.mainId = main.getId();
});
} );
doInJPA( this::entityManagerFactory, em -> {
scope.inTransaction( em -> {
EntityPersister entityPersister = ( (MetamodelImplementor) em.getMetamodel() )
.entityPersister( MainEntity.class );
@ -61,8 +54,9 @@ public class BatchingEntityLoaderInitializationWithNoLockModeTest extends BaseEn
LockOptions lockOptions = new LockOptions( LockMode.NONE );
lockOptions.setTimeOut( 10 );
MainEntity main = (MainEntity) entityPersister.load( this.mainId, null, lockOptions,
(SharedSessionContractImplementor) em );
MainEntity main = (MainEntity) entityPersister.
load( this.mainId, null, lockOptions, (SharedSessionContractImplementor) em );
assertNotNull( main.getSub() );
} );
}

View File

@ -4,7 +4,7 @@
* 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;
package org.hibernate.orm.test.batchfetch;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;

View File

@ -4,7 +4,7 @@
* 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;
package org.hibernate.orm.test.batchfetch;
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.
* 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 static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertNotNull;
package org.hibernate.orm.test.batchfetch;
import java.util.List;
import java.util.stream.IntStream;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
import org.hibernate.loader.BatchFetchStyle;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
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;
public class PaddedBatchFetchTestCase extends BaseCoreFunctionalTestCase {
import static org.junit.Assert.assertNotNull;
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[]{ Country.class, City.class };
}
@Override
protected void configure(Configuration configuration) {
super.configure( configuration );
configuration.setProperty( AvailableSettings.SHOW_SQL, Boolean.TRUE.toString() );
configuration.setProperty( AvailableSettings.FORMAT_SQL, Boolean.TRUE.toString() );
configuration.setProperty( AvailableSettings.BATCH_FETCH_STYLE, BatchFetchStyle.PADDED.name() );
configuration.setProperty( AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, "15" );
}
@DomainModel(
annotatedClasses = { Country.class, City.class }
)
@SessionFactory
@ServiceRegistry(
settings = {
@Setting(name = AvailableSettings.SHOW_SQL, value = "true"),
@Setting(name = AvailableSettings.FORMAT_SQL, value = "true"),
@Setting(name = AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, value = "15")
}
)
public class DynamicBatchFetchTestCase {
@Test
@TestForIssue(jiraKey = "HHH-12835")
public void paddedBatchFetchTest() throws Exception {
doInHibernate( this::sessionFactory, session -> {
public void batchFetchTest(SessionFactoryScope scope) {
scope.inTransaction( session -> {
// Having DEFAULT_BATCH_FETCH_SIZE=15
// 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,
@ -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();
// 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 $
package org.hibernate.test.batchfetch;
package org.hibernate.orm.test.batchfetch;
/**

View File

@ -9,7 +9,7 @@
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"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 $
package org.hibernate.test.batchfetch;
package org.hibernate.orm.test.batchfetch;
import java.util.HashSet;
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/VariousKeywordPropertyEntity.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/Order.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.testing.DialectChecks;
import org.hibernate.testing.FailureExpected;
import org.hibernate.testing.RequiresDialectFeature;
import org.hibernate.testing.SkipForDialect;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
@ -35,7 +34,6 @@ import static org.junit.Assert.fail;
*
* @author Steve Ebersole
*/
@FailureExpected( jiraKey = "none", message = "Support for scrolling collection fetches not yet implemented" )
public class ScrollableCollectionFetchingTest extends BaseCoreFunctionalTestCase {
public String[] getMappings() {
return new String[] { "hql/Animal.hbm.xml" };

View File

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

View File

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