Skip to content

Commit 92ea565

Browse files
authored
Merge branch 'hibernate:main' into main-perf
2 parents b0be548 + 8929761 commit 92ea565

File tree

6 files changed

+265
-20
lines changed

6 files changed

+265
-20
lines changed

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

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -877,7 +877,7 @@ static PropertyData getPropertyOverriddenByMapperOrMapsId(
877877
return metadataCollector.getPropertyAnnotatedWithMapsId( mappedClass, isId ? "" : propertyName );
878878
}
879879
}
880-
880+
881881
public static Map<String,String> toAliasTableMap(SqlFragmentAlias[] aliases){
882882
final Map<String,String> ret = new HashMap<>();
883883
for ( SqlFragmentAlias alias : aliases ) {
@@ -887,7 +887,7 @@ public static Map<String,String> toAliasTableMap(SqlFragmentAlias[] aliases){
887887
}
888888
return ret;
889889
}
890-
890+
891891
public static Map<String,String> toAliasEntityMap(SqlFragmentAlias[] aliases){
892892
final Map<String,String> result = new HashMap<>();
893893
for ( SqlFragmentAlias alias : aliases ) {
@@ -1076,7 +1076,8 @@ public static void checkMappedByType(
10761076
String mappedBy,
10771077
Value targetValue,
10781078
String propertyName,
1079-
PropertyHolder propertyHolder) {
1079+
PropertyHolder propertyHolder,
1080+
Map<String, PersistentClass> persistentClasses) {
10801081
final ToOne toOne;
10811082
if ( targetValue instanceof Collection ) {
10821083
toOne = (ToOne) ( (Collection) targetValue ).getElement();
@@ -1085,13 +1086,14 @@ public static void checkMappedByType(
10851086
toOne = (ToOne) targetValue;
10861087
}
10871088
final String referencedEntityName = toOne.getReferencedEntityName();
1088-
PersistentClass referencedClass = propertyHolder.getPersistentClass();
1089-
while ( referencedClass != null ) {
1090-
if ( referencedClass.getEntityName().equals( referencedEntityName ) ) {
1089+
final PersistentClass referencedClass = persistentClasses.get( referencedEntityName );
1090+
PersistentClass ownerClass = propertyHolder.getPersistentClass();
1091+
while ( ownerClass != null ) {
1092+
if ( checkReferencedClass( ownerClass, referencedClass ) ) {
10911093
return;
10921094
}
10931095
else {
1094-
referencedClass = referencedClass.getSuperclass();
1096+
ownerClass = getSuperPersistentClass( ownerClass );
10951097
}
10961098
}
10971099
throw new AnnotationException(
@@ -1101,4 +1103,31 @@ public static void checkMappedByType(
11011103
+ "', expected '" + propertyHolder.getEntityName() + "'"
11021104
);
11031105
}
1106+
1107+
private static boolean checkReferencedClass(PersistentClass ownerClass, PersistentClass referencedClass) {
1108+
while ( referencedClass != null ) {
1109+
// Allow different entity types as long as they map to the same table
1110+
if ( ownerClass.getTable() == referencedClass.getTable() ) {
1111+
return true;
1112+
}
1113+
referencedClass = getSuperPersistentClass( referencedClass );
1114+
}
1115+
return false;
1116+
}
1117+
1118+
private static PersistentClass getSuperPersistentClass(PersistentClass persistentClass) {
1119+
return persistentClass.getSuperclass() != null ? persistentClass.getSuperclass()
1120+
: getSuperPersistentClass( persistentClass.getSuperMappedSuperclass() );
1121+
}
1122+
1123+
private static PersistentClass getSuperPersistentClass(MappedSuperclass mappedSuperclass) {
1124+
if ( mappedSuperclass != null ) {
1125+
final PersistentClass superClass = mappedSuperclass.getSuperPersistentClass();
1126+
if ( superClass != null ) {
1127+
return superClass;
1128+
}
1129+
return getSuperPersistentClass( mappedSuperclass.getSuperMappedSuperclass() );
1130+
}
1131+
return null;
1132+
}
11041133
}

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

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1540,8 +1540,7 @@ public void secondPass(Map<String, PersistentClass> persistentClasses) throws Ma
15401540
* return true if it's a Fk, false if it's an association table
15411541
*/
15421542
protected boolean bindStarToManySecondPass(Map<String, PersistentClass> persistentClasses) {
1543-
final PersistentClass persistentClass = persistentClasses.get( getElementType().getName() );
1544-
if ( noAssociationTable( persistentClass ) ) {
1543+
if ( noAssociationTable( persistentClasses ) ) {
15451544
//this is a foreign key
15461545
bindOneToManySecondPass( persistentClasses );
15471546
return true;
@@ -1553,7 +1552,10 @@ protected boolean bindStarToManySecondPass(Map<String, PersistentClass> persiste
15531552
}
15541553
}
15551554

1556-
private boolean isReversePropertyInJoin(XClass elementType, PersistentClass persistentClass) {
1555+
private boolean isReversePropertyInJoin(
1556+
XClass elementType,
1557+
PersistentClass persistentClass,
1558+
Map<String, PersistentClass> persistentClasses) {
15571559
if ( persistentClass != null && isUnownedCollection() ) {
15581560
final Property mappedByProperty;
15591561
try {
@@ -1566,17 +1568,18 @@ private boolean isReversePropertyInJoin(XClass elementType, PersistentClass pers
15661568
+ "' which does not exist in the target entity '" + elementType.getName() + "'"
15671569
);
15681570
}
1569-
checkMappedByType( mappedBy, mappedByProperty.getValue(), propertyName, propertyHolder );
1571+
checkMappedByType( mappedBy, mappedByProperty.getValue(), propertyName, propertyHolder, persistentClasses );
15701572
return persistentClass.getJoinNumber( mappedByProperty ) != 0;
15711573
}
15721574
else {
15731575
return false;
15741576
}
15751577
}
15761578

1577-
private boolean noAssociationTable(PersistentClass persistentClass) {
1579+
private boolean noAssociationTable(Map<String, PersistentClass> persistentClasses) {
1580+
final PersistentClass persistentClass = persistentClasses.get( getElementType().getName() );
15781581
return persistentClass != null
1579-
&& !isReversePropertyInJoin( getElementType(), persistentClass )
1582+
&& !isReversePropertyInJoin( getElementType(), persistentClass, persistentClasses )
15801583
&& oneToMany
15811584
&& !isExplicitAssociationTable
15821585
&& ( implicitJoinColumn() || explicitForeignJoinColumn() );

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,13 @@ else if ( targetProperty.getValue() instanceof ManyToOne ) {
153153
+ "' of the target entity type '" + oneToOne.getReferencedEntityName()
154154
+ "' which is not a '@OneToOne' or '@ManyToOne' association" );
155155
}
156-
checkMappedByType( mappedBy, targetProperty.getValue(), oneToOne.getPropertyName(), propertyHolder );
156+
checkMappedByType(
157+
mappedBy,
158+
targetProperty.getValue(),
159+
oneToOne.getPropertyName(),
160+
propertyHolder,
161+
persistentClasses
162+
);
157163
}
158164

159165
private void bindTargetManyToOne(

hibernate-core/src/test/java/org/hibernate/orm/test/mapping/mappedBy/ManyToManyMappedByTypeTest.java

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@
1717

1818
import jakarta.persistence.Entity;
1919
import jakarta.persistence.Id;
20+
import jakarta.persistence.Inheritance;
21+
import jakarta.persistence.InheritanceType;
2022
import jakarta.persistence.ManyToMany;
23+
import jakarta.persistence.MappedSuperclass;
24+
import jakarta.persistence.Table;
2125

2226
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
2327
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -29,8 +33,7 @@ public void testCorrect() {
2933
try (StandardServiceRegistry ssr = new StandardServiceRegistryBuilder().build()) {
3034
final MetadataSources metadataSources = new MetadataSources( ssr )
3135
.addAnnotatedClass( EntityACorrect.class )
32-
.addAnnotatedClass( EntityBCorrect.class )
33-
.addAnnotatedClass( EntityC.class );
36+
.addAnnotatedClass( EntityBCorrect.class );
3437
assertDoesNotThrow( () -> metadataSources.buildMetadata() );
3538
}
3639
}
@@ -57,7 +60,35 @@ public void testCorrectSuperclass() {
5760
}
5861
}
5962

63+
@Test
64+
public void testCorrectSameTable() {
65+
// Allow different entity types which map to the same table since the mappedBy
66+
// in that case would still make sense from a database perspective
67+
try (StandardServiceRegistry ssr = new StandardServiceRegistryBuilder().build()) {
68+
final MetadataSources metadataSources = new MetadataSources( ssr )
69+
.addAnnotatedClass( EntityACorrect.class )
70+
.addAnnotatedClass( EntityBCorrect.class )
71+
.addAnnotatedClass( EntityA2Correct.class );
72+
assertDoesNotThrow( () -> metadataSources.buildMetadata() );
73+
}
74+
}
75+
76+
@Test
77+
public void testCorrectSubtype() {
78+
// Allow mappedBy subtypes given that users might want to filter the
79+
// association with custom @Where annotations and still use a supertype
80+
try (StandardServiceRegistry ssr = new StandardServiceRegistryBuilder().build()) {
81+
final MetadataSources metadataSources = new MetadataSources( ssr )
82+
.addAnnotatedClass( EntityASupertype.class )
83+
.addAnnotatedClass( EntityAMappedSuperclass.class )
84+
.addAnnotatedClass( EntityASubtype.class )
85+
.addAnnotatedClass( EntityBSubtype.class );
86+
assertDoesNotThrow( () -> metadataSources.buildMetadata() );
87+
}
88+
}
89+
6090
@Entity( name = "EntityACorrect" )
91+
@Table( name = "entity_a_correct" )
6192
public static class EntityACorrect {
6293
@Id
6394
private Long id;
@@ -75,6 +106,43 @@ public static class EntityBCorrect {
75106
private List<EntityACorrect> parents;
76107
}
77108

109+
@Entity( name = "EntityA2Correct" )
110+
@Table( name = "entity_a_correct" )
111+
public static class EntityA2Correct {
112+
@Id
113+
private Long id;
114+
115+
@ManyToMany( mappedBy = "parents" )
116+
private List<EntityBCorrect> children;
117+
}
118+
119+
@Entity( name = "EntityASupertype" )
120+
@Inheritance( strategy = InheritanceType.TABLE_PER_CLASS )
121+
public static class EntityASupertype {
122+
@Id
123+
private Long id;
124+
125+
@ManyToMany( mappedBy = "parents" )
126+
private List<EntityBSubtype> children;
127+
}
128+
129+
@MappedSuperclass
130+
public static class EntityAMappedSuperclass extends EntityASupertype {
131+
}
132+
133+
@Entity( name = "EntityASubtype" )
134+
public static class EntityASubtype extends EntityAMappedSuperclass {
135+
}
136+
137+
@Entity( name = "EntityBSubtype" )
138+
public static class EntityBSubtype {
139+
@Id
140+
private Long id;
141+
142+
@ManyToMany
143+
private List<EntityASubtype> parents;
144+
}
145+
78146
@Entity( name = "EntityAWrong" )
79147
public static class EntityAWrong {
80148
@Id
@@ -106,6 +174,7 @@ public static class SubclassEntity extends SuperclassEntity {
106174
}
107175

108176
@Entity( name = "SuperclassEntity" )
177+
@Inheritance( strategy = InheritanceType.TABLE_PER_CLASS )
109178
public static class SuperclassEntity {
110179
@Id
111180
private Long id;

hibernate-core/src/test/java/org/hibernate/orm/test/mapping/mappedBy/OneToManyMappedByTypeTest.java

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,12 @@
1717

1818
import jakarta.persistence.Entity;
1919
import jakarta.persistence.Id;
20+
import jakarta.persistence.Inheritance;
21+
import jakarta.persistence.InheritanceType;
2022
import jakarta.persistence.ManyToOne;
23+
import jakarta.persistence.MappedSuperclass;
2124
import jakarta.persistence.OneToMany;
25+
import jakarta.persistence.Table;
2226

2327
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
2428
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -30,8 +34,7 @@ public void testCorrect() {
3034
try (StandardServiceRegistry ssr = new StandardServiceRegistryBuilder().build()) {
3135
final MetadataSources metadataSources = new MetadataSources( ssr )
3236
.addAnnotatedClass( EntityACorrect.class )
33-
.addAnnotatedClass( EntityBCorrect.class )
34-
.addAnnotatedClass( EntityC.class );
37+
.addAnnotatedClass( EntityBCorrect.class );
3538
assertDoesNotThrow( () -> metadataSources.buildMetadata() );
3639
}
3740
}
@@ -58,7 +61,35 @@ public void testCorrectSuperclass() {
5861
}
5962
}
6063

64+
@Test
65+
public void testCorrectSameTable() {
66+
// Allow different entity types which map to the same table since the mappedBy
67+
// in that case would still make sense from a database perspective
68+
try (StandardServiceRegistry ssr = new StandardServiceRegistryBuilder().build()) {
69+
final MetadataSources metadataSources = new MetadataSources( ssr )
70+
.addAnnotatedClass( EntityACorrect.class )
71+
.addAnnotatedClass( EntityBCorrect.class )
72+
.addAnnotatedClass( EntityA2Correct.class );
73+
assertDoesNotThrow( () -> metadataSources.buildMetadata() );
74+
}
75+
}
76+
77+
@Test
78+
public void testCorrectSubtype() {
79+
// Allow mappedBy subtypes given that users might want to filter the
80+
// association with custom @Where annotations and still use a supertype
81+
try (StandardServiceRegistry ssr = new StandardServiceRegistryBuilder().build()) {
82+
final MetadataSources metadataSources = new MetadataSources( ssr )
83+
.addAnnotatedClass( EntityASupertype.class )
84+
.addAnnotatedClass( EntityAMappedSuperclass.class )
85+
.addAnnotatedClass( EntityASubtype.class )
86+
.addAnnotatedClass( EntityBSubtype.class );
87+
assertDoesNotThrow( () -> metadataSources.buildMetadata() );
88+
}
89+
}
90+
6191
@Entity( name = "EntityACorrect" )
92+
@Table( name = "entity_a_correct" )
6293
public static class EntityACorrect {
6394
@Id
6495
private Long id;
@@ -76,6 +107,43 @@ public static class EntityBCorrect {
76107
private EntityACorrect parent;
77108
}
78109

110+
@Entity( name = "EntityA2Correct" )
111+
@Table( name = "entity_a_correct" )
112+
public static class EntityA2Correct {
113+
@Id
114+
private Long id;
115+
116+
@OneToMany( mappedBy = "parent" )
117+
private List<EntityBCorrect> children;
118+
}
119+
120+
@Entity( name = "EntityASupertype" )
121+
@Inheritance( strategy = InheritanceType.TABLE_PER_CLASS )
122+
public static class EntityASupertype {
123+
@Id
124+
private Long id;
125+
126+
@OneToMany( mappedBy = "parent" )
127+
private List<EntityBSubtype> children;
128+
}
129+
130+
@MappedSuperclass
131+
public static class EntityAMappedSuperclass extends EntityASupertype {
132+
}
133+
134+
@Entity( name = "EntityASubtype" )
135+
public static class EntityASubtype extends EntityAMappedSuperclass {
136+
}
137+
138+
@Entity( name = "EntityBSubtype" )
139+
public static class EntityBSubtype {
140+
@Id
141+
private Long id;
142+
143+
@ManyToOne
144+
private EntityASubtype parent;
145+
}
146+
79147
@Entity( name = "EntityAWrong" )
80148
public static class EntityAWrong {
81149
@Id
@@ -107,6 +175,7 @@ public static class SubclassEntity extends SuperclassEntity {
107175
}
108176

109177
@Entity( name = "SuperclassEntity" )
178+
@Inheritance( strategy = InheritanceType.TABLE_PER_CLASS )
110179
public static class SuperclassEntity {
111180
@Id
112181
private Long id;

0 commit comments

Comments
 (0)