Skip to content

Commit 858e131

Browse files
committed
HHH-17228 - apply the discriminator for 1-1 mappings to single-table-inheritance subclass attributes
Signed-off-by: Jan Schatteman <[email protected]>
1 parent 2be7461 commit 858e131

File tree

4 files changed

+329
-0
lines changed

4 files changed

+329
-0
lines changed

hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ToOneAttributeMapping.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1978,6 +1978,13 @@ public TableGroupJoin createTableGroupJoin(
19781978
true,
19791979
creationState
19801980
);
1981+
if ( getAssociatedEntityMappingType().getSuperMappingType() != null && !creationState.supportsEntityNameUsage() ) {
1982+
getAssociatedEntityMappingType().applyDiscriminator(
1983+
join::applyPredicate,
1984+
sideNature == ForeignKeyDescriptor.Nature.TARGET ? keyTableReference.getIdentificationVariable() : targetTableReference.getIdentificationVariable(),
1985+
tableGroup,
1986+
creationState );
1987+
}
19811988
}
19821989
);
19831990

hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3042,6 +3042,11 @@ private void registerPathAttributeEntityNameUsage(SqmPath<?> sqmPath, TableGroup
30423042
}
30433043
}
30443044

3045+
@Override
3046+
public boolean supportsEntityNameUsage() {
3047+
return true;
3048+
}
3049+
30453050
@Override
30463051
public void registerEntityNameUsage(
30473052
TableGroup tableGroup,

hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlAstCreationState.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,9 @@ default void registerEntityNameUsage(
4242
String hibernateEntityName) {
4343
// No-op
4444
}
45+
46+
@Internal
47+
default boolean supportsEntityNameUsage() {
48+
return false;
49+
}
4550
}
Lines changed: 312 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,312 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
package org.hibernate.orm.test.onetoone.singletable;
8+
9+
import java.util.List;
10+
11+
import org.hibernate.dialect.SybaseDialect;
12+
import org.hibernate.exception.ConstraintViolationException;
13+
14+
import org.hibernate.testing.orm.junit.DomainModel;
15+
import org.hibernate.testing.orm.junit.JiraKey;
16+
import org.hibernate.testing.orm.junit.JiraKeyGroup;
17+
import org.hibernate.testing.orm.junit.SessionFactory;
18+
import org.hibernate.testing.orm.junit.SessionFactoryScope;
19+
import org.hibernate.testing.orm.junit.SkipForDialect;
20+
import org.junit.jupiter.api.AfterEach;
21+
import org.junit.jupiter.api.Test;
22+
23+
import jakarta.persistence.CascadeType;
24+
import jakarta.persistence.DiscriminatorColumn;
25+
import jakarta.persistence.DiscriminatorValue;
26+
import jakarta.persistence.Entity;
27+
import jakarta.persistence.FetchType;
28+
import jakarta.persistence.Id;
29+
import jakarta.persistence.Inheritance;
30+
import jakarta.persistence.InheritanceType;
31+
import jakarta.persistence.JoinColumn;
32+
import jakarta.persistence.ManyToOne;
33+
import jakarta.persistence.OneToOne;
34+
35+
import static org.junit.jupiter.api.Assertions.assertEquals;
36+
import static org.junit.jupiter.api.Assertions.assertNull;
37+
import static org.junit.jupiter.api.Assertions.assertThrows;
38+
39+
/**
40+
* @author Jan Schatteman
41+
*/
42+
@DomainModel(
43+
annotatedClasses = {
44+
SingleTableOneToOneTest.Container1.class, SingleTableOneToOneTest.Container2.class, SingleTableOneToOneTest.BaseClass.class, SingleTableOneToOneTest.SubClass2.class, SingleTableOneToOneTest.SubClass1.class
45+
}
46+
)
47+
@SessionFactory
48+
@JiraKeyGroup( value = {
49+
@JiraKey( value = "HHH-16916"),
50+
@JiraKey( value = "HHH-17228")
51+
} )
52+
public class SingleTableOneToOneTest {
53+
54+
@AfterEach
55+
public void tearDown(SessionFactoryScope scope) {
56+
scope.inTransaction(
57+
session -> {
58+
session.createMutationQuery( "delete from SubClass2" ).executeUpdate();
59+
session.createMutationQuery( "delete from SubClass1" ).executeUpdate();
60+
session.createMutationQuery( "delete from Container1" ).executeUpdate();
61+
session.createMutationQuery( "delete from Container2" ).executeUpdate();
62+
}
63+
);
64+
}
65+
66+
@Test
67+
@SkipForDialect(dialectClass = SybaseDialect.class, matchSubTypes = true, reason = "Sybase ignores unique constraints on nullable columns")
68+
public void testMultipleRelationshipsOnSingleTableInheritanceWronglyMappedAsOneToOne(SessionFactoryScope scope) {
69+
assertThrows(
70+
ConstraintViolationException.class,
71+
() -> scope.inTransaction(
72+
session -> {
73+
SubClass1 subClass11 = new SubClass1();
74+
subClass11.setId(11L);
75+
76+
SubClass2 subClass21 = new SubClass2();
77+
subClass21.setId(21L);
78+
79+
Container1 container = new Container1();
80+
container.setId( 1L );
81+
container.setSubClass11( subClass11 );
82+
container.setSubClass21( subClass21 );
83+
subClass11.set1To1Container( container );
84+
subClass21.set1To1Container( container );
85+
86+
session.persist( container );
87+
}
88+
)
89+
);
90+
}
91+
92+
@Test
93+
public void testMultipleRelationshipsOnSingleTableInheritanceCorrectlyMappedAsManyToOne(SessionFactoryScope scope) {
94+
scope.inTransaction(
95+
session -> {
96+
SubClass1 subClass121 = new SubClass1();
97+
subClass121.setId(121L);
98+
99+
SubClass2 subClass221 = new SubClass2();
100+
subClass221.setId(221L);
101+
102+
Container2 container21 = new Container2();
103+
container21.setId( 21L );
104+
container21.setSubClass12( subClass121 );
105+
container21.setSubClass22( subClass221 );
106+
subClass121.setManyToOneContainer( container21 );
107+
subClass221.setManyToOneContainer( container21 );
108+
109+
session.persist( container21 );
110+
111+
SubClass2 subClass222 = new SubClass2();
112+
subClass222.setId(222L);
113+
114+
Container2 container22 = new Container2();
115+
container22.setId( 22L );
116+
container22.setSubClass22( subClass222 );
117+
subClass222.setManyToOneContainer( container22 );
118+
119+
session.persist( container22 );
120+
}
121+
);
122+
scope.inTransaction(
123+
session -> {
124+
List<SubClass1> result1 = session.createSelectionQuery( "from SubClass1", SubClass1.class ).getResultList();
125+
assertEquals( 1, result1.size() );
126+
assertEquals( 121L, result1.get(0).getId() );
127+
128+
List<SubClass2> result2 = session.createSelectionQuery( "from SubClass2 sc order by sc.id", SubClass2.class ).getResultList();
129+
assertEquals( 2, result2.size() );
130+
assertEquals( 221L, result2.get(0).getId() );
131+
assertEquals( 222L, result2.get(1).getId() );
132+
133+
List<Container2> result3 = session.createSelectionQuery( "from Container2 c order by c.id", Container2.class ).getResultList();
134+
assertEquals( 2, result3.size() );
135+
assertEquals( 21L, result3.get(0).getId() );
136+
assertEquals( 22L, result3.get(1).getId() );
137+
138+
SubClass1 sc1 = session.find( SubClass1.class, 121L );
139+
assertEquals( 21L, sc1.getManyToOneContainer().getId() );
140+
141+
SubClass2 sc2 = session.find( SubClass2.class, 222L );
142+
assertEquals( 22L, sc2.getManyToOneContainer().getId() );
143+
144+
Container2 c2 = session.find( Container2.class, 21L );
145+
assertEquals( 121L, c2.getSubClass12().getId() );
146+
assertEquals( 221L, c2.getSubClass22().getId() );
147+
148+
c2 = session.find( Container2.class, 22L );
149+
assertEquals( 222L, c2.getSubClass22().getId() );
150+
assertNull( c2.getSubClass12() );
151+
}
152+
);
153+
154+
tearDown( scope );
155+
156+
scope.inTransaction(
157+
session -> {
158+
SubClass1 subClass12 = new SubClass1();
159+
subClass12.setId(12L);
160+
161+
Container2 container = new Container2();
162+
container.setId( 2L );
163+
container.setSubClass12( subClass12 );
164+
subClass12.setManyToOneContainer( container );
165+
166+
session.persist( container );
167+
}
168+
);
169+
scope.inTransaction(
170+
session -> {
171+
List<SubClass1> result1 = session.createSelectionQuery( "from SubClass1", SubClass1.class ).getResultList();
172+
assertEquals( 1, result1.size() );
173+
assertEquals( 12L, result1.get(0).getId() );
174+
List<SubClass2> result2 = session.createSelectionQuery( "from SubClass2", SubClass2.class ).getResultList();
175+
assertEquals( 0, result2.size() );
176+
}
177+
);
178+
179+
tearDown( scope );
180+
181+
scope.inTransaction(
182+
session -> {
183+
SubClass2 subClass22 = new SubClass2();
184+
subClass22.setId(22L);
185+
186+
Container2 container = new Container2();
187+
container.setId( 2L );
188+
container.setSubClass22( subClass22 );
189+
subClass22.setManyToOneContainer( container );
190+
191+
session.persist( container );
192+
}
193+
);
194+
scope.inTransaction(
195+
session -> {
196+
List<SubClass1> result1 = session.createSelectionQuery( "from SubClass1", SubClass1.class ).getResultList();
197+
assertEquals( 0, result1.size() );
198+
List<SubClass2> result2 = session.createSelectionQuery( "from SubClass2", SubClass2.class ).getResultList();
199+
assertEquals( 1, result2.size() );
200+
assertEquals( 22L, result2.get(0).getId() );
201+
}
202+
);
203+
204+
}
205+
206+
@Entity(name = "BaseClass")
207+
@DiscriminatorColumn(name = "BASE_TYPE")
208+
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
209+
public static class BaseClass {
210+
@Id
211+
private Long id;
212+
213+
@OneToOne(cascade = CascadeType.ALL)
214+
@JoinColumn(name = "oneToOneContainer_id")
215+
private Container1 oneToOneContainer;
216+
217+
@ManyToOne(cascade = CascadeType.ALL)
218+
@JoinColumn(name = "manyToOneContainer_id")
219+
private Container2 manyToOneContainer;
220+
221+
public Long getId() {
222+
return id;
223+
}
224+
225+
public void setId(Long id) {
226+
this.id = id;
227+
}
228+
229+
public void set1To1Container(Container1 oneToOneContainer) {
230+
this.oneToOneContainer = oneToOneContainer;
231+
}
232+
233+
public Container2 getManyToOneContainer() {
234+
return manyToOneContainer;
235+
}
236+
237+
public void setManyToOneContainer(Container2 manyToOneContainer) {
238+
this.manyToOneContainer = manyToOneContainer;
239+
}
240+
}
241+
242+
@Entity(name = "SubClass1")
243+
@DiscriminatorValue(value = "SUB1")
244+
public static class SubClass1 extends BaseClass {
245+
}
246+
247+
@Entity(name = "SubClass2")
248+
@DiscriminatorValue(value = "SUB2")
249+
public static class SubClass2 extends BaseClass {
250+
}
251+
252+
@Entity(name = "Container1")
253+
public static class Container1 {
254+
@Id
255+
private Long id;
256+
257+
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "oneToOneContainer", orphanRemoval = true)
258+
private SubClass1 subClass11;
259+
260+
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "oneToOneContainer", orphanRemoval = true)
261+
private SubClass2 subClass21;
262+
263+
public void setId(Long id) {
264+
this.id = id;
265+
}
266+
267+
public void setSubClass11(SubClass1 subClass11) {
268+
this.subClass11 = subClass11;
269+
}
270+
271+
public void setSubClass21(SubClass2 subClass21) {
272+
this.subClass21 = subClass21;
273+
}
274+
}
275+
276+
@Entity(name = "Container2")
277+
public static class Container2 {
278+
@Id
279+
private Long id;
280+
281+
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "manyToOneContainer", orphanRemoval = true)
282+
private SubClass1 subClass12;
283+
284+
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "manyToOneContainer", orphanRemoval = true)
285+
private SubClass2 subClass22;
286+
287+
public Long getId() {
288+
return id;
289+
}
290+
291+
public void setId(Long id) {
292+
this.id = id;
293+
}
294+
295+
public SubClass1 getSubClass12() {
296+
return subClass12;
297+
}
298+
299+
public SubClass2 getSubClass22() {
300+
return subClass22;
301+
}
302+
303+
public void setSubClass12(SubClass1 subClass12) {
304+
this.subClass12 = subClass12;
305+
}
306+
307+
public void setSubClass22(SubClass2 subClass22) {
308+
this.subClass22 = subClass22;
309+
}
310+
}
311+
312+
}

0 commit comments

Comments
 (0)