Skip to content

Commit 9905897

Browse files
committed
allow sensible conversions on query parameter arguments
as defined by what our JavaTypes already know how to convert
1 parent 8c3c336 commit 9905897

File tree

8 files changed

+211
-243
lines changed

8 files changed

+211
-243
lines changed
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.query.internal;
6+
7+
import jakarta.persistence.metamodel.Type;
8+
import org.hibernate.HibernateException;
9+
import org.hibernate.type.BindingContext;
10+
import org.hibernate.type.descriptor.WrapperOptions;
11+
12+
import java.util.Collection;
13+
14+
/**
15+
* @since 7.3
16+
*
17+
* @author Gavin King
18+
*/
19+
public class QueryArguments {
20+
21+
public static boolean isInstance(
22+
Type<?> parameterType, Object value,
23+
BindingContext bindingContext, WrapperOptions options) {
24+
if ( value == null ) {
25+
return true;
26+
}
27+
final var sqmExpressible = bindingContext.resolveExpressible( parameterType );
28+
assert sqmExpressible != null;
29+
final var javaType = sqmExpressible.getExpressibleJavaType();
30+
if ( !javaType.isInstance( value ) ) {
31+
try {
32+
// if this succeeds, we are good
33+
javaType.wrap( value, options );
34+
}
35+
catch (HibernateException | UnsupportedOperationException e) {
36+
return false;
37+
}
38+
}
39+
return true;
40+
}
41+
42+
public static boolean areInstances(
43+
Type<?> parameterType, Collection<?> values,
44+
BindingContext bindingContext, WrapperOptions options) {
45+
if ( values.isEmpty() ) {
46+
return true;
47+
}
48+
final var sqmExpressible = bindingContext.resolveExpressible( parameterType );
49+
assert sqmExpressible != null;
50+
final var javaType = sqmExpressible.getExpressibleJavaType();
51+
for ( Object value : values ) {
52+
if ( !javaType.isInstance( value ) ) {
53+
try {
54+
// if this succeeds, we are good
55+
javaType.wrap( value, options );
56+
}
57+
catch (HibernateException | UnsupportedOperationException e) {
58+
return false;
59+
}
60+
}
61+
}
62+
return true;
63+
}
64+
65+
public static boolean areInstances(
66+
Type<?> parameterType, Object[] values,
67+
BindingContext bindingContext, WrapperOptions options) {
68+
if ( values.length == 0 ) {
69+
return true;
70+
}
71+
if ( parameterType.getJavaType().isAssignableFrom( values.getClass().getComponentType() ) ) {
72+
return true;
73+
}
74+
final var sqmExpressible = bindingContext.resolveExpressible( parameterType );
75+
assert sqmExpressible != null;
76+
final var javaType = sqmExpressible.getExpressibleJavaType();
77+
for ( Object value : values ) {
78+
if ( !javaType.isInstance( value ) ) {
79+
try {
80+
// if this succeeds, we are good
81+
javaType.wrap( value, options );
82+
}
83+
catch (HibernateException | UnsupportedOperationException e) {
84+
return false;
85+
}
86+
}
87+
}
88+
return true;
89+
}
90+
}

hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingImpl.java

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@
1616
import org.hibernate.query.QueryParameter;
1717
import org.hibernate.query.spi.QueryParameterBinding;
1818
import org.hibernate.query.spi.QueryParameterBindingTypeResolver;
19-
import org.hibernate.query.spi.QueryParameterBindingValidator;
2019
import org.hibernate.query.sqm.NodeBuilder;
2120
import org.hibernate.type.descriptor.java.JavaType;
2221
import org.hibernate.type.spi.TypeConfiguration;
2322

2423
import jakarta.persistence.TemporalType;
2524

25+
import static org.hibernate.query.spi.QueryParameterBindingValidator.validate;
2626
import static org.hibernate.type.descriptor.java.JavaTypeHelper.isTemporal;
2727
import static org.hibernate.type.internal.BindingTypeHelper.resolveTemporalPrecision;
2828

