66 */
77package org .hibernate .orm .test .jpa .criteria ;
88
9- import org .hibernate .testing .TestForIssue ;
9+ import java .math .BigDecimal ;
10+ import java .util .List ;
11+
12+ import org .hibernate .testing .orm .junit .JiraKey ;
1013import org .hibernate .testing .orm .junit .EntityManagerFactoryScope ;
14+ import org .hibernate .testing .orm .junit .Jira ;
1115import org .hibernate .testing .orm .junit .Jpa ;
1216
17+ import org .junit .jupiter .api .AfterAll ;
18+ import org .junit .jupiter .api .BeforeAll ;
1319import org .junit .jupiter .api .Test ;
1420
1521import jakarta .persistence .Column ;
22+ import jakarta .persistence .Embeddable ;
23+ import jakarta .persistence .Embedded ;
1624import jakarta .persistence .Entity ;
1725import jakarta .persistence .Id ;
26+ import jakarta .persistence .Tuple ;
1827import jakarta .persistence .TypedQuery ;
1928import jakarta .persistence .criteria .CriteriaBuilder ;
2029import jakarta .persistence .criteria .CriteriaQuery ;
2130import jakarta .persistence .criteria .Expression ;
2231import jakarta .persistence .criteria .ParameterExpression ;
2332import jakarta .persistence .criteria .Root ;
2433
34+ import static org .assertj .core .api .Assertions .assertThat ;
35+
2536/**
2637 * @author Will Dazy
2738 */
28- @ Jpa (annotatedClasses = CoalesceTest .HHH15291Entity .class )
29- @ TestForIssue ( jiraKey = "HHH-15291" )
39+ @ Jpa ( annotatedClasses = {
40+ CoalesceTest .HHH15291Entity .class ,
41+ CoalesceTest .ComponentEntity .class ,
42+ CoalesceTest .ComponentA .class ,
43+ } )
44+ @ JiraKey ( value = "HHH-15291" )
3045public class CoalesceTest {
31-
3246 @ Test
3347 public void hhh15291JPQL1Test (EntityManagerFactoryScope scope ) {
3448 scope .inEntityManager (
@@ -97,9 +111,74 @@ public void hhh15291Criteria2Test(EntityManagerFactoryScope scope) {
97111 );
98112 }
99113
114+ @ Test
115+ @ Jira ( "https://hibernate.atlassian.net/browse/HHH-18321" )
116+ public void testCoalesceInBinaryArithmetic (EntityManagerFactoryScope scope ) {
117+ scope .inTransaction ( entityManager -> {
118+ final CriteriaBuilder cb = entityManager .getCriteriaBuilder ();
119+ final CriteriaQuery <Tuple > cquery = cb .createTupleQuery ();
120+ final Root <ComponentEntity > root = cquery .from ( ComponentEntity .class );
121+
122+ cquery .select ( cb .tuple (
123+ root .get ( "id" ),
124+ cb .diff (
125+ cb .coalesce ( root .get ( "componentA" ).get ( "income" ), BigDecimal .ZERO ),
126+ cb .coalesce ( root .get ( "componentA" ).get ( "expense" ), BigDecimal .ZERO )
127+ )
128+ ) );
129+
130+ final List <Tuple > resultList = entityManager .createQuery ( cquery ).getResultList ();
131+ assertThat ( resultList ).hasSize ( 2 );
132+ for ( Tuple result : resultList ) {
133+ final Long id = result .get ( 0 , Long .class );
134+ assertThat ( result .get ( 1 , BigDecimal .class ).intValue () ).isEqualTo ( id == 1L ? 0 : 1 );
135+ }
136+ } );
137+ }
138+
139+ @ Test
140+ @ Jira ( "https://hibernate.atlassian.net/browse/HHH-18321" )
141+ public void testCoalesceInBinaryArithmeticParam (EntityManagerFactoryScope scope ) {
142+ scope .inTransaction ( entityManager -> {
143+ final CriteriaBuilder cb = entityManager .getCriteriaBuilder ();
144+ final CriteriaQuery <Tuple > cquery = cb .createTupleQuery ();
145+ final Root <ComponentEntity > root = cquery .from ( ComponentEntity .class );
146+
147+ final ParameterExpression <BigDecimal > defaultValue = cb .parameter ( BigDecimal .class , "default-value" );
148+
149+ cquery .select ( cb .tuple (
150+ root .get ( "id" ),
151+ cb .diff (
152+ defaultValue ,
153+ cb .coalesce ( root .get ( "componentA" ).get ( "expense" ), defaultValue )
154+ )
155+ ) );
156+
157+ final List <Tuple > resultList = entityManager .createQuery ( cquery )
158+ .setParameter ( "default-value" , BigDecimal .ZERO ).getResultList ();
159+ assertThat ( resultList ).hasSize ( 2 );
160+ for ( Tuple result : resultList ) {
161+ final Long id = result .get ( 0 , Long .class );
162+ assertThat ( result .get ( 1 , BigDecimal .class ).intValue () ).isEqualTo ( id == 1L ? -1 : 0 );
163+ }
164+ } );
165+ }
166+
167+ @ BeforeAll
168+ public void setUp (EntityManagerFactoryScope scope ) {
169+ scope .inTransaction ( entityManager -> {
170+ entityManager .persist ( new ComponentEntity ( 1L , new ComponentA ( BigDecimal .ONE , BigDecimal .ONE ) ) );
171+ entityManager .persist ( new ComponentEntity ( 2L , new ComponentA ( BigDecimal .ONE , null ) ) );
172+ } );
173+ }
174+
175+ @ AfterAll
176+ public void tearDown (EntityManagerFactoryScope scope ) {
177+ scope .inTransaction ( entityManager -> entityManager .createQuery ( "delete from ComponentEntity" ).executeUpdate () );
178+ }
179+
100180 @ Entity (name = "HHH15291Entity" )
101181 public static class HHH15291Entity {
102-
103182 @ Id
104183 @ Column (name = "KEY_CHAR" )
105184 private String KeyString ;
@@ -118,53 +197,36 @@ public static class HHH15291Entity {
118197
119198 @ Column (name = "ITEM_INTEGER1" )
120199 private Integer itemInteger1 ;
200+ }
121201
122- public String getKeyString () {
123- return KeyString ;
124- }
125-
126- public void setKeyString (String keyString ) {
127- KeyString = keyString ;
128- }
129-
130- public String getItemString1 () {
131- return itemString1 ;
132- }
133-
134- public void setItemString1 (String itemString1 ) {
135- this .itemString1 = itemString1 ;
136- }
137-
138- public String getItemString2 () {
139- return itemString2 ;
140- }
141-
142- public void setItemString2 (String itemString2 ) {
143- this .itemString2 = itemString2 ;
144- }
202+ @ Entity ( name = "ComponentEntity" )
203+ static class ComponentEntity {
204+ @ Id
205+ private Long id ;
145206
146- public String getItemString3 () {
147- return itemString3 ;
148- }
207+ @ Embedded
208+ private ComponentA componentA ;
149209
150- public void setItemString3 (String itemString3 ) {
151- this .itemString3 = itemString3 ;
210+ public ComponentEntity () {
152211 }
153212
154- public String getItemString4 () {
155- return itemString4 ;
213+ public ComponentEntity (Long id , ComponentA componentA ) {
214+ this .id = id ;
215+ this .componentA = componentA ;
156216 }
217+ }
157218
158- public void setItemString4 (String itemString4 ) {
159- this .itemString4 = itemString4 ;
160- }
219+ @ Embeddable
220+ static class ComponentA {
221+ private BigDecimal income ;
222+ private BigDecimal expense ;
161223
162- public Integer getItemInteger1 () {
163- return itemInteger1 ;
224+ public ComponentA () {
164225 }
165226
166- public void setItemInteger1 (Integer itemInteger1 ) {
167- this .itemInteger1 = itemInteger1 ;
227+ public ComponentA (BigDecimal income , BigDecimal expense ) {
228+ this .income = income ;
229+ this .expense = expense ;
168230 }
169231 }
170232}
0 commit comments