44 */
55package org .hibernate .query .internal ;
66
7+ import java .util .ArrayList ;
78import java .util .Collection ;
9+ import java .util .List ;
810
911import org .checkerframework .checker .nullness .qual .NonNull ;
1012import org .checkerframework .checker .nullness .qual .Nullable ;
@@ -117,7 +119,10 @@ public T getBindValue() {
117119
118120 @ Override
119121 public void setBindValue (Object value , boolean resolveJdbcTypeIfNecessary ) {
120- if ( !handleAsMultiValue ( value , null ) ) {
122+ if ( handleAsMultiValue ( value , bindType ) ) {
123+ setBindValues ( (Collection <?>) value );
124+ }
125+ else {
121126 final Object coerced = coerce ( value );
122127 validate ( coerced );
123128 if ( value == null ) {
@@ -126,50 +131,49 @@ public void setBindValue(Object value, boolean resolveJdbcTypeIfNecessary) {
126131 bindNull ( resolveJdbcTypeIfNecessary );
127132 }
128133 else {
129- bindValue ( coerced );
134+ initBindType ( value );
135+ bindSingleValue ( coerced );
130136 }
131137 }
132138 }
133139
134140 @ Override
135- public <A > void setBindValue (A value , @ Nullable BindableType <A > clarifiedType ) {
136- if ( !handleAsMultiValue ( value , clarifiedType ) ) {
137- final Object coerced = coerce ( value );
138- validate ( coerced );
139- bindValue = cast ( value );
140- isBound = true ;
141- isMultiValued = false ;
142- bindValues = null ;
143-
144- if ( clarifiedType != null ) {
145- checkClarifiedType ( clarifiedType , value );
146- @ SuppressWarnings ("unchecked" ) // safe
147- final var newType = (BindableType <T >) clarifiedType ;
148- bindType = newType ;
149- }
141+ public void setBindValue (
142+ Object value ,
143+ @ SuppressWarnings ("deprecation" )
144+ TemporalType temporalTypePrecision ) {
145+ if ( handleAsMultiValue ( value , bindType ) ) {
146+ setBindValues ( (Collection <?>) value , temporalTypePrecision );
150147 }
151- }
152-
153- @ Override
154- public void setBindValue (Object value , @ SuppressWarnings ("deprecation" ) TemporalType temporalTypePrecision ) {
155- if ( !handleAsMultiValue ( value , null ) ) {
148+ else {
156149 final Object coerced = coerce ( value );
157150 validate ( coerced );
158- bindValue ( coerced );
151+ initBindType ( value );
152+ bindSingleValue ( coerced );
159153 setExplicitTemporalPrecision ( temporalTypePrecision );
160154 }
161155 }
162156
163- private void bindValue (Object value ) {
164- if ( canBindValueBeSet ( value , bindType ) ) {
165- bindType = (BindableType <T >) (BindableType )
157+ @ Override
158+ public <A > void setBindValue (A value , @ Nullable BindableType <A > clarifiedType ) {
159+ // don't coerce, because value is already of the clarified type
160+ validate ( value );
161+ clarifyType ( value , clarifiedType );
162+ bindSingleValue ( value );
163+ }
164+
165+ private void initBindType (Object value ) {
166+ if ( bindType == null ) {
167+ @ SuppressWarnings ("unchecked" )
168+ // If there is no bindType set, then this is effectively a
169+ // parameter of the top type. At least arguably safe cast.
170+ final var self = (QueryParameterBindingImpl <Object >) this ;
171+ //noinspection UnnecessaryLocalVariable (needed to make javac happy)
172+ final var valueType =
166173 getParameterBindingTypeResolver ()
167174 .resolveParameterBindType ( value );
175+ self .bindType = valueType ;
168176 }
169- bindValue = cast ( value );
170- isBound = true ;
171- isMultiValued = false ;
172- bindValues = null ;
173177 }
174178
175179 private void bindNull (boolean resolveJdbcTypeIfNecessary ) {
@@ -185,17 +189,22 @@ private void bindNull(boolean resolveJdbcTypeIfNecessary) {
185189 }
186190
187191 private boolean handleAsMultiValue (Object value , @ Nullable BindableType <?> bindableType ) {
188- if ( queryParameter .allowsMultiValuedBinding ()
192+ return queryParameter .allowsMultiValuedBinding ()
189193 && value instanceof Collection
190- && !( bindableType == null
194+ && !validInstance ( value , bindableType );
195+ }
196+
197+ private void bindSingleValue (Object value ) {
198+ bindValue = cast ( value );
199+ bindValues = null ;
200+ isMultiValued = false ;
201+ isBound = true ;
202+ }
203+
204+ private boolean validInstance (Object value , @ Nullable BindableType <?> bindableType ) {
205+ return bindableType == null
191206 ? isRegisteredAsBasicType ( value .getClass () )
192- : bindableType .getJavaType ().isInstance ( value ) ) ) {
193- setBindValues ( (Collection <?>) value );
194- return true ;
195- }
196- else {
197- return false ;
198- }
207+ : bindableType .getJavaType ().isInstance ( value );
199208 }
200209
201210 private boolean isRegisteredAsBasicType (Class <?> valueClass ) {
@@ -215,66 +224,47 @@ public Collection<? extends T> getBindValues() {
215224
216225 @ Override
217226 public void setBindValues (Collection <?> values ) {
218- if ( !queryParameter .allowsMultiValuedBinding () ) {
219- throw new IllegalArgumentException (
220- "Illegal attempt to bind a collection value to a single-valued parameter"
221- );
222- }
223-
227+ assertMultivalued ();
224228 final var coerced = values .stream ().map ( this ::coerce ).toList ();
225229 coerced .forEach ( this ::validate );
226- isBound = true ;
227- isMultiValued = true ;
228- bindValue = null ;
229- bindValues = coerced .stream ().map ( this ::cast ).toList ();
230-
231- final T value = firstNonNull ( bindValues );
232- if ( canBindValueBeSet ( value , bindType ) ) {
233- bindType = (BindableType <T >) (BindableType )
234- getParameterBindingTypeResolver ()
235- .resolveParameterBindType ( value );
236- }
230+ initBindType ( firstNonNull ( values ) );
231+ bindMultipleValues ( coerced );
237232 }
238233
239- private static <T > @ Nullable T firstNonNull (Collection <? extends T > values ) {
240- final var iterator = values .iterator ();
241- T value = null ;
242- while ( value == null && iterator .hasNext () ) {
243- value = iterator .next ();
244- }
245- return value ;
234+ @ Override
235+ public void setBindValues (
236+ Collection <?> values ,
237+ @ SuppressWarnings ("deprecation" ) TemporalType temporalTypePrecision ) {
238+ setBindValues ( values );
239+ setExplicitTemporalPrecision ( temporalTypePrecision );
246240 }
247241
248242 @ Override
249243 public <V > void setBindValues (Collection <? extends V > values , BindableType <V > clarifiedType ) {
244+ assertMultivalued ();
245+ // don't coerce, because value is already of the clarified type
246+ values .forEach ( this ::validate );
247+ clarifyType ( values , clarifiedType );
248+ bindMultipleValues ( values );
249+ }
250+
251+ private void bindMultipleValues (Collection <?> coerced ) {
252+ final List <T > list = new ArrayList <>();
253+ for ( var value : coerced ) {
254+ list .add ( cast ( value ) );
255+ }
256+ bindValues = list ;
257+ bindValue = null ;
258+ isMultiValued = true ;
259+ isBound = true ;
260+ }
261+
262+ private void assertMultivalued () {
250263 if ( !queryParameter .allowsMultiValuedBinding () ) {
251264 throw new IllegalArgumentException (
252265 "Illegal attempt to bind a collection value to a single-valued parameter"
253266 );
254267 }
255-
256- final var coerced = values .stream ().map ( this ::coerce ).toList ();
257- coerced .forEach ( this ::validate );
258- isBound = true ;
259- isMultiValued = true ;
260- bindValue = null ;
261- bindValues = coerced .stream ().map ( this ::cast ).toList ();
262-
263- if ( clarifiedType != null ) {
264- checkClarifiedType ( clarifiedType , values );
265- @ SuppressWarnings ("unchecked" ) // safe
266- final var newType = (BindableType <T >) clarifiedType ;
267- bindType = newType ;
268- }
269- }
270-
271- @ Override
272- public void setBindValues (
273- Collection <?> values ,
274- @ SuppressWarnings ("deprecation" ) TemporalType temporalTypePrecision ,
275- TypeConfiguration typeConfiguration ) {
276- setBindValues ( values );
277- setExplicitTemporalPrecision ( temporalTypePrecision );
278268 }
279269
280270 private void setExplicitTemporalPrecision (@ SuppressWarnings ("deprecation" ) TemporalType precision ) {
@@ -315,14 +305,25 @@ else if ( type instanceof BasicValuedMapping basicValuedMapping ) {
315305 return false ;
316306 }
317307
318- private <A > void checkClarifiedType (@ NonNull BindableType <A > clarifiedType , Object value ) {
308+ private <V > void clarifyType (Object valueOrValues , BindableType <V > clarifiedType ) {
309+ if ( clarifiedType != null ) {
310+ checkClarifiedType ( clarifiedType , valueOrValues );
311+ @ SuppressWarnings ("unchecked" ) // safe
312+ final var newType = (BindableType <T >) clarifiedType ;
313+ bindType = newType ;
314+ }
315+ }
316+
317+ private <A > void checkClarifiedType (
318+ @ NonNull BindableType <A > clarifiedType ,
319+ Object valueOrValues ) {
319320 final var parameterType = queryParameter .getParameterType ();
320321 if ( parameterType != null ) {
321322 final var clarifiedJavaType = clarifiedType .getJavaType ();
322323 if ( !parameterType .isAssignableFrom ( clarifiedJavaType ) ) {
323324 throw new QueryArgumentException (
324325 "Given type is incompatible with parameter type" ,
325- parameterType , clarifiedJavaType , value
326+ parameterType , clarifiedJavaType , valueOrValues
326327 );
327328 }
328329 }
@@ -332,10 +333,27 @@ private <A> void checkClarifiedType(@NonNull BindableType<A> clarifiedType, Obje
332333 }
333334
334335 private T cast (Object value ) {
335- final var bindableType = getCriteriaBuilder ().resolveExpressible ( bindType );
336- return bindableType == null
337- ? (T ) value // YOLO
338- : QueryArguments .cast ( value , bindableType .getExpressibleJavaType () );
336+ if ( value == null ) {
337+ return null ;
338+ }
339+ else {
340+ final var bindableType =
341+ getCriteriaBuilder ()
342+ .resolveExpressible ( bindType );
343+ if ( bindableType != null ) {
344+ return QueryArguments .cast ( value ,
345+ bindableType .getExpressibleJavaType () );
346+ }
347+ else if ( bindType != null ) {
348+ return bindType .getJavaType ().cast ( value );
349+ }
350+ else {
351+ // no typing information, but in this
352+ // case we can view this as T = Object
353+ // noinspection unchecked
354+ return (T ) value ;
355+ }
356+ }
339357 }
340358
341359 private void validate (Object value ) {
@@ -344,12 +362,12 @@ private void validate(Object value) {
344362
345363 private Object coerce (Object value ) {
346364 try {
347- if ( canValueBeCoerced ( bindType ) ) {
365+ if ( bindType != null ) {
348366 return coerce ( value , bindType );
349367 }
350- else if ( canValueBeCoerced ( queryParameter .getHibernateType () ) ) {
351- return coerce ( value , queryParameter .getHibernateType () );
352- }
368+ // else if ( queryParameter.getHibernateType() != null ) {
369+ // return coerce( value, queryParameter.getHibernateType() );
370+ // }
353371 else {
354372 return value ;
355373 }
@@ -366,11 +384,13 @@ private Object coerce(Object value, BindableType<T> parameterType) {
366384 .getExpressibleJavaType ().coerce ( value );
367385 }
368386
369- private static boolean canValueBeCoerced (BindableType <?> bindType ) {
370- return bindType != null ;
387+ private static <T > @ Nullable T firstNonNull (Collection <? extends T > values ) {
388+ final var iterator = values .iterator ();
389+ T value = null ;
390+ while ( value == null && iterator .hasNext () ) {
391+ value = iterator .next ();
392+ }
393+ return value ;
371394 }
372395
373- private static boolean canBindValueBeSet (Object value , BindableType <?> bindType ) {
374- return value != null && bindType == null ;
375- }
376396}
0 commit comments