HHH-15644 Add test for issue
This commit is contained in:
parent
7c72115bcf
commit
00717c6911
|
@ -21,14 +21,12 @@ 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.spi.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.EntityLoadingLogging;
|
||||
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
|
||||
|
@ -148,14 +146,21 @@ public class BatchEntitySelectFetchInitializer extends AbstractFetchParentAccess
|
|||
}
|
||||
|
||||
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 );
|
||||
if ( objects == null ) {
|
||||
objects = new ArrayList<>();
|
||||
toBatchLoad.put( entityKey, objects );
|
||||
}
|
||||
objects.add( parentAccess.getInitializedInstance() );
|
||||
|
||||
isInitialized = true;
|
||||
return objects;
|
||||
}
|
||||
|
||||
protected boolean isAttributeAssignableToConcreteDescriptor() {
|
||||
|
@ -195,6 +200,11 @@ public class BatchEntitySelectFetchInitializer extends AbstractFetchParentAccess
|
|||
return entityKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityPersister findFirstEntityPersister() {
|
||||
return getEntityDescriptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getParentKey() {
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
|
@ -222,8 +232,6 @@ public class BatchEntitySelectFetchInitializer extends AbstractFetchParentAccess
|
|||
|
||||
@Override
|
||||
public void endLoading(ExecutionContext context) {
|
||||
final int propertyIndex = ( (UniqueKeyLoadable) ( (AbstractEntityInitializer) parentAccess ).getEntityDescriptor() )
|
||||
.getPropertyIndex( referencedModelPart.getPartName() );
|
||||
toBatchLoad.forEach(
|
||||
(entityKey, parentInstances) -> {
|
||||
final Object instance = context.getSession().internalLoad(
|
||||
|
@ -232,19 +240,14 @@ public class BatchEntitySelectFetchInitializer extends AbstractFetchParentAccess
|
|||
true,
|
||||
referencedModelPart.isInternalLoadNullable()
|
||||
);
|
||||
|
||||
for ( Object parentInstance : parentInstances ) {
|
||||
( (AbstractEntityPersister) referencedModelPart.getDeclaringType() ).setPropertyValue(
|
||||
parentInstance,
|
||||
referencedModelPart.getPartName(),
|
||||
instance
|
||||
);
|
||||
final EntityEntry entry = context.getSession()
|
||||
.getPersistenceContext()
|
||||
.getEntry( parentInstance );
|
||||
referencedModelPart.getPropertyAccess().getSetter().set(parentInstance, instance);
|
||||
final EntityEntry entry = context.getSession().getPersistenceContext().getEntry( parentInstance );
|
||||
if ( entry != null ) {
|
||||
final Object[] loadedState = entry.getLoadedState();
|
||||
if ( loadedState != null ) {
|
||||
loadedState[propertyIndex] = instance;
|
||||
loadedState[0] = instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
package org.hibernate.orm.test.batch;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
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.orm.junit.EntityManagerFactoryScope;
|
||||
import org.hibernate.testing.orm.junit.Jpa;
|
||||
import org.hibernate.testing.orm.junit.Setting;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jakarta.persistence.Embeddable;
|
||||
|
@ -13,56 +20,340 @@ import jakarta.persistence.Embedded;
|
|||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.OneToOne;
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.CriteriaQuery;
|
||||
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
|
||||
@Jpa(
|
||||
annotatedClasses = {
|
||||
EmbeddableBatchingTest.A.class,
|
||||
EmbeddableBatchingTest.C.class
|
||||
EmbeddableBatchingTest.EntityA.class,
|
||||
EmbeddableBatchingTest.EntityB.class,
|
||||
EmbeddableBatchingTest.EntityC.class,
|
||||
EmbeddableBatchingTest.EntityD.class
|
||||
},
|
||||
integrationSettings = { @Setting(name = Environment.DEFAULT_BATCH_FETCH_SIZE, value = "2") }
|
||||
)
|
||||
@TestForIssue(jiraKey = "HHH-15644")
|
||||
class EmbeddableBatchingTest {
|
||||
|
||||
@Test
|
||||
void testSelect(EntityManagerFactoryScope scope) {
|
||||
scope.inEntityManager(
|
||||
private static final EntityC ENTITY_C = new EntityC( 2, true );
|
||||
private static final EntityB ENTITY_B = new EntityB( 1, 2f );
|
||||
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 -> {
|
||||
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||
final CriteriaQuery<A> c = cb.createQuery( A.class );
|
||||
c.from( A.class );
|
||||
entityManager.createQuery( c ).getResultList();
|
||||
entityManager.persist( ENTITY_B );
|
||||
entityManager.persist( ENTITY_C );
|
||||
entityManager.persist( ENTITY_D );
|
||||
entityManager.persist( ENTITY_A );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Entity
|
||||
public static class A {
|
||||
@Test
|
||||
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
|
||||
private Integer id;
|
||||
|
||||
@Embedded
|
||||
private B b;
|
||||
private String name;
|
||||
|
||||
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
|
||||
public static class B {
|
||||
public static class EmbeddableA {
|
||||
|
||||
private String aString;
|
||||
|
||||
@OneToOne
|
||||
private C c;
|
||||
private EntityB entityB;
|
||||
|
||||
public EmbeddableA() {
|
||||
}
|
||||
|
||||
@Entity
|
||||
public static class C {
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
||||
@Embeddable
|
||||
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
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue