Skip to content

Commit 33cfc7f

Browse files
committed
HHH-19542: Use either the secondary table of a component, by looking at the first @column available (to avoid nested component to be picked up), or fall-back on the property holder table if no secondary table is defined.
1 parent 4843cfc commit 33cfc7f

File tree

7 files changed

+240
-2
lines changed

7 files changed

+240
-2
lines changed

hibernate-core/src/main/java/org/hibernate/boot/model/internal/ClassPropertyHolder.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,15 @@ public Table getTable() {
457457
return persistentClass.getTable();
458458
}
459459

460+
@Override
461+
public Table getSecondaryTable(String tableName) {
462+
final Join join = persistentClass.getSecondaryTable( tableName );
463+
if ( join != null) {
464+
return join.getTable();
465+
}
466+
return null;
467+
}
468+
460469
@Override
461470
public boolean isComponent() {
462471
return false;

hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionPropertyHolder.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,11 @@ public Table getTable() {
260260
return collection.getCollectionTable();
261261
}
262262

263+
@Override
264+
public Table getSecondaryTable(String tableName) {
265+
throw new AssertionFailure( "Cannot get secondary table for collection" );
266+
}
267+
263268
@Override
264269
public void addProperty(Property prop, MemberDetails memberDetails, ClassDetails declaringClass) {
265270
throw new AssertionFailure( "Cannot add property to a collection" );

hibernate-core/src/main/java/org/hibernate/boot/model/internal/ComponentPropertyHolder.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,11 @@ public Table getTable() {
273273
return component.getTable();
274274
}
275275

276+
@Override
277+
public Table getSecondaryTable(String tableName) {
278+
return parent.getSecondaryTable(tableName);
279+
}
280+
276281
@Override
277282
public void addProperty(Property prop, MemberDetails attributeMemberDetails, ClassDetails declaringClass) {
278283
handleGenericComponentProperty( prop, attributeMemberDetails, getContext() );

hibernate-core/src/main/java/org/hibernate/boot/model/internal/EmbeddableBinder.java

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import org.hibernate.mapping.Selectable;
4242
import org.hibernate.mapping.SimpleValue;
4343
import org.hibernate.mapping.SingleTableSubclass;
44+
import org.hibernate.mapping.Table;
4445
import org.hibernate.mapping.Value;
4546
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
4647
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
@@ -514,6 +515,12 @@ static Component fillEmbeddable(
514515
subclassToSuperclass
515516
);
516517

518+
// //sort elements according to the ComponentPropertyHolder
519+
// classElements.sort( EmbeddableBinder::embeddableLast );
520+
// if ( ReflectHelper.isRecord(component.getComponentClass()) ) {
521+
// component.setSimpleRecord( true );
522+
// }
523+
517524
if ( component.isPolymorphic() ) {
518525
validateInheritanceIsSupported( subholder, compositeUserType );
519526
final BasicType<?> discriminatorType = (BasicType<?>) component.getDiscriminator().getType();
@@ -755,6 +762,14 @@ private static List<PropertyData> collectClassElements(
755762
return classElements;
756763
}
757764

765+
private static int embeddableLast(
766+
PropertyData elementA,
767+
PropertyData elementB) {
768+
final boolean elementAEmbeddable = elementA.getAttributeMember().getType().determineRawClass().hasDirectAnnotationUsage( Embeddable.class );
769+
final boolean elementBEmbeddable = elementB.getAttributeMember().getType().determineRawClass().hasDirectAnnotationUsage( Embeddable.class );
770+
return Boolean.compare( elementAEmbeddable, elementBEmbeddable );
771+
}
772+
758773
private static void collectSubclassElements(
759774
AccessType propertyAccessor,
760775
MetadataBuildingContext context,
@@ -981,8 +996,7 @@ static Component createEmbeddable(
981996
MetadataBuildingContext context) {
982997
final Component component = new Component( context, propertyHolder.getPersistentClass() );
983998
component.setEmbedded( isComponentEmbedded );
984-
//yuk
985-
component.setTable( propertyHolder.getTable() );
999+
component.setTable( resolveTable( propertyHolder, inferredData ) );
9861000
if ( isIdentifierMapper
9871001
|| isComponentEmbedded && inferredData.getPropertyName() == null ) {
9881002
component.setComponentClassName( component.getOwner().getClassName() );
@@ -998,11 +1012,31 @@ static Component createEmbeddable(
9981012
if ( propertyHolder.isComponent() ) {
9991013
final ComponentPropertyHolder componentPropertyHolder = (ComponentPropertyHolder) propertyHolder;
10001014
component.setParentAggregateColumn( componentPropertyHolder.getAggregateColumn() );
1015+
10011016
}
10021017
applyColumnNamingPattern( component, inferredData );
10031018
return component;
10041019
}
10051020

1021+
private static Table resolveTable(
1022+
PropertyHolder propertyHolder,
1023+
PropertyData inferredData) {
1024+
1025+
for ( FieldDetails fieldDetails : inferredData.getPropertyType().determineRawClass().getFields() ) {
1026+
if ( fieldDetails.hasDirectAnnotationUsage( Column.class ) ) {
1027+
final String tableName = fieldDetails.getDirectAnnotationUsage( Column.class ).table();
1028+
if ( tableName != null ) {
1029+
final Table secondaryTable = propertyHolder.getSecondaryTable( tableName );
1030+
if ( secondaryTable != null ) {
1031+
return secondaryTable;
1032+
}
1033+
}
1034+
}
1035+
}
1036+
1037+
return propertyHolder.getTable();
1038+
}
1039+
10061040
private static void applyColumnNamingPattern(Component component, PropertyData inferredData) {
10071041
final Class<?> componentClass = component.getComponentClass();
10081042
if ( componentClass == null || Map.class.equals( componentClass ) ) {

hibernate-core/src/main/java/org/hibernate/boot/model/internal/PropertyHolder.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ public interface PropertyHolder {
3131

3232
Table getTable();
3333

34+
Table getSecondaryTable(String tableName);
35+
3436
void addProperty(Property prop, MemberDetails memberDetails, ClassDetails declaringClass);
3537

3638
void addProperty(Property prop, MemberDetails memberDetails, AnnotatedColumns columns, ClassDetails declaringClass);
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.orm.test.embeddable;
6+
7+
import jakarta.persistence.*;
8+
import org.hibernate.testing.orm.junit.DomainModel;
9+
import org.hibernate.testing.orm.junit.JiraKey;
10+
import org.hibernate.testing.orm.junit.SessionFactory;
11+
import org.hibernate.testing.orm.junit.SessionFactoryScope;
12+
import org.junit.jupiter.api.BeforeAll;
13+
import org.junit.jupiter.api.Test;
14+
15+
import static org.assertj.core.api.Assertions.assertThat;
16+
17+
@JiraKey("HHH-19542")
18+
@DomainModel(annotatedClasses = {
19+
EmbeddableAsSecondaryTableTest.UserEntity.class
20+
})
21+
@SessionFactory
22+
public class EmbeddableAsSecondaryTableTest {
23+
24+
private UserEntity user;
25+
26+
@BeforeAll
27+
public void prepare(SessionFactoryScope scope) {
28+
scope.inTransaction( session -> {
29+
Person person = new Person( new FullName( "Sylvain", "Lecoy" ), 38 );
30+
user = new UserEntity( person );
31+
session.persist( user );
32+
} );
33+
}
34+
35+
@Test
36+
public void test(SessionFactoryScope scope) {
37+
scope.inTransaction(session -> {
38+
UserEntity entity = session.find( UserEntity.class, user.id );
39+
assertThat( entity ).isNotNull();
40+
assertThat( entity.id ).isEqualTo( entity.id );
41+
assertThat( entity.person ).isNotNull();
42+
assertThat( entity.person.zAge ).isEqualTo( 38 );
43+
assertThat( entity.person.fullName.firstName ).isEqualTo( "Sylvain" );
44+
assertThat( entity.person.fullName.lastName ).isEqualTo( "Lecoy" );
45+
});
46+
}
47+
48+
@Entity
49+
@SecondaryTable(name = "Person")
50+
public static class UserEntity {
51+
@Id
52+
@GeneratedValue
53+
private Integer id;
54+
private Person person;
55+
56+
public UserEntity(
57+
final Person person) {
58+
this.person = person;
59+
}
60+
61+
protected UserEntity() {
62+
63+
}
64+
}
65+
66+
@Embeddable
67+
public static class Person {
68+
69+
@Column(table = "Person")
70+
private Integer zAge;
71+
72+
private FullName fullName;
73+
74+
public Person(final FullName fullName, final Integer age) {
75+
this.fullName = fullName;
76+
this.zAge = age;
77+
}
78+
79+
protected Person() {
80+
}
81+
}
82+
83+
@Embeddable
84+
public static class FullName {
85+
86+
private String firstName;
87+
88+
private String lastName;
89+
90+
public FullName(final String firstName, final String lastName) {
91+
this.firstName = firstName;
92+
this.lastName = lastName;
93+
}
94+
95+
protected FullName() {
96+
}
97+
}
98+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.orm.test.records;
6+
7+
import jakarta.persistence.Column;
8+
import jakarta.persistence.Embeddable;
9+
import jakarta.persistence.Entity;
10+
import jakarta.persistence.GeneratedValue;
11+
import jakarta.persistence.Id;
12+
import jakarta.persistence.SecondaryTable;
13+
import org.hibernate.testing.orm.junit.DomainModel;
14+
import org.hibernate.testing.orm.junit.JiraKey;
15+
import org.hibernate.testing.orm.junit.SessionFactory;
16+
import org.hibernate.testing.orm.junit.SessionFactoryScope;
17+
import org.junit.jupiter.api.BeforeAll;
18+
import org.junit.jupiter.api.Test;
19+
20+
import static org.assertj.core.api.Assertions.assertThat;
21+
22+
@JiraKey("HHH-19542")
23+
@DomainModel(annotatedClasses = {
24+
RecordEmbeddedAsSecondaryTableTest.UserEntity.class
25+
})
26+
@SessionFactory
27+
public class RecordEmbeddedAsSecondaryTableTest {
28+
29+
private UserEntity user;
30+
31+
@BeforeAll
32+
public void prepare(SessionFactoryScope scope) {
33+
scope.inTransaction( session -> {
34+
Person person = new Person( new FullName( "Sylvain", "Lecoy" ), 38 );
35+
user = new UserEntity( person );
36+
session.persist( user );
37+
} );
38+
}
39+
40+
@Test
41+
public void test(SessionFactoryScope scope) {
42+
scope.inTransaction(session -> {
43+
UserEntity entity = session.find( UserEntity.class, user.id );
44+
assertThat( entity ).isNotNull();
45+
assertThat( entity.id ).isEqualTo( user.id );
46+
assertThat( entity.person ).isNotNull();
47+
assertThat( entity.person.age ).isEqualTo( 38 );
48+
assertThat( entity.person.fullName.firstName ).isEqualTo( "Sylvain" );
49+
assertThat( entity.person.fullName.lastName ).isEqualTo( "Lecoy" );
50+
});
51+
}
52+
53+
@Entity
54+
@SecondaryTable(name = "Person")
55+
public static class UserEntity {
56+
@Id
57+
@GeneratedValue
58+
private Integer id;
59+
private Person person;
60+
61+
public UserEntity(
62+
final Person person) {
63+
this.person = person;
64+
}
65+
66+
protected UserEntity() {
67+
68+
}
69+
}
70+
71+
@Embeddable
72+
public record Person(
73+
FullName fullName,
74+
@Column(table = "Person")
75+
Integer age) {
76+
77+
}
78+
79+
@Embeddable
80+
public record FullName(
81+
String firstName,
82+
String lastName) {
83+
84+
}
85+
}

0 commit comments

Comments
 (0)