44 */
55package org .hibernate .orm .test .jpa .criteria ;
66
7+ import java .math .BigDecimal ;
8+ import java .util .List ;
9+
710import org .hibernate .testing .orm .junit .JiraKey ;
811import org .hibernate .testing .orm .junit .EntityManagerFactoryScope ;
12+ import org .hibernate .testing .orm .junit .Jira ;
913import org .hibernate .testing .orm .junit .Jpa ;
1014
15+ import org .junit .jupiter .api .AfterAll ;
16+ import org .junit .jupiter .api .BeforeAll ;
1117import org .junit .jupiter .api .Test ;
1218
1319import jakarta .persistence .Column ;
20+ import jakarta .persistence .Embeddable ;
21+ import jakarta .persistence .Embedded ;
1422import jakarta .persistence .Entity ;
1523import jakarta .persistence .Id ;
24+ import jakarta .persistence .Tuple ;
1625import jakarta .persistence .TypedQuery ;
1726import jakarta .persistence .criteria .CriteriaBuilder ;
1827import jakarta .persistence .criteria .CriteriaQuery ;
1928import jakarta .persistence .criteria .Expression ;
2029import jakarta .persistence .criteria .ParameterExpression ;
2130import jakarta .persistence .criteria .Root ;
2231
32+ import static org .assertj .core .api .Assertions .assertThat ;
33+
2334/**
2435 * @author Will Dazy
2536 */
26- @ Jpa (annotatedClasses = CoalesceTest .HHH15291Entity .class )
37+ @ Jpa ( annotatedClasses = {
38+ CoalesceTest .HHH15291Entity .class ,
39+ CoalesceTest .ComponentEntity .class ,
40+ CoalesceTest .ComponentA .class ,
41+ } )
2742@ JiraKey ( value = "HHH-15291" )
2843public class CoalesceTest {
29-
3044 @ Test
3145 public void hhh15291JPQL1Test (EntityManagerFactoryScope scope ) {
3246 scope .inEntityManager (
@@ -95,9 +109,74 @@ public void hhh15291Criteria2Test(EntityManagerFactoryScope scope) {
95109 );
96110 }
97111
112+ @ Test
113+ @ Jira ( "https://hibernate.atlassian.net/browse/HHH-18321" )
114+ public void testCoalesceInBinaryArithmetic (EntityManagerFactoryScope scope ) {
115+ scope .inTransaction ( entityManager -> {
116+ final CriteriaBuilder cb = entityManager .getCriteriaBuilder ();
117+ final CriteriaQuery <Tuple > cquery = cb .createTupleQuery ();
118+ final Root <ComponentEntity > root = cquery .from ( ComponentEntity .class );
119+
120+ cquery .select ( cb .tuple (
121+ root .get ( "id" ),
122+ cb .diff (
123+ cb .coalesce ( root .get ( "componentA" ).get ( "income" ), BigDecimal .ZERO ),
124+ cb .coalesce ( root .get ( "componentA" ).get ( "expense" ), BigDecimal .ZERO )
125+ )
126+ ) );
127+
128+ final List <Tuple > resultList = entityManager .createQuery ( cquery ).getResultList ();
129+ assertThat ( resultList ).hasSize ( 2 );
130+ for ( Tuple result : resultList ) {
131+ final Long id = result .get ( 0 , Long .class );
132+ assertThat ( result .get ( 1 , BigDecimal .class ).intValue () ).isEqualTo ( id == 1L ? 0 : 1 );
133+ }
134+ } );
135+ }
136+
137+ @ Test
138+ @ Jira ( "https://hibernate.atlassian.net/browse/HHH-18321" )
139+ public void testCoalesceInBinaryArithmeticParam (EntityManagerFactoryScope scope ) {
140+ scope .inTransaction ( entityManager -> {
141+ final CriteriaBuilder cb = entityManager .getCriteriaBuilder ();
142+ final CriteriaQuery <Tuple > cquery = cb .createTupleQuery ();
143+ final Root <ComponentEntity > root = cquery .from ( ComponentEntity .class );
144+
145+ final ParameterExpression <BigDecimal > defaultValue = cb .parameter ( BigDecimal .class , "default-value" );
146+
147+ cquery .select ( cb .tuple (
148+ root .get ( "id" ),
149+ cb .diff (
150+ defaultValue ,
151+ cb .coalesce ( root .get ( "componentA" ).get ( "expense" ), defaultValue )
152+ )
153+ ) );
154+
155+ final List <Tuple > resultList = entityManager .createQuery ( cquery )
156+ .setParameter ( "default-value" , BigDecimal .ZERO ).getResultList ();
157+ assertThat ( resultList ).hasSize ( 2 );
158+ for ( Tuple result : resultList ) {
159+ final Long id = result .get ( 0 , Long .class );
160+ assertThat ( result .get ( 1 , BigDecimal .class ).intValue () ).isEqualTo ( id == 1L ? -1 : 0 );
161+ }
162+ } );
163+ }
164+
165+ @ BeforeAll
166+ public void setUp (EntityManagerFactoryScope scope ) {
167+ scope .inTransaction ( entityManager -> {
168+ entityManager .persist ( new ComponentEntity ( 1L , new ComponentA ( BigDecimal .ONE , BigDecimal .ONE ) ) );
169+ entityManager .persist ( new ComponentEntity ( 2L , new ComponentA ( BigDecimal .ONE , null ) ) );
170+ } );
171+ }
172+
173+ @ AfterAll
174+ public void tearDown (EntityManagerFactoryScope scope ) {
175+ scope .inTransaction ( entityManager -> entityManager .createQuery ( "delete from ComponentEntity" ).executeUpdate () );
176+ }
177+
98178 @ Entity (name = "HHH15291Entity" )
99179 public static class HHH15291Entity {
100-
101180 @ Id
102181 @ Column (name = "KEY_CHAR" )
103182 private String KeyString ;
@@ -116,53 +195,36 @@ public static class HHH15291Entity {
116195
117196 @ Column (name = "ITEM_INTEGER1" )
118197 private Integer itemInteger1 ;
198+ }
119199
120- public String getKeyString () {
121- return KeyString ;
122- }
123-
124- public void setKeyString (String keyString ) {
125- KeyString = keyString ;
126- }
127-
128- public String getItemString1 () {
129- return itemString1 ;
130- }
131-
132- public void setItemString1 (String itemString1 ) {
133- this .itemString1 = itemString1 ;
134- }
135-
136- public String getItemString2 () {
137- return itemString2 ;
138- }
139-
140- public void setItemString2 (String itemString2 ) {
141- this .itemString2 = itemString2 ;
142- }
200+ @ Entity ( name = "ComponentEntity" )
201+ static class ComponentEntity {
202+ @ Id
203+ private Long id ;
143204
144- public String getItemString3 () {
145- return itemString3 ;
146- }
205+ @ Embedded
206+ private ComponentA componentA ;
147207
148- public void setItemString3 (String itemString3 ) {
149- this .itemString3 = itemString3 ;
208+ public ComponentEntity () {
150209 }
151210
152- public String getItemString4 () {
153- return itemString4 ;
211+ public ComponentEntity (Long id , ComponentA componentA ) {
212+ this .id = id ;
213+ this .componentA = componentA ;
154214 }
215+ }
155216
156- public void setItemString4 (String itemString4 ) {
157- this .itemString4 = itemString4 ;
158- }
217+ @ Embeddable
218+ static class ComponentA {
219+ private BigDecimal income ;
220+ private BigDecimal expense ;
159221
160- public Integer getItemInteger1 () {
161- return itemInteger1 ;
222+ public ComponentA () {
162223 }
163224
164- public void setItemInteger1 (Integer itemInteger1 ) {
165- this .itemInteger1 = itemInteger1 ;
225+ public ComponentA (BigDecimal income , BigDecimal expense ) {
226+ this .income = income ;
227+ this .expense = expense ;
166228 }
167229 }
168230}
0 commit comments