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.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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue