44 */
55package org .hibernate .orm .test .jpa .query ;
66
7- import org .hibernate .testing .orm .junit .EntityManagerFactoryScope ;
8- import org .hibernate .testing .orm .junit .Jpa ;
9- import org .junit .jupiter .api .Test ;
10-
117import jakarta .persistence .Entity ;
8+ import jakarta .persistence .GeneratedValue ;
129import jakarta .persistence .Id ;
10+ import jakarta .persistence .Lob ;
1311import jakarta .persistence .Query ;
1412import jakarta .persistence .criteria .CriteriaBuilder ;
1513import jakarta .persistence .criteria .CriteriaUpdate ;
14+ import jakarta .persistence .criteria .Expression ;
1615import jakarta .persistence .criteria .ParameterExpression ;
16+ import jakarta .persistence .criteria .Path ;
1717import jakarta .persistence .criteria .Root ;
1818import jakarta .persistence .metamodel .EntityType ;
19+ import org .hibernate .query .criteria .HibernateCriteriaBuilder ;
20+ import org .hibernate .testing .orm .junit .EntityManagerFactoryScope ;
21+ import org .hibernate .testing .orm .junit .Jpa ;
22+ import org .junit .jupiter .api .Test ;
1923
20- @ Jpa (
21- annotatedClasses = CriteriaUpdateWithParametersTest .Person .class
22- )
24+
25+ @ Jpa (annotatedClasses = {
26+ CriteriaUpdateWithParametersTest .Person .class ,
27+ CriteriaUpdateWithParametersTest .Process .class
28+ })
2329public class CriteriaUpdateWithParametersTest {
2430
2531 @ Test
2632 public void testCriteriaUpdate (EntityManagerFactoryScope scope ) {
27- scope .inTransaction (
28- entityManager -> {
29- final CriteriaBuilder criteriaBuilder = entityManager .getCriteriaBuilder ();
30- final CriteriaUpdate <Person > criteriaUpdate = criteriaBuilder .createCriteriaUpdate ( Person .class );
31- final Root <Person > root = criteriaUpdate .from ( Person .class );
32-
33- final ParameterExpression <Integer > intValueParameter = criteriaBuilder .parameter ( Integer .class );
34- final ParameterExpression <String > stringValueParameter = criteriaBuilder .parameter ( String .class );
35-
36- final EntityType <Person > personEntityType = entityManager .getMetamodel ().entity ( Person .class );
37-
38- criteriaUpdate .set (
39- root .get ( personEntityType .getSingularAttribute ( "age" , Integer .class ) ),
40- intValueParameter
41- );
42- criteriaUpdate .where ( criteriaBuilder .equal (
43- root .get ( personEntityType .getSingularAttribute ( "name" , String .class ) ),
44- stringValueParameter
45- ) );
46-
47- final Query query = entityManager .createQuery ( criteriaUpdate );
48- query .setParameter ( intValueParameter , 9 );
49- query .setParameter ( stringValueParameter , "Luigi" );
50-
51- query .executeUpdate ();
52- }
53- );
33+ scope .inTransaction ( entityManager -> {
34+ final CriteriaBuilder criteriaBuilder = entityManager .getCriteriaBuilder ();
35+ final CriteriaUpdate <Person > criteriaUpdate = criteriaBuilder .createCriteriaUpdate ( Person .class );
36+ final Root <Person > root = criteriaUpdate .from ( Person .class );
37+
38+ final ParameterExpression <Integer > intValueParameter = criteriaBuilder .parameter ( Integer .class );
39+ final ParameterExpression <String > stringValueParameter = criteriaBuilder .parameter ( String .class );
40+
41+ final EntityType <Person > personEntityType = entityManager .getMetamodel ().entity ( Person .class );
42+
43+ criteriaUpdate .set ( root .get ( personEntityType .getSingularAttribute ( "age" , Integer .class ) ),
44+ intValueParameter );
45+ criteriaUpdate .where (
46+ criteriaBuilder .equal ( root .get ( personEntityType .getSingularAttribute ( "name" , String .class ) ),
47+ stringValueParameter ) );
48+
49+ final Query query = entityManager .createQuery ( criteriaUpdate );
50+ query .setParameter ( intValueParameter , 9 );
51+ query .setParameter ( stringValueParameter , "Luigi" );
52+
53+ query .executeUpdate ();
54+ } );
5455 }
5556
5657 @ Test
5758 public void testCriteriaUpdate2 (EntityManagerFactoryScope scope ) {
58- scope .inTransaction (
59- entityManager -> {
60- final CriteriaBuilder criteriaBuilder = entityManager .getCriteriaBuilder ();
61- final CriteriaUpdate <Person > criteriaUpdate = criteriaBuilder .createCriteriaUpdate ( Person .class );
62- final Root <Person > root = criteriaUpdate .from ( Person .class );
59+ scope .inTransaction ( entityManager -> {
60+ final CriteriaBuilder criteriaBuilder = entityManager .getCriteriaBuilder ();
61+ final CriteriaUpdate <Person > criteriaUpdate = criteriaBuilder .createCriteriaUpdate ( Person .class );
62+ final Root <Person > root = criteriaUpdate .from ( Person .class );
6363
64- final ParameterExpression <Integer > intValueParameter = criteriaBuilder .parameter ( Integer .class );
65- final ParameterExpression <String > stringValueParameter = criteriaBuilder .parameter ( String .class );
64+ final ParameterExpression <Integer > intValueParameter = criteriaBuilder .parameter ( Integer .class );
65+ final ParameterExpression <String > stringValueParameter = criteriaBuilder .parameter ( String .class );
6666
67- criteriaUpdate .set ( "age" , intValueParameter );
68- criteriaUpdate .where ( criteriaBuilder .equal ( root .get ( "name" ), stringValueParameter ) );
67+ criteriaUpdate .set ( "age" , intValueParameter );
68+ criteriaUpdate .where ( criteriaBuilder .equal ( root .get ( "name" ), stringValueParameter ) );
6969
70- final Query query = entityManager .createQuery ( criteriaUpdate );
71- query .setParameter ( intValueParameter , 9 );
72- query .setParameter ( stringValueParameter , "Luigi" );
70+ final Query query = entityManager .createQuery ( criteriaUpdate );
71+ query .setParameter ( intValueParameter , 9 );
72+ query .setParameter ( stringValueParameter , "Luigi" );
7373
74- query .executeUpdate ();
75- }
76- );
74+ query .executeUpdate ();
75+ } );
76+ }
77+
78+ @ Test
79+ public void testCriteriaUpdate3 (EntityManagerFactoryScope scope ) {
80+ scope .inTransaction ( em -> {
81+ // test separate value-bind parameters
82+ final CriteriaBuilder cb = em .getCriteriaBuilder ();
83+ final CriteriaUpdate <Process > cu = cb .createCriteriaUpdate ( Process .class );
84+ final Root <Process > root = cu .from ( Process .class );
85+ cu .set ( root .get ( "name" ), (Object ) null );
86+ cu .set ( root .get ( "payload" ), (Object ) null );
87+ em .createQuery ( cu ).executeUpdate ();
88+ } );
89+
90+ scope .inTransaction ( em -> {
91+ // test with the same cb.value( null ) parameter instance
92+ final HibernateCriteriaBuilder cb = (HibernateCriteriaBuilder ) em .getCriteriaBuilder ();
93+ final CriteriaUpdate <Process > cu = cb .createCriteriaUpdate ( Process .class );
94+ final Root <Process > root = cu .from ( Process .class );
95+ final Expression <Object > nullValue = cb .value ( null );
96+ // a bit unfortunate, but we need to cast here to prevent ambiguous method references
97+ final Path <String > name = root .get ( "name" );
98+ final Path <byte []> payload = root .get ( "payload" );
99+ final Expression <? extends String > nullString = cast ( nullValue );
100+ final Expression <? extends byte []> nullBytes = cast ( nullValue );
101+ cu .set ( name , nullString );
102+ cu .set ( payload , nullBytes );
103+ em .createQuery ( cu ).executeUpdate ();
104+ } );
105+ }
106+
107+ private static <X > Expression <? extends X > cast (Expression <?> expression ) {
108+ //noinspection unchecked
109+ return (Expression <? extends X >) expression ;
77110 }
78111
79112 @ Entity (name = "Person" )
80113 public static class Person {
81-
82114 @ Id
83115 private String id ;
84116
@@ -101,4 +133,18 @@ public Integer getAge() {
101133 return age ;
102134 }
103135 }
136+
137+ @ Entity
138+ public static class Process {
139+ @ Id
140+ @ GeneratedValue
141+ private Long id ;
142+
143+ // All attributes below are necessary to reproduce the issue
144+
145+ private String name ;
146+
147+ @ Lob
148+ private byte [] payload ;
149+ }
104150}
0 commit comments