HHH-15604 Identically-named association in entity root and elementcollection of embeddables leads to assertion error

This commit is contained in:
Andrea Boriero 2023-01-12 15:19:08 +01:00 committed by Christian Beikov
parent b774f80ce9
commit 2143ced49e
2 changed files with 277 additions and 1 deletions

View File

@ -1009,7 +1009,7 @@ public class ToOneAttributeMapping
} }
return false; return false;
} }
else if ( parentNavigablePath.getParent() != null && creationState.resolveModelPart( parentNavigablePath.getParent() ) instanceof EmbeddedCollectionPart ) {//todo: handle recursively? else if ( isParentEmbeddedCollectionPart( creationState, parentNavigablePath.getParent() ) ) {
/* /*
class EntityA{ class EntityA{
@OneToOne(mappedBy = "identicallyNamedAssociation", fetch = FetchType.EAGER) @OneToOne(mappedBy = "identicallyNamedAssociation", fetch = FetchType.EAGER)
@ -1081,6 +1081,22 @@ public class ToOneAttributeMapping
return parentNavigablePath.isSuffix( bidirectionalAttributePath ); return parentNavigablePath.isSuffix( bidirectionalAttributePath );
} }
private boolean isParentEmbeddedCollectionPart(DomainResultCreationState creationState, NavigablePath parentNavigablePath) {
while ( parentNavigablePath != null ) {
final ModelPart parentModelPart = creationState.resolveModelPart( parentNavigablePath );
if ( parentModelPart instanceof EmbeddedCollectionPart ) {
return true;
}
else if ( parentModelPart instanceof EmbeddableValuedModelPart ) {
parentNavigablePath = parentNavigablePath.getParent();
}
else {
return false;
}
}
return false;
}
private Fetch createCircularBiDirectionalFetch( private Fetch createCircularBiDirectionalFetch(
NavigablePath fetchablePath, NavigablePath fetchablePath,
FetchParent fetchParent, FetchParent fetchParent,

View File

@ -0,0 +1,260 @@
package org.hibernate.orm.test.mapping.embeddable.elementcollection;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.testing.jdbc.SQLStatementInspector;
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.BeforeAll;
import org.junit.jupiter.api.Test;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToOne;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
@DomainModel(
annotatedClasses = {
EmbeddedElementCollectionWithIdenticallyNamedAssociation2Test.EntityA.class,
EmbeddedElementCollectionWithIdenticallyNamedAssociation2Test.EntityB.class,
}
)
@SessionFactory(
statementInspectorClass = SQLStatementInspector.class
)
public class EmbeddedElementCollectionWithIdenticallyNamedAssociation2Test {
@BeforeAll
public void setUp(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
EntityA entityA1 = new EntityA( 1, "Fab" );
EntityA entityA2 = new EntityA( 2, "And" );
EntityB entityB = new EntityB( 1, "Chris" );
ElementCollectionHolder elementCollectionHolder = new ElementCollectionHolder();
EmbeddableB embeddableB = new EmbeddableB( entityA2 );
elementCollectionHolder.addElementCollection( embeddableB );
entityB.setElementCollectionHolder( elementCollectionHolder );
entityB.setNested( new IdenticallyNamedAssociationHolder( entityA1 ) );
entityA1.setB( entityB );
session.persist( entityA1 );
session.persist( entityA2 );
session.persist( entityB );
}
);
}
@Test
public void testGet(SessionFactoryScope scope) {
SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector();
statementInspector.clear();
scope.inTransaction(
session -> {
EntityA entityA = session.get( EntityA.class, 1 );
assertThat( entityA ).isNotNull();
EntityB entityB = entityA.getB();
assertThat( entityB.getNested().getIdenticallyNamedAssociation() ).isEqualTo( entityA );
Set<EmbeddableB> elementCollection = entityB.getElementCollectionHolder().getElementCollection();
assertThat( elementCollection.size() ).isEqualTo( 1 );
EmbeddableB embeddableB = elementCollection.iterator().next();
EntityA identicallyNamedAssociation = embeddableB.getNested().getIdenticallyNamedAssociation();
assertThat( identicallyNamedAssociation ).isNotEqualTo( entityA );
assertThat( identicallyNamedAssociation.getId()).isEqualTo( 2 );
assertThat( identicallyNamedAssociation.getB() ).isNull();
assertThat( statementInspector.getSqlQueries().size() ).isEqualTo( 2 );
assertThat( statementInspector.getNumberOfJoins( 0 ) ).isEqualTo( 3 );
assertThat( statementInspector.getNumberOfJoins( 1 ) ).isEqualTo( 4 );
}
);
}
@Entity(name = "EntityA")
public static class EntityA {
@Id
int id;
String name;
@OneToOne(mappedBy = "nested.identicallyNamedAssociation", fetch = FetchType.EAGER)
EntityB b;
public EntityA() {
}
public EntityA(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public EntityB getB() {
return b;
}
public void setB(EntityB b) {
this.b = b;
}
}
@Entity(name = "EntityB")
public static class EntityB {
@Id
int id;
String name;
@Embedded
IdenticallyNamedAssociationHolder nested;
@Embedded
ElementCollectionHolder elementCollectionHolder;
public EntityB() {
}
public EntityB(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public ElementCollectionHolder getElementCollectionHolder() {
return elementCollectionHolder;
}
public void setElementCollectionHolder(ElementCollectionHolder elementCollectionHolder) {
this.elementCollectionHolder = elementCollectionHolder;
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public IdenticallyNamedAssociationHolder getNested() {
return nested;
}
public void setNested(IdenticallyNamedAssociationHolder nested) {
this.nested = nested;
}
}
@Embeddable
public static class IdenticallyNamedAssociationHolder {
@OneToOne
@JoinColumn(name = "entityA_id")
EntityA identicallyNamedAssociation;
public IdenticallyNamedAssociationHolder() {
}
public IdenticallyNamedAssociationHolder(EntityA identicallyNamedAssociation) {
this.identicallyNamedAssociation = identicallyNamedAssociation;
}
public EntityA getIdenticallyNamedAssociation() {
return identicallyNamedAssociation;
}
}
@Embeddable
public static class ElementCollectionHolder {
@ElementCollection(fetch = FetchType.EAGER)
Set<EmbeddableB> elementCollection = new HashSet<>();
public ElementCollectionHolder() {
}
public Set<EmbeddableB> getElementCollection() {
return elementCollection;
}
public void setElementCollection(Set<EmbeddableB> elementCollection) {
this.elementCollection = elementCollection;
}
public void addElementCollection(EmbeddableB embeddableB) {
this.elementCollection.add( embeddableB );
}
}
@Embeddable
public static class EmbeddableB {
@Embedded
NestedEmbeddableB nested;
public EmbeddableB() {
}
public EmbeddableB(EntityA identicallyNamedAssociation) {
this.nested = new NestedEmbeddableB( identicallyNamedAssociation );
}
public NestedEmbeddableB getNested() {
return nested;
}
}
@Embeddable
public static class NestedEmbeddableB {
@OneToOne
@JoinColumn(name = "entityA_id")
EntityA identicallyNamedAssociation;
public NestedEmbeddableB() {
}
public NestedEmbeddableB(EntityA identicallyNamedAssociation) {
this.identicallyNamedAssociation = identicallyNamedAssociation;
}
public EntityA getIdenticallyNamedAssociation() {
return identicallyNamedAssociation;
}
}
}