HHH-17355 Support binding single element value for basic plural parameter types
This commit is contained in:
parent
eebb305837
commit
6bbf58973e
|
@ -24,6 +24,7 @@ import org.hibernate.loader.ast.internal.SimpleNaturalIdLoader;
|
|||
import org.hibernate.loader.ast.spi.MultiNaturalIdLoader;
|
||||
import org.hibernate.loader.ast.spi.NaturalIdLoader;
|
||||
import org.hibernate.metamodel.mapping.AttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.BasicValuedMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.mapping.MappingType;
|
||||
|
@ -43,7 +44,8 @@ import static org.hibernate.loader.ast.internal.MultiKeyLoadHelper.supportsSqlAr
|
|||
/**
|
||||
* Single-attribute NaturalIdMapping implementation
|
||||
*/
|
||||
public class SimpleNaturalIdMapping extends AbstractNaturalIdMapping implements JavaType.CoercionContext {
|
||||
public class SimpleNaturalIdMapping extends AbstractNaturalIdMapping implements JavaType.CoercionContext,
|
||||
BasicValuedMapping {
|
||||
private final SingularAttributeMapping attribute;
|
||||
private final SessionFactoryImplementor sessionFactory;
|
||||
private final TypeConfiguration typeConfiguration;
|
||||
|
@ -241,6 +243,11 @@ public class SimpleNaturalIdMapping extends AbstractNaturalIdMapping implements
|
|||
return attribute.getSingleJdbcMapping();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdbcMapping getJdbcMapping() {
|
||||
return attribute.getSingleJdbcMapping();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int forEachJdbcType(int offset, IndexedConsumer<JdbcMapping> action) {
|
||||
return attribute.forEachJdbcType( offset, action );
|
||||
|
@ -317,4 +324,9 @@ public class SimpleNaturalIdMapping extends AbstractNaturalIdMapping implements
|
|||
public boolean hasPartitionedSelectionMapping() {
|
||||
return attribute.hasPartitionedSelectionMapping();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MappingType getMappedType() {
|
||||
return attribute.getMappedType();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|||
import org.hibernate.query.BindableType;
|
||||
import org.hibernate.query.QueryArgumentException;
|
||||
import org.hibernate.query.sqm.SqmExpressible;
|
||||
import org.hibernate.type.descriptor.java.BasicPluralJavaType;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
||||
import jakarta.persistence.TemporalType;
|
||||
|
@ -142,13 +141,6 @@ public class QueryParameterBindingValidator {
|
|||
|
||||
return parameterDeclarationIsTemporal && bindIsTemporal;
|
||||
}
|
||||
// Allow binding a single element for a basic plural parameter type
|
||||
else if ( expectedJavaType instanceof BasicPluralJavaType<?> ) {
|
||||
final JavaType<?> elementJavaType = ( (BasicPluralJavaType<?>) expectedJavaType ).getElementJavaType();
|
||||
if ( elementJavaType.isInstance( value ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -9,9 +9,11 @@ package org.hibernate.sql.ast.tree.expression;
|
|||
import org.hibernate.cache.MutableCacheKeyBuilder;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.internal.util.IndexedConsumer;
|
||||
import org.hibernate.metamodel.mapping.BasicValuedMapping;
|
||||
import org.hibernate.metamodel.mapping.DiscriminatorType;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.mapping.MappingModelExpressible;
|
||||
import org.hibernate.metamodel.mapping.MappingType;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.persister.entity.Queryable;
|
||||
import org.hibernate.query.sqm.sql.internal.DomainResultProducer;
|
||||
|
@ -27,7 +29,7 @@ import org.hibernate.type.descriptor.java.JavaTypedExpressible;
|
|||
* @author Steve Ebersole
|
||||
*/
|
||||
public class EntityTypeLiteral
|
||||
implements Expression, MappingModelExpressible<Object>, DomainResultProducer<Object>, JavaTypedExpressible<Object> {
|
||||
implements Expression, DomainResultProducer<Object>, BasicValuedMapping {
|
||||
private final EntityPersister entityTypeDescriptor;
|
||||
private final DiscriminatorType<?> discriminatorType;
|
||||
|
||||
|
@ -41,13 +43,23 @@ public class EntityTypeLiteral
|
|||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// MappingModelExpressible
|
||||
// BasicValuedMapping
|
||||
|
||||
@Override
|
||||
public MappingModelExpressible getExpressionType() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdbcMapping getJdbcMapping() {
|
||||
return discriminatorType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MappingType getMappedType() {
|
||||
return discriminatorType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getJdbcTypeCount() {
|
||||
return discriminatorType.getJdbcTypeCount();
|
||||
|
|
|
@ -13,8 +13,10 @@ import java.sql.SQLException;
|
|||
import org.hibernate.cache.MutableCacheKeyBuilder;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.internal.util.IndexedConsumer;
|
||||
import org.hibernate.metamodel.mapping.BasicValuedMapping;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.mapping.MappingModelExpressible;
|
||||
import org.hibernate.metamodel.mapping.MappingType;
|
||||
import org.hibernate.metamodel.mapping.SqlExpressible;
|
||||
import org.hibernate.metamodel.model.domain.internal.BasicTypeImpl;
|
||||
import org.hibernate.query.BindableType;
|
||||
|
@ -38,7 +40,7 @@ import org.hibernate.type.spi.TypeConfiguration;
|
|||
* @author Steve Ebersole
|
||||
*/
|
||||
public abstract class AbstractJdbcParameter
|
||||
implements JdbcParameter, JdbcParameterBinder, MappingModelExpressible, SqlExpressible {
|
||||
implements JdbcParameter, JdbcParameterBinder, MappingModelExpressible, SqlExpressible, BasicValuedMapping {
|
||||
|
||||
private final JdbcMapping jdbcMapping;
|
||||
|
||||
|
@ -56,6 +58,11 @@ public abstract class AbstractJdbcParameter
|
|||
return jdbcMapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MappingType getMappedType() {
|
||||
return jdbcMapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(SqlAstWalker sqlTreeWalker) {
|
||||
sqlTreeWalker.visitParameter( this );
|
||||
|
|
|
@ -11,6 +11,7 @@ import java.util.Collections;
|
|||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.metamodel.mapping.BasicValuedMapping;
|
||||
import org.hibernate.metamodel.mapping.Bindable;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.query.internal.BindingTypeHelper;
|
||||
|
@ -74,8 +75,15 @@ public interface JdbcParameterBindings {
|
|||
Bindable bindable,
|
||||
JdbcParametersList jdbcParameters,
|
||||
SharedSessionContractImplementor session) {
|
||||
final Object valueToBind;
|
||||
if ( bindable instanceof BasicValuedMapping ) {
|
||||
valueToBind = ( (BasicValuedMapping) bindable ).getJdbcMapping().getMappedJavaType().wrap( value, session );
|
||||
}
|
||||
else {
|
||||
valueToBind = value;
|
||||
}
|
||||
return bindable.forEachJdbcValue(
|
||||
value,
|
||||
valueToBind,
|
||||
offset,
|
||||
jdbcParameters,
|
||||
session.getFactory().getTypeConfiguration(),
|
||||
|
|
|
@ -6,10 +6,8 @@
|
|||
*/
|
||||
package org.hibernate.type;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
|
||||
|
@ -56,20 +54,6 @@ public class BasicArrayType<T,E>
|
|||
return (BasicType<X>) this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object disassemble(Object value, SharedSessionContractImplementor session) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
if ( baseDescriptor.isInstance( (E) value ) ) {
|
||||
// Support binding a single element as parameter value
|
||||
final Object array = Array.newInstance( baseDescriptor.getJavaType(), 1 );
|
||||
Array.set( array, 0, value );
|
||||
return array;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return o == this || o.getClass() == BasicArrayType.class
|
||||
|
|
|
@ -6,11 +6,9 @@
|
|||
*/
|
||||
package org.hibernate.type;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.java.spi.BasicCollectionJavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
|
@ -77,21 +75,6 @@ public class BasicCollectionType<C extends Collection<E>, E>
|
|||
return (BasicType<X>) this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object disassemble(Object value, SharedSessionContractImplementor session) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
if ( baseDescriptor.isInstance( (E) value ) ) {
|
||||
// Support binding a single element as parameter value
|
||||
final BasicCollectionJavaType<C, E> javaType = (BasicCollectionJavaType<C, E>) getJavaTypeDescriptor();
|
||||
final C collection = javaType.getSemantics().instantiateRaw( 1, null );
|
||||
collection.add( (E) value );
|
||||
return collection;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return o == this || o.getClass() == BasicCollectionType.class
|
||||
|
|
|
@ -6,10 +6,8 @@
|
|||
*/
|
||||
package org.hibernate.type;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.type.descriptor.ValueBinder;
|
||||
import org.hibernate.type.descriptor.ValueExtractor;
|
||||
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
|
||||
|
@ -78,20 +76,6 @@ public class ConvertedBasicArrayType<T,S,E>
|
|||
return (BasicType<X>) this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object disassemble(Object value, SharedSessionContractImplementor session) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
if ( baseDescriptor.isInstance( (E) value ) ) {
|
||||
// Support binding a single element as parameter value
|
||||
final Object array = Array.newInstance( baseDescriptor.getJavaType(), 1 );
|
||||
Array.set( array, 0, value );
|
||||
return array;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BasicValueConverter<T, ?> getValueConverter() {
|
||||
return converter;
|
||||
|
|
|
@ -320,6 +320,14 @@ public class ArrayJavaType<T> extends AbstractArrayJavaType<T[], T> {
|
|||
// When the value is a BinaryStream, this is a deserialization request
|
||||
return fromBytes( ( (BinaryStream) value ).getBytes() );
|
||||
}
|
||||
else if ( getElementJavaType().isInstance( value ) ) {
|
||||
// Support binding a single element as parameter value
|
||||
//noinspection unchecked
|
||||
final T[] wrapped = (T[]) java.lang.reflect.Array.newInstance( getElementJavaType().getJavaTypeClass(), 1 );
|
||||
//noinspection unchecked
|
||||
wrapped[0] = (T) value;
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
throw unknownWrap( value.getClass() );
|
||||
}
|
||||
|
|
|
@ -168,6 +168,10 @@ public class BooleanPrimitiveArrayJavaType extends AbstractArrayJavaType<boolean
|
|||
}
|
||||
return wrapped;
|
||||
}
|
||||
else if ( value instanceof Boolean ) {
|
||||
// Support binding a single element as parameter value
|
||||
return new boolean[]{ (boolean) value };
|
||||
}
|
||||
|
||||
throw unknownWrap( value.getClass() );
|
||||
}
|
||||
|
|
|
@ -168,6 +168,10 @@ public class DoublePrimitiveArrayJavaType extends AbstractArrayJavaType<double[]
|
|||
}
|
||||
return wrapped;
|
||||
}
|
||||
else if ( value instanceof Double ) {
|
||||
// Support binding a single element as parameter value
|
||||
return new double[]{ (double) value };
|
||||
}
|
||||
|
||||
throw unknownWrap( value.getClass() );
|
||||
}
|
||||
|
|
|
@ -168,6 +168,10 @@ public class FloatPrimitiveArrayJavaType extends AbstractArrayJavaType<float[],
|
|||
}
|
||||
return wrapped;
|
||||
}
|
||||
else if ( value instanceof Float ) {
|
||||
// Support binding a single element as parameter value
|
||||
return new float[]{ (float) value };
|
||||
}
|
||||
|
||||
throw unknownWrap( value.getClass() );
|
||||
}
|
||||
|
|
|
@ -168,6 +168,10 @@ public class IntegerPrimitiveArrayJavaType extends AbstractArrayJavaType<int[],
|
|||
}
|
||||
return wrapped;
|
||||
}
|
||||
else if ( value instanceof Integer ) {
|
||||
// Support binding a single element as parameter value
|
||||
return new int[]{ (int) value };
|
||||
}
|
||||
|
||||
throw unknownWrap( value.getClass() );
|
||||
}
|
||||
|
|
|
@ -168,6 +168,10 @@ public class LongPrimitiveArrayJavaType extends AbstractArrayJavaType<long[], Lo
|
|||
}
|
||||
return wrapped;
|
||||
}
|
||||
else if ( value instanceof Long ) {
|
||||
// Support binding a single element as parameter value
|
||||
return new long[]{ (long) value };
|
||||
}
|
||||
|
||||
throw unknownWrap( value.getClass() );
|
||||
}
|
||||
|
|
|
@ -136,6 +136,10 @@ public class PrimitiveByteArrayJavaType extends AbstractClassJavaType<byte[]>
|
|||
throw new HibernateException( "Unable to access lob stream", e );
|
||||
}
|
||||
}
|
||||
else if ( value instanceof Byte ) {
|
||||
// Support binding a single element as parameter value
|
||||
return new byte[]{ (byte) value };
|
||||
}
|
||||
|
||||
throw unknownWrap( value.getClass() );
|
||||
}
|
||||
|
|
|
@ -90,6 +90,10 @@ public class PrimitiveCharacterArrayJavaType extends AbstractClassJavaType<char[
|
|||
if (value instanceof Reader) {
|
||||
return DataHelper.extractString( ( (Reader) value ) ).toCharArray();
|
||||
}
|
||||
else if ( value instanceof Character ) {
|
||||
// Support binding a single element as parameter value
|
||||
return new char[]{ (char) value };
|
||||
}
|
||||
throw unknownWrap( value.getClass() );
|
||||
}
|
||||
|
||||
|
|
|
@ -168,6 +168,10 @@ public class ShortPrimitiveArrayJavaType extends AbstractArrayJavaType<short[],
|
|||
}
|
||||
return wrapped;
|
||||
}
|
||||
else if ( value instanceof Short ) {
|
||||
// Support binding a single element as parameter value
|
||||
return new short[]{ (short) value };
|
||||
}
|
||||
|
||||
throw unknownWrap( value.getClass() );
|
||||
}
|
||||
|
|
|
@ -463,6 +463,13 @@ public class BasicCollectionJavaType<C extends Collection<E>, E> extends Abstrac
|
|||
}
|
||||
return wrapped;
|
||||
}
|
||||
else if ( getElementJavaType().isInstance( value ) ) {
|
||||
// Support binding a single element as parameter value
|
||||
final C wrapped = semantics.instantiateRaw( 1, null );
|
||||
//noinspection unchecked
|
||||
wrapped.add( (E) value );
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
throw unknownWrap( value.getClass() );
|
||||
}
|
||||
|
|
|
@ -70,8 +70,10 @@ public class ArrayConstructorTest {
|
|||
@Test
|
||||
public void testNonExisting(SessionFactoryScope scope) {
|
||||
scope.inSession( em -> {
|
||||
//tag::hql-array-example[]
|
||||
List<EntityWithArrays> results = em.createQuery( "from EntityWithArrays e where e.theArray = array('abc')", EntityWithArrays.class )
|
||||
.getResultList();
|
||||
//end::hql-array-example[]
|
||||
assertEquals( 0, results.size() );
|
||||
} );
|
||||
}
|
||||
|
|
|
@ -101,6 +101,35 @@ public class CriteriaToBigDecimalTest {
|
|||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToBigDecimal3(EntityManagerFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
entityManager -> {
|
||||
final CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
|
||||
final CriteriaQuery<Object> query = criteriaBuilder.createQuery();
|
||||
final Root<Person> person = query.from( Person.class );
|
||||
query.select( criteriaBuilder.sum( person.get( "ageAsBigDecimal" ), 1 ) );
|
||||
|
||||
BigDecimal result = (BigDecimal) entityManager.createQuery( query ).getSingleResult();
|
||||
|
||||
assertEquals( new BigDecimal( "21" ), result.stripTrailingZeros() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToBigDecimal4(EntityManagerFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
entityManager -> {
|
||||
BigDecimal result = (BigDecimal) entityManager.createQuery( "select p.ageAsBigDecimal + :val from Person p" )
|
||||
.setParameter( "val", 1 )
|
||||
.getSingleResult();
|
||||
|
||||
assertEquals( new BigDecimal( "21" ), result.stripTrailingZeros() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Entity(name = "Person")
|
||||
public static class Person {
|
||||
@Id
|
||||
|
@ -109,6 +138,7 @@ public class CriteriaToBigDecimalTest {
|
|||
private String name;
|
||||
|
||||
private Integer age;
|
||||
private BigDecimal ageAsBigDecimal;
|
||||
|
||||
Person() {
|
||||
}
|
||||
|
@ -117,6 +147,7 @@ public class CriteriaToBigDecimalTest {
|
|||
this.id = id;
|
||||
this.name = name;
|
||||
this.age = age;
|
||||
this.ageAsBigDecimal = new BigDecimal( age );
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue