HHH-16514 - Property sorting can lead to incorrect column mappings with derived embeddable keys

(cherry picked from commit 60b6fe3d9f)
This commit is contained in:
Steve Ebersole 2023-04-26 17:54:23 -05:00
parent 26ba40365f
commit 1ead5d2b37
4 changed files with 134 additions and 3 deletions

View File

@ -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 ) {

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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" );
} );
}
}