Skip to content

Commit d7b5bd3

Browse files
committed
rethink of the previous approach to query parameter binding validation
1 parent 603e896 commit d7b5bd3

File tree

2 files changed

+67
-79
lines changed

2 files changed

+67
-79
lines changed

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

Lines changed: 66 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
4444
import org.hibernate.type.BindableType;
4545
import org.hibernate.type.descriptor.java.JavaType;
46+
import org.hibernate.type.descriptor.java.TemporalJavaType;
4647
import org.hibernate.type.spi.TypeConfiguration;
4748

4849
import java.time.Instant;
@@ -702,9 +703,11 @@ private <P> JavaType<P> getJavaType(Class<P> javaType) {
702703
.resolveDescriptor( javaType );
703704
}
704705

705-
protected <P> QueryParameterBinding<P> locateBinding(Parameter<P> parameter, P value) {
706+
protected <P> QueryParameterBinding<P> locateBinding(Parameter<P> parameter, Object value) {
706707
if ( parameter instanceof QueryParameterImplementor<P> parameterImplementor ) {
707-
return locateBinding( parameterImplementor );
708+
getCheckOpen();
709+
return getQueryParameterBindings()
710+
.getBinding( getQueryParameter( parameterImplementor ) );
708711
}
709712
else {
710713
final String name = parameter.getName();
@@ -723,43 +726,17 @@ else if ( position != null ) {
723726
);
724727
}
725728

726-
protected <P> QueryParameterBinding<P> locateBinding(Parameter<P> parameter, Collection<? extends P> values) {
727-
if ( parameter instanceof QueryParameterImplementor<P> parameterImplementor ) {
728-
return locateBinding( parameterImplementor );
729-
}
730-
else {
731-
final String name = parameter.getName();
732-
final Integer position = parameter.getPosition();
733-
final var parameterType = parameter.getParameterType();
734-
if ( name != null ) {
735-
return locateBinding( name, parameterType, values );
736-
}
737-
else if ( position != null ) {
738-
return locateBinding( position, parameterType, values );
739-
}
740-
}
741-
742-
throw getExceptionConverter().convert(
743-
new IllegalArgumentException( "Could not resolve binding for given parameter reference [" + parameter + "]" )
744-
);
745-
}
746-
747-
protected <P> QueryParameterBinding<P> locateBinding(QueryParameterImplementor<P> parameter) {
748-
getCheckOpen();
749-
return getQueryParameterBindings()
750-
.getBinding( getQueryParameter( parameter ) );
751-
}
752-
753-
protected <P> QueryParameterBinding<P> locateBinding(String name, Class<P> javaType, P value) {
729+
protected <P> QueryParameterBinding<P> locateBinding(String name, Class<P> javaType, Object value) {
754730
getCheckOpen();
755731
final var binding = getQueryParameterBindings().getBinding( name );
756732
final var parameterType = binding.getBindType();
757-
if ( parameterType != null ) {
733+
if ( parameterType != null && value != null ) {
758734
final var parameterJavaType = parameterType.getJavaType();
759735
if ( !parameterJavaType.isAssignableFrom( javaType )
760-
&& !isInstance( parameterType, value, getNodeBuilder() ) ) {
736+
&& !( java.util.Date.class.isAssignableFrom( parameterJavaType )
737+
&& javaType == java.sql.Timestamp.class ) ) {
761738
throw new QueryArgumentException(
762-
"Argument to parameter named '" + name + "' has an incompatible type",
739+
"Parameter named '" + name + "' has an incompatible type",
763740
parameterJavaType, javaType, value );
764741
}
765742
}
@@ -768,58 +745,72 @@ protected <P> QueryParameterBinding<P> locateBinding(String name, Class<P> javaT
768745
return castBinding;
769746
}
770747

771-
protected <P> QueryParameterBinding<P> locateBinding(int position, Class<P> javaType, P value) {
748+
protected QueryParameterBinding<?> locateBinding(String name, TemporalType temporalType, Object value) {
772749
getCheckOpen();
773-
final var binding = getQueryParameterBindings().getBinding( position );
750+
final var binding = getQueryParameterBindings().getBinding( name );
774751
final var parameterType = binding.getBindType();
775-
if ( parameterType != null ) {
776-
final var parameterJavaType = parameterType.getJavaType();
777-
if ( !parameterJavaType.isAssignableFrom( javaType )
778-
&& !isInstance( parameterType, value, getNodeBuilder() ) ) {
779-
throw new QueryArgumentException(
780-
"Argument to parameter at position " + position + " has an incompatible type",
781-
parameterJavaType, javaType, value );
782-
}
752+
if ( parameterType != null && value != null
753+
&& !isCompatibleTemporal( temporalType, parameterType ) ) {
754+
throw new QueryArgumentException(
755+
"Parameter named '" + name + "' has an incompatible type",
756+
parameterType.getJavaType(), temporalJavaType( temporalType ), value );
783757
}
784-
@SuppressWarnings("unchecked") // safe, just checked
785-
var castBinding = (QueryParameterBinding<P>) binding;
786-
return castBinding;
758+
return binding;
787759
}
788760

789-
protected <P> QueryParameterBinding<P> locateBinding(String name, Class<P> javaType, Collection<? extends P> values) {
761+
protected <P> QueryParameterBinding<P> locateBinding(int position, Class<P> javaType, Object value) {
790762
getCheckOpen();
791-
final var binding = getQueryParameterBindings().getBinding( name );
763+
final var binding = getQueryParameterBindings().getBinding( position );
792764
final var parameterType = binding.getBindType();
793-
if ( parameterType != null ) {
765+
if ( parameterType != null && value != null ) {
794766
final var parameterJavaType = parameterType.getJavaType();
795-
if ( !parameterJavaType.isAssignableFrom( javaType )
796-
&& !areInstances( parameterType, values, getNodeBuilder() ) ) {
767+
if ( !parameterJavaType.isAssignableFrom( javaType ) ) {
797768
throw new QueryArgumentException(
798-
"Argument to parameter named '" + name + "' has an incompatible type",
799-
parameterJavaType, javaType, values );
769+
"Parameter at position " + position + " has an incompatible type",
770+
parameterJavaType, javaType, value );
800771
}
801772
}
802773
@SuppressWarnings("unchecked") // safe, just checked
803774
var castBinding = (QueryParameterBinding<P>) binding;
804775
return castBinding;
805776
}
806777

807-
protected <P> QueryParameterBinding<P> locateBinding(int position, Class<P> javaType, Collection<? extends P> values) {
778+
protected QueryParameterBinding<?> locateBinding(int position, TemporalType temporalType, Object value) {
808779
getCheckOpen();
809780
final var binding = getQueryParameterBindings().getBinding( position );
810781
final var parameterType = binding.getBindType();
811-
if ( parameterType != null ) {
812-
final var parameterJavaType = parameterType.getJavaType();
813-
if ( !parameterJavaType.isAssignableFrom( javaType )
814-
&& !areInstances( parameterType, values, getNodeBuilder() ) ) {
815-
throw new QueryArgumentException(
816-
"Argument to parameter at position " + position + " has an incompatible type",
817-
parameterJavaType, javaType, values );
818-
}
782+
if ( parameterType != null && value != null
783+
&& !isCompatibleTemporal( temporalType, parameterType ) ) {
784+
throw new QueryArgumentException(
785+
"Parameter at position '" + position + "' has an incompatible type",
786+
parameterType.getJavaType(), temporalJavaType( temporalType ), value );
819787
}
820-
@SuppressWarnings("unchecked") // safe, just checked
821-
var castBinding = (QueryParameterBinding<P>) binding;
822-
return castBinding;
788+
return binding;
789+
}
790+
791+
@SuppressWarnings("deprecation")
792+
private boolean isCompatibleTemporal(TemporalType temporalType, BindableType<?> parameterType) {
793+
if ( parameterType.resolveExpressible( getNodeBuilder() ).getExpressibleJavaType()
794+
instanceof TemporalJavaType<?> temporalJavaType ) {
795+
return switch ( temporalJavaType.getPrecision() ) {
796+
// we have tests that assert I can compare anything with a timestamp
797+
case TIMESTAMP -> true; // temporalType == TemporalType.TIMESTAMP;
798+
case DATE -> temporalType != TemporalType.TIME;
799+
case TIME -> temporalType != TemporalType.DATE;
800+
};
801+
}
802+
else {
803+
return false;
804+
}
805+
}
806+
807+
private static Class<?> temporalJavaType(
808+
@SuppressWarnings("deprecation") TemporalType temporalType) {
809+
return switch ( temporalType ) {
810+
case DATE -> java.sql.Date.class;
811+
case TIME -> java.sql.Time.class;
812+
case TIMESTAMP -> java.sql.Timestamp.class;
813+
};
823814
}
824815

825816
protected <P> QueryParameterImplementor<P> getQueryParameter(QueryParameterImplementor<P> parameter) {
@@ -958,7 +949,7 @@ public <P> CommonQueryContract setParameter(String name, P value, Type<P> type)
958949

959950
@Override @Deprecated(since = "7")
960951
public CommonQueryContract setParameter(String name, Instant value, TemporalType temporalType) {
961-
locateBinding( name, Instant.class, value ).setBindValue( value, temporalType );
952+
locateBinding( name, temporalType, value ).setBindValue( value, temporalType );
962953
return this;
963954
}
964955

@@ -1004,14 +995,13 @@ public <P> CommonQueryContract setParameter(int position, P value, Type<P> type)
1004995

1005996
@Override @Deprecated(since = "7")
1006997
public CommonQueryContract setParameter(int position, Instant value, TemporalType temporalType) {
1007-
locateBinding( position, Instant.class, value ).setBindValue( value, temporalType );
998+
locateBinding( position, temporalType, value ).setBindValue( value, temporalType );
1008999
return this;
10091000
}
10101001

10111002
@Override
10121003
public <P> CommonQueryContract setParameter(QueryParameter<P> parameter, P value) {
1013-
locateBinding( parameter, value )
1014-
.setBindValue( value, resolveJdbcParameterTypeIfNecessary() );
1004+
locateBinding( parameter, value ).setBindValue( value, resolveJdbcParameterTypeIfNecessary() );
10151005
return this;
10161006
}
10171007

@@ -1029,8 +1019,7 @@ public <P> CommonQueryContract setParameter(QueryParameter<P> parameter, P value
10291019

10301020
@Override
10311021
public <P> CommonQueryContract setParameter(QueryParameter<P> parameter, P value, Type<P> type) {
1032-
locateBinding( parameter, value )
1033-
.setBindValue( value, (BindableType<P>) type );
1022+
locateBinding( parameter, value ).setBindValue( value, (BindableType<P>) type );
10341023
return this;
10351024
}
10361025

@@ -1074,31 +1063,31 @@ public CommonQueryContract setParameter(Parameter<Calendar> param, Calendar valu
10741063

10751064
@Override @Deprecated
10761065
public CommonQueryContract setParameter(Parameter<Date> param, Date value, TemporalType temporalType) {
1077-
locateBinding( param, value ).setBindValue( value, temporalType );
1066+
locateBinding( param.getName(), temporalType, value ).setBindValue( value, temporalType );
10781067
return this;
10791068
}
10801069

10811070
@Override @Deprecated
10821071
public CommonQueryContract setParameter(String name, Calendar value, TemporalType temporalType) {
1083-
locateBinding( name, Calendar.class, value ).setBindValue( value, temporalType );
1072+
locateBinding( name, temporalType, value ).setBindValue( value, temporalType );
10841073
return this;
10851074
}
10861075

10871076
@Override @Deprecated
10881077
public CommonQueryContract setParameter(String name, Date value, TemporalType temporalType) {
1089-
locateBinding( name, Date.class, value ).setBindValue( value, temporalType );
1078+
locateBinding( name, temporalType, value ).setBindValue( value, temporalType );
10901079
return this;
10911080
}
10921081

10931082
@Override @Deprecated
10941083
public CommonQueryContract setParameter(int position, Calendar value, TemporalType temporalType) {
1095-
locateBinding( position, Calendar.class, value ).setBindValue( value, temporalType );
1084+
locateBinding( position, temporalType, value ).setBindValue( value, temporalType );
10961085
return this;
10971086
}
10981087

10991088
@Override @Deprecated
11001089
public CommonQueryContract setParameter(int position, Date value, TemporalType temporalType) {
1101-
locateBinding( position, Date.class, value ).setBindValue( value, temporalType );
1090+
locateBinding( position, temporalType, value ).setBindValue( value, temporalType );
11021091
return this;
11031092
}
11041093

@@ -1122,8 +1111,7 @@ public <P> CommonQueryContract setParameterList(String name, Collection<? extend
11221111

11231112
@Override
11241113
public <P> CommonQueryContract setParameterList(String name, Collection<? extends P> values, Type<P> type) {
1125-
locateBinding( name, type.getJavaType(), values )
1126-
.setBindValues( values, (BindableType<P>) type );
1114+
locateBinding( name, type.getJavaType(), values ).setBindValues( values, (BindableType<P>) type );
11271115
return this;
11281116
}
11291117

hibernate-core/src/test/java/org/hibernate/orm/test/procedure/StoredProcedureParameterTypeTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,7 @@ public void testTypedParameterValueInParameterWithNotSpecifiedType(SessionFactor
452452
procedureCall.setParameter( 1, TypedParameterValue.of( StandardBasicTypes.INTEGER, 1 ) );
453453
}
454454
catch (IllegalArgumentException e) {
455-
assertTrue( e.getMessage().contains( "was not of specified type" ) );
455+
assertTrue( e.getMessage().contains( "incompatible type" ) );
456456
}
457457
}
458458
);

0 commit comments

Comments
 (0)