Skip to content

Commit 2c5004d

Browse files
committed
fix most of the unsoundness
1 parent 2a2b995 commit 2c5004d

File tree

4 files changed

+70
-34
lines changed

4 files changed

+70
-34
lines changed

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

Lines changed: 50 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import java.util.Collection;
88

9+
import org.checkerframework.checker.nullness.qual.NonNull;
910
import org.checkerframework.checker.nullness.qual.Nullable;
1011
import org.hibernate.HibernateException;
1112
import org.hibernate.engine.spi.SessionFactoryImplementor;
@@ -131,27 +132,27 @@ public void setBindValue(Object value, boolean resolveJdbcTypeIfNecessary) {
131132
}
132133

133134
@Override
134-
public void setBindValue(Object value, @Nullable BindableType<?> clarifiedType) {
135+
public <A> void setBindValue(A value, @Nullable BindableType<A> clarifiedType) {
135136
if ( !handleAsMultiValue( value, clarifiedType ) ) {
136-
if ( clarifiedType != null ) {
137-
// UNSOUND!!!
138-
// We should be creating a whole new object
139-
bindType = (BindableType<T>) clarifiedType;
140-
}
141-
142137
final Object coerced = coerce( value );
143138
validate( coerced );
144-
bindValue( 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+
}
145150
}
146151
}
147152

148153
@Override
149154
public void setBindValue(Object value, @SuppressWarnings("deprecation") TemporalType temporalTypePrecision) {
150155
if ( !handleAsMultiValue( value, null ) ) {
151-
if ( bindType == null ) {
152-
bindType = queryParameter.getHibernateType();
153-
}
154-
155156
final Object coerced = coerce( value );
156157
validate( coerced );
157158
bindValue( coerced );
@@ -167,6 +168,8 @@ private void bindValue(Object value) {
167168
}
168169
bindValue = cast( value );
169170
isBound = true;
171+
isMultiValued = false;
172+
bindValues = null;
170173
}
171174

172175
private void bindNull(boolean resolveJdbcTypeIfNecessary) {
@@ -219,11 +222,9 @@ public void setBindValues(Collection<?> values) {
219222
}
220223

221224
final var coerced = values.stream().map( this::coerce ).toList();
222-
values.forEach( this::validate );
223-
225+
coerced.forEach( this::validate );
224226
isBound = true;
225227
isMultiValued = true;
226-
227228
bindValue = null;
228229
bindValues = coerced.stream().map( this::cast ).toList();
229230

@@ -245,12 +246,26 @@ public void setBindValues(Collection<?> values) {
245246
}
246247

247248
@Override
248-
public void setBindValues(Collection<?> values, BindableType<?> clarifiedType) {
249+
public <V> void setBindValues(Collection<? extends V> values, BindableType<V> clarifiedType) {
250+
if ( !queryParameter.allowsMultiValuedBinding() ) {
251+
throw new IllegalArgumentException(
252+
"Illegal attempt to bind a collection value to a single-valued parameter"
253+
);
254+
}
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+
249263
if ( clarifiedType != null ) {
250-
// UNSOUND
251-
bindType = (BindableType<T>) clarifiedType;
264+
checkClarifiedType( clarifiedType, values );
265+
@SuppressWarnings("unchecked") // safe
266+
final var newType = (BindableType<T>) clarifiedType;
267+
bindType = newType;
252268
}
253-
setBindValues( values );
254269
}
255270

256271
@Override
@@ -300,6 +315,22 @@ else if ( type instanceof BasicValuedMapping basicValuedMapping ) {
300315
return false;
301316
}
302317

318+
private <A> void checkClarifiedType(@NonNull BindableType<A> clarifiedType, Object value) {
319+
final var parameterType = queryParameter.getParameterType();
320+
if ( parameterType != null ) {
321+
final var clarifiedJavaType = clarifiedType.getJavaType();
322+
if ( !parameterType.isAssignableFrom( clarifiedJavaType ) ) {
323+
throw new QueryArgumentException(
324+
"Given type is incompatible with parameter type",
325+
parameterType, clarifiedJavaType, value
326+
);
327+
}
328+
}
329+
else {
330+
assert queryParameter.getHibernateType() == null;
331+
}
332+
}
333+
303334
private T cast(Object value) {
304335
final var bindableType = getCriteriaBuilder().resolveExpressible( bindType );
305336
return bindableType == null

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ default void setBindValue(Object value) {
6666
* @param value The bind value
6767
* @param clarifiedType The explicit Type to use
6868
*/
69-
void setBindValue(Object value, @Nullable BindableType<?> clarifiedType);
69+
<A> void setBindValue(A value, @Nullable BindableType<A> clarifiedType);
7070

7171
/**
7272
* Sets the parameter binding value using the explicit TemporalType.
@@ -95,7 +95,7 @@ default void setBindValue(Object value) {
9595
* @param values The bind values
9696
* @param clarifiedType The explicit Type to use
9797
*/
98-
void setBindValues(Collection<?> values, BindableType<?> clarifiedType);
98+
<A> void setBindValues(Collection<? extends A> values, BindableType<A> clarifiedType);
9999

100100
/**Sets the parameter binding value using the explicit TemporalType in regards to the individual values.
101101
*

hibernate-core/src/main/java/org/hibernate/query/sqm/internal/AbstractSqmSelectionQuery.java

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import org.hibernate.engine.spi.SharedSessionContractImplementor;
1010
import org.hibernate.query.spi.QueryParameterImplementor;
1111
import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter;
12+
import org.hibernate.query.sqm.tree.expression.SqmParameter;
1213
import org.hibernate.query.sqm.tree.expression.ValueBindJpaCriteriaParameter;
1314
import org.hibernate.query.KeyedPage;
1415
import org.hibernate.query.KeyedResultList;
@@ -142,18 +143,22 @@ protected static void bindValueBindCriteriaParameters(
142143
DomainParameterXref domainParameterXref,
143144
QueryParameterBindings bindings) {
144145
for ( var entry : domainParameterXref.getQueryParameters().entrySet() ) {
145-
final var sqmParameter = entry.getValue().get( 0 );
146-
if ( sqmParameter instanceof SqmJpaCriteriaParameterWrapper<?> wrapper ) {
147-
final var criteriaParameter =
148-
(JpaCriteriaParameter<?>)
149-
wrapper.getJpaCriteriaParameter();
150-
if ( criteriaParameter instanceof ValueBindJpaCriteriaParameter<?> ) {
151-
// Use the anticipated type for binding the value if possible
152-
final var parameter = (QueryParameterImplementor<?>) entry.getKey();
153-
bindings.getBinding( parameter )
154-
.setBindValue( criteriaParameter.getValue(),
155-
criteriaParameter.getAnticipatedType() );
156-
}
146+
bindValueToCriteriaParameter( bindings, entry.getKey(),
147+
entry.getValue().get( 0 ) );
148+
}
149+
}
150+
151+
private static <T> void bindValueToCriteriaParameter(
152+
QueryParameterBindings bindings,
153+
QueryParameterImplementor<?> queryParameterImplementor,
154+
SqmParameter<T> sqmParameter) {
155+
if ( sqmParameter instanceof SqmJpaCriteriaParameterWrapper<T> wrapper ) {
156+
final var criteriaParameter = wrapper.getJpaCriteriaParameter();
157+
if ( criteriaParameter instanceof ValueBindJpaCriteriaParameter<T> ) {
158+
// Use the anticipated type for binding the value if possible
159+
bindings.getBinding( queryParameterImplementor )
160+
.setBindValue( criteriaParameter.getValue(),
161+
criteriaParameter.getAnticipatedType() );
157162
}
158163
}
159164
}

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( "incompatible type" ) );
455+
assertTrue( e.getMessage().contains( "incompatible" ) );
456456
}
457457
}
458458
);

0 commit comments

Comments
 (0)