66
77import java .util .Collection ;
88
9+ import org .checkerframework .checker .nullness .qual .NonNull ;
910import org .hibernate .engine .spi .SessionFactoryImplementor ;
1011import org .hibernate .query .QueryArgumentException ;
12+ import org .hibernate .query .QueryParameter ;
1113import org .hibernate .type .BindableType ;
1214
1315import static org .hibernate .query .internal .QueryArguments .areInstances ;
@@ -21,30 +23,41 @@ class QueryParameterBindingValidator {
2123 private QueryParameterBindingValidator () {
2224 }
2325
24- public static void validate (BindableType <?> parameterType , Object argument , SessionFactoryImplementor factory ) {
26+ public static void validate (
27+ QueryParameter <?> parameter ,
28+ BindableType <?> parameterType ,
29+ Object argument ,
30+ SessionFactoryImplementor factory ) {
2531 if ( argument != null && parameterType != null ) {
2632 final var parameterJavaType = getParameterJavaType ( parameterType , factory );
2733 if ( parameterJavaType != null ) {
34+ final var criteriaBuilder = factory .getQueryEngine ().getCriteriaBuilder ();
2835 if ( argument instanceof Collection <?> collection
2936 && !Collection .class .isAssignableFrom ( parameterJavaType ) ) {
3037 // We have a collection passed in where we were expecting a non-collection.
3138 // NOTE: This can happen in Hibernate's notion of "parameter list" binding.
3239 // NOTE2: The case of a collection value and an expected collection
3340 // (if that can even happen) will fall through to the main check.
34- validateCollectionValuedParameterBinding ( parameterType , parameterJavaType , collection , factory );
41+ if ( !areInstances ( parameterType , collection , criteriaBuilder ) ) {
42+ throw queryArgumentException ( parameterJavaType , collection , parameter );
43+ }
3544 }
36- else if ( argument .getClass ().isArray () ) {
37- validateArrayValuedParameterBinding ( parameterJavaType , argument );
45+ else if ( !argument .getClass ().isArray () ) {
46+ // assume single-valued argument
47+ if ( !isInstance ( parameterType , argument , criteriaBuilder ) ) {
48+ throw queryArgumentException ( parameterJavaType , argument , parameter );
49+ }
3850 }
3951 else {
40- validateSingleValuedParameterBinding ( parameterType , parameterJavaType , argument , factory );
52+ validateArrayValuedParameterBinding ( parameterJavaType , argument , parameter );
4153 }
4254 }
4355 // else nothing we can check
4456 }
4557 }
4658
47- private static Class <?> getParameterJavaType (BindableType <?> parameterType , SessionFactoryImplementor factory ) {
59+ private static Class <?> getParameterJavaType (
60+ BindableType <?> parameterType , SessionFactoryImplementor factory ) {
4861 final var javaType = parameterType .getJavaType ();
4962 return javaType != null
5063 ? javaType
@@ -53,30 +66,37 @@ private static Class<?> getParameterJavaType(BindableType<?> parameterType, Sess
5366 .getJavaType ();
5467 }
5568
56- private static void validateSingleValuedParameterBinding (
57- BindableType <?> parameterType , Class <?> parameterJavaType ,
58- Object value ,
59- SessionFactoryImplementor factory ) {
60- if ( !isInstance ( parameterType , value ,
61- factory .getQueryEngine ().getCriteriaBuilder () ) ) {
62- throw new QueryArgumentException ( "Argument did not match parameter type" ,
69+ private static @ NonNull QueryArgumentException queryArgumentException (
70+ Class <?> parameterJavaType , Object value , QueryParameter <?> parameter ) {
71+ if ( parameter .isNamed () ) {
72+ return new QueryArgumentException ( "Argument to parameter named '"
73+ + parameter .getName () + "' has an element with an incompatible type" ,
74+ parameterJavaType , value );
75+ }
76+ else {
77+ return new QueryArgumentException ( "Argument to parameter at position "
78+ + parameter .isOrdinal () + " has an element with an incompatible type" ,
6379 parameterJavaType , value );
6480 }
6581 }
6682
67- private static void validateCollectionValuedParameterBinding (
68- BindableType <?> parameterType , Class <?> parameterJavaType ,
69- Collection <?> values ,
70- SessionFactoryImplementor factory ) {
71- if ( !areInstances ( parameterType , values ,
72- factory .getQueryEngine ().getCriteriaBuilder () ) ) {
73- throw new QueryArgumentException ( "Collection-valued argument did not match parameter type" ,
83+ private static @ NonNull QueryArgumentException queryArgumentException (
84+ Class <?> parameterJavaType , Collection <?> values , QueryParameter <?> parameter ) {
85+ if ( parameter .isNamed () ) {
86+ return new QueryArgumentException ( "Collection-values argument to parameter named '"
87+ + parameter .getName () + "' has an incompatible type" ,
88+ parameterJavaType , values );
89+ }
90+ else {
91+ return new QueryArgumentException ( "Collection-values argument to parameter at position "
92+ + parameter .isOrdinal () + " has has an incompatible type" ,
7493 parameterJavaType , values );
75-
7694 }
7795 }
7896
79- private static void validateArrayValuedParameterBinding (Class <?> parameterType , Object value ) {
97+ private static void validateArrayValuedParameterBinding (
98+ Class <?> parameterType , Object value , QueryParameter <?> parameter ) {
99+ // TODO: improve the error messages using the given parameter info
80100 if ( !parameterType .isArray () ) {
81101 throw new QueryArgumentException ( "Unexpected array-valued parameter binding" ,
82102 parameterType , value );
0 commit comments