mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-02-17 00:24:57 +00:00
HHH-16333 Get rid of special Character[] and Byte[] handling
This commit is contained in:
parent
c54e156c14
commit
4b1f56951b
@ -689,7 +689,11 @@ See <<basic-nationalized>> for details on mapping strings using nationalized cha
|
||||
[[basic-chararray]]
|
||||
==== Character arrays
|
||||
|
||||
By default, Hibernate maps `Character[]` and `char[]` to the `VARCHAR` JDBC type.
|
||||
By default, Hibernate maps `char[]` to the `VARCHAR` JDBC type.
|
||||
Since `Character[]` can contain null elements, it is mapped as <<basic-mapping-array,basic array type>> instead.
|
||||
Prior to Hibernate 6.2, also `Character[]` mapped to `VARCHAR`, yet disallowed `null` elements.
|
||||
To continue mapping `Character[]` to the `VARCHAR` JDBC type, or for LOBs mapping to the `CLOB` JDBC type,
|
||||
it is necessary to annotate the persistent attribute with `@JavaType( CharacterArrayJavaType.class )`.
|
||||
|
||||
[[basic-string-example-implicit]]
|
||||
.Mapping Character
|
||||
@ -872,8 +876,11 @@ include::{example-dir-basic-mapping}/basic/NClobCharArrayTest.java[tags=basic-nc
|
||||
[[basic-bytearray]]
|
||||
==== Byte array
|
||||
|
||||
By default, Hibernate maps values of type `byte[]` and `Byte[]` to the JDBC type
|
||||
`VARBINARY`.
|
||||
By default, Hibernate maps `byte[]` to the `VARBINARY` JDBC type.
|
||||
Since `Byte[]` can contain null elements, it is mapped as <<basic-mapping-array,basic array type>> instead.
|
||||
Prior to Hibernate 6.2, also `Byte[]` mapped to `VARBINARY`, yet disallowed `null` elements.
|
||||
To continue mapping `Byte[]` to the `VARBINARY` JDBC type, or for LOBs mapping to the `BLOB` JDBC type,
|
||||
it is necessary to annotate the persistent attribute with `@JavaType( ByteArrayJavaType.class )`.
|
||||
|
||||
[[basic-bytearray-example]]
|
||||
.Mapping arrays of bytes
|
||||
|
@ -12,11 +12,16 @@
|
||||
import jakarta.persistence.Lob;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import org.hibernate.annotations.JavaType;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.metamodel.MappingMetamodel;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping;
|
||||
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
import org.hibernate.type.descriptor.java.ByteArrayJavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
@ -27,6 +32,9 @@
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.isOneOf;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
@ -40,6 +48,7 @@ public void verifyMappings(SessionFactoryScope scope) {
|
||||
final MappingMetamodelImplementor mappingMetamodel = scope.getSessionFactory()
|
||||
.getRuntimeMetamodels()
|
||||
.getMappingMetamodel();
|
||||
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
|
||||
final JdbcTypeRegistry jdbcTypeRegistry = mappingMetamodel.getTypeConfiguration().getJdbcTypeRegistry();
|
||||
final EntityPersister entityDescriptor = mappingMetamodel.getEntityDescriptor(EntityOfByteArrays.class);
|
||||
|
||||
@ -54,6 +63,28 @@ public void verifyMappings(SessionFactoryScope scope) {
|
||||
final BasicAttributeMapping primitive = (BasicAttributeMapping) entityDescriptor.findAttributeMapping("wrapper");
|
||||
final JdbcMapping jdbcMapping = primitive.getJdbcMapping();
|
||||
assertThat(jdbcMapping.getJavaTypeDescriptor().getJavaTypeClass(), equalTo(Byte[].class));
|
||||
if ( dialect.supportsStandardArrays() ) {
|
||||
assertThat( jdbcMapping.getJdbcType(), instanceOf( ArrayJdbcType.class ) );
|
||||
assertThat(
|
||||
( (ArrayJdbcType) jdbcMapping.getJdbcType() ).getElementJdbcType(),
|
||||
is( jdbcTypeRegistry.getDescriptor( Types.TINYINT ) )
|
||||
);
|
||||
}
|
||||
else {
|
||||
assertThat(
|
||||
jdbcMapping.getJdbcType(),
|
||||
isOneOf(
|
||||
jdbcTypeRegistry.getDescriptor( SqlTypes.ARRAY ),
|
||||
jdbcTypeRegistry.getDescriptor( SqlTypes.SQLXML )
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
final BasicAttributeMapping primitive = (BasicAttributeMapping) entityDescriptor.findAttributeMapping("wrapperOld");
|
||||
final JdbcMapping jdbcMapping = primitive.getJdbcMapping();
|
||||
assertThat(jdbcMapping.getJavaTypeDescriptor().getJavaTypeClass(), equalTo(Byte[].class));
|
||||
assertThat( jdbcMapping.getJdbcType(), equalTo( jdbcTypeRegistry.getDescriptor( Types.VARBINARY ) ) );
|
||||
}
|
||||
|
||||
@ -102,11 +133,14 @@ public static class EntityOfByteArrays {
|
||||
// mapped as VARBINARY
|
||||
private byte[] primitive;
|
||||
private Byte[] wrapper;
|
||||
@JavaType( ByteArrayJavaType.class )
|
||||
private Byte[] wrapperOld;
|
||||
|
||||
// mapped as (materialized) BLOB
|
||||
@Lob
|
||||
private byte[] primitiveLob;
|
||||
@Lob
|
||||
@JavaType( ByteArrayJavaType.class )
|
||||
private Byte[] wrapperLob;
|
||||
//end::basic-bytearray-example[]
|
||||
|
||||
|
@ -12,10 +12,15 @@
|
||||
import jakarta.persistence.Lob;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import org.hibernate.annotations.JavaType;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping;
|
||||
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
import org.hibernate.type.descriptor.java.CharacterArrayJavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
@ -25,6 +30,9 @@
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.isOneOf;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
@ -37,6 +45,7 @@ public void verifyMappings(SessionFactoryScope scope) {
|
||||
final MappingMetamodelImplementor mappingMetamodel = scope.getSessionFactory()
|
||||
.getRuntimeMetamodels()
|
||||
.getMappingMetamodel();
|
||||
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
|
||||
final JdbcTypeRegistry jdbcRegistry = mappingMetamodel.getTypeConfiguration().getJdbcTypeRegistry();
|
||||
final EntityPersister entityDescriptor = mappingMetamodel.getEntityDescriptor(EntityWithCharArrays.class);
|
||||
|
||||
@ -49,6 +58,27 @@ public void verifyMappings(SessionFactoryScope scope) {
|
||||
{
|
||||
final BasicAttributeMapping attributeMapping = (BasicAttributeMapping) entityDescriptor.findAttributeMapping("wrapper");
|
||||
final JdbcMapping jdbcMapping = attributeMapping.getJdbcMapping();
|
||||
if ( dialect.supportsStandardArrays() ) {
|
||||
assertThat( jdbcMapping.getJdbcType(), instanceOf( ArrayJdbcType.class ) );
|
||||
assertThat(
|
||||
( (ArrayJdbcType) jdbcMapping.getJdbcType() ).getElementJdbcType(),
|
||||
is( jdbcRegistry.getDescriptor( Types.CHAR ) )
|
||||
);
|
||||
}
|
||||
else {
|
||||
assertThat(
|
||||
jdbcMapping.getJdbcType(),
|
||||
isOneOf(
|
||||
jdbcRegistry.getDescriptor( SqlTypes.ARRAY ),
|
||||
jdbcRegistry.getDescriptor( SqlTypes.SQLXML )
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
final BasicAttributeMapping attributeMapping = (BasicAttributeMapping) entityDescriptor.findAttributeMapping("wrapperOld");
|
||||
final JdbcMapping jdbcMapping = attributeMapping.getJdbcMapping();
|
||||
assertThat( jdbcMapping.getJdbcType(), equalTo( jdbcRegistry.getDescriptor( Types.VARCHAR)));
|
||||
}
|
||||
|
||||
@ -76,11 +106,14 @@ public static class EntityWithCharArrays {
|
||||
// mapped as VARCHAR
|
||||
char[] primitive;
|
||||
Character[] wrapper;
|
||||
@JavaType( CharacterArrayJavaType.class )
|
||||
Character[] wrapperOld;
|
||||
|
||||
// mapped as CLOB
|
||||
@Lob
|
||||
char[] primitiveClob;
|
||||
@Lob
|
||||
@JavaType( CharacterArrayJavaType.class )
|
||||
Character[] wrapperClob;
|
||||
//end::basic-chararray-example[]
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
import jakarta.persistence.Lob;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import org.hibernate.annotations.JavaType;
|
||||
import org.hibernate.annotations.Nationalized;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.dialect.NationalizationSupport;
|
||||
@ -18,6 +19,9 @@
|
||||
import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping;
|
||||
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
import org.hibernate.type.descriptor.java.CharacterArrayJavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||
|
||||
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
|
||||
@ -28,7 +32,10 @@
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.anyOf;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.isOneOf;
|
||||
|
||||
/**
|
||||
* @see CharacterArrayMappingTests
|
||||
@ -59,6 +66,27 @@ public void verifyMappings(SessionFactoryScope scope) {
|
||||
{
|
||||
final BasicAttributeMapping attributeMapping = (BasicAttributeMapping) entityDescriptor.findAttributeMapping("wrapperNVarchar");
|
||||
final JdbcMapping jdbcMapping = attributeMapping.getJdbcMapping();
|
||||
if ( dialect.supportsStandardArrays() ) {
|
||||
assertThat( jdbcMapping.getJdbcType(), instanceOf( ArrayJdbcType.class ) );
|
||||
assertThat(
|
||||
( (ArrayJdbcType) jdbcMapping.getJdbcType() ).getElementJdbcType(),
|
||||
is( jdbcTypeRegistry.getDescriptor( nationalizationSupport.getCharVariantCode() ) )
|
||||
);
|
||||
}
|
||||
else {
|
||||
assertThat(
|
||||
jdbcMapping.getJdbcType(),
|
||||
isOneOf(
|
||||
jdbcTypeRegistry.getDescriptor( SqlTypes.ARRAY ),
|
||||
jdbcTypeRegistry.getDescriptor( SqlTypes.SQLXML )
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
final BasicAttributeMapping attributeMapping = (BasicAttributeMapping) entityDescriptor.findAttributeMapping("wrapperNVarcharOld");
|
||||
final JdbcMapping jdbcMapping = attributeMapping.getJdbcMapping();
|
||||
assertThat( jdbcMapping.getJdbcType(), is( jdbcTypeRegistry.getDescriptor( nationalizationSupport.getVarcharVariantCode())));
|
||||
}
|
||||
|
||||
@ -88,6 +116,9 @@ public static class EntityWithCharArrays {
|
||||
char[] primitiveNVarchar;
|
||||
@Nationalized
|
||||
Character[] wrapperNVarchar;
|
||||
@Nationalized
|
||||
@JavaType( CharacterArrayJavaType.class )
|
||||
Character[] wrapperNVarcharOld;
|
||||
|
||||
// mapped as NCLOB
|
||||
@Lob
|
||||
@ -95,6 +126,7 @@ public static class EntityWithCharArrays {
|
||||
char[] primitiveNClob;
|
||||
@Lob
|
||||
@Nationalized
|
||||
@JavaType( CharacterArrayJavaType.class )
|
||||
Character[] wrapperNClob;
|
||||
//end::basic-nchararray-example[]
|
||||
}
|
||||
|
@ -198,7 +198,7 @@ else if ( JavaTypeHelper.isTemporal( elementJtd ) ) {
|
||||
registeredType = registeredElementType == null ? null : containerJtd.resolveType(
|
||||
typeConfiguration,
|
||||
dialect,
|
||||
registeredElementType,
|
||||
resolveSqlTypeIndicators( stdIndicators, registeredElementType, elementJtd ),
|
||||
columnTypeInformation
|
||||
);
|
||||
if ( registeredType != null ) {
|
||||
|
@ -777,7 +777,7 @@ public static void prime(TypeConfiguration typeConfiguration) {
|
||||
BINARY_WRAPPER,
|
||||
"org.hibernate.type.WrapperBinaryType",
|
||||
basicTypeRegistry,
|
||||
"binary_wrapper", "wrapper-binary", "Byte[]", Byte[].class.getName()
|
||||
"binary_wrapper", "wrapper-binary"//, "Byte[]", Byte[].class.getName()
|
||||
);
|
||||
|
||||
handle(
|
||||
@ -905,7 +905,7 @@ public static void prime(TypeConfiguration typeConfiguration) {
|
||||
CHARACTER_ARRAY,
|
||||
"org.hibernate.type.CharacterArrayType",
|
||||
basicTypeRegistry,
|
||||
"wrapper-characters", Character[].class.getName(), "Character[]"
|
||||
"wrapper-characters"//, Character[].class.getName(), "Character[]"
|
||||
);
|
||||
|
||||
handle(
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.sql.Types;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
|
||||
@ -71,22 +72,23 @@ public BasicType<?> resolveType(
|
||||
}
|
||||
final BasicValueConverter<E, ?> valueConverter = elementType.getValueConverter();
|
||||
if ( valueConverter == null ) {
|
||||
return typeConfiguration.standardBasicTypeForJavaType(
|
||||
getJavaType(),
|
||||
javaType -> {
|
||||
JdbcType arrayJdbcType = typeConfiguration.getJdbcTypeRegistry().getDescriptor( Types.ARRAY );
|
||||
if ( arrayJdbcType instanceof ArrayJdbcType ) {
|
||||
arrayJdbcType = ( (ArrayJdbcType) arrayJdbcType ).resolveType(
|
||||
typeConfiguration,
|
||||
dialect,
|
||||
elementType,
|
||||
columnTypeInformation
|
||||
);
|
||||
}
|
||||
//noinspection unchecked,rawtypes
|
||||
return new BasicArrayType( elementType, arrayJdbcType, javaType );
|
||||
}
|
||||
);
|
||||
final Function<JavaType<T>, BasicType<T>> creator = javaType -> {
|
||||
JdbcType arrayJdbcType = typeConfiguration.getJdbcTypeRegistry().getDescriptor( Types.ARRAY );
|
||||
if ( arrayJdbcType instanceof ArrayJdbcType ) {
|
||||
arrayJdbcType = ( (ArrayJdbcType) arrayJdbcType ).resolveType(
|
||||
typeConfiguration,
|
||||
dialect,
|
||||
elementType,
|
||||
columnTypeInformation
|
||||
);
|
||||
}
|
||||
//noinspection unchecked,rawtypes
|
||||
return new BasicArrayType( elementType, arrayJdbcType, javaType );
|
||||
};
|
||||
if ( typeConfiguration.getBasicTypeRegistry().getRegisteredType( elementType.getName() ) == elementType ) {
|
||||
return typeConfiguration.standardBasicTypeForJavaType( getJavaType(), creator );
|
||||
}
|
||||
return creator.apply( this );
|
||||
}
|
||||
else {
|
||||
final JavaType<Object> relationalJavaType = typeConfiguration.getJavaTypeRegistry().getDescriptor(
|
||||
|
@ -10,6 +10,7 @@
|
||||
import java.lang.reflect.Array;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.SharedSessionContract;
|
||||
@ -71,21 +72,22 @@ public BasicType<?> resolveType(
|
||||
//noinspection unchecked
|
||||
final BasicValueConverter<Object, Object> valueConverter = (BasicValueConverter<Object, Object>) elementType.getValueConverter();
|
||||
if ( valueConverter == null ) {
|
||||
return typeConfiguration.standardBasicTypeForJavaType(
|
||||
arrayJavaType.getJavaType(),
|
||||
javaType -> {
|
||||
JdbcType arrayJdbcType = typeConfiguration.getJdbcTypeRegistry().getDescriptor( Types.ARRAY );
|
||||
if ( arrayJdbcType instanceof ArrayJdbcType ) {
|
||||
arrayJdbcType = ( (ArrayJdbcType) arrayJdbcType ).resolveType(
|
||||
typeConfiguration,
|
||||
dialect,
|
||||
elementType,
|
||||
columnTypeInformation
|
||||
);
|
||||
}
|
||||
return new BasicArrayType<>( elementType, arrayJdbcType, javaType );
|
||||
}
|
||||
);
|
||||
final Function<JavaType<T[]>, BasicType<T[]>> creator = javaType -> {
|
||||
JdbcType arrayJdbcType = typeConfiguration.getJdbcTypeRegistry().getDescriptor( Types.ARRAY );
|
||||
if ( arrayJdbcType instanceof ArrayJdbcType ) {
|
||||
arrayJdbcType = ( (ArrayJdbcType) arrayJdbcType ).resolveType(
|
||||
typeConfiguration,
|
||||
dialect,
|
||||
elementType,
|
||||
columnTypeInformation
|
||||
);
|
||||
}
|
||||
return new BasicArrayType<>( elementType, arrayJdbcType, javaType );
|
||||
};
|
||||
if ( typeConfiguration.getBasicTypeRegistry().getRegisteredType( elementType.getName() ) == elementType ) {
|
||||
return typeConfiguration.standardBasicTypeForJavaType( arrayJavaType.getJavaType(), creator );
|
||||
}
|
||||
return creator.apply( arrayJavaType );
|
||||
}
|
||||
else {
|
||||
final JavaType<Object> relationalJavaType = typeConfiguration.getJavaTypeRegistry().getDescriptor(
|
||||
|
@ -15,17 +15,20 @@
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.engine.jdbc.BinaryStream;
|
||||
import org.hibernate.engine.jdbc.internal.BinaryStreamImpl;
|
||||
import org.hibernate.internal.util.SerializationHelper;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
import org.hibernate.type.descriptor.jdbc.AdjustableJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
|
||||
|
||||
/**
|
||||
* Descriptor for {@code Byte[]} handling.
|
||||
* Descriptor for {@code Byte[]} handling, which disallows {@code null} elements.
|
||||
* This {@link JavaType} is useful if the domain model uses {@code Byte[]} and wants to map to {@link SqlTypes#VARBINARY}.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class ByteArrayJavaType extends AbstractClassJavaType<Byte[]> {
|
||||
public static final ByteArrayJavaType INSTANCE = new ByteArrayJavaType();
|
||||
private static final Byte[] EMPTY_BYTE_ARRAY = new Byte[0];
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public ByteArrayJavaType() {
|
||||
@ -45,6 +48,15 @@ public int extractHashCode(Byte[] bytes) {
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) {
|
||||
// match legacy behavior
|
||||
final JdbcType descriptor = indicators.getJdbcType( indicators.resolveJdbcTypeCode( SqlTypes.VARBINARY ) );
|
||||
return descriptor instanceof AdjustableJdbcType
|
||||
? ( (AdjustableJdbcType) descriptor ).resolveIndicatedType( indicators, this )
|
||||
: descriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(Byte[] bytes) {
|
||||
final StringBuilder buf = new StringBuilder();
|
||||
@ -119,30 +131,6 @@ public <X> Byte[] wrap(X value, WrapperOptions options) {
|
||||
throw new HibernateException( "Unable to access lob stream", e );
|
||||
}
|
||||
}
|
||||
if ( value instanceof java.sql.Array ) {
|
||||
try {
|
||||
//noinspection unchecked
|
||||
value = (X) ( (java.sql.Array) value ).getArray();
|
||||
if ( value instanceof Byte[] ) {
|
||||
return (Byte[]) value;
|
||||
}
|
||||
else if ( value instanceof Object[] ) {
|
||||
final Object[] array = (Object[]) value;
|
||||
if ( array.length == 0 ) {
|
||||
return EMPTY_BYTE_ARRAY;
|
||||
}
|
||||
final Byte[] bytes = new Byte[array.length];
|
||||
for ( int i = 0; i < array.length; i++ ) {
|
||||
bytes[i] = ByteJavaType.INSTANCE.wrap( array[i], options );
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
catch ( SQLException ex ) {
|
||||
// This basically shouldn't happen unless you've lost connection to the database.
|
||||
throw new HibernateException( ex );
|
||||
}
|
||||
}
|
||||
|
||||
throw unknownWrap( value.getClass() );
|
||||
}
|
||||
@ -151,16 +139,21 @@ private Byte[] wrapBytes(byte[] bytes) {
|
||||
if ( bytes == null ) {
|
||||
return null;
|
||||
}
|
||||
// Since a Byte[] can contain nulls but a byte[] can't, we have to serialize/deserialize the content
|
||||
return (Byte[]) SerializationHelper.deserialize( bytes );
|
||||
final Byte[] result = new Byte[bytes.length];
|
||||
for ( int i = 0; i < bytes.length; i++ ) {
|
||||
result[i] = bytes[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private byte[] unwrapBytes(Byte[] bytes) {
|
||||
if ( bytes == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Since a Byte[] can contain nulls but a byte[] can't, we have to serialize/deserialize the content
|
||||
return SerializationHelper.serialize( bytes );
|
||||
final byte[] result = new byte[bytes.length];
|
||||
for ( int i = 0; i < bytes.length; i++ ) {
|
||||
result[i] = bytes[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -13,10 +13,16 @@
|
||||
|
||||
import org.hibernate.engine.jdbc.CharacterStream;
|
||||
import org.hibernate.engine.jdbc.internal.CharacterStreamImpl;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
import org.hibernate.type.descriptor.jdbc.AdjustableJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcTypeJavaClassMappings;
|
||||
|
||||
/**
|
||||
* Descriptor for {@code Character[]} handling.
|
||||
* Descriptor for {@code Character[]} handling, which disallows {@code null} elements.
|
||||
* This {@link JavaType} is useful if the domain model uses {@code Character[]} and wants to map to {@link SqlTypes#VARCHAR}.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@ -53,6 +59,15 @@ public int extractHashCode(Character[] chars) {
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) {
|
||||
// match legacy behavior
|
||||
final JdbcType descriptor = indicators.getJdbcType( indicators.resolveJdbcTypeCode( SqlTypes.VARCHAR ) );
|
||||
return descriptor instanceof AdjustableJdbcType
|
||||
? ( (AdjustableJdbcType) descriptor ).resolveIndicatedType( indicators, this )
|
||||
: descriptor;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <X> X unwrap(Character[] value, Class<X> type, WrapperOptions options) {
|
||||
|
@ -14,6 +14,7 @@
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.Incubating;
|
||||
@ -114,22 +115,24 @@ public BasicType<?> resolveType(
|
||||
}
|
||||
final BasicValueConverter<E, ?> valueConverter = elementType.getValueConverter();
|
||||
if ( valueConverter == null ) {
|
||||
return typeConfiguration.standardBasicTypeForJavaType(
|
||||
collectionJavaType.getJavaType(),
|
||||
javaType -> {
|
||||
JdbcType arrayJdbcType = typeConfiguration.getJdbcTypeRegistry().getDescriptor( Types.ARRAY );
|
||||
if ( arrayJdbcType instanceof ArrayJdbcType ) {
|
||||
arrayJdbcType = ( (ArrayJdbcType) arrayJdbcType ).resolveType(
|
||||
typeConfiguration,
|
||||
dialect,
|
||||
elementType,
|
||||
columnTypeInformation
|
||||
);
|
||||
}
|
||||
//noinspection unchecked,rawtypes
|
||||
return new BasicCollectionType( elementType, arrayJdbcType, collectionJavaType );
|
||||
}
|
||||
);
|
||||
final Function<JavaType<Object>, BasicType<Object>> creator = javaType -> {
|
||||
JdbcType arrayJdbcType = typeConfiguration.getJdbcTypeRegistry().getDescriptor( Types.ARRAY );
|
||||
if ( arrayJdbcType instanceof ArrayJdbcType ) {
|
||||
arrayJdbcType = ( (ArrayJdbcType) arrayJdbcType ).resolveType(
|
||||
typeConfiguration,
|
||||
dialect,
|
||||
elementType,
|
||||
columnTypeInformation
|
||||
);
|
||||
}
|
||||
//noinspection unchecked,rawtypes
|
||||
return new BasicCollectionType( elementType, arrayJdbcType, collectionJavaType );
|
||||
};
|
||||
if ( typeConfiguration.getBasicTypeRegistry().getRegisteredType( elementType.getName() ) == elementType ) {
|
||||
return typeConfiguration.standardBasicTypeForJavaType( collectionJavaType.getJavaType(), creator );
|
||||
}
|
||||
//noinspection unchecked
|
||||
return creator.apply( (JavaType<Object>) (JavaType<?>) collectionJavaType );
|
||||
}
|
||||
else {
|
||||
final JavaType<Object> relationalJavaType = typeConfiguration.getJavaTypeRegistry().resolveDescriptor(
|
||||
|
@ -123,8 +123,8 @@ public static void prime(BaselineTarget target) {
|
||||
target.addBaselineDescriptor( ClobJavaType.INSTANCE );
|
||||
target.addBaselineDescriptor( NClobJavaType.INSTANCE );
|
||||
|
||||
target.addBaselineDescriptor( ByteArrayJavaType.INSTANCE );
|
||||
target.addBaselineDescriptor( CharacterArrayJavaType.INSTANCE );
|
||||
// target.addBaselineDescriptor( ByteArrayJavaType.INSTANCE );
|
||||
// target.addBaselineDescriptor( CharacterArrayJavaType.INSTANCE );
|
||||
target.addBaselineDescriptor( PrimitiveByteArrayJavaType.INSTANCE );
|
||||
target.addBaselineDescriptor( PrimitiveCharacterArrayJavaType.INSTANCE );
|
||||
|
||||
|
@ -147,8 +147,8 @@ private static ConcurrentHashMap<Class, Integer> buildJavaClassToJdbcTypeCodeMap
|
||||
// additional "common sense" registrations
|
||||
workMap.put( Character.class, SqlTypes.CHAR );
|
||||
workMap.put( char[].class, SqlTypes.VARCHAR );
|
||||
workMap.put( Character[].class, SqlTypes.VARCHAR );
|
||||
workMap.put( Byte[].class, SqlTypes.VARBINARY );
|
||||
// workMap.put( Character[].class, SqlTypes.VARCHAR );
|
||||
// workMap.put( Byte[].class, SqlTypes.VARBINARY );
|
||||
workMap.put( java.util.Date.class, SqlTypes.TIMESTAMP );
|
||||
workMap.put( Calendar.class, SqlTypes.TIMESTAMP );
|
||||
|
||||
|
@ -6,49 +6,72 @@
|
||||
*/
|
||||
package org.hibernate.type.descriptor.jdbc;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
|
||||
import org.hibernate.type.descriptor.ValueBinder;
|
||||
import org.hibernate.type.descriptor.ValueExtractor;
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
/**
|
||||
* Descriptor for {@link Types#TINYINT TINYINT} handling, when the {@link Types#SMALLINT SMALLINT} DDL type code is used.
|
||||
* This type extends {@link SmallIntJdbcType}, because on the JDBC side we must bind {@link Short} instead of {@link Byte},
|
||||
* This type binds and extracts {@link Short} instead of {@link Byte} through JDBC,
|
||||
* because it is not specified whether the conversion from {@link Byte} to {@link Short} is signed or unsigned,
|
||||
* and we need the conversion to be signed, which is properly handled by the {@link org.hibernate.type.descriptor.java.JavaType#unwrap(Object, Class, WrapperOptions)} implementations.
|
||||
* yet we need the conversion to be signed, which is properly handled by the {@link org.hibernate.type.descriptor.java.JavaType#unwrap(Object, Class, WrapperOptions)} implementations.
|
||||
*/
|
||||
public class TinyIntAsSmallIntJdbcType extends SmallIntJdbcType {
|
||||
public class TinyIntAsSmallIntJdbcType extends TinyIntJdbcType {
|
||||
public static final TinyIntAsSmallIntJdbcType INSTANCE = new TinyIntAsSmallIntJdbcType();
|
||||
|
||||
public TinyIntAsSmallIntJdbcType() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getJdbcTypeCode() {
|
||||
return Types.TINYINT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDdlTypeCode() {
|
||||
return Types.SMALLINT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFriendlyName() {
|
||||
return "TINYINT";
|
||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||
return Short.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TinyIntAsSmallIntTypeDescriptor";
|
||||
public <X> ValueBinder<X> getBinder(final JavaType<X> javaType) {
|
||||
return new BasicBinder<>( javaType, this ) {
|
||||
@Override
|
||||
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException {
|
||||
st.setShort( index, javaType.unwrap( value, Short.class, options ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
|
||||
throws SQLException {
|
||||
st.setShort( name, javaType.unwrap( value, Short.class, options ) );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> JavaType<T> getJdbcRecommendedJavaTypeMapping(
|
||||
Integer length,
|
||||
Integer scale,
|
||||
TypeConfiguration typeConfiguration) {
|
||||
return typeConfiguration.getJavaTypeRegistry().getDescriptor( Byte.class );
|
||||
public <X> ValueExtractor<X> getExtractor(final JavaType<X> javaType) {
|
||||
return new BasicExtractor<>( javaType, this ) {
|
||||
@Override
|
||||
protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
|
||||
return javaType.wrap( rs.getShort( paramIndex ), options );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
|
||||
return javaType.wrap( statement.getShort( index ), options );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected X doExtract(CallableStatement statement, String name, WrapperOptions options) throws SQLException {
|
||||
return javaType.wrap( statement.getShort( name ), options );
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,9 @@
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.orm.test.annotations.lob;
|
||||
import org.hibernate.annotations.JavaType;
|
||||
import org.hibernate.type.descriptor.java.CharacterArrayJavaType;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Lob;
|
||||
import jakarta.persistence.MappedSuperclass;
|
||||
@ -40,6 +43,7 @@ public void setFullText(String fullText) {
|
||||
|
||||
@Lob
|
||||
@Column(name = "fld_code")
|
||||
@JavaType( CharacterArrayJavaType.class )
|
||||
public Character[] getCode() {
|
||||
return code;
|
||||
}
|
||||
|
@ -5,6 +5,9 @@
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.orm.test.annotations.lob;
|
||||
import org.hibernate.annotations.JavaType;
|
||||
import org.hibernate.type.descriptor.java.ByteArrayJavaType;
|
||||
|
||||
import jakarta.persistence.Lob;
|
||||
import jakarta.persistence.MappedSuperclass;
|
||||
|
||||
@ -26,6 +29,7 @@ public void setMetadata(byte[] metadata) {
|
||||
}
|
||||
|
||||
@Lob
|
||||
@JavaType( ByteArrayJavaType.class )
|
||||
public Byte[] getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
@ -11,7 +11,9 @@
|
||||
|
||||
import java.sql.Types;
|
||||
|
||||
import org.hibernate.annotations.JavaType;
|
||||
import org.hibernate.annotations.JdbcTypeCode;
|
||||
import org.hibernate.type.descriptor.java.CharacterArrayJavaType;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
@ -60,6 +62,7 @@ public void setName(char[] name) {
|
||||
}
|
||||
|
||||
@JdbcTypeCode( Types.LONGVARCHAR )
|
||||
@JavaType( CharacterArrayJavaType.class )
|
||||
public Character[] getWhatEver() {
|
||||
return whatEver;
|
||||
}
|
||||
|
@ -13,7 +13,9 @@
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import org.hibernate.annotations.JavaType;
|
||||
import org.hibernate.query.Query;
|
||||
import org.hibernate.type.descriptor.java.CharacterArrayJavaType;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
@ -121,6 +123,7 @@ public void testMultipleUpdates(SessionFactoryScope scope) {
|
||||
@Table(name = "DemoEntity")
|
||||
public static class DemoEntity {
|
||||
@Id
|
||||
@JavaType( CharacterArrayJavaType.class )
|
||||
public Character[] id;
|
||||
public String name;
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Lob;
|
||||
|
||||
import org.hibernate.annotations.JavaType;
|
||||
import org.hibernate.annotations.Nationalized;
|
||||
import org.hibernate.boot.Metadata;
|
||||
import org.hibernate.boot.MetadataSources;
|
||||
@ -32,6 +33,7 @@
|
||||
import org.hibernate.testing.orm.junit.BaseUnitTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
|
||||
@ -62,6 +64,7 @@ public static class NationalizedEntity {
|
||||
private Character ncharacterAtt;
|
||||
|
||||
@Nationalized
|
||||
@JavaType( CharacterArrayJavaType.class )
|
||||
private Character[] ncharArrAtt;
|
||||
|
||||
@Lob
|
||||
@ -126,7 +129,7 @@ public void simpleNationalizedTest() {
|
||||
|
||||
prop = pc.getProperty( "ncharArrAtt" );
|
||||
type = (BasicType<?>) prop.getType();
|
||||
assertSame( CharacterArrayJavaType.INSTANCE, type.getJavaTypeDescriptor() );
|
||||
assertInstanceOf( CharacterArrayJavaType.class, type.getJavaTypeDescriptor() );
|
||||
if ( dialect.getNationalizationSupport() != NationalizationSupport.EXPLICIT ) {
|
||||
assertSame( jdbcTypeRegistry.getDescriptor( Types.VARCHAR ), type.getJdbcType() );
|
||||
}
|
||||
|
@ -14,23 +14,6 @@ earlier versions, see any other pertinent migration guides as well.
|
||||
* link:{docsBase}/6.1/migration-guide/migration-guide.html[6.1 Migration guide]
|
||||
* link:{docsBase}/6.0/migration-guide/migration-guide.html[6.0 Migration guide]
|
||||
|
||||
[[data-format-changes]]
|
||||
== Data format changes
|
||||
|
||||
[[data-format-wrapper-byte-array]]
|
||||
=== Byte[] data format changes
|
||||
|
||||
Prior to Hibernate 6.2, a `Byte[]` in the domain model was always just blindly converted to a `byte[]`,
|
||||
possibly running into a `NullPointerException` if the `Byte[]` contains a `null` array element.
|
||||
|
||||
To fix this, a `Byte[]` is now serialized with Java serialization to a `byte[]`, which allows handling nulls better.
|
||||
This change is necessary to properly support plural basic types where the element type maps to the `Byte` Java type,
|
||||
i.e. an array or collection of `Enum`.
|
||||
|
||||
Since the use of `Byte[]` in a domain model did not make sense so far because null elements were impossible,
|
||||
it is expected that most domain models were already using `byte[]`, so the breaking change was considered to be ok.
|
||||
|
||||
Domain models that really used a `Byte[]` but never stored a `null`, must be migrated to `byte[]` now.
|
||||
|
||||
[[ddl-changes]]
|
||||
== DDL type changes
|
||||
|
Loading…
x
Reference in New Issue
Block a user