@@ -116,7 +116,7 @@ public T getBindValue() {
116116
public void setBindValue(T value, boolean resolveJdbcTypeIfNecessary) {
117117
if ( !handleAsMultiValue( value, null ) ) {
118118
final Object coerced = coerceIfNotJpa( value );
119-
validate( coerced );
119+
validate( getBindType(), coerced, sessionFactory );
120120

121121
if ( value == null ) {
122122
// needed when setting a null value to the parameter of a native SQL query
@@ -174,7 +174,7 @@ public void setBindValue(T value, @Nullable BindableType<T> clarifiedType) {
174174
}
175175

176176
final Object coerced = coerce( value );
177-
validate( coerced, clarifiedType );
177+
validate( clarifiedType, coerced, sessionFactory );
178178
bindValue( coerced );
179179
}
180180
}
@@ -187,7 +187,7 @@ public void setBindValue(T value, TemporalType temporalTypePrecision) {
187187
}
188188

189189
final Object coerced = coerceIfNotJpa( value );
190-
validate( coerced, temporalTypePrecision );
190+
validate( getBindType(), coerced, sessionFactory );
191191
bindValue( coerced );
192192
setExplicitTemporalPrecision( temporalTypePrecision );
193193
}
@@ -284,18 +284,6 @@ else if ( type instanceof BasicValuedMapping basicValuedMapping ) {
284284
return false;
285285
}
286286

287-
private void validate(Object value) {
288-
QueryParameterBindingValidator.INSTANCE.validate( getBindType(), value, getCriteriaBuilder() );
289-
}
290-
291-
private void validate(Object value, BindableType<?> clarifiedType) {
292-
QueryParameterBindingValidator.INSTANCE.validate( clarifiedType, value, getCriteriaBuilder() );
293-
}
294-
295-
private void validate(Object value, TemporalType clarifiedTemporalType) {
296-
QueryParameterBindingValidator.INSTANCE.validate( getBindType(), value, clarifiedTemporalType, getCriteriaBuilder() );
297-
}
298-
299287
@Override
300288
public TypeConfiguration getTypeConfiguration() {
301289
return sessionFactory.getTypeConfiguration();

hibernate-core/src/main/java/org/hibernate/query/spi/AbstractCommonQueryContract.java

Lines changed: 11 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@
8989
import static org.hibernate.jpa.internal.util.ConfigurationHelper.getCacheMode;
9090
import static org.hibernate.jpa.internal.util.ConfigurationHelper.getInteger;
9191
import static org.hibernate.query.QueryLogging.QUERY_MESSAGE_LOGGER;
92+
import static org.hibernate.query.internal.QueryArguments.areInstances;
93+
import static org.hibernate.query.internal.QueryArguments.isInstance;
9294

9395
/**
9496
* Base implementation of {@link CommonQueryContract}.
@@ -755,7 +757,7 @@ protected <P> QueryParameterBinding<P> locateBinding(String name, Class<P> javaT
755757
if ( parameterType != null ) {
756758
final var parameterJavaType = parameterType.getJavaType();
757759
if ( !parameterJavaType.isAssignableFrom( javaType )
758-
&& !isInstance( parameterType, value ) ) {
760+
&& !isInstance( parameterType, value, getNodeBuilder(), getSession() ) ) {
759761
throw new QueryArgumentException(
760762
"Argument to parameter named '" + name + "' has an incompatible type",
761763
parameterJavaType, javaType, value );
@@ -773,7 +775,7 @@ protected <P> QueryParameterBinding<P> locateBinding(int position, Class<P> java
773775
if ( parameterType != null ) {
774776
final var parameterJavaType = parameterType.getJavaType();
775777
if ( !parameterJavaType.isAssignableFrom( javaType )
776-
&& !isInstance( parameterType, value ) ) {
778+
&& !isInstance( parameterType, value, getNodeBuilder(), getSession() ) ) {
777779
throw new QueryArgumentException(
778780
"Argument to parameter at position " + position + " has an incompatible type",
779781
parameterJavaType, javaType, value );
@@ -791,7 +793,7 @@ protected <P> QueryParameterBinding<P> locateBinding(String name, Class<P> javaT
791793
if ( parameterType != null ) {
792794
final var parameterJavaType = parameterType.getJavaType();
793795
if ( !parameterJavaType.isAssignableFrom( javaType )
794-
&& !areInstances( parameterType, values ) ) {
796+
&& !areInstances( parameterType, values, getNodeBuilder(), getSession() ) ) {
795797
throw new QueryArgumentException(
796798
"Argument to parameter named '" + name + "' has an incompatible type",
797799
parameterJavaType, javaType, values );
@@ -809,7 +811,7 @@ protected <P> QueryParameterBinding<P> locateBinding(int position, Class<P> java
809811
if ( parameterType != null ) {
810812
final var parameterJavaType = parameterType.getJavaType();
811813
if ( !parameterJavaType.isAssignableFrom( javaType )
812-
&& !areInstances( parameterType, values ) ) {
814+
&& !areInstances( parameterType, values, getNodeBuilder(), getSession() ) ) {
813815
throw new QueryArgumentException(
814816
"Argument to parameter at position " + position + " has an incompatible type",
815817
parameterJavaType, javaType, values );
@@ -886,8 +888,8 @@ private <P> void setParameterValue(Object value, QueryParameterBinding<P> bindin
886888
private <P> boolean isInstanceOrAreInstances(
887889
Object value, QueryParameterBinding<P> binding, BindableType<? super P> parameterType) {
888890
return binding.isMultiValued() && value instanceof Collection<?> values
889-
? areInstances( parameterType, values )
890-
: isInstance( parameterType, value );
891+
? areInstances( parameterType, values, getNodeBuilder(), getSession() )
892+
: isInstance( parameterType, value, getNodeBuilder(), getSession() );
891893
}
892894

893895
@Override
@@ -916,8 +918,8 @@ private boolean multipleBinding(QueryParameter<?> parameter, Object value){
916918
final var hibernateType = parameter.getHibernateType();
917919
return hibernateType == null
918920
|| values.isEmpty()
919-
|| !isInstance( hibernateType, value )
920-
|| isInstance( hibernateType, values.iterator().next() );
921+
|| !isInstance( hibernateType, value, getNodeBuilder(), getSession() )
922+
|| isInstance( hibernateType, values.iterator().next(), getNodeBuilder(), getSession() );
921923
}
922924
else {
923925
return false;
@@ -932,67 +934,6 @@ private <T> void setTypedParameter(int position, TypedParameterValue<T> typedVal
932934
setParameter( position, typedValue.value(), typedValue.type() );
933935
}
934936

935-
private boolean isInstance(Type<?> parameterType, Object value) {
936-
if ( value == null ) {
937-
return true;
938-
}
939-
final var sqmExpressible = getNodeBuilder().resolveExpressible( parameterType );
940-
assert sqmExpressible != null;
941-
final var javaType = sqmExpressible.getExpressibleJavaType();
942-
if ( !javaType.isInstance( value ) ) {
943-
try {
944-
// if this succeeds, we are good
945-
javaType.wrap( value, session );
946-
}
947-
catch ( HibernateException|UnsupportedOperationException e) {
948-
return false;
949-
}
950-
}
951-
return true;
952-
}
953-
954-
private boolean areInstances(Type<?> parameterType, Collection<?> values) {
955-
if ( values.isEmpty() ) {
956-
return true;
957-
}
958-
final var sqmExpressible = getNodeBuilder().resolveExpressible( parameterType );
959-
assert sqmExpressible != null;
960-
final var javaType = sqmExpressible.getExpressibleJavaType();
961-
for ( Object value : values ) {
962-
if ( !javaType.isInstance( value ) ) {
963-
try {
964-
// if this succeeds, we are good
965-
javaType.wrap( value, session );
966-
}
967-
catch (HibernateException | UnsupportedOperationException e) {
968-
return false;
969-
}
970-
}
971-
}
972-
return true;
973-
}
974-
975-
private boolean areInstances(Type<?> parameterType, Object[] values) {
976-
if ( values.length == 0 ) {
977-
return true;
978-
}
979-
final var sqmExpressible = getNodeBuilder().resolveExpressible( parameterType );
980-
assert sqmExpressible != null;
981-
final var javaType = sqmExpressible.getExpressibleJavaType();
982-
for ( Object value : values ) {
983-
if ( !javaType.isInstance( value ) ) {
984-
try {
985-
// if this succeeds, we are good
986-
javaType.wrap( value, session );
987-
}
988-
catch (HibernateException | UnsupportedOperationException e) {
989-
return false;
990-
}
991-
}
992-
}
993-
return true;
994-
}
995-
996937
private NodeBuilder getNodeBuilder() {
997938
return getSessionFactory().getQueryEngine().getCriteriaBuilder();
998939
}
@@ -1196,7 +1137,7 @@ public CommonQueryContract setParameterList(String name, Object[] values) {
11961137
private <P> void setParameterValues(Object[] values, QueryParameterBinding<P> binding) {
11971138
final var parameterType = binding.getBindType();
11981139
if ( parameterType != null
1199-
&& !areInstances( values, parameterType ) ) {
1140+
&& !areInstances( parameterType, values, getNodeBuilder(), getSession() ) ) {
12001141
throw new QueryArgumentException( "Argument to query parameter has an incompatible type",
12011142
parameterType.getJavaType(), values.getClass().getComponentType(), values );
12021143
}
@@ -1205,11 +1146,6 @@ private <P> void setParameterValues(Object[] values, QueryParameterBinding<P> bi
12051146
binding.setBindValues( List.of( castArray ) );
12061147
}
12071148

1208-
private <P> boolean areInstances(Object[] values, BindableType<? super P> parameterType) {
1209-
return parameterType.getJavaType().isAssignableFrom( values.getClass().getComponentType() )
1210-
|| areInstances( parameterType, values );
1211-
}
1212-
12131149
@Override
12141150
public <P> CommonQueryContract setParameterList(String name, P[] values, Class<P> javaType) {
12151151
final var javaDescriptor = getJavaType( javaType );

0 commit comments

Comments
 (0)