Fix DynamicBatch loading
This commit is contained in:
parent
29e22c68ac
commit
9b569932d2
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
*
|
||||
|
|
|
@ -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();
|
||||
|
||||
if ( parentInjectionPropertyAccess != null ) {
|
||||
Initializer initializer = rowProcessingState.resolveInitializer( navigablePath.getParent() );
|
||||
if ( parentInjectionPropertyAccess != null ) {
|
||||
final Object owner;
|
||||
if ( initializer instanceof CollectionInitializer ) {
|
||||
owner = ( (CollectionInitializer) initializer ).getCollectionInstance().getOwner();
|
||||
|
@ -214,6 +215,17 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
|
|||
else {
|
||||
notifyParentResolutionListeners( compositeInstance );
|
||||
if ( compositeInstance instanceof HibernateProxy ) {
|
||||
if ( initializer != this ) {
|
||||
( (AbstractEntityInitializer) initializer ).registerResolutionListener(
|
||||
entityInstance -> {
|
||||
embeddedModelPartDescriptor.getEmbeddableTypeDescriptor().setPropertyValues(
|
||||
entityInstance,
|
||||
resolvedValues
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
Object target = embeddedModelPartDescriptor.getEmbeddableTypeDescriptor()
|
||||
.getRepresentationStrategy()
|
||||
.getInstantiator()
|
||||
|
@ -224,6 +236,7 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
|
|||
);
|
||||
( (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.
|
||||
// If the values are all null, we would normally not create a composite instance at all because no values exist.
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -68,6 +68,16 @@ public class EntityFetchSelectImpl extends AbstractNonJoinedEntityFetch {
|
|||
result.createResultAssembler( creationState )
|
||||
);
|
||||
}
|
||||
if ( entityPersister.isBatchLoadable() ) {
|
||||
return new BatchEntitySelectFetchInitializer(
|
||||
parentAccess,
|
||||
fetchedAttribute,
|
||||
getNavigablePath(),
|
||||
entityPersister,
|
||||
result.createResultAssembler( creationState )
|
||||
);
|
||||
}
|
||||
else {
|
||||
return new EntitySelectFetchInitializer(
|
||||
parentAccess,
|
||||
fetchedAttribute,
|
||||
|
@ -76,6 +86,7 @@ public class EntityFetchSelectImpl extends AbstractNonJoinedEntityFetch {
|
|||
result.createResultAssembler( creationState )
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return new EntityAssembler( getResultJavaTypeDescriptor(), initializer );
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
|
@ -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,14 +118,26 @@ public class CircularFetchImpl implements BiDirectionalFetch, Association {
|
|||
);
|
||||
}
|
||||
if ( timing == FetchTiming.IMMEDIATE ) {
|
||||
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(),
|
||||
entityMappingType.getEntityPersister(),
|
||||
entityPersister,
|
||||
resultAssembler
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return new EntityDelayedFetchInitializer(
|
||||
getReferencedPath(),
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
||||
|
|
@ -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 + ")";
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
@ -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( '?' );
|
|
@ -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 };
|
||||
@DomainModel(
|
||||
annotatedClasses = {
|
||||
BatchFetchReferencedColumnNameTest.Child.class,
|
||||
BatchFetchReferencedColumnNameTest.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" );
|
||||
)
|
||||
@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() );
|
||||
} );
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
|
@ -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() );
|
||||
} );
|
||||
}
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
|
||||
|
||||
/**
|
|
@ -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">
|
||||
|
||||
<!--
|
||||
|
|
@ -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;
|
||||
|
|
@ -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 );
|
||||
}
|
||||
}
|
|
@ -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 );
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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",
|
||||
|
|
|
@ -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" };
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue