Fix ToOneAttributeMapping bidirectionalAttributeName value determination

This commit is contained in:
Andrea Boriero 2021-12-06 14:18:42 +01:00 committed by Andrea Boriero
parent 71204a9e97
commit 7dc70e9350
4 changed files with 65 additions and 34 deletions

View File

@ -31,6 +31,7 @@ import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.ToOne;
import org.hibernate.mapping.Value;
import org.hibernate.metamodel.mapping.AssociationKey;
import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
@ -236,7 +237,10 @@ public class ToOneAttributeMapping
final Iterator<Property> propertyClosureIterator = entityBinding.getPropertyClosureIterator();
while ( propertyClosureIterator.hasNext() ) {
final Property property = propertyClosureIterator.next();
if ( property.getValue() instanceof OneToOne && name.equals( ( (OneToOne) property.getValue() ).getMappedByProperty() ) ) {
if ( property.getValue() instanceof OneToOne
&& name.equals( ( (OneToOne) property.getValue() ).getMappedByProperty() )
&& ( (OneToOne) property.getValue() ).getReferencedEntityName().equals(
declaringType.getJavaTypeDescriptor().getJavaType().getTypeName() ) ) {
bidirectionalAttributeName = property.getName();
break;
}
@ -247,7 +251,11 @@ public class ToOneAttributeMapping
final Iterator<Property> propertyClosureIterator = entityBinding.getPropertyClosureIterator();
while ( propertyClosureIterator.hasNext() ) {
final Property property = propertyClosureIterator.next();
if ( property.getValue() instanceof Collection && name.equals( ( (Collection) property.getValue() ).getMappedByProperty() ) ) {
final Value value = property.getValue();
if ( value instanceof Collection
&& name.equals( ( (Collection) value ).getMappedByProperty() )
&& ( (Collection) value ).getElement().getType().getName()
.equals( declaringType.getJavaTypeDescriptor().getJavaType().getTypeName() ) ) {
bidirectionalAttributeName = property.getName();
break;
}
@ -1046,6 +1054,7 @@ public class ToOneAttributeMapping
final ModelPart bidirectionalModelPart = entityMappingType.findSubPart( bidirectionalAttributeName );
// Add the inverse association key side as well to be able to resolve to a CircularFetch
if ( bidirectionalModelPart instanceof ToOneAttributeMapping ) {
assert bidirectionalModelPart.getPartMappingType() == declaringTableGroupProducer;
final ToOneAttributeMapping bidirectionalAttribute = (ToOneAttributeMapping) bidirectionalModelPart;
final AssociationKey secondKey = bidirectionalAttribute.getForeignKeyDescriptor().getAssociationKey();
if ( creationState.registerVisitedAssociationKey( secondKey ) ) {

View File

@ -15,6 +15,7 @@ import org.hibernate.PropertyAccessException;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.property.access.internal.AbstractFieldSerialForm;
import org.hibernate.proxy.HibernateProxy;
/**
* Field-based implementation of Setter
@ -67,15 +68,19 @@ public class SetterFieldImpl implements Setter {
);
}
else {
final String valueType;
if ( value instanceof HibernateProxy ) {
valueType = ( (HibernateProxy) value ).getHibernateLazyInitializer().getEntityName();
}
else {
valueType = value.getClass().getTypeName();
}
throw new PropertyAccessException(
e,
String.format(
Locale.ROOT,
"Could not set value [%s (`%s`)] by reflection",
value,
value == null ? "<null>" : value.getClass().getTypeName(),
containerClass == null ? "<dynamic>" : containerClass.getTypeName(),
propertyName
"Could not set value of type [%s]",
valueType
),
true,
containerClass,

View File

@ -8,7 +8,6 @@ package org.hibernate.sql.results.graph;
import java.util.List;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.metamodel.mapping.AssociationKey;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.ModelPart;
@ -50,9 +49,7 @@ public interface DomainResultCreationState {
* Resolve the ModelPart associated with a given NavigablePath. More specific ModelParts should be preferred - e.g.
* the SingularAssociationAttributeMapping rather than just the EntityTypeMapping for the associated type
*/
default ModelPart resolveModelPart(NavigablePath navigablePath) {
throw new NotYetImplementedFor6Exception( getClass() );
}
ModelPart resolveModelPart(NavigablePath navigablePath);
/**
* Visit fetches for the given parent.

View File

@ -6,8 +6,11 @@
*/
package org.hibernate.orm.test.loading;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
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.BeforeEach;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
@ -15,16 +18,19 @@ import jakarta.persistence.OneToOne;
import static org.assertj.core.api.Assertions.assertThat;
public class LoadContainedInDoubleContainingTest extends BaseCoreFunctionalTestCase {
@DomainModel(
annotatedClasses = {
LoadContainedInDoubleContainingTest.Containing.class,
LoadContainedInDoubleContainingTest.OtherContained.class,
LoadContainedInDoubleContainingTest.Contained.class
}
)
@SessionFactory
public class LoadContainedInDoubleContainingTest {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { Containing.class, OtherContained.class, Contained.class };
}
@Test
public void test() {
inTransaction( session -> {
@BeforeEach
public void setUp(SessionFactoryScope scope) {
scope.inTransaction( session -> {
Containing containing = new Containing( 1, "initial" );
OtherContained otherContained = new OtherContained( 2, "initial" );
containing.setOtherContained( otherContained );
@ -32,19 +38,20 @@ public class LoadContainedInDoubleContainingTest extends BaseCoreFunctionalTestC
session.persist( containing );
session.persist( otherContained );
} );
}
inTransaction( session -> {
OtherContained entity = session.load( OtherContained.class, 2 );
String text = entity.getText();
@Test
public void test(SessionFactoryScope scope) {
scope.inTransaction( session -> {
OtherContained otherContained = session.load( OtherContained.class, 2 );
String text = otherContained.getText();
assertThat( text ).isEqualTo( "initial" );
} );
}
@Entity(name = Containing.NAME)
@Entity(name = "Containing")
public static class Containing {
static final String NAME = "Containing";
@Id
private Integer id;
private String text;
@ -69,34 +76,39 @@ public class LoadContainedInDoubleContainingTest extends BaseCoreFunctionalTestC
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public Contained getContained() {
return contained;
}
public void setContained(Contained contained) {
this.contained = contained;
}
public OtherContained getOtherContained() {
return otherContained;
}
public void setOtherContained(OtherContained otherContained) {
this.otherContained = otherContained;
}
}
@Entity(name = Contained.NAME)
@Entity(name = "Contained")
public static class Contained {
static final String NAME = "Contained";
@Id
private Integer id;
private String text;
@ -115,28 +127,31 @@ public class LoadContainedInDoubleContainingTest extends BaseCoreFunctionalTestC
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public Containing getContaining() {
return containing;
}
public void setContaining(Containing containing) {
this.containing = containing;
}
}
@Entity(name = OtherContained.NAME)
@Entity(name = "OtherContained")
public static class OtherContained {
static final String NAME = "OtherContained";
@Id
private Integer id;
private String text;
@ -155,18 +170,23 @@ public class LoadContainedInDoubleContainingTest extends BaseCoreFunctionalTestC
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public Containing getContaining() {
return containing;
}
public void setContaining(Containing containing) {
this.containing = containing;
}