Skip to content

Commit 6bbf589

Browse files
committed
HHH-17355 Support binding single element value for basic plural parameter types
1 parent eebb305 commit 6bbf589

20 files changed

+124
-62
lines changed

hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleNaturalIdMapping.java

+13-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.hibernate.loader.ast.spi.MultiNaturalIdLoader;
2525
import org.hibernate.loader.ast.spi.NaturalIdLoader;
2626
import org.hibernate.metamodel.mapping.AttributeMapping;
27+
import org.hibernate.metamodel.mapping.BasicValuedMapping;
2728
import org.hibernate.metamodel.mapping.EntityMappingType;
2829
import org.hibernate.metamodel.mapping.JdbcMapping;
2930
import org.hibernate.metamodel.mapping.MappingType;
@@ -43,7 +44,8 @@
4344
/**
4445
* Single-attribute NaturalIdMapping implementation
4546
*/
46-
public class SimpleNaturalIdMapping extends AbstractNaturalIdMapping implements JavaType.CoercionContext {
47+
public class SimpleNaturalIdMapping extends AbstractNaturalIdMapping implements JavaType.CoercionContext,
48+
BasicValuedMapping {
4749
private final SingularAttributeMapping attribute;
4850
private final SessionFactoryImplementor sessionFactory;
4951
private final TypeConfiguration typeConfiguration;
@@ -241,6 +243,11 @@ public JdbcMapping getSingleJdbcMapping() {
241243
return attribute.getSingleJdbcMapping();
242244
}
243245

246+
@Override
247+
public JdbcMapping getJdbcMapping() {
248+
return attribute.getSingleJdbcMapping();
249+
}
250+
244251
@Override
245252
public int forEachJdbcType(int offset, IndexedConsumer<JdbcMapping> action) {
246253
return attribute.forEachJdbcType( offset, action );
@@ -317,4 +324,9 @@ public AttributeMapping asAttributeMapping() {
317324
public boolean hasPartitionedSelectionMapping() {
318325
return attribute.hasPartitionedSelectionMapping();
319326
}
327+
328+
@Override
329+
public MappingType getMappedType() {
330+
return attribute.getMappedType();
331+
}
320332
}

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

-8
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
import org.hibernate.query.BindableType;
1515
import org.hibernate.query.QueryArgumentException;
1616
import org.hibernate.query.sqm.SqmExpressible;
17-
import org.hibernate.type.descriptor.java.BasicPluralJavaType;
1817
import org.hibernate.type.descriptor.java.JavaType;
1918

2019
import jakarta.persistence.TemporalType;
@@ -142,13 +141,6 @@ else if ( temporalType != null ) {
142141

143142
return parameterDeclarationIsTemporal && bindIsTemporal;
144143
}
145-
// Allow binding a single element for a basic plural parameter type
146-
else if ( expectedJavaType instanceof BasicPluralJavaType<?> ) {
147-
final JavaType<?> elementJavaType = ( (BasicPluralJavaType<?>) expectedJavaType ).getElementJavaType();
148-
if ( elementJavaType.isInstance( value ) ) {
149-
return true;
150-
}
151-
}
152144

153145
return false;
154146
}

hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/EntityTypeLiteral.java

+14-2
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@
99
import org.hibernate.cache.MutableCacheKeyBuilder;
1010
import org.hibernate.engine.spi.SharedSessionContractImplementor;
1111
import org.hibernate.internal.util.IndexedConsumer;
12+
import org.hibernate.metamodel.mapping.BasicValuedMapping;
1213
import org.hibernate.metamodel.mapping.DiscriminatorType;
1314
import org.hibernate.metamodel.mapping.JdbcMapping;
1415
import org.hibernate.metamodel.mapping.MappingModelExpressible;
16+
import org.hibernate.metamodel.mapping.MappingType;
1517
import org.hibernate.persister.entity.EntityPersister;
1618
import org.hibernate.persister.entity.Queryable;
1719
import org.hibernate.query.sqm.sql.internal.DomainResultProducer;
@@ -27,7 +29,7 @@
2729
* @author Steve Ebersole
2830
*/
2931
public class EntityTypeLiteral
30-
implements Expression, MappingModelExpressible<Object>, DomainResultProducer<Object>, JavaTypedExpressible<Object> {
32+
implements Expression, DomainResultProducer<Object>, BasicValuedMapping {
3133
private final EntityPersister entityTypeDescriptor;
3234
private final DiscriminatorType<?> discriminatorType;
3335

@@ -41,13 +43,23 @@ public EntityPersister getEntityTypeDescriptor() {
4143
}
4244

4345
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
44-
// MappingModelExpressible
46+
// BasicValuedMapping
4547

4648
@Override
4749
public MappingModelExpressible getExpressionType() {
4850
return this;
4951
}
5052

53+
@Override
54+
public JdbcMapping getJdbcMapping() {
55+
return discriminatorType;
56+
}
57+
58+
@Override
59+
public MappingType getMappedType() {
60+
return discriminatorType;
61+
}
62+
5163
@Override
5264
public int getJdbcTypeCount() {
5365
return discriminatorType.getJdbcTypeCount();

hibernate-core/src/main/java/org/hibernate/sql/exec/internal/AbstractJdbcParameter.java

+8-1
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
import org.hibernate.cache.MutableCacheKeyBuilder;
1414
import org.hibernate.engine.spi.SharedSessionContractImplementor;
1515
import org.hibernate.internal.util.IndexedConsumer;
16+
import org.hibernate.metamodel.mapping.BasicValuedMapping;
1617
import org.hibernate.metamodel.mapping.JdbcMapping;
1718
import org.hibernate.metamodel.mapping.MappingModelExpressible;
19+
import org.hibernate.metamodel.mapping.MappingType;
1820
import org.hibernate.metamodel.mapping.SqlExpressible;
1921
import org.hibernate.metamodel.model.domain.internal.BasicTypeImpl;
2022
import org.hibernate.query.BindableType;
@@ -38,7 +40,7 @@
3840
* @author Steve Ebersole
3941
*/
4042
public abstract class AbstractJdbcParameter
41-
implements JdbcParameter, JdbcParameterBinder, MappingModelExpressible, SqlExpressible {
43+
implements JdbcParameter, JdbcParameterBinder, MappingModelExpressible, SqlExpressible, BasicValuedMapping {
4244

4345
private final JdbcMapping jdbcMapping;
4446

@@ -56,6 +58,11 @@ public JdbcMapping getJdbcMapping() {
5658
return jdbcMapping;
5759
}
5860

61+
@Override
62+
public MappingType getMappedType() {
63+
return jdbcMapping;
64+
}
65+
5966
@Override
6067
public void accept(SqlAstWalker sqlTreeWalker) {
6168
sqlTreeWalker.visitParameter( this );

hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcParameterBindings.java

+9-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import java.util.function.BiConsumer;
1212

1313
import org.hibernate.engine.spi.SharedSessionContractImplementor;
14+
import org.hibernate.metamodel.mapping.BasicValuedMapping;
1415
import org.hibernate.metamodel.mapping.Bindable;
1516
import org.hibernate.metamodel.mapping.JdbcMapping;
1617
import org.hibernate.query.internal.BindingTypeHelper;
@@ -74,8 +75,15 @@ default int registerParametersForEachJdbcValue(
7475
Bindable bindable,
7576
JdbcParametersList jdbcParameters,
7677
SharedSessionContractImplementor session) {
78+
final Object valueToBind;
79+
if ( bindable instanceof BasicValuedMapping ) {
80+
valueToBind = ( (BasicValuedMapping) bindable ).getJdbcMapping().getMappedJavaType().wrap( value, session );
81+
}
82+
else {
83+
valueToBind = value;
84+
}
7785
return bindable.forEachJdbcValue(
78-
value,
86+
valueToBind,
7987
offset,
8088
jdbcParameters,
8189
session.getFactory().getTypeConfiguration(),

hibernate-core/src/main/java/org/hibernate/type/BasicArrayType.java

-16
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,8 @@
66
*/
77
package org.hibernate.type;
88

9-
import java.lang.reflect.Array;
109
import java.util.Objects;
1110

12-
import org.hibernate.engine.spi.SharedSessionContractImplementor;
1311
import org.hibernate.type.descriptor.java.JavaType;
1412
import org.hibernate.type.descriptor.jdbc.JdbcType;
1513
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
@@ -56,20 +54,6 @@ public <X> BasicType<X> resolveIndicatedType(JdbcTypeIndicators indicators, Java
5654
return (BasicType<X>) this;
5755
}
5856

59-
@Override
60-
public Object disassemble(Object value, SharedSessionContractImplementor session) {
61-
if ( value == null ) {
62-
return null;
63-
}
64-
if ( baseDescriptor.isInstance( (E) value ) ) {
65-
// Support binding a single element as parameter value
66-
final Object array = Array.newInstance( baseDescriptor.getJavaType(), 1 );
67-
Array.set( array, 0, value );
68-
return array;
69-
}
70-
return value;
71-
}
72-
7357
@Override
7458
public boolean equals(Object o) {
7559
return o == this || o.getClass() == BasicArrayType.class

hibernate-core/src/main/java/org/hibernate/type/BasicCollectionType.java

-17
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,9 @@
66
*/
77
package org.hibernate.type;
88

9-
import java.lang.reflect.Array;
109
import java.util.Collection;
1110
import java.util.Objects;
1211

13-
import org.hibernate.engine.spi.SharedSessionContractImplementor;
1412
import org.hibernate.type.descriptor.java.JavaType;
1513
import org.hibernate.type.descriptor.java.spi.BasicCollectionJavaType;
1614
import org.hibernate.type.descriptor.jdbc.JdbcType;
@@ -77,21 +75,6 @@ public <X> BasicType<X> resolveIndicatedType(JdbcTypeIndicators indicators, Java
7775
return (BasicType<X>) this;
7876
}
7977

80-
@Override
81-
public Object disassemble(Object value, SharedSessionContractImplementor session) {
82-
if ( value == null ) {
83-
return null;
84-
}
85-
if ( baseDescriptor.isInstance( (E) value ) ) {
86-
// Support binding a single element as parameter value
87-
final BasicCollectionJavaType<C, E> javaType = (BasicCollectionJavaType<C, E>) getJavaTypeDescriptor();
88-
final C collection = javaType.getSemantics().instantiateRaw( 1, null );
89-
collection.add( (E) value );
90-
return collection;
91-
}
92-
return value;
93-
}
94-
9578
@Override
9679
public boolean equals(Object o) {
9780
return o == this || o.getClass() == BasicCollectionType.class

hibernate-core/src/main/java/org/hibernate/type/ConvertedBasicArrayType.java

-16
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,8 @@
66
*/
77
package org.hibernate.type;
88

9-
import java.lang.reflect.Array;
109
import java.util.Objects;
1110

12-
import org.hibernate.engine.spi.SharedSessionContractImplementor;
1311
import org.hibernate.type.descriptor.ValueBinder;
1412
import org.hibernate.type.descriptor.ValueExtractor;
1513
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
@@ -78,20 +76,6 @@ public <X> BasicType<X> resolveIndicatedType(JdbcTypeIndicators indicators, Java
7876
return (BasicType<X>) this;
7977
}
8078

81-
@Override
82-
public Object disassemble(Object value, SharedSessionContractImplementor session) {
83-
if ( value == null ) {
84-
return null;
85-
}
86-
if ( baseDescriptor.isInstance( (E) value ) ) {
87-
// Support binding a single element as parameter value
88-
final Object array = Array.newInstance( baseDescriptor.getJavaType(), 1 );
89-
Array.set( array, 0, value );
90-
return array;
91-
}
92-
return value;
93-
}
94-
9579
@Override
9680
public BasicValueConverter<T, ?> getValueConverter() {
9781
return converter;

hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayJavaType.java

+8
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,14 @@ else if ( value instanceof BinaryStream ) {
320320
// When the value is a BinaryStream, this is a deserialization request
321321
return fromBytes( ( (BinaryStream) value ).getBytes() );
322322
}
323+
else if ( getElementJavaType().isInstance( value ) ) {
324+
// Support binding a single element as parameter value
325+
//noinspection unchecked
326+
final T[] wrapped = (T[]) java.lang.reflect.Array.newInstance( getElementJavaType().getJavaTypeClass(), 1 );
327+
//noinspection unchecked
328+
wrapped[0] = (T) value;
329+
return wrapped;
330+
}
323331

324332
throw unknownWrap( value.getClass() );
325333
}

hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanPrimitiveArrayJavaType.java

+4
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,10 @@ else if ( value.getClass().isArray() ) {
168168
}
169169
return wrapped;
170170
}
171+
else if ( value instanceof Boolean ) {
172+
// Support binding a single element as parameter value
173+
return new boolean[]{ (boolean) value };
174+
}
171175

172176
throw unknownWrap( value.getClass() );
173177
}

hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DoublePrimitiveArrayJavaType.java

+4
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,10 @@ else if ( value.getClass().isArray() ) {
168168
}
169169
return wrapped;
170170
}
171+
else if ( value instanceof Double ) {
172+
// Support binding a single element as parameter value
173+
return new double[]{ (double) value };
174+
}
171175

172176
throw unknownWrap( value.getClass() );
173177
}

hibernate-core/src/main/java/org/hibernate/type/descriptor/java/FloatPrimitiveArrayJavaType.java

+4
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,10 @@ else if ( value.getClass().isArray() ) {
168168
}
169169
return wrapped;
170170
}
171+
else if ( value instanceof Float ) {
172+
// Support binding a single element as parameter value
173+
return new float[]{ (float) value };
174+
}
171175

172176
throw unknownWrap( value.getClass() );
173177
}

hibernate-core/src/main/java/org/hibernate/type/descriptor/java/IntegerPrimitiveArrayJavaType.java

+4
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,10 @@ else if ( value.getClass().isArray() ) {
168168
}
169169
return wrapped;
170170
}
171+
else if ( value instanceof Integer ) {
172+
// Support binding a single element as parameter value
173+
return new int[]{ (int) value };
174+
}
171175

172176
throw unknownWrap( value.getClass() );
173177
}

hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LongPrimitiveArrayJavaType.java

+4
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,10 @@ else if ( value.getClass().isArray() ) {
168168
}
169169
return wrapped;
170170
}
171+
else if ( value instanceof Long ) {
172+
// Support binding a single element as parameter value
173+
return new long[]{ (long) value };
174+
}
171175

172176
throw unknownWrap( value.getClass() );
173177
}

hibernate-core/src/main/java/org/hibernate/type/descriptor/java/PrimitiveByteArrayJavaType.java

+4
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,10 @@ public <X> byte[] wrap(X value, WrapperOptions options) {
136136
throw new HibernateException( "Unable to access lob stream", e );
137137
}
138138
}
139+
else if ( value instanceof Byte ) {
140+
// Support binding a single element as parameter value
141+
return new byte[]{ (byte) value };
142+
}
139143

140144
throw unknownWrap( value.getClass() );
141145
}

hibernate-core/src/main/java/org/hibernate/type/descriptor/java/PrimitiveCharacterArrayJavaType.java

+4
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ public <X> char[] wrap(X value, WrapperOptions options) {
9090
if (value instanceof Reader) {
9191
return DataHelper.extractString( ( (Reader) value ) ).toCharArray();
9292
}
93+
else if ( value instanceof Character ) {
94+
// Support binding a single element as parameter value
95+
return new char[]{ (char) value };
96+
}
9397
throw unknownWrap( value.getClass() );
9498
}
9599

hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ShortPrimitiveArrayJavaType.java

+4
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,10 @@ else if ( value.getClass().isArray() ) {
168168
}
169169
return wrapped;
170170
}
171+
else if ( value instanceof Short ) {
172+
// Support binding a single element as parameter value
173+
return new short[]{ (short) value };
174+
}
171175

172176
throw unknownWrap( value.getClass() );
173177
}

hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/BasicCollectionJavaType.java

+7
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,13 @@ else if ( value.getClass().isArray() ) {
463463
}
464464
return wrapped;
465465
}
466+
else if ( getElementJavaType().isInstance( value ) ) {
467+
// Support binding a single element as parameter value
468+
final C wrapped = semantics.instantiateRaw( 1, null );
469+
//noinspection unchecked
470+
wrapped.add( (E) value );
471+
return wrapped;
472+
}
466473

467474
throw unknownWrap( value.getClass() );
468475
}

hibernate-core/src/test/java/org/hibernate/orm/test/function/array/ArrayConstructorTest.java

+2
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,10 @@ public void testEmpty(SessionFactoryScope scope) {
7070
@Test
7171
public void testNonExisting(SessionFactoryScope scope) {
7272
scope.inSession( em -> {
73+
//tag::hql-array-example[]
7374
List<EntityWithArrays> results = em.createQuery( "from EntityWithArrays e where e.theArray = array('abc')", EntityWithArrays.class )
7475
.getResultList();
76+
//end::hql-array-example[]
7577
assertEquals( 0, results.size() );
7678
} );
7779
}

0 commit comments

Comments
 (0)