Support for type coercion for values passed as ids and as query parameter bindings

- widening coercions
- valid (no over/under flow) narrowing coercions
This commit is contained in:
Steve Ebersole 2021-04-29 13:02:26 -05:00
parent 27662f91a9
commit d95806b516
19 changed files with 1266 additions and 44 deletions

View File

@ -35,6 +35,8 @@ import org.hibernate.resource.jdbc.spi.JdbcSessionOwner;
import org.hibernate.resource.transaction.spi.TransactionCoordinator;
import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder.Options;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Defines the internal contract shared between {@link org.hibernate.Session} and
@ -66,7 +68,7 @@ import org.hibernate.type.descriptor.WrapperOptions;
* @author Steve Ebersole
*/
public interface SharedSessionContractImplementor
extends SharedSessionContract, JdbcSessionOwner, Options, LobCreationContext, WrapperOptions, QueryProducerImplementor {
extends SharedSessionContract, JdbcSessionOwner, Options, LobCreationContext, WrapperOptions, QueryProducerImplementor, JavaTypeDescriptor.CoercionContext {
// todo : this is the shared contract between Session and StatelessSession, but it defines methods that StatelessSession does not implement
// (it just throws UnsupportedOperationException). To me it seems like it is better to properly isolate those methods
@ -82,6 +84,11 @@ public interface SharedSessionContractImplementor
*/
SessionFactoryImplementor getFactory();
@Override
default TypeConfiguration getTypeConfiguration() {
return getFactory().getTypeConfiguration();
}
SessionEventListenerManager getEventListenerManager();
/**

View File

@ -27,11 +27,13 @@ import org.hibernate.graph.spi.RootGraphImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.LazyInitializer;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
/**
* @author Steve Ebersole
*/
public class IdentifierLoadAccessImpl<T> implements IdentifierLoadAccess<T> {
public class IdentifierLoadAccessImpl<T> implements IdentifierLoadAccess<T>, JavaTypeDescriptor.CoercionContext {
private final LoadAccessContext context;
private final EntityPersister entityPersister;
@ -114,6 +116,8 @@ public class IdentifierLoadAccessImpl<T> implements IdentifierLoadAccess<T> {
final EventSource eventSource = (EventSource) session;
final LoadQueryInfluencers loadQueryInfluencers = session.getLoadQueryInfluencers();
id = entityPersister.getIdentifierMapping().getJavaTypeDescriptor().coerce( id, this );
if ( this.lockOptions != null ) {
LoadEvent event = new LoadEvent( id, entityPersister.getEntityName(), lockOptions, eventSource, loadQueryInfluencers.getReadOnly() );
context.fireLoad( event, LoadEventListener.LOAD );
@ -154,6 +158,8 @@ public class IdentifierLoadAccessImpl<T> implements IdentifierLoadAccess<T> {
final EventSource eventSource = (EventSource) session;
final LoadQueryInfluencers loadQueryInfluencers = session.getLoadQueryInfluencers();
id = entityPersister.getIdentifierMapping().getJavaTypeDescriptor().coerce( id, this );
if ( this.lockOptions != null ) {
LoadEvent event = new LoadEvent( id, entityPersister.getEntityName(), lockOptions, eventSource, loadQueryInfluencers.getReadOnly() );
context.fireLoad( event, LoadEventListener.GET );
@ -206,4 +212,9 @@ public class IdentifierLoadAccessImpl<T> implements IdentifierLoadAccess<T> {
( (EnhancementAsProxyLazinessInterceptor) interceptor ).forceInitialize( result, null );
}
}
@Override
public TypeConfiguration getTypeConfiguration() {
return context.getSession().getSessionFactory().getTypeConfiguration();
}
}

View File

@ -124,7 +124,7 @@ public class MultiIdLoaderStandard<T> implements MultiIdEntityLoader<T> {
final List<Integer> elementPositionsLoadedByBatch = new ArrayList<>();
for ( int i = 0; i < ids.length; i++ ) {
final Object id = ids[i];
final Object id = entityDescriptor.getIdentifierMapping().getJavaTypeDescriptor().coerce( ids[i], session );
final EntityKey entityKey = new EntityKey( id, entityDescriptor );
if ( loadOptions.isSessionCheckingEnabled() || loadOptions.isSecondLevelCacheCheckingEnabled() ) {
@ -175,7 +175,7 @@ public class MultiIdLoaderStandard<T> implements MultiIdEntityLoader<T> {
// if we did not hit any of the continues above, then we need to batch
// load the entity state.
idsInBatch.add( ids[i] );
idsInBatch.add( id );
if ( idsInBatch.size() >= maxBatchSize ) {
// we've hit the allotted max-batch-size, perform an "intermediate load"
@ -352,7 +352,7 @@ public class MultiIdLoaderStandard<T> implements MultiIdEntityLoader<T> {
final List<Object> nonManagedIds = new ArrayList<>();
for ( int i = 0; i < ids.length; i++ ) {
final Object id = ids[ i ];
final Object id = entityDescriptor.getIdentifierMapping().getJavaTypeDescriptor().coerce( ids[ i ], session );
final EntityKey entityKey = new EntityKey( id, entityDescriptor );
LoadEvent loadEvent = new LoadEvent(

View File

@ -8,6 +8,7 @@ package org.hibernate.metamodel.mapping.internal;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.function.BiConsumer;
@ -32,12 +33,14 @@ import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Single-attribute NaturalIdMapping implementation
*/
public class SimpleNaturalIdMapping extends AbstractNaturalIdMapping {
public class SimpleNaturalIdMapping extends AbstractNaturalIdMapping implements JavaTypeDescriptor.CoercionContext {
private final SingularAttributeMapping attribute;
private final TypeConfiguration typeConfiguration;
public SimpleNaturalIdMapping(
SingularAttributeMapping attribute,
@ -48,6 +51,11 @@ public class SimpleNaturalIdMapping extends AbstractNaturalIdMapping {
attribute.getAttributeMetadataAccess().resolveAttributeMetadata( declaringType ).isUpdatable()
);
this.attribute = attribute;
typeConfiguration = creationProcess.getCreationContext()
.getSessionFactory()
.getTypeConfiguration();
}
@Override
@ -112,15 +120,21 @@ public class SimpleNaturalIdMapping extends AbstractNaturalIdMapping {
if ( ! getJavaTypeDescriptor().getJavaTypeClass().isInstance( naturalIdValue ) ) {
throw new IllegalArgumentException(
"Incoming natural-id value [" + naturalIdValue + "] is not of expected type ["
+ getJavaTypeDescriptor().getJavaType().getTypeName() + "]"
String.format(
Locale.ROOT,
"Incoming natural-id value [%s (`%s`)] is not of expected type [`%s`] and could not be coerced",
naturalIdValue,
naturalIdValue.getClass().getName(),
getJavaTypeDescriptor().getJavaType().getTypeName()
)
);
}
}
@Override
public int calculateHashCode(Object value, SharedSessionContractImplementor session) {
return 0;
//noinspection unchecked
return value == null ? 0 : ( (JavaTypeDescriptor<Object>) getJavaTypeDescriptor() ).extractHashCode( value );
}
@Override
@ -134,16 +148,16 @@ public class SimpleNaturalIdMapping extends AbstractNaturalIdMapping {
final Map valueMap = (Map) naturalIdToLoad;
assert valueMap.size() == 1;
assert valueMap.containsKey( getAttribute().getAttributeName() );
return valueMap.get( getAttribute().getAttributeName() );
return getJavaTypeDescriptor().coerce( valueMap.get( getAttribute().getAttributeName() ), this );
}
if ( naturalIdToLoad instanceof Object[] ) {
final Object[] values = (Object[]) naturalIdToLoad;
assert values.length == 1;
return values[0];
return getJavaTypeDescriptor().coerce( values[0], this );
}
return naturalIdToLoad;
return getJavaTypeDescriptor().coerce( naturalIdToLoad, this );
}
public SingularAttributeMapping getAttribute() {
@ -247,4 +261,9 @@ public class SimpleNaturalIdMapping extends AbstractNaturalIdMapping {
public MultiNaturalIdLoader<?> makeMultiLoader(EntityMappingType entityDescriptor) {
return new MultiNaturalIdLoaderStandard<>( entityDescriptor );
}
@Override
public TypeConfiguration getTypeConfiguration() {
return typeConfiguration;
}
}

View File

@ -19,6 +19,8 @@ import org.hibernate.query.QueryParameter;
import org.hibernate.query.spi.QueryParameterBinding;
import org.hibernate.query.spi.QueryParameterBindingTypeResolver;
import org.hibernate.query.spi.QueryParameterBindingValidator;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
/**
@ -26,7 +28,7 @@ import org.hibernate.type.spi.TypeConfiguration;
*
* @author Steve Ebersole
*/
public class QueryParameterBindingImpl<T> implements QueryParameterBinding<T> {
public class QueryParameterBindingImpl<T> implements QueryParameterBinding<T>, JavaTypeDescriptor.CoercionContext {
private final QueryParameter<T> queryParameter;
private final QueryParameterBindingTypeResolver typeResolver;
private final boolean isBindingValidationRequired;
@ -103,6 +105,13 @@ public class QueryParameterBindingImpl<T> implements QueryParameterBinding<T> {
return;
}
if ( bindType != null ) {
value = bindType.getExpressableJavaTypeDescriptor().coerce( value, this );
}
else if ( queryParameter.getHibernateType() != null ) {
value = queryParameter.getHibernateType().getExpressableJavaTypeDescriptor().coerce( value, this );
}
if ( isBindingValidationRequired ) {
validate( value );
}
@ -148,15 +157,22 @@ public class QueryParameterBindingImpl<T> implements QueryParameterBinding<T> {
return;
}
if ( clarifiedType != null ) {
this.bindType = clarifiedType;
}
if ( bindType != null ) {
value = bindType.getExpressableJavaTypeDescriptor().coerce( value, this );
}
else if ( queryParameter.getHibernateType() != null ) {
value = queryParameter.getHibernateType().getExpressableJavaTypeDescriptor().coerce( value, this );
}
if ( isBindingValidationRequired ) {
validate( value, clarifiedType );
}
bindValue( value );
if ( clarifiedType != null ) {
this.bindType = clarifiedType;
}
}
@Override
@ -165,16 +181,23 @@ public class QueryParameterBindingImpl<T> implements QueryParameterBinding<T> {
return;
}
if ( bindType == null ) {
bindType = queryParameter.getHibernateType();
}
if ( bindType != null ) {
value = bindType.getExpressableJavaTypeDescriptor().coerce( value, this );
}
else if ( queryParameter.getHibernateType() != null ) {
value = queryParameter.getHibernateType().getExpressableJavaTypeDescriptor().coerce( value, this );
}
if ( isBindingValidationRequired ) {
validate( value, temporalTypePrecision );
}
bindValue( value );
if ( bindType == null ) {
bindType = queryParameter.getHibernateType();
}
if ( bindType != null ) {
bindType = (AllowableParameterType) BindingTypeHelper.INSTANCE.resolveDateTemporalTypeVariant(
bindType.getExpressableJavaTypeDescriptor().getJavaTypeClass(),
@ -221,10 +244,10 @@ public class QueryParameterBindingImpl<T> implements QueryParameterBinding<T> {
@Override
public void setBindValues(Collection<T> values, AllowableParameterType<T> clarifiedType) {
setBindValues( values );
if ( clarifiedType != null ) {
this.bindType = clarifiedType;
}
setBindValues( values );
}
@Override
@ -237,7 +260,7 @@ public class QueryParameterBindingImpl<T> implements QueryParameterBinding<T> {
this.bindType = BindingTypeHelper.INSTANCE.resolveTemporalPrecision(
temporalTypePrecision,
bindType,
typeConfiguration
getTypeConfiguration()
);
this.explicitTemporalPrecision = temporalTypePrecision;
@ -273,4 +296,9 @@ public class QueryParameterBindingImpl<T> implements QueryParameterBinding<T> {
private void validate(T value, TemporalType clarifiedTemporalType) {
QueryParameterBindingValidator.INSTANCE.validate( getBindType(), value, clarifiedTemporalType );
}
@Override
public TypeConfiguration getTypeConfiguration() {
return typeResolver.getTypeConfiguration();
}
}

View File

@ -57,7 +57,6 @@ import org.hibernate.property.access.spi.BuiltInPropertyAccessStrategies;
import org.hibernate.property.access.spi.Getter;
import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.query.IllegalQueryOperationException;
import org.hibernate.query.ParameterMetadata;
import org.hibernate.query.QueryLogging;
import org.hibernate.query.QueryParameter;
import org.hibernate.query.ResultListTransformer;

View File

@ -54,7 +54,6 @@ public interface QueryParameterBinding<T> {
/**
* Sets the parameter binding value using the explicit Type.
*
* @param value The bind value
* @param clarifiedType The explicit Type to use
*/
@ -62,7 +61,6 @@ public interface QueryParameterBinding<T> {
/**
* Sets the parameter binding value using the explicit TemporalType.
*
* @param value The bind value
* @param temporalTypePrecision The temporal type to use
*/
@ -78,14 +76,13 @@ public interface QueryParameterBinding<T> {
/**
* Sets the parameter binding values. The inherent parameter type (if known) is assumed in regards to the
* individual values.
* @param values The bind values
*
* @param values The bind values
*/
void setBindValues(Collection<T> values);
/**
* Sets the parameter binding values using the explicit Type in regards to the individual values.
*
* @param values The bind values
* @param clarifiedType The explicit Type to use
*/
@ -93,8 +90,7 @@ public interface QueryParameterBinding<T> {
/**Sets the parameter binding value using the explicit TemporalType in regards to the individual values.
*
*
* @param values The bind values
* @param values The bind values
* @param temporalTypePrecision The temporal type to use
*/
void setBindValues(Collection<T> values, TemporalType temporalTypePrecision, TypeConfiguration typeConfiguration);

View File

@ -8,6 +8,7 @@ package org.hibernate.type.descriptor.java;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Locale;
import org.hibernate.dialect.Dialect;
import org.hibernate.type.descriptor.WrapperOptions;
@ -78,13 +79,13 @@ public class BigDecimalTypeDescriptor extends AbstractClassTypeDescriptor<BigDec
if ( value == null ) {
return null;
}
if ( BigDecimal.class.isInstance( value ) ) {
if ( value instanceof BigDecimal ) {
return (BigDecimal) value;
}
if ( BigInteger.class.isInstance( value ) ) {
if ( value instanceof BigInteger ) {
return new BigDecimal( (BigInteger) value );
}
if ( Number.class.isInstance( value ) ) {
if ( value instanceof Number ) {
return BigDecimal.valueOf( ( (Number) value ).doubleValue() );
}
throw unknownWrap( value.getClass() );
@ -99,4 +100,30 @@ public class BigDecimalTypeDescriptor extends AbstractClassTypeDescriptor<BigDec
public int getDefaultSqlPrecision(Dialect dialect) {
return dialect.getDefaultDecimalPrecision();
}
@Override
public <X> BigDecimal coerce(X value, CoercionContext coercionContext) {
if ( value == null ) {
return null;
}
if ( value instanceof Number ) {
return BigDecimal.valueOf( ( (Number) value ).doubleValue() );
}
if ( value instanceof String ) {
return CoercionHelper.coerceWrappingError(
() -> BigDecimal.valueOf( Double.parseDouble( (String) value ) )
);
}
throw new CoercionException(
String.format(
Locale.ROOT,
"Unable to coerce value [%s (%s)] to BigDecimal",
value,
value.getClass().getName()
)
);
}
}

View File

@ -8,6 +8,7 @@ package org.hibernate.type.descriptor.java;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Locale;
import org.hibernate.dialect.Dialect;
import org.hibernate.type.descriptor.WrapperOptions;
@ -82,13 +83,13 @@ public class BigIntegerTypeDescriptor extends AbstractClassTypeDescriptor<BigInt
if ( value == null ) {
return null;
}
if ( BigInteger.class.isInstance( value ) ) {
if ( value instanceof BigInteger ) {
return (BigInteger) value;
}
if ( BigDecimal.class.isInstance( value ) ) {
if ( value instanceof BigDecimal ) {
return ( (BigDecimal) value ).toBigIntegerExact();
}
if ( Number.class.isInstance( value ) ) {
if ( value instanceof Number ) {
return BigInteger.valueOf( ( (Number) value ).longValue() );
}
throw unknownWrap( value.getClass() );
@ -108,4 +109,58 @@ public class BigIntegerTypeDescriptor extends AbstractClassTypeDescriptor<BigInt
public int getDefaultSqlScale() {
return 0;
}
@Override
public <X> BigInteger coerce(X value, CoercionContext coercionContext) {
if ( value == null ) {
return null;
}
if ( value instanceof BigInteger ) {
return (BigInteger) value;
}
if ( value instanceof Byte ) {
return BigInteger.valueOf( ( (byte) value ) );
}
if ( value instanceof Short ) {
return BigInteger.valueOf( ( (short) value ) );
}
if ( value instanceof Integer ) {
return BigInteger.valueOf( ( (int) value ) );
}
if ( value instanceof Long ) {
return BigInteger.valueOf( ( (long) value ) );
}
if ( value instanceof Double ) {
return CoercionHelper.toBigInteger( (Double) value );
}
if ( value instanceof Float ) {
return CoercionHelper.toBigInteger( (Float) value );
}
if ( value instanceof BigDecimal ) {
return CoercionHelper.toBigInteger( (BigDecimal) value );
}
if ( value instanceof String ) {
return CoercionHelper.coerceWrappingError(
() -> BigInteger.valueOf( Long.parseLong( (String) value ) )
);
}
throw new CoercionException(
String.format(
Locale.ROOT,
"Unable to coerce value [%s (%s)] to BigInteger",
value,
value.getClass().getName()
)
);
}
}

View File

@ -5,6 +5,10 @@
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.type.descriptor.java;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Locale;
import org.hibernate.dialect.Dialect;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.spi.Primitive;
@ -66,20 +70,20 @@ public class ByteTypeDescriptor extends AbstractClassTypeDescriptor<Byte> implem
if ( value == null ) {
return null;
}
if ( Byte.class.isInstance( value ) ) {
if ( value instanceof Byte ) {
return (Byte) value;
}
if ( Number.class.isInstance( value ) ) {
if ( value instanceof Number ) {
return ( (Number) value ).byteValue();
}
if ( String.class.isInstance( value ) ) {
if ( value instanceof String ) {
return Byte.valueOf( ( (String) value ) );
}
throw unknownWrap( value.getClass() );
}
@Override
public Class getPrimitiveClass() {
public Class<Byte> getPrimitiveClass() {
return byte.class;
}
@ -102,4 +106,58 @@ public class ByteTypeDescriptor extends AbstractClassTypeDescriptor<Byte> implem
public int getDefaultSqlScale() {
return 0;
}
@Override
public <X> Byte coerce(X value, CoercionContext coercionContext) {
if ( value == null ) {
return null;
}
if ( value instanceof Byte ) {
return (byte) value;
}
if ( value instanceof Short ) {
return CoercionHelper.toByte( (short) value );
}
if ( value instanceof Integer ) {
return CoercionHelper.toByte( (Integer) value );
}
if ( value instanceof Long ) {
return CoercionHelper.toByte( (Long) value );
}
if ( value instanceof Double ) {
return CoercionHelper.toByte( (Double) value );
}
if ( value instanceof Float ) {
return CoercionHelper.toByte( (Float) value );
}
if ( value instanceof BigInteger ) {
return CoercionHelper.toByte( (BigInteger) value );
}
if ( value instanceof BigDecimal ) {
return CoercionHelper.toByte( (BigDecimal) value );
}
if ( value instanceof String ) {
return CoercionHelper.coerceWrappingError(
() -> Byte.parseByte( (String) value )
);
}
throw new CoercionException(
String.format(
Locale.ROOT,
"Cannot coerce value `%s` [%s] as Byte",
value,
value.getClass().getName()
)
);
}
}

View File

@ -0,0 +1,22 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.type.descriptor.java;
import org.hibernate.HibernateException;
/**
* @author Steve Ebersole
*/
public class CoercionException extends HibernateException {
public CoercionException(String message) {
super( message );
}
public CoercionException(String message, Throwable cause) {
super( message, cause );
}
}

View File

@ -0,0 +1,399 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.type.descriptor.java;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Locale;
/**
* Helper for type coercions. Mainly used for narrowing coercions which
* might lead to under/over-flow problems
*
* @author Steve Ebersole
*/
public class CoercionHelper {
private CoercionHelper() {
// disallow direct instantiation
}
public static Byte toByte(Short value) {
if ( value > Byte.MAX_VALUE ) {
throw new CoercionException(
String.format(
Locale.ROOT,
"Cannot coerce Short value `%s` to Byte : overflow",
value
)
);
}
if ( value < Byte.MIN_VALUE ) {
throw new CoercionException(
String.format(
Locale.ROOT,
"Cannot coerce Short value `%s` to Byte : underflow",
value
)
);
}
return value.byteValue();
}
public static Byte toByte(Integer value) {
if ( value > Byte.MAX_VALUE ) {
throw new CoercionException(
String.format(
Locale.ROOT,
"Cannot coerce Integer value `%s` to Byte : overflow",
value
)
);
}
if ( value < Byte.MIN_VALUE ) {
throw new CoercionException(
String.format(
Locale.ROOT,
"Cannot coerce Integer value `%s` to Byte : underflow",
value
)
);
}
return value.byteValue();
}
public static Byte toByte(Long value) {
if ( value > Byte.MAX_VALUE ) {
throw new CoercionException(
String.format(
Locale.ROOT,
"Cannot coerce Long value `%s` to Byte : overflow",
value
)
);
}
if ( value < Byte.MIN_VALUE ) {
throw new CoercionException(
String.format(
Locale.ROOT,
"Cannot coerce Long value `%s` to Byte : underflow",
value
)
);
}
return value.byteValue();
}
public static Byte toByte(Double value) {
if ( ! isWholeNumber( value ) ) {
throw new CoercionException(
String.format(
Locale.ROOT,
"Cannot coerce Double value `%s` to Byte : not a whole number",
value
)
);
}
if ( value > Byte.MAX_VALUE ) {
throw new CoercionException(
String.format(
Locale.ROOT,
"Cannot coerce Double value `%s` to Byte : overflow",
value
)
);
}
if ( value < Byte.MIN_VALUE ) {
throw new CoercionException(
String.format(
Locale.ROOT,
"Cannot coerce Double value `%s` to Byte : underflow",
value
)
);
}
return value.byteValue();
}
public static Byte toByte(Float value) {
if ( ! isWholeNumber( value ) ) {
throw new CoercionException(
String.format(
Locale.ROOT,
"Cannot coerce Float value `%s` to Byte : not a whole number",
value
)
);
}
if ( value > Byte.MAX_VALUE ) {
throw new CoercionException(
String.format(
Locale.ROOT,
"Cannot coerce Float value `%s` to Byte : overflow",
value
)
);
}
if ( value < Byte.MIN_VALUE ) {
throw new CoercionException(
String.format(
Locale.ROOT,
"Cannot coerce Float value `%s` to Byte : underflow",
value
)
);
}
return value.byteValue();
}
public static Byte toByte(BigInteger value) {
return coerceWrappingError( value::byteValueExact );
}
public static Byte toByte(BigDecimal value) {
return coerceWrappingError( value::byteValueExact );
}
public static Short toShort(Byte value) {
return value.shortValue();
}
public static Short toShort(Integer value) {
if ( value > Short.MAX_VALUE ) {
throw new CoercionException( "Cannot coerce Integer value `" + value + "` as Short : overflow" );
}
if ( value < Short.MIN_VALUE ) {
throw new CoercionException( "Cannot coerce Integer value `" + value + "` as Short : underflow" );
}
return value.shortValue();
}
public static Short toShort(Long value) {
if ( value > Short.MAX_VALUE ) {
throw new CoercionException( "Cannot coerce Long value `" + value + "` as Short : overflow" );
}
if ( value < Short.MIN_VALUE ) {
throw new CoercionException( "Cannot coerce Long value `" + value + "` as Short : underflow" );
}
return value.shortValue();
}
public static Short toShort(Double doubleValue) {
if ( ! isWholeNumber( doubleValue ) ) {
throw new CoercionException( "Cannot coerce Double value `" + doubleValue + "` as Short : not a whole number" );
}
return toShort( doubleValue.longValue() );
}
public static Short toShort(Float floatValue) {
if ( ! isWholeNumber( floatValue ) ) {
throw new CoercionException( "Cannot coerce Float value `" + floatValue + "` as Short : not a whole number" );
}
return toShort( floatValue.longValue() );
}
public static Short toShort(BigInteger value) {
return coerceWrappingError( value::shortValueExact );
}
public static Short toShort(BigDecimal value) {
return coerceWrappingError( value::shortValueExact );
}
public static Integer toInteger(Byte value) {
return value.intValue();
}
public static Integer toInteger(Short value) {
return value.intValue();
}
public static Integer toInteger(Long value) {
return coerceWrappingError( () -> Math.toIntExact( value ) );
}
public static Integer toInteger(Double doubleValue) {
if ( ! isWholeNumber( doubleValue ) ) {
throw new CoercionException(
String.format(
Locale.ROOT,
"Unable to coerce Double value `%s` to Integer: not a whole number",
doubleValue
)
);
}
return toInteger( doubleValue.longValue() );
}
public static Integer toInteger(Float floatValue) {
if ( ! isWholeNumber( floatValue ) ) {
throw new CoercionException(
String.format(
Locale.ROOT,
"Unable to coerce Float value `%s` to Integer: not a whole number",
floatValue
)
);
}
return toInteger( floatValue.longValue() );
}
public static Integer toInteger(BigInteger value) {
return coerceWrappingError( value::intValueExact );
}
public static Integer toInteger(BigDecimal value) {
return coerceWrappingError( value::intValueExact );
}
public static Long toLong(Byte value) {
return value.longValue();
}
public static Long toLong(Short value) {
return value.longValue();
}
public static Long toLong(Integer value) {
return value.longValue();
}
public static Long toLong(Double doubleValue) {
if ( ! isWholeNumber( doubleValue ) ) {
throw new CoercionException(
String.format(
Locale.ROOT,
"Unable to coerce Double value `%s` as Integer: not a whole number",
doubleValue
)
);
}
return doubleValue.longValue();
}
public static Long toLong(Float floatValue) {
if ( ! isWholeNumber( floatValue ) ) {
throw new CoercionException(
String.format(
Locale.ROOT,
"Unable to coerce Float value `%s` as Integer: not a whole number",
floatValue
)
);
}
return floatValue.longValue();
}
public static Long toLong(BigInteger value) {
return coerceWrappingError( value::longValueExact );
}
public static Long toLong(BigDecimal value) {
return coerceWrappingError( value::longValueExact );
}
public static BigInteger toBigInteger(Double doubleValue) {
if ( ! isWholeNumber( doubleValue ) ) {
throw new CoercionException(
String.format(
Locale.ROOT,
"Unable to coerce Double value `%s` as BigInteger: not a whole number",
doubleValue
)
);
}
return BigInteger.valueOf( doubleValue.longValue() );
}
public static BigInteger toBigInteger(Float floatValue) {
if ( ! isWholeNumber( floatValue ) ) {
throw new CoercionException(
String.format(
Locale.ROOT,
"Unable to coerce Double Float `%s` as BigInteger: not a whole number",
floatValue
)
);
}
return BigInteger.valueOf( floatValue.longValue() );
}
public static BigInteger toBigInteger(BigDecimal value) {
return coerceWrappingError( value::toBigIntegerExact );
}
public static Double toDouble(Float floatValue) {
if ( floatValue > (float) Double.MAX_VALUE ) {
throw new CoercionException(
String.format(
Locale.ROOT,
"Cannot coerce Float value `%s` to Double : overflow",
floatValue
)
);
}
if ( floatValue < (float) Double.MIN_VALUE ) {
throw new CoercionException(
String.format(
Locale.ROOT,
"Cannot coerce Float value `%s` to Double : underflow",
floatValue
)
);
}
return (double) floatValue;
}
public static Double toDouble(BigInteger value) {
return coerceWrappingError( value::doubleValue );
}
public static Double toDouble(BigDecimal value) {
return coerceWrappingError( value::doubleValue );
}
public static boolean isWholeNumber(double doubleValue) {
return doubleValue % 1 == 0;
}
public static boolean isWholeNumber(float floatValue) {
return floatValue == ( (float) (long) floatValue );
}
@FunctionalInterface
public interface Coercer<T> {
T doCoercion();
}
public static <T> T coerceWrappingError(Coercer<T> coercer) {
try {
return coercer.doCoercion();
}
catch (ArithmeticException | NumberFormatException e) {
throw new CoercionException( "Error coercing value", e );
}
}
}

View File

@ -9,6 +9,7 @@ package org.hibernate.type.descriptor.java;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Types;
import java.util.Locale;
import org.hibernate.dialect.Dialect;
import org.hibernate.type.descriptor.WrapperOptions;
@ -121,4 +122,58 @@ public class DoubleTypeDescriptor extends AbstractClassTypeDescriptor<Double> im
}
@Override
public <X> Double coerce(X value, CoercionContext coercionContext) {
if ( value == null ) {
return null;
}
if ( value instanceof Double ) {
return ( (Double) value );
}
if ( value instanceof Byte ) {
return ( (Byte) value ).doubleValue();
}
if ( value instanceof Short ) {
return ( (Short) value ).doubleValue();
}
if ( value instanceof Integer ) {
return ( (Integer) value ).doubleValue();
}
if ( value instanceof Long ) {
return ( (Long) value ).doubleValue();
}
if ( value instanceof Float ) {
return CoercionHelper.toDouble( (float) value );
}
if ( value instanceof BigInteger ) {
return CoercionHelper.toDouble( (BigInteger) value );
}
if ( value instanceof BigDecimal ) {
return CoercionHelper.toDouble( (BigDecimal) value );
}
if ( value instanceof String ) {
return CoercionHelper.coerceWrappingError(
() -> Double.parseDouble( (String) value )
);
}
throw new CoercionException(
String.format(
Locale.ROOT,
"Cannot coerce value `%s` [%s] as Double",
value,
value.getClass().getName()
)
);
}
}

View File

@ -8,6 +8,7 @@ package org.hibernate.type.descriptor.java;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Locale;
import org.hibernate.dialect.Dialect;
import org.hibernate.type.descriptor.WrapperOptions;
@ -109,4 +110,58 @@ public class FloatTypeDescriptor extends AbstractClassTypeDescriptor<Float> impl
//in a single-precision FP number
return dialect.getFloatPrecision();
}
@Override
public <X> Float coerce(X value, CoercionContext coercionContext) {
if ( value == null ) {
return null;
}
if ( value instanceof Float ) {
return (Float) value;
}
if ( value instanceof Double ) {
return ( (Double) value ).floatValue();
}
if ( value instanceof Byte ) {
return ( (Byte) value ).floatValue();
}
if ( value instanceof Short ) {
return ( (Short) value ).floatValue();
}
if ( value instanceof Integer ) {
return ( (Integer) value ).floatValue();
}
if ( value instanceof Long ) {
return ( (Long) value ).floatValue();
}
if ( value instanceof BigInteger ) {
return ( (BigInteger) value ).floatValue();
}
if ( value instanceof BigDecimal ) {
return ( (BigDecimal) value ).floatValue();
}
if ( value instanceof String ) {
return CoercionHelper.coerceWrappingError(
() -> Float.parseFloat( (String) value )
);
}
throw new CoercionException(
String.format(
Locale.ROOT,
"Cannot coerce value `%s` [%s] as Float",
value,
value.getClass().getName()
)
);
}
}

View File

@ -8,6 +8,7 @@ package org.hibernate.type.descriptor.java;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Locale;
import org.hibernate.dialect.Dialect;
import org.hibernate.type.descriptor.WrapperOptions;
@ -110,4 +111,58 @@ public class IntegerTypeDescriptor extends AbstractClassTypeDescriptor<Integer>
public int getDefaultSqlScale() {
return 0;
}
@Override
public Integer coerce(Object value, CoercionContext coercionContext) {
if ( value == null ) {
return null;
}
if ( value instanceof Integer ) {
return (int) value;
}
if ( value instanceof Short ) {
return CoercionHelper.toInteger( (short) value );
}
if ( value instanceof Byte ) {
return CoercionHelper.toInteger( (byte) value );
}
if ( value instanceof Long ) {
return CoercionHelper.toInteger( (long) value );
}
if ( value instanceof Double ) {
return CoercionHelper.toInteger( (double) value );
}
if ( value instanceof Float ) {
return CoercionHelper.toInteger( (float) value );
}
if ( value instanceof BigInteger ) {
return CoercionHelper.toInteger( (BigInteger) value );
}
if ( value instanceof BigDecimal ) {
return CoercionHelper.toInteger( (BigDecimal) value );
}
if ( value instanceof String ) {
return CoercionHelper.coerceWrappingError(
() -> Integer.parseInt( (String) value )
);
}
throw new CoercionException(
String.format(
Locale.ROOT,
"Cannot coerce vale `%s` [%s] as Integer",
value,
value.getClass().getName()
)
);
}
}

View File

@ -18,6 +18,7 @@ import org.hibernate.internal.util.compare.ComparableComparator;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.jdbc.JdbcTypeDescriptor;
import org.hibernate.type.descriptor.jdbc.JdbcTypeDescriptorIndicators;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Descriptor for the Java side of a value mapping.
@ -179,6 +180,15 @@ public interface JavaTypeDescriptor<T> extends Serializable {
*/
<X> T wrap(X value, WrapperOptions options);
interface CoercionContext {
TypeConfiguration getTypeConfiguration();
}
default <X> T coerce(X value, CoercionContext coercionContext) {
//noinspection unchecked
return (T) value;
}
/**
* Retrieve the Java type handled here.
*

View File

@ -8,6 +8,7 @@ package org.hibernate.type.descriptor.java;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Locale;
import org.hibernate.dialect.Dialect;
import org.hibernate.type.descriptor.WrapperOptions;
@ -74,18 +75,72 @@ public class LongTypeDescriptor extends AbstractClassTypeDescriptor<Long> implem
if ( value == null ) {
return null;
}
if ( Long.class.isInstance( value ) ) {
if ( value instanceof Long ) {
return (Long) value;
}
if ( Number.class.isInstance( value ) ) {
if ( value instanceof Number ) {
return ( (Number) value ).longValue();
}
else if ( String.class.isInstance( value ) ) {
else if ( value instanceof String ) {
return Long.valueOf( ( (String) value ) );
}
throw unknownWrap( value.getClass() );
}
@Override
public <X> Long coerce(X value, CoercionContext coercionContext) {
if ( value == null ) {
return null;
}
if ( value instanceof Long ) {
return ( (Long) value );
}
if ( value instanceof Byte ) {
return CoercionHelper.toLong( (Byte) value );
}
if ( value instanceof Short ) {
return CoercionHelper.toLong( (Short) value );
}
if ( value instanceof Integer ) {
return CoercionHelper.toLong( (Integer) value );
}
if ( value instanceof Double ) {
return CoercionHelper.toLong( (Double) value );
}
if ( value instanceof Float ) {
return CoercionHelper.toLong( (Float) value );
}
if ( value instanceof BigInteger ) {
return CoercionHelper.toLong( (BigInteger) value );
}
if ( value instanceof BigDecimal ) {
return CoercionHelper.toLong( (BigDecimal) value );
}
if ( value instanceof String ) {
return CoercionHelper.coerceWrappingError(
() -> Long.parseLong( (String) value )
);
}
throw new CoercionException(
String.format(
Locale.ROOT,
"Cannot coerce value `%s` [%s] as Long",
value,
value.getClass().getName()
)
);
}
@Override
public Class getPrimitiveClass() {
return long.class;

View File

@ -5,6 +5,10 @@
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.type.descriptor.java;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Locale;
import org.hibernate.dialect.Dialect;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.spi.Primitive;
@ -76,7 +80,7 @@ public class ShortTypeDescriptor extends AbstractClassTypeDescriptor<Short> impl
}
@Override
public Class getPrimitiveClass() {
public Class<Short> getPrimitiveClass() {
return short.class;
}
@ -99,4 +103,58 @@ public class ShortTypeDescriptor extends AbstractClassTypeDescriptor<Short> impl
public int getDefaultSqlScale() {
return 0;
}
@Override
public Short coerce(Object value, CoercionContext coercionContext) {
if ( value == null ) {
return null;
}
if ( value instanceof Short ) {
return (short) value;
}
if ( value instanceof Byte ) {
return CoercionHelper.toShort( (Byte) value );
}
if ( value instanceof Integer ) {
return CoercionHelper.toShort( (Integer) value );
}
if ( value instanceof Long ) {
return CoercionHelper.toShort( (Long) value );
}
if ( value instanceof Double ) {
return CoercionHelper.toShort( (Double) value );
}
if ( value instanceof Float ) {
return CoercionHelper.toShort( (Float) value );
}
if ( value instanceof BigInteger ) {
return CoercionHelper.toShort( (BigInteger) value );
}
if ( value instanceof BigDecimal ) {
return CoercionHelper.toShort( (BigDecimal) value );
}
if ( value instanceof String ) {
return CoercionHelper.coerceWrappingError(
() -> Short.parseShort( (String) value )
);
}
throw new CoercionException(
String.format(
Locale.ROOT,
"Cannot coerce value `%s` [%s] as Short",
value,
value.getClass().getName()
)
);
}
}

View File

@ -0,0 +1,313 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.test.mapping.type.java;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.NaturalId;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.query.spi.QueryImplementor;
import org.hibernate.type.descriptor.java.CoercionException;
import org.hibernate.type.descriptor.java.CoercionHelper;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.java.spi.JavaTypeDescriptorRegistry;
import org.hibernate.type.spi.TypeConfiguration;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.Test;
import org.hamcrest.Matchers;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.fail;
/**
* Tests for implicit widening coercions
*
* @author Steve Ebersole
*/
@DomainModel( annotatedClasses = CoercionTests.TheEntity.class )
@SessionFactory
public class CoercionTests {
final short shortValue = 1;
final byte byteValue = 1;
final int intValue = 1;
final long longValue = 1L;
final double doubleValue = 0.5;
final float floatValue = 0.5F;
final long largeLongValue = Integer.MAX_VALUE + 1L;
final float largeFloatValue = (float) Double.MAX_VALUE + 1.5F;
@Test
public void testCoercibleDetection(SessionFactoryScope scope) {
final TypeConfiguration typeConfiguration = scope.getSessionFactory().getTypeConfiguration();
final JavaTypeDescriptorRegistry jtdRegistry = typeConfiguration.getJavaTypeDescriptorRegistry();
final JavaTypeDescriptor<Integer> integerType = jtdRegistry.resolveDescriptor( Integer.class );
final JavaTypeDescriptor<Long> longType = jtdRegistry.resolveDescriptor( Long.class );
final JavaTypeDescriptor<Double> doubleType = jtdRegistry.resolveDescriptor( Double.class );
final JavaTypeDescriptor<Float> floatType = jtdRegistry.resolveDescriptor( Float.class );
scope.inTransaction(
(session) -> {
checkIntegerConversions( integerType, session );
checkLongConversions( longType, session );
checkDoubleConversions( doubleType, session );
}
);
}
private void checkDoubleConversions(JavaTypeDescriptor<Double> doubleType, SessionImplementor session) {
assertThat( doubleType.coerce( (double) 1, session ), Matchers.is( 1.0 ) );
assertThat( doubleType.coerce( 1F, session ), Matchers.is( 1.0 ) );
assertThat( doubleType.coerce( doubleValue, session ), Matchers.is( doubleValue ) );
assertThat( doubleType.coerce( floatValue, session ), Matchers.is( doubleValue ) );
assertThat( doubleType.coerce( largeFloatValue, session ), Matchers.is( (double) largeFloatValue ) );
assertThat( doubleType.coerce( shortValue, session ), Matchers.is( 1.0 ) );
assertThat( doubleType.coerce( byteValue, session ), Matchers.is( 1.0 ) );
assertThat( doubleType.coerce( longValue, session ), Matchers.is( 1.0 ) );
assertThat( doubleType.coerce( BigInteger.ONE, session ), Matchers.is( 1.0 ) );
assertThat( doubleType.coerce( BigDecimal.ONE, session ), Matchers.is( 1.0 ) );
// negative checks
}
private void checkIntegerConversions(JavaTypeDescriptor<Integer> integerType, SessionImplementor session) {
assertThat( integerType.coerce( intValue, session ), Matchers.is( intValue) );
assertThat( integerType.coerce( shortValue, session ), Matchers.is( intValue) );
assertThat( integerType.coerce( byteValue, session ), Matchers.is( intValue) );
assertThat( integerType.coerce( longValue, session ), Matchers.is( intValue) );
assertThat( integerType.coerce( (double) 1, session ), Matchers.is( intValue) );
assertThat( integerType.coerce( 1F, session ), Matchers.is( intValue) );
assertThat( integerType.coerce( BigInteger.ONE, session ), Matchers.is( intValue) );
assertThat( integerType.coerce( BigDecimal.ONE, session ), Matchers.is( intValue) );
// negative checks
checkDisallowedConversion( () -> integerType.coerce( largeLongValue, session ) );
checkDisallowedConversion( () -> integerType.coerce( largeFloatValue, session ) );
checkDisallowedConversion( () -> integerType.coerce( doubleValue, session ) );
checkDisallowedConversion( () -> integerType.coerce( floatValue, session ) );
}
private void checkLongConversions(JavaTypeDescriptor<Long> longType, SessionImplementor session) {
assertThat( longType.coerce( longValue, session ), Matchers.is( longValue ) );
assertThat( longType.coerce( largeLongValue, session ), Matchers.is( largeLongValue ) );
assertThat( longType.coerce( intValue, session ), Matchers.is( longValue ) );
assertThat( longType.coerce( shortValue, session ), Matchers.is( longValue ) );
assertThat( longType.coerce( byteValue, session ), Matchers.is( longValue ) );
assertThat( longType.coerce( (double) 1, session ), Matchers.is( longValue ) );
assertThat( longType.coerce( 1F, session ), Matchers.is( longValue ) );
assertThat( longType.coerce( BigInteger.ONE, session ), Matchers.is( longValue ) );
assertThat( longType.coerce( BigDecimal.ONE, session ), Matchers.is( longValue ) );
// negative checks
checkDisallowedConversion( () -> longType.coerce( largeFloatValue, session ) );
checkDisallowedConversion( () -> longType.coerce( doubleValue, session ) );
checkDisallowedConversion( () -> longType.coerce( floatValue, session ) );
}
private void checkDisallowedConversion(CoercionHelper.Coercer callback) {
try {
callback.doCoercion();
fail( "Expecting coercion to fail" );
}
catch (CoercionException expected) {
}
}
@Test
public void testLoading(SessionFactoryScope scope) {
scope.inTransaction(
(session) -> {
session.byId( TheEntity.class ).load( 1L );
session.byId( TheEntity.class ).load( (byte) 1 );
session.byId( TheEntity.class ).load( (short) 1 );
session.byId( TheEntity.class ).load( 1 );
session.byId( TheEntity.class ).load( 1.0 );
session.byId( TheEntity.class ).load( 1.0F );
session.byId( TheEntity.class ).load( BigInteger.ONE );
session.byId( TheEntity.class ).load( BigDecimal.ONE );
}
);
scope.inTransaction(
(session) -> {
session.byId( TheEntity.class ).getReference( 1L );
session.byId( TheEntity.class ).getReference( 1 );
}
);
}
@Test
public void testMultiIdLoading(SessionFactoryScope scope) {
scope.inTransaction(
(session) -> {
session.byMultipleIds( TheEntity.class ).multiLoad( 1L );
session.byMultipleIds( TheEntity.class ).multiLoad( (byte) 1 );
session.byMultipleIds( TheEntity.class ).multiLoad( (short) 1 );
session.byMultipleIds( TheEntity.class ).multiLoad( 1 );
session.byMultipleIds( TheEntity.class ).multiLoad( 1.0 );
session.byMultipleIds( TheEntity.class ).multiLoad( 1.0F );
session.byMultipleIds( TheEntity.class ).multiLoad( BigInteger.ONE );
session.byMultipleIds( TheEntity.class ).multiLoad( BigDecimal.ONE );
}
);
scope.inTransaction(
(session) -> {
session.byMultipleIds( TheEntity.class ).multiLoad( Arrays.asList( 1L ) );
session.byMultipleIds( TheEntity.class ).multiLoad( Arrays.asList( 1 ) );
}
);
}
@Test
public void testNaturalIdLoading(SessionFactoryScope scope) {
scope.inTransaction(
(session) -> {
session.bySimpleNaturalId( TheEntity.class ).load( 1L );
session.bySimpleNaturalId( TheEntity.class ).load( (byte) 1 );
session.bySimpleNaturalId( TheEntity.class ).load( (short) 1 );
session.bySimpleNaturalId( TheEntity.class ).load( 1 );
session.bySimpleNaturalId( TheEntity.class ).load( 1.0 );
session.bySimpleNaturalId( TheEntity.class ).load( 1.0F );
session.bySimpleNaturalId( TheEntity.class ).load( BigInteger.ONE );
session.bySimpleNaturalId( TheEntity.class ).load( BigDecimal.ONE );
}
);
scope.inTransaction(
(session) -> {
session.byMultipleIds( TheEntity.class ).multiLoad( Arrays.asList( 1L ) );
session.byMultipleIds( TheEntity.class ).multiLoad( Arrays.asList( 1 ) );
}
);
}
@Test
public void testMultiNaturalIdLoading(SessionFactoryScope scope) {
scope.inTransaction(
(session) -> {
session.byMultipleNaturalId( TheEntity.class ).enableOrderedReturn( false ).multiLoad( 1L );
session.byMultipleNaturalId( TheEntity.class ).enableOrderedReturn( false ).multiLoad( (byte) 1 );
session.byMultipleNaturalId( TheEntity.class ).enableOrderedReturn( false ).multiLoad( (short) 1 );
session.byMultipleNaturalId( TheEntity.class ).enableOrderedReturn( false ).multiLoad( 1 );
session.byMultipleNaturalId( TheEntity.class ).enableOrderedReturn( false ).multiLoad( 1.0 );
session.byMultipleNaturalId( TheEntity.class ).enableOrderedReturn( false ).multiLoad( 1.0F );
session.byMultipleNaturalId( TheEntity.class ).enableOrderedReturn( false ).multiLoad( BigInteger.ONE );
session.byMultipleNaturalId( TheEntity.class ).enableOrderedReturn( false ).multiLoad( BigDecimal.ONE );
}
);
scope.inTransaction(
(session) -> {
session.byMultipleNaturalId( TheEntity.class ).enableOrderedReturn( false ).multiLoad( Arrays.asList( 1L ) );
session.byMultipleNaturalId( TheEntity.class ).enableOrderedReturn( false ).multiLoad( Arrays.asList( 1 ) );
}
);
}
@Test
public void testQueryParameterIntegralWiden(SessionFactoryScope scope) {
final String qry = "select e from TheEntity e where e.longId = :id";
scope.inTransaction(
(session) -> {
final QueryImplementor query = session.createQuery( qry );
query.setParameter( "id", 1L ).list();
query.setParameter( "id", 1 ).list();
}
);
}
@Test
public void testQueryParameterIntegralNarrow(SessionFactoryScope scope) {
final String qry = "select e from TheEntity e where e.intValue = ?1";
scope.inTransaction(
(session) -> {
final QueryImplementor query = session.createQuery( qry );
query.setParameter( 1, 1 ).list();
query.setParameter( 1, 1L ).list();
}
);
}
@Test
public void testQueryParameterFloatingWiden(SessionFactoryScope scope) {
final String qry = "select e from TheEntity e where e.floatValue = :p";
scope.inTransaction(
(session) -> {
final QueryImplementor query = session.createQuery( qry );
query.setParameter( "p", 0.5f ).list();
query.setParameter( "p", 0.5 ).list();
}
);
}
@Test
public void testQueryParameterFloatingNarrow(SessionFactoryScope scope) {
final String qry = "select e from TheEntity e where e.doubleValue = :p";
scope.inTransaction(
(session) -> {
final QueryImplementor query = session.createQuery( qry );
query.setParameter( "p", 0.5 ).list();
query.setParameter( "p", 0.5f ).list();
}
);
}
@Entity( name = "TheEntity" )
@Table( name = "the_entity" )
public static class TheEntity {
@Id
private Long longId;
@NaturalId
private Long longNaturalId;
private Integer intValue;
private Float floatValue;
private Double doubleValue;
}
}