diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/TableBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/TableBinder.java index 7b8083dff4..da75e5f32d 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/TableBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/TableBinder.java @@ -57,7 +57,6 @@ import static org.hibernate.internal.util.collections.CollectionHelper.isNotEmpt * @author Emmanuel Bernard */ public class TableBinder { - //TODO move it to a getter/setter strategy private static final CoreMessageLogger LOG = Logger.getMessageLogger( CoreMessageLogger.class, TableBinder.class.getName() ); private MetadataBuildingContext buildingContext; @@ -550,6 +549,12 @@ public class TableBinder { // if columns are implicit, then create the columns based // on the referenced entity id columns bindImplicitColumns( referencedEntity, joinColumns, value ); + if ( value instanceof ToOne ) { + // in the case of implicit foreign-keys, make sure the columns making up + // the foreign-key do not get resorted since the order is already properly + // ascertained from the referenced identifier + ( (ToOne) value ).setSorted( true ); + } } else { bindExplicitColumns( referencedEntity, joinColumns, value, buildingContext, associatedClass ); @@ -586,7 +591,6 @@ public class TableBinder { PersistentClass associatedClass) { //implicit case, we hope PK and FK columns are in the same order if ( joinColumns.getColumns().size() != referencedEntity.getIdentifier().getColumnSpan() ) { - // TODO: what about secondary tables?? associatedClass is null? throw new AnnotationException( "An association that targets entity '" + referencedEntity.getEntityName() + "' from entity '" + associatedClass.getEntityName() @@ -669,7 +673,7 @@ public class TableBinder { columns = referencedTable.getPrimaryKey().getColumns(); break; } - catch ( MappingException i ) { + catch (MappingException ignore) { } } if ( referencedColumn == null ) { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/foreignkeys/sorting/A.java b/hibernate-core/src/test/java/org/hibernate/orm/test/foreignkeys/sorting/A.java new file mode 100644 index 0000000000..1e26aee586 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/foreignkeys/sorting/A.java @@ -0,0 +1,34 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html. + */ +package org.hibernate.orm.test.foreignkeys.sorting; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; + +/** + * @author Steve Ebersole + */ +@Entity +public class A { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + + @ManyToOne(cascade = CascadeType.PERSIST) + B b; + + public A() { + } + + public A(B b) { + this.b = b; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/foreignkeys/sorting/B.java b/hibernate-core/src/test/java/org/hibernate/orm/test/foreignkeys/sorting/B.java new file mode 100644 index 0000000000..595431fabf --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/foreignkeys/sorting/B.java @@ -0,0 +1,51 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html. + */ +package org.hibernate.orm.test.foreignkeys.sorting; + +import java.math.BigDecimal; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.Id; + +/** + * @author Steve Ebersole + */ +@Entity +public class B { + enum Type {ONE,ANOTHER} + + @Id + @Column(length = 12) + String code; + + @Id + @Column(precision = 10, scale = 0) + BigDecimal cost; + + @Id + @Enumerated(EnumType.STRING) + Type type; + + @Id + long id; + + String name; + + public B() { + } + + public B(String code, BigDecimal cost, Type type, long id, String name) { + this.code = code; + this.cost = cost; + this.type = type; + this.id = id; + this.name = name; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/foreignkeys/sorting/ForeignKeyColumnSortingTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/foreignkeys/sorting/ForeignKeyColumnSortingTests.java new file mode 100644 index 0000000000..a2081651be --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/foreignkeys/sorting/ForeignKeyColumnSortingTests.java @@ -0,0 +1,42 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html. + */ +package org.hibernate.orm.test.foreignkeys.sorting; + +import java.math.BigDecimal; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Steve Ebersole + */ +@DomainModel(annotatedClasses = { A.class, B.class }) +@SessionFactory +public class ForeignKeyColumnSortingTests { + @Test + @Jira( "https://hibernate.atlassian.net/browse/HHH-16514" ) + public void testDerivedCompositeFk(SessionFactoryScope sfScope) { + + sfScope.inTransaction( (session) -> { + final B b = new B( "abc", new BigDecimal( 1 ), B.Type.ONE, 1L, "tester" ); + session.persist( b ); + session.persist( new A( b ) ); + } ); + + sfScope.inTransaction( (session) -> { + final A loaded = session.get( A.class, 1 ); + assertThat( loaded ).isNotNull(); + assertThat( loaded.b ).isNotNull(); + assertThat( loaded.b.name ).isEqualTo( "tester" ); + } ); + } +}