Skip to content

Commit c1cbbf1

Browse files
committed
HHH-18569 Add test for issue
1 parent afa550b commit c1cbbf1

File tree

1 file changed

+307
-0
lines changed

1 file changed

+307
-0
lines changed
Lines changed: 307 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,307 @@
1+
/*
2+
* SPDX-License-Identifier: LGPL-2.1-or-later
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.orm.test.jpa.criteria;
6+
7+
import java.util.List;
8+
9+
import org.hibernate.Hibernate;
10+
import org.hibernate.SessionFactory;
11+
12+
import org.hibernate.query.sqm.PathElementException;
13+
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
14+
import org.hibernate.testing.orm.junit.Jira;
15+
import org.hibernate.testing.orm.junit.Jpa;
16+
import org.junit.jupiter.api.AfterAll;
17+
import org.junit.jupiter.api.BeforeAll;
18+
import org.junit.jupiter.api.Test;
19+
20+
import jakarta.persistence.Column;
21+
import jakarta.persistence.Entity;
22+
import jakarta.persistence.FetchType;
23+
import jakarta.persistence.GeneratedValue;
24+
import jakarta.persistence.Id;
25+
import jakarta.persistence.ManyToOne;
26+
import jakarta.persistence.Table;
27+
import jakarta.persistence.criteria.CriteriaBuilder;
28+
import jakarta.persistence.criteria.CriteriaQuery;
29+
import jakarta.persistence.criteria.Root;
30+
31+
import static org.assertj.core.api.Assertions.assertThat;
32+
import static org.assertj.core.api.Fail.fail;
33+
34+
/**
35+
* @author Marco Belladelli
36+
*/
37+
@Jpa( annotatedClasses = {
38+
CriteriaSubtypeAttributesTest.ParentEntity.class,
39+
CriteriaSubtypeAttributesTest.ChildEntity.class,
40+
CriteriaSubtypeAttributesTest.AssociatedEntity.class,
41+
} )
42+
@Jira( "https://hibernate.atlassian.net/browse/HHH-18569" )
43+
public class CriteriaSubtypeAttributesTest {
44+
@Test
45+
public void testGetParentAttribute(EntityManagerFactoryScope scope) {
46+
// parent root
47+
scope.inTransaction( entityManager -> {
48+
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
49+
final CriteriaQuery<ParentEntity> cq = cb.createQuery( ParentEntity.class );
50+
final Root<ParentEntity> parent = cq.from( ParentEntity.class );
51+
cq.where( cb.isNotNull( parent.get( "name" ) ) );
52+
assertResult( entityManager.createQuery( cq ).getResultList(), 2, "name", true );
53+
} );
54+
// child root
55+
scope.inTransaction( entityManager -> {
56+
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
57+
final CriteriaQuery<ChildEntity> cq = cb.createQuery( ChildEntity.class );
58+
final Root<ChildEntity> parent = cq.from( ChildEntity.class );
59+
cq.where( cb.isNotNull( parent.get( "name" ) ) );
60+
assertResult( entityManager.createQuery( cq ).getResultList(), 1, "name", true );
61+
} );
62+
}
63+
64+
@Test
65+
public void testGetSubtypeAttribute(EntityManagerFactoryScope scope) {
66+
// parent root
67+
scope.inTransaction( entityManager -> {
68+
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
69+
final CriteriaQuery<ParentEntity> cq = cb.createQuery( ParentEntity.class );
70+
final Root<ParentEntity> parent = cq.from( ParentEntity.class );
71+
try {
72+
cq.where( cb.isNotNull( parent.get( "age" ) ) );
73+
fail( "Invoking get() for a subtype attribute on a supertype root should not be allowed" );
74+
}
75+
catch (Exception e) {
76+
assertThat( e ).isInstanceOf( PathElementException.class ).hasMessageContaining( "Could not resolve attribute" );
77+
}
78+
} );
79+
// child root
80+
scope.inTransaction( entityManager -> {
81+
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
82+
final CriteriaQuery<ChildEntity> cq = cb.createQuery( ChildEntity.class );
83+
final Root<ChildEntity> child = cq.from( ChildEntity.class );
84+
cq.where( cb.isNotNull( child.get( "age" ) ) );
85+
assertResult( entityManager.createQuery( cq ).getResultList(), 1, "age", true );
86+
} );
87+
// parent root treat as child
88+
scope.inTransaction( entityManager -> {
89+
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
90+
final CriteriaQuery<ChildEntity> cq = cb.createQuery( ChildEntity.class );
91+
final Root<ParentEntity> parent = cq.from( ParentEntity.class );
92+
final Root<ChildEntity> treated = cb.treat( parent, ChildEntity.class );
93+
cq.select( treated );
94+
cq.where( cb.isNotNull( treated.get( "age" ) ) );
95+
assertResult( entityManager.createQuery( cq ).getResultList(), 1, "age", true );
96+
} );
97+
}
98+
99+
@Test
100+
public void testJoinParentAttribute(EntityManagerFactoryScope scope) {
101+
// parent root
102+
scope.inTransaction( entityManager -> {
103+
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
104+
final CriteriaQuery<ParentEntity> cq = cb.createQuery( ParentEntity.class );
105+
final Root<ParentEntity> parent = cq.from( ParentEntity.class );
106+
parent.join( "association" );
107+
assertResult( entityManager.createQuery( cq ).getResultList(), 2, "association", false );
108+
} );
109+
// child root
110+
scope.inTransaction( entityManager -> {
111+
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
112+
final CriteriaQuery<ChildEntity> cq = cb.createQuery( ChildEntity.class );
113+
final Root<ChildEntity> child = cq.from( ChildEntity.class );
114+
child.join( "association" );
115+
assertResult( entityManager.createQuery( cq ).getResultList(), 1, "association", false );
116+
} );
117+
}
118+
119+
@Test
120+
public void testJoinSubtypeAttribute(EntityManagerFactoryScope scope) {
121+
// parent root
122+
scope.inTransaction( entityManager -> {
123+
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
124+
final CriteriaQuery<ParentEntity> cq = cb.createQuery( ParentEntity.class );
125+
final Root<ParentEntity> parent = cq.from( ParentEntity.class );
126+
try {
127+
parent.join( "childAssociation" );
128+
fail( "Invoking join() for a subtype attribute on a supertype root should not be allowed" );
129+
}
130+
catch (Exception e) {
131+
assertThat( e ).isInstanceOf( PathElementException.class ).hasMessageContaining( "Could not resolve attribute" );
132+
}
133+
} );
134+
// child root
135+
scope.inTransaction( entityManager -> {
136+
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
137+
final CriteriaQuery<ChildEntity> cq = cb.createQuery( ChildEntity.class );
138+
final Root<ChildEntity> child = cq.from( ChildEntity.class );
139+
child.join( "childAssociation" );
140+
assertResult( entityManager.createQuery( cq ).getResultList(), 1, "childAssociation", false );
141+
} );
142+
// parent root treat as child
143+
scope.inTransaction( entityManager -> {
144+
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
145+
final CriteriaQuery<ChildEntity> cq = cb.createQuery( ChildEntity.class );
146+
final Root<ParentEntity> parent = cq.from( ParentEntity.class );
147+
final Root<ChildEntity> treated = cb.treat( parent, ChildEntity.class );
148+
cq.select( treated );
149+
treated.join( "childAssociation" );
150+
assertResult( entityManager.createQuery( cq ).getResultList(), 1, "childAssociation", false );
151+
} );
152+
}
153+
154+
@Test
155+
public void testFetchParentAttribute(EntityManagerFactoryScope scope) {
156+
// parent root
157+
scope.inTransaction( entityManager -> {
158+
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
159+
final CriteriaQuery<ParentEntity> cq = cb.createQuery( ParentEntity.class );
160+
final Root<ParentEntity> parent = cq.from( ParentEntity.class );
161+
parent.fetch( "association" );
162+
assertResult( entityManager.createQuery( cq ).getResultList(), 2, "association", true );
163+
} );
164+
// child root
165+
scope.inTransaction( entityManager -> {
166+
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
167+
final CriteriaQuery<ChildEntity> cq = cb.createQuery( ChildEntity.class );
168+
final Root<ChildEntity> child = cq.from( ChildEntity.class );
169+
child.fetch( "association" );
170+
assertResult( entityManager.createQuery( cq ).getResultList(), 1, "association", true );
171+
} );
172+
}
173+
174+
@Test
175+
public void testFetchSubtypeAttribute(EntityManagerFactoryScope scope) {
176+
// parent root
177+
scope.inTransaction( entityManager -> {
178+
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
179+
final CriteriaQuery<ParentEntity> cq = cb.createQuery( ParentEntity.class );
180+
final Root<ParentEntity> parent = cq.from( ParentEntity.class );
181+
try {
182+
parent.fetch( "childAssociation" );
183+
fail( "Invoking fetch() for a subtype attribute on a supertype root should not be allowed" );
184+
}
185+
catch (Exception e) {
186+
assertThat( e ).isInstanceOf( PathElementException.class ).hasMessageContaining( "Could not resolve attribute" );
187+
}
188+
} );
189+
// child root
190+
scope.inTransaction( entityManager -> {
191+
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
192+
final CriteriaQuery<ChildEntity> cq = cb.createQuery( ChildEntity.class );
193+
final Root<ChildEntity> child = cq.from( ChildEntity.class );
194+
child.fetch( "childAssociation" );
195+
assertResult( entityManager.createQuery( cq ).getResultList(), 1, "childAssociation", true );
196+
} );
197+
// parent root treat as child
198+
scope.inTransaction( entityManager -> {
199+
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
200+
final CriteriaQuery<ChildEntity> cq = cb.createQuery( ChildEntity.class );
201+
final Root<ParentEntity> parent = cq.from( ParentEntity.class );
202+
final Root<ChildEntity> treated = cb.treat( parent, ChildEntity.class );
203+
cq.select( treated );
204+
treated.fetch( "childAssociation" );
205+
assertResult( entityManager.createQuery( cq ).getResultList(), 1, "childAssociation", true );
206+
} );
207+
}
208+
209+
private void assertResult(
210+
final List<? extends ParentEntity> result,
211+
int size,
212+
String attributeName,
213+
boolean initialized) {
214+
assertThat( result ).hasSize( size )
215+
.extracting( attributeName )
216+
.allMatch( r -> Hibernate.isInitialized( r ) || !initialized );
217+
if ( size == 1 ) {
218+
assertThat( result.get( 0 ) ).isExactlyInstanceOf( ChildEntity.class );
219+
}
220+
}
221+
222+
@BeforeAll
223+
public void setUp(EntityManagerFactoryScope scope) {
224+
scope.inTransaction( entityManager -> {
225+
final AssociatedEntity associated1 = new AssociatedEntity( 1L, "associated_1" );
226+
entityManager.persist( associated1 );
227+
final AssociatedEntity associated2 = new AssociatedEntity( 2L, "associated_2" );
228+
entityManager.persist( associated2 );
229+
entityManager.persist( new ChildEntity( associated1, "child", associated2, 2 ) );
230+
final AssociatedEntity associated3 = new AssociatedEntity( 3L, "associated_3" );
231+
entityManager.persist( associated3 );
232+
entityManager.persist( new ParentEntity( associated3, "parent" ) );
233+
} );
234+
}
235+
236+
@AfterAll
237+
public void tearDown(EntityManagerFactoryScope scope) {
238+
scope.getEntityManagerFactory().unwrap( SessionFactory.class ).getSchemaManager().truncateMappedObjects();
239+
}
240+
241+
@Entity( name = "ParentEntity" )
242+
static class ParentEntity {
243+
@Id
244+
@GeneratedValue
245+
private Long id;
246+
247+
@ManyToOne( fetch = FetchType.LAZY )
248+
private AssociatedEntity association;
249+
250+
private String name;
251+
252+
public ParentEntity() {
253+
}
254+
255+
public ParentEntity(AssociatedEntity association, String name) {
256+
this.name = name;
257+
this.association = association;
258+
}
259+
260+
public String getName() {
261+
return name;
262+
}
263+
}
264+
265+
@Entity( name = "ChildEntity" )
266+
static class ChildEntity extends ParentEntity {
267+
@ManyToOne( fetch = FetchType.LAZY )
268+
private AssociatedEntity childAssociation;
269+
270+
@Column( name = "age_col" )
271+
private Integer age;
272+
273+
public ChildEntity() {
274+
}
275+
276+
public ChildEntity(AssociatedEntity association, String name, AssociatedEntity childAssociation, Integer age) {
277+
super( association, name );
278+
this.childAssociation = childAssociation;
279+
this.age = age;
280+
}
281+
282+
public AssociatedEntity getChildAssociation() {
283+
return childAssociation;
284+
}
285+
}
286+
287+
@Entity( name = "AssociatedEntity" )
288+
@Table( name = "association_test" )
289+
static class AssociatedEntity {
290+
@Id
291+
private Long id;
292+
293+
private String name;
294+
295+
public AssociatedEntity() {
296+
}
297+
298+
public AssociatedEntity(Long id, String name) {
299+
this.id = id;
300+
this.name = name;
301+
}
302+
303+
public String getName() {
304+
return name;
305+
}
306+
}
307+
}

0 commit comments

Comments
 (0)