HHH-15644 Add test for issue

This commit is contained in:
Andrea Boriero 2022-11-07 22:59:56 +01:00 committed by Andrea Boriero
parent 7c72115bcf
commit 00717c6911
2 changed files with 328 additions and 34 deletions

View File

@ -21,14 +21,12 @@ import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.persister.entity.AbstractEntityPersister; import org.hibernate.persister.entity.AbstractEntityPersister;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.UniqueKeyLoadable;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.exec.spi.ExecutionContext; import org.hibernate.sql.exec.spi.ExecutionContext;
import org.hibernate.sql.results.graph.AbstractFetchParentAccess; import org.hibernate.sql.results.graph.AbstractFetchParentAccess;
import org.hibernate.sql.results.graph.DomainResultAssembler; import org.hibernate.sql.results.graph.DomainResultAssembler;
import org.hibernate.sql.results.graph.FetchParentAccess; import org.hibernate.sql.results.graph.FetchParentAccess;
import org.hibernate.sql.results.graph.Initializer; import org.hibernate.sql.results.graph.Initializer;
import org.hibernate.sql.results.graph.entity.AbstractEntityInitializer;
import org.hibernate.sql.results.graph.entity.EntityInitializer; import org.hibernate.sql.results.graph.entity.EntityInitializer;
import org.hibernate.sql.results.graph.entity.EntityLoadingLogging; import org.hibernate.sql.results.graph.entity.EntityLoadingLogging;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry; import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
@ -148,14 +146,21 @@ public class BatchEntitySelectFetchInitializer extends AbstractFetchParentAccess
} }
persistenceContext.getBatchFetchQueue().addBatchLoadableEntityKey( entityKey ); persistenceContext.getBatchFetchQueue().addBatchLoadableEntityKey( entityKey );
List<Object> objects = getObjects();
parentAccess.registerResolutionListener(
o -> objects.add( o )
);
isInitialized = true;
}
private List<Object> getObjects() {
List<Object> objects = toBatchLoad.get( entityKey ); List<Object> objects = toBatchLoad.get( entityKey );
if ( objects == null ) { if ( objects == null ) {
objects = new ArrayList<>(); objects = new ArrayList<>();
toBatchLoad.put( entityKey, objects ); toBatchLoad.put( entityKey, objects );
} }
objects.add( parentAccess.getInitializedInstance() ); return objects;
isInitialized = true;
} }
protected boolean isAttributeAssignableToConcreteDescriptor() { protected boolean isAttributeAssignableToConcreteDescriptor() {
@ -195,6 +200,11 @@ public class BatchEntitySelectFetchInitializer extends AbstractFetchParentAccess
return entityKey; return entityKey;
} }
@Override
public EntityPersister findFirstEntityPersister() {
return getEntityDescriptor();
}
@Override @Override
public Object getParentKey() { public Object getParentKey() {
throw new NotYetImplementedFor6Exception( getClass() ); throw new NotYetImplementedFor6Exception( getClass() );
@ -222,8 +232,6 @@ public class BatchEntitySelectFetchInitializer extends AbstractFetchParentAccess
@Override @Override
public void endLoading(ExecutionContext context) { public void endLoading(ExecutionContext context) {
final int propertyIndex = ( (UniqueKeyLoadable) ( (AbstractEntityInitializer) parentAccess ).getEntityDescriptor() )
.getPropertyIndex( referencedModelPart.getPartName() );
toBatchLoad.forEach( toBatchLoad.forEach(
(entityKey, parentInstances) -> { (entityKey, parentInstances) -> {
final Object instance = context.getSession().internalLoad( final Object instance = context.getSession().internalLoad(
@ -232,19 +240,14 @@ public class BatchEntitySelectFetchInitializer extends AbstractFetchParentAccess
true, true,
referencedModelPart.isInternalLoadNullable() referencedModelPart.isInternalLoadNullable()
); );
for ( Object parentInstance : parentInstances ) { for ( Object parentInstance : parentInstances ) {
( (AbstractEntityPersister) referencedModelPart.getDeclaringType() ).setPropertyValue( referencedModelPart.getPropertyAccess().getSetter().set(parentInstance, instance);
parentInstance, final EntityEntry entry = context.getSession().getPersistenceContext().getEntry( parentInstance );
referencedModelPart.getPartName(),
instance
);
final EntityEntry entry = context.getSession()
.getPersistenceContext()
.getEntry( parentInstance );
if ( entry != null ) { if ( entry != null ) {
final Object[] loadedState = entry.getLoadedState(); final Object[] loadedState = entry.getLoadedState();
if ( loadedState != null ) { if ( loadedState != null ) {
loadedState[propertyIndex] = instance; loadedState[0] = instance;
} }
} }
} }

View File

@ -1,11 +1,18 @@
package org.hibernate.orm.test.batch; package org.hibernate.orm.test.batch;
import java.util.List;
import java.util.Objects;
import org.hibernate.cfg.Environment; import org.hibernate.cfg.Environment;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.internal.SessionImpl;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.TestForIssue; import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.Jpa; import org.hibernate.testing.orm.junit.Jpa;
import org.hibernate.testing.orm.junit.Setting; import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import jakarta.persistence.Embeddable; import jakarta.persistence.Embeddable;
@ -13,56 +20,340 @@ import jakarta.persistence.Embedded;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.Id; import jakarta.persistence.Id;
import jakarta.persistence.OneToOne; import jakarta.persistence.OneToOne;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.CriteriaQuery;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
@Jpa( @Jpa(
annotatedClasses = { annotatedClasses = {
EmbeddableBatchingTest.A.class, EmbeddableBatchingTest.EntityA.class,
EmbeddableBatchingTest.C.class EmbeddableBatchingTest.EntityB.class,
EmbeddableBatchingTest.EntityC.class,
EmbeddableBatchingTest.EntityD.class
}, },
integrationSettings = { @Setting(name = Environment.DEFAULT_BATCH_FETCH_SIZE, value = "2") } integrationSettings = { @Setting(name = Environment.DEFAULT_BATCH_FETCH_SIZE, value = "2") }
) )
@TestForIssue(jiraKey = "HHH-15644") @TestForIssue(jiraKey = "HHH-15644")
class EmbeddableBatchingTest { class EmbeddableBatchingTest {
@Test private static final EntityC ENTITY_C = new EntityC( 2, true );
void testSelect(EntityManagerFactoryScope scope) { private static final EntityB ENTITY_B = new EntityB( 1, 2f );
scope.inEntityManager( private static final EntityD ENTITY_D = new EntityD( 3, (byte) 100 );
private static final EmbeddableA EMBEDDABLE_A = new EmbeddableA( "b1", ENTITY_B );
private static final EmbeddableB EMBEDDABLE_B = new EmbeddableB( (short) 3, new EmbeddableC( 4, ENTITY_C ) );
private static final EntityA ENTITY_A = new EntityA( 4, "a", null, ENTITY_D, EMBEDDABLE_A, EMBEDDABLE_B );
@BeforeEach
public void setUp(EntityManagerFactoryScope scope) {
scope.inTransaction(
entityManager -> { entityManager -> {
final CriteriaBuilder cb = entityManager.getCriteriaBuilder(); entityManager.persist( ENTITY_B );
final CriteriaQuery<A> c = cb.createQuery( A.class ); entityManager.persist( ENTITY_C );
c.from( A.class ); entityManager.persist( ENTITY_D );
entityManager.createQuery( c ).getResultList(); entityManager.persist( ENTITY_A );
} }
); );
} }
@Entity @Test
public static class A { void testSelect(EntityManagerFactoryScope scope) {
scope.inEntityManager(
entityManager -> {
final CriteriaQuery<EntityA> query = entityManager.getCriteriaBuilder()
.createQuery( EntityA.class );
query.from( EntityA.class );
List<EntityA> results = entityManager.createQuery( query ).getResultList();
assertThat( results.size() ).isEqualTo( 1 );
EntityA entityA = results.get( 0 );
assertThat( entityA ).isEqualTo( ENTITY_A );
EntityEntry entry = ( (SessionImpl) entityManager ).getPersistenceContext().getEntry( entityA );
Object[] loadedState = entry.getLoadedState();
EntityPersister persister = ( (SessionImpl) entityManager ).getEntityPersister(
"EntityA",
entityA
);
assertThat( loadedState[persister.getPropertyIndex( "name" )] ).isEqualTo( entityA.name );
assertThat( loadedState[persister.getPropertyIndex( "anotherName" )] ).isEqualTo( null );
assertThat( loadedState[persister.getPropertyIndex( "entityD" )] ).isEqualTo( ENTITY_D );
assertThat( loadedState[persister.getPropertyIndex( "embeddableA" )] ).isEqualTo( EMBEDDABLE_A );
assertThat( loadedState[persister.getPropertyIndex( "embeddableB" )] ).isEqualTo( EMBEDDABLE_B );
}
);
}
@Entity(name = "EntityA")
public static class EntityA {
@Id @Id
private Integer id; private Integer id;
@Embedded private String name;
private B b;
private String anotherName;
@OneToOne
private EntityD entityD;
@Embedded
private EmbeddableA embeddableA;
@Embedded
private EmbeddableB embeddableB;
public EntityA() {
}
public EntityA(
Integer id,
String name,
String anotherName,
EntityD entityD,
EmbeddableA embeddableA,
EmbeddableB embeddableB) {
this.id = id;
this.name = name;
this.anotherName = anotherName;
this.entityD = entityD;
this.embeddableA = embeddableA;
this.embeddableB = embeddableB;
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
EntityA entityA = (EntityA) o;
return Objects.equals( id, entityA.id ) && Objects.equals(
name,
entityA.name
) && Objects.equals( anotherName, entityA.anotherName ) && Objects.equals(
entityD,
entityA.entityD
) && Objects.equals( embeddableA, entityA.embeddableA ) && Objects.equals(
embeddableB,
entityA.embeddableB
);
}
@Override
public int hashCode() {
return Objects.hash( id, name, anotherName, entityD, embeddableA, embeddableB );
}
} }
@Embeddable @Embeddable
public static class B { public static class EmbeddableA {
private String aString;
@OneToOne @OneToOne
private C c; private EntityB entityB;
public EmbeddableA() {
}
public EmbeddableA(String aString, EntityB entityB) {
this.aString = aString;
this.entityB = entityB;
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
EmbeddableA that = (EmbeddableA) o;
return Objects.equals( aString, that.aString ) && Objects.equals( entityB, that.entityB );
}
@Override
public int hashCode() {
return Objects.hash( aString, entityB );
}
} }
@Entity @Embeddable
public static class C { public static class EmbeddableB {
private short aShort;
@Embedded
private EmbeddableC d;
public EmbeddableB() {
}
public EmbeddableB(short aShort, EmbeddableC d) {
this.aShort = aShort;
this.d = d;
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
EmbeddableB that = (EmbeddableB) o;
return aShort == that.aShort && Objects.equals( d, that.d );
}
@Override
public int hashCode() {
return Objects.hash( aShort, d );
}
}
@Embeddable
public static class EmbeddableC {
private int anInteger;
@OneToOne
private EntityC entityC;
public EmbeddableC() {
}
public EmbeddableC(int anInteger, EntityC entityC) {
this.anInteger = anInteger;
this.entityC = entityC;
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
EmbeddableC that = (EmbeddableC) o;
return Objects.equals( anInteger, that.anInteger ) && Objects.equals( entityC, that.entityC );
}
@Override
public int hashCode() {
return Objects.hash( anInteger, entityC );
}
}
@Entity(name = "EntityB")
public static class EntityB {
@Id @Id
private Integer id; private Integer id;
private Float aFloat;
public EntityB() {
}
public EntityB(Integer id, Float aFloat) {
this.id = id;
this.aFloat = aFloat;
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
EntityB entityB = (EntityB) o;
return Objects.equals( id, entityB.id ) && Objects.equals( aFloat, entityB.aFloat );
}
@Override
public int hashCode() {
return Objects.hash( id, aFloat );
}
}
@Entity(name = "EntityC")
public static class EntityC {
@Id
private Integer id;
private boolean aBoolean;
public EntityC() {
}
public EntityC(Integer id, boolean aBoolean) {
this.id = id;
this.aBoolean = aBoolean;
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
EntityC entityC = (EntityC) o;
return aBoolean == entityC.aBoolean && Objects.equals( id, entityC.id );
}
@Override
public int hashCode() {
return Objects.hash( id, aBoolean );
}
}
@Entity(name = "EntityD")
public static class EntityD {
@Id
private Integer id;
private byte aByte;
public EntityD() {
}
public EntityD(Integer id, byte aByte) {
this.id = id;
this.aByte = aByte;
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
EntityD entityD = (EntityD) o;
return aByte == entityD.aByte && Objects.equals( id, entityD.id );
}
@Override
public int hashCode() {
return Objects.hash( id, aByte );
}
} }
} }