HHH-16125 introduce JdbcTypeConstructor instead of using JdbcTypes as their own factories

previously, there was a global instance of ArrayJdbcType registered by the Dialects, in
an inconsistent state, that acted as a factory for correctly-initialized instances
This commit is contained in:
Gavin 2023-05-02 01:26:37 +03:00 committed by Gavin King
parent cd0504ceda
commit 473984f1eb
18 changed files with 392 additions and 201 deletions

View File

@ -72,8 +72,8 @@ import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
import org.hibernate.type.JavaObjectType;
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor;
import org.hibernate.type.descriptor.jdbc.ObjectNullAsBinaryTypeJdbcType;
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
import org.hibernate.type.descriptor.jdbc.VarbinaryJdbcType;
@ -285,13 +285,13 @@ public class CockroachLegacyDialect extends Dialect {
}
break;
case ARRAY:
final JdbcType jdbcType = jdbcTypeRegistry.getDescriptor( jdbcTypeCode );
final JdbcTypeConstructor jdbcTypeConstructor = jdbcTypeRegistry.getConstructor( jdbcTypeCode );
// PostgreSQL names array types by prepending an underscore to the base name
if ( jdbcType instanceof ArrayJdbcType && columnTypeName.charAt( 0 ) == '_' ) {
if ( jdbcTypeConstructor != null && columnTypeName.charAt( 0 ) == '_' ) {
final String componentTypeName = columnTypeName.substring( 1 );
final Integer sqlTypeCode = resolveSqlTypeCode( componentTypeName, jdbcTypeRegistry.getTypeConfiguration() );
if ( sqlTypeCode != null ) {
return ( (ArrayJdbcType) jdbcType ).resolveType(
return jdbcTypeConstructor.resolveType(
jdbcTypeRegistry.getTypeConfiguration(),
this,
jdbcTypeRegistry.getDescriptor( sqlTypeCode ),
@ -299,7 +299,7 @@ public class CockroachLegacyDialect extends Dialect {
);
}
}
return jdbcType;
break;
}
return jdbcTypeRegistry.getDescriptor( jdbcTypeCode );
}

View File

@ -793,7 +793,7 @@ public class OracleLegacyDialect extends Dialect {
}
if ( OracleJdbcHelper.isUsable( serviceRegistry ) ) {
typeContributions.contributeJdbcType( OracleJdbcHelper.getArrayJdbcType( serviceRegistry ) );
typeContributions.contributeJdbcTypeConstructor( OracleJdbcHelper.getArrayJdbcTypeConstructor( serviceRegistry ) );
}
else {
typeContributions.contributeJdbcType( OracleReflectionStructJdbcType.INSTANCE );

View File

@ -89,10 +89,10 @@ import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
import org.hibernate.type.JavaObjectType;
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
import org.hibernate.type.descriptor.jdbc.ClobJdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor;
import org.hibernate.type.descriptor.jdbc.ObjectNullAsBinaryTypeJdbcType;
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
import org.hibernate.type.descriptor.jdbc.XmlJdbcType;
@ -337,13 +337,13 @@ public class PostgreSQLLegacyDialect extends Dialect {
}
break;
case ARRAY:
final JdbcType jdbcType = jdbcTypeRegistry.getDescriptor( jdbcTypeCode );
final JdbcTypeConstructor jdbcTypeConstructor = jdbcTypeRegistry.getConstructor( jdbcTypeCode );
// PostgreSQL names array types by prepending an underscore to the base name
if ( jdbcType instanceof ArrayJdbcType && columnTypeName.charAt( 0 ) == '_' ) {
if ( jdbcTypeConstructor != null && columnTypeName.charAt( 0 ) == '_' ) {
final String componentTypeName = columnTypeName.substring( 1 );
final Integer sqlTypeCode = resolveSqlTypeCode( componentTypeName, jdbcTypeRegistry.getTypeConfiguration() );
if ( sqlTypeCode != null ) {
return ( (ArrayJdbcType) jdbcType ).resolveType(
return jdbcTypeConstructor.resolveType(
jdbcTypeRegistry.getTypeConfiguration(),
this,
jdbcTypeRegistry.getDescriptor( sqlTypeCode ),
@ -351,7 +351,7 @@ public class PostgreSQLLegacyDialect extends Dialect {
);
}
}
return jdbcType;
break;
case STRUCT:
final AggregateJdbcType aggregateDescriptor = jdbcTypeRegistry.findAggregateDescriptor( columnTypeName );
if ( aggregateDescriptor != null ) {

View File

@ -13,6 +13,7 @@ import org.hibernate.type.CustomType;
import org.hibernate.type.StandardBasicTypeTemplate;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.spi.TypeConfiguration;
@ -48,6 +49,10 @@ public interface TypeContributions {
getTypeConfiguration().getJdbcTypeRegistry().addDescriptor( descriptor );
}
default void contributeJdbcTypeConstructor(JdbcTypeConstructor typeConstructor) {
getTypeConfiguration().getJdbcTypeRegistry().addTypeConstructor( typeConstructor );
}
/**
* Register a {@link UserType} as the implicit (auto-applied)
* type for values of type {@link UserType#returnedClass()}.

View File

@ -59,8 +59,8 @@ import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
import org.hibernate.type.JavaObjectType;
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor;
import org.hibernate.type.descriptor.jdbc.ObjectNullAsBinaryTypeJdbcType;
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
import org.hibernate.type.descriptor.jdbc.VarbinaryJdbcType;
@ -304,13 +304,13 @@ public class CockroachDialect extends Dialect {
}
break;
case ARRAY:
final JdbcType jdbcType = jdbcTypeRegistry.getDescriptor( jdbcTypeCode );
final JdbcTypeConstructor jdbcTypeConstructor = jdbcTypeRegistry.getConstructor( jdbcTypeCode );
// PostgreSQL names array types by prepending an underscore to the base name
if ( jdbcType instanceof ArrayJdbcType && columnTypeName.charAt( 0 ) == '_' ) {
if ( jdbcTypeConstructor != null && columnTypeName.charAt( 0 ) == '_' ) {
final String componentTypeName = columnTypeName.substring( 1 );
final Integer sqlTypeCode = resolveSqlTypeCode( componentTypeName, jdbcTypeRegistry.getTypeConfiguration() );
if ( sqlTypeCode != null ) {
return ( (ArrayJdbcType) jdbcType ).resolveType(
return jdbcTypeConstructor.resolveType(
jdbcTypeRegistry.getTypeConfiguration(),
this,
jdbcTypeRegistry.getDescriptor( sqlTypeCode ),
@ -318,7 +318,7 @@ public class CockroachDialect extends Dialect {
);
}
}
return jdbcType;
break;
}
return jdbcTypeRegistry.getDescriptor( jdbcTypeCode );
}

View File

@ -175,11 +175,12 @@ import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
import org.hibernate.type.descriptor.jdbc.ArrayJdbcTypeConstructor;
import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
import org.hibernate.type.descriptor.jdbc.ClobJdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcLiteralFormatter;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor;
import org.hibernate.type.descriptor.jdbc.LongNVarcharJdbcType;
import org.hibernate.type.descriptor.jdbc.NCharJdbcType;
import org.hibernate.type.descriptor.jdbc.NClobJdbcType;
@ -694,25 +695,27 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun
int precision,
int scale,
JdbcTypeRegistry jdbcTypeRegistry) {
final JdbcType jdbcType = jdbcTypeRegistry.getDescriptor( jdbcTypeCode );
if ( jdbcTypeCode == Types.ARRAY && jdbcType instanceof ArrayJdbcType ) {
// Special handling for array types, because we need the proper element/component type
// To determine the element JdbcType, we pass the database reported type to #resolveSqlTypeCode
final int arraySuffixIndex = columnTypeName.toLowerCase( Locale.ROOT ).indexOf( " array" );
if ( arraySuffixIndex != -1 ) {
final String componentTypeName = columnTypeName.substring( 0, arraySuffixIndex );
final Integer sqlTypeCode = resolveSqlTypeCode( componentTypeName, jdbcTypeRegistry.getTypeConfiguration() );
if ( sqlTypeCode != null ) {
return ( (ArrayJdbcType) jdbcType ).resolveType(
jdbcTypeRegistry.getTypeConfiguration(),
this,
jdbcTypeRegistry.getDescriptor( sqlTypeCode ),
ColumnTypeInformation.EMPTY
);
if ( jdbcTypeCode == ARRAY ) {
final JdbcTypeConstructor jdbcTypeConstructor = jdbcTypeRegistry.getConstructor( jdbcTypeCode );
if ( jdbcTypeConstructor != null ) {
// Special handling for array types, because we need the proper element/component type
// To determine the element JdbcType, we pass the database reported type to #resolveSqlTypeCode
final int arraySuffixIndex = columnTypeName.toLowerCase( Locale.ROOT ).indexOf( " array" );
if ( arraySuffixIndex != -1 ) {
final String componentTypeName = columnTypeName.substring( 0, arraySuffixIndex );
final Integer sqlTypeCode = resolveSqlTypeCode( componentTypeName, jdbcTypeRegistry.getTypeConfiguration() );
if ( sqlTypeCode != null ) {
return jdbcTypeConstructor.resolveType(
jdbcTypeRegistry.getTypeConfiguration(),
this,
jdbcTypeRegistry.getDescriptor( sqlTypeCode ),
ColumnTypeInformation.EMPTY
);
}
}
}
}
return jdbcType;
return jdbcTypeRegistry.getDescriptor( jdbcTypeCode );
}
/**
@ -1626,7 +1629,7 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun
}
if ( supportsStandardArrays() ) {
jdbcTypeRegistry.addDescriptor( ArrayJdbcType.INSTANCE );
jdbcTypeRegistry.addTypeConstructor( ArrayJdbcTypeConstructor.INSTANCE );
}
if ( supportsMaterializedLobAccess() ) {
jdbcTypeRegistry.addDescriptor( SqlTypes.MATERIALIZED_BLOB, BlobJdbcType.MATERIALIZED );

View File

@ -9,6 +9,7 @@ package org.hibernate.dialect;
import java.lang.reflect.Array;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Locale;
@ -17,14 +18,13 @@ import org.hibernate.HibernateException;
import org.hibernate.boot.model.relational.Database;
import org.hibernate.boot.model.relational.NamedAuxiliaryDatabaseObject;
import org.hibernate.engine.jdbc.Size;
import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
import org.hibernate.type.BasicType;
import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.BasicPluralJavaType;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
import org.hibernate.type.descriptor.jdbc.BasicBinder;
import org.hibernate.type.descriptor.jdbc.BasicExtractor;
import org.hibernate.type.descriptor.jdbc.JdbcLiteralFormatter;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.internal.BasicTypeImpl;
@ -42,17 +42,38 @@ import static org.hibernate.internal.util.collections.ArrayHelper.EMPTY_STRING_A
* @author Christian Beikov
* @author Jordan Gigov
*/
public class OracleArrayJdbcType extends ArrayJdbcType {
public class OracleArrayJdbcType implements JdbcType {
private final JdbcType elementJdbcType;
private final String typeName;
public OracleArrayJdbcType() {
this( null, null );
public OracleArrayJdbcType(JdbcType elementJdbcType, String typeName) {
this.elementJdbcType = elementJdbcType;
this.typeName = typeName;
}
public OracleArrayJdbcType(JdbcType elementJdbcType, String typeName) {
super( elementJdbcType );
this.typeName = typeName;
@Override
public int getJdbcTypeCode() {
return Types.ARRAY;
}
public JdbcType getElementJdbcType() {
return elementJdbcType;
}
@Override
public <T> JavaType<T> getJdbcRecommendedJavaTypeMapping(
Integer precision,
Integer scale,
TypeConfiguration typeConfiguration) {
final JavaType<Object> elementJavaType = elementJdbcType.getJdbcRecommendedJavaTypeMapping(
precision,
scale,
typeConfiguration
);
return typeConfiguration.getJavaTypeRegistry().resolveDescriptor(
Array.newInstance( elementJavaType.getJavaTypeClass(), 0 ).getClass()
);
}
@Override
@ -61,30 +82,8 @@ public class OracleArrayJdbcType extends ArrayJdbcType {
}
@Override
public JdbcType resolveType(
TypeConfiguration typeConfiguration,
Dialect dialect, BasicType<?> elementType,
ColumnTypeInformation columnTypeInformation) {
String typeName = columnTypeInformation.getTypeName();
if ( typeName == null || typeName.isBlank() ) {
typeName = getTypeName( elementType.getJavaTypeDescriptor(), dialect );
}
// if ( typeName == null ) {
// // Fallback to XML type for the representation of arrays as the native JSON type was only introduced in 21
// // Also, use the XML type if the Oracle JDBC driver classes are not visible
// return typeConfiguration.getJdbcTypeRegistry().getDescriptor( SqlTypes.SQLXML );
// }
return new OracleArrayJdbcType( elementType.getJdbcType(), typeName );
}
@Override
public JdbcType resolveType(
TypeConfiguration typeConfiguration,
Dialect dialect,
JdbcType elementType,
ColumnTypeInformation columnTypeInformation) {
// a bit wrong!
return new OracleArrayJdbcType( elementType, columnTypeInformation.getTypeName() );
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
return java.sql.Array.class;
}
@Override
@ -146,12 +145,32 @@ public class OracleArrayJdbcType extends ArrayJdbcType {
};
}
private static String getTypeName(WrapperOptions options, BasicPluralJavaType<?> containerJavaType) {
@Override
public <X> ValueExtractor<X> getExtractor(final JavaType<X> javaTypeDescriptor) {
return new BasicExtractor<>( javaTypeDescriptor, this ) {
@Override
protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( rs.getArray( paramIndex ), options );
}
@Override
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( statement.getArray( index ), options );
}
@Override
protected X doExtract(CallableStatement statement, String name, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( statement.getArray( name ), options );
}
};
}
static String getTypeName(WrapperOptions options, BasicPluralJavaType<?> containerJavaType) {
Dialect dialect = options.getSessionFactory().getJdbcServices().getDialect();
return getTypeName( containerJavaType.getElementJavaType(), dialect );
}
private static String getTypeName(JavaType<?> elementJavaType, Dialect dialect) {
static String getTypeName(JavaType<?> elementJavaType, Dialect dialect) {
return dialect.getArrayTypeName(
elementJavaType.getJavaTypeClass().getSimpleName(),
null // not needed by OracleDialect.getArrayTypeName()
@ -212,4 +231,14 @@ public class OracleArrayJdbcType extends ArrayJdbcType {
// String elementTypeName = getTypeName( pluralJavaType.getElementJavaType(), dialect );
// return " nested table " + columnName + " store as " + tableName + columnName + elementTypeName;
// }
@Override
public String getFriendlyName() {
return typeName;
}
@Override
public String toString() {
return "OracleArrayTypeDescriptor(" + typeName + ")";
}
}

View File

@ -0,0 +1,56 @@
/*
* 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.dialect;
import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
import org.hibernate.type.BasicType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor;
import org.hibernate.type.spi.TypeConfiguration;
import java.sql.Types;
/**
* Factory for {@link OracleArrayJdbcType}.
*
* @see OracleJdbcHelper#getArrayJdbcTypeConstructor
*
* @author Gavin King
*/
public class OracleArrayJdbcTypeConstructor implements JdbcTypeConstructor {
@Override
public JdbcType resolveType(
TypeConfiguration typeConfiguration,
Dialect dialect, BasicType<?> elementType,
ColumnTypeInformation columnTypeInformation) {
String typeName = columnTypeInformation.getTypeName();
if ( typeName == null || typeName.isBlank() ) {
typeName = OracleArrayJdbcType.getTypeName( elementType.getJavaTypeDescriptor(), dialect );
}
// if ( typeName == null ) {
// // Fallback to XML type for the representation of arrays as the native JSON type was only introduced in 21
// // Also, use the XML type if the Oracle JDBC driver classes are not visible
// return typeConfiguration.getJdbcTypeRegistry().getDescriptor( SqlTypes.SQLXML );
// }
return new OracleArrayJdbcType( elementType.getJdbcType(), typeName );
}
@Override
public JdbcType resolveType(
TypeConfiguration typeConfiguration,
Dialect dialect,
JdbcType elementType,
ColumnTypeInformation columnTypeInformation) {
// a bit wrong, since columnTypeInformation.getTypeName() is typically null!
return new OracleArrayJdbcType( elementType, columnTypeInformation.getTypeName() );
}
@Override
public int getDefaultSqlTypeCode() {
return Types.ARRAY;
}
}

View File

@ -99,6 +99,7 @@ import static org.hibernate.LockOptions.NO_WAIT;
import static org.hibernate.LockOptions.SKIP_LOCKED;
import static org.hibernate.LockOptions.WAIT_FOREVER;
import static org.hibernate.cfg.AvailableSettings.BATCH_VERSIONED_DATA;
import static org.hibernate.dialect.OracleJdbcHelper.getArrayJdbcTypeConstructor;
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
import static org.hibernate.internal.util.StringHelper.isEmpty;
import static org.hibernate.query.sqm.TemporalUnit.DAY;
@ -831,7 +832,7 @@ public class OracleDialect extends Dialect {
}
if ( OracleJdbcHelper.isUsable( serviceRegistry ) ) {
typeContributions.contributeJdbcType( OracleJdbcHelper.getArrayJdbcType( serviceRegistry ) );
typeContributions.contributeJdbcTypeConstructor( getArrayJdbcTypeConstructor( serviceRegistry ) );
}
else {
typeContributions.contributeJdbcType( OracleReflectionStructJdbcType.INSTANCE );

View File

@ -6,7 +6,6 @@
*/
package org.hibernate.dialect;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import org.hibernate.HibernateError;
@ -14,6 +13,7 @@ import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor;
/**
* The following class provides some convenience methods for accessing JdbcType instance,
@ -34,20 +34,18 @@ public class OracleJdbcHelper {
}
}
public static JdbcType getArrayJdbcType(ServiceRegistry serviceRegistry) {
return createJdbcType( serviceRegistry, "org.hibernate.dialect.OracleArrayJdbcType" );
public static JdbcTypeConstructor getArrayJdbcTypeConstructor(ServiceRegistry serviceRegistry) {
return create( serviceRegistry, "org.hibernate.dialect.OracleArrayJdbcTypeConstructor" );
}
public static JdbcType getStructJdbcType(ServiceRegistry serviceRegistry) {
return createJdbcType( serviceRegistry, "org.hibernate.dialect.OracleStructJdbcType" );
return create( serviceRegistry, "org.hibernate.dialect.OracleStructJdbcType" );
}
public static JdbcType createJdbcType(ServiceRegistry serviceRegistry, String className) {
public static <X> X create(ServiceRegistry serviceRegistry, String className) {
final ClassLoaderService classLoaderService = serviceRegistry.getService( ClassLoaderService.class );
try {
final Class<?> clazz = classLoaderService.classForName( className );
final Constructor<?> constructor = clazz.getConstructor();
return (JdbcType) constructor.newInstance();
return classLoaderService.<X>classForName( className ).getConstructor().newInstance();
}
catch (NoSuchMethodException e) {
throw new HibernateError( "Class does not have an empty constructor", e );

View File

@ -82,6 +82,7 @@ import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
import org.hibernate.type.descriptor.jdbc.ClobJdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor;
import org.hibernate.type.descriptor.jdbc.ObjectNullAsBinaryTypeJdbcType;
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
import org.hibernate.type.descriptor.jdbc.XmlJdbcType;
@ -316,13 +317,13 @@ public class PostgreSQLDialect extends Dialect {
}
break;
case ARRAY:
final JdbcType jdbcType = jdbcTypeRegistry.getDescriptor( jdbcTypeCode );
final JdbcTypeConstructor jdbcTypeConstructor = jdbcTypeRegistry.getConstructor( jdbcTypeCode );
// PostgreSQL names array types by prepending an underscore to the base name
if ( jdbcType instanceof ArrayJdbcType && columnTypeName.charAt( 0 ) == '_' ) {
if ( jdbcTypeConstructor != null && columnTypeName.charAt( 0 ) == '_' ) {
final String componentTypeName = columnTypeName.substring( 1 );
final Integer sqlTypeCode = resolveSqlTypeCode( componentTypeName, jdbcTypeRegistry.getTypeConfiguration() );
if ( sqlTypeCode != null ) {
return ( (ArrayJdbcType) jdbcType ).resolveType(
return jdbcTypeConstructor.resolveType(
jdbcTypeRegistry.getTypeConfiguration(),
this,
jdbcTypeRegistry.getDescriptor( sqlTypeCode ),
@ -330,7 +331,7 @@ public class PostgreSQLDialect extends Dialect {
);
}
}
return jdbcType;
break;
case STRUCT:
final AggregateJdbcType aggregateDescriptor = jdbcTypeRegistry.findAggregateDescriptor( columnTypeName );
if ( aggregateDescriptor != null ) {

View File

@ -18,9 +18,10 @@ import org.hibernate.type.BasicPluralType;
import org.hibernate.type.BasicType;
import org.hibernate.type.ConvertedBasicArrayType;
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor;
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
import org.hibernate.type.internal.BasicTypeImpl;
import org.hibernate.type.spi.TypeConfiguration;
@ -45,20 +46,14 @@ public abstract class AbstractArrayJavaType<T, E> extends AbstractClassJavaType<
@Override
public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) {
final int preferredSqlTypeCodeForArray = indicators.getPreferredSqlTypeCodeForArray();
// Always determine the recommended type to make sure this is a valid basic java type
final JdbcType recommendedComponentJdbcType = componentJavaType.getRecommendedJdbcType( indicators );
final TypeConfiguration typeConfiguration = indicators.getTypeConfiguration();
final JdbcType jdbcType = typeConfiguration.getJdbcTypeRegistry().getDescriptor( preferredSqlTypeCodeForArray );
if ( jdbcType instanceof ArrayJdbcType ) {
return ( (ArrayJdbcType) jdbcType ).resolveType(
typeConfiguration,
indicators.getDialect(),
new BasicTypeImpl<>( getElementJavaType(), recommendedComponentJdbcType ),
ColumnTypeInformation.EMPTY
);
}
return jdbcType;
return getArrayJdbcType(
indicators.getTypeConfiguration(),
indicators.getDialect(),
indicators.getPreferredSqlTypeCodeForArray(),
new BasicTypeImpl<>( getElementJavaType(), componentJavaType.getRecommendedJdbcType( indicators ) ),
ColumnTypeInformation.EMPTY
);
}
@Override
@ -75,44 +70,51 @@ public abstract class AbstractArrayJavaType<T, E> extends AbstractClassJavaType<
final BasicValueConverter<E, ?> valueConverter = elementType.getValueConverter();
if ( valueConverter == null ) {
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
);
}
final JdbcType arrayJdbcType =
getArrayJdbcType( typeConfiguration, dialect, Types.ARRAY, 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 {
return creator.apply( this );
}
}
else {
final JavaType<Object> relationalJavaType = typeConfiguration.getJavaTypeRegistry().getDescriptor(
Array.newInstance( valueConverter.getRelationalJavaType().getJavaTypeClass(), 0 ).getClass()
);
JdbcType arrayJdbcType = typeConfiguration.getJdbcTypeRegistry().getDescriptor( Types.ARRAY );
if ( arrayJdbcType instanceof ArrayJdbcType ) {
arrayJdbcType = ( (ArrayJdbcType) arrayJdbcType ).resolveType(
typeConfiguration,
dialect,
elementType,
columnTypeInformation
);
}
//noinspection unchecked,rawtypes
return new ConvertedBasicArrayType(
elementType,
arrayJdbcType,
getArrayJdbcType( typeConfiguration, dialect, Types.ARRAY, elementType, columnTypeInformation ),
this,
new ArrayConverter( valueConverter, this, relationalJavaType )
);
}
}
private static JdbcType getArrayJdbcType(
TypeConfiguration typeConfiguration,
Dialect dialect,
int preferredSqlTypeCodeForArray,
BasicType<?> elementType,
ColumnTypeInformation columnTypeInformation) {
final JdbcTypeRegistry jdbcTypeRegistry = typeConfiguration.getJdbcTypeRegistry();
final JdbcTypeConstructor arrayJdbcTypeConstructor =
jdbcTypeRegistry.getConstructor( preferredSqlTypeCodeForArray );
if ( arrayJdbcTypeConstructor != null ) {
return arrayJdbcTypeConstructor.resolveType(
typeConfiguration,
dialect,
elementType,
columnTypeInformation
);
}
else {
return jdbcTypeRegistry.getDescriptor( preferredSqlTypeCodeForArray );
}
}
}

View File

@ -26,9 +26,10 @@ import org.hibernate.type.BasicType;
import org.hibernate.type.ConvertedBasicArrayType;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor;
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
import org.hibernate.type.spi.TypeConfiguration;
/**
@ -86,19 +87,11 @@ public class ArrayJavaType<T> extends AbstractArrayJavaType<T[], T> {
// Register the array type as that will be resolved in the next step
typeConfiguration.getJavaTypeRegistry().addDescriptor( arrayJavaType );
}
//noinspection unchecked
final BasicValueConverter<Object, Object> valueConverter = (BasicValueConverter<Object, Object>) elementType.getValueConverter();
final BasicValueConverter<T, ?> valueConverter = elementType.getValueConverter();
if ( valueConverter == null ) {
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
);
}
final JdbcType arrayJdbcType =
getArrayJdbcType( typeConfiguration, dialect, Types.ARRAY, elementType, columnTypeInformation );
return new BasicArrayType<>( elementType, arrayJdbcType, javaType );
};
if ( typeConfiguration.getBasicTypeRegistry().getRegisteredType( elementType.getName() ) == elementType ) {
@ -110,26 +103,39 @@ public class ArrayJavaType<T> extends AbstractArrayJavaType<T[], T> {
final JavaType<Object> relationalJavaType = typeConfiguration.getJavaTypeRegistry().getDescriptor(
Array.newInstance( valueConverter.getRelationalJavaType().getJavaTypeClass(), 0 ).getClass()
);
JdbcType arrayJdbcType = typeConfiguration.getJdbcTypeRegistry().getDescriptor( Types.ARRAY );
if ( arrayJdbcType instanceof ArrayJdbcType ) {
arrayJdbcType = ( (ArrayJdbcType) arrayJdbcType ).resolveType(
typeConfiguration,
dialect,
elementType,
columnTypeInformation
);
}
//noinspection unchecked
return new ConvertedBasicArrayType<>(
//noinspection unchecked,rawtypes
return new ConvertedBasicArrayType(
elementType,
arrayJdbcType,
getArrayJdbcType( typeConfiguration, dialect, Types.ARRAY, elementType, columnTypeInformation ),
arrayJavaType,
new ArrayConverter<>( valueConverter, arrayJavaType, relationalJavaType )
new ArrayConverter( valueConverter, arrayJavaType, relationalJavaType )
);
}
}
//TODO: copy/pasted from AbstractArrayJavaType
private static JdbcType getArrayJdbcType(
TypeConfiguration typeConfiguration,
Dialect dialect,
int preferredSqlTypeCodeForArray,
BasicType<?> elementType,
ColumnTypeInformation columnTypeInformation) {
final JdbcTypeRegistry jdbcTypeRegistry = typeConfiguration.getJdbcTypeRegistry();
final JdbcTypeConstructor arrayJdbcTypeConstructor =
jdbcTypeRegistry.getConstructor( preferredSqlTypeCodeForArray );
if ( arrayJdbcTypeConstructor != null ) {
return arrayJdbcTypeConstructor.resolveType(
typeConfiguration,
dialect,
elementType,
columnTypeInformation
);
}
else {
return jdbcTypeRegistry.getDescriptor( preferredSqlTypeCodeForArray );
}
}
@Override
public String extractLoggableRepresentation(T[] value) {
if ( value == null ) {

View File

@ -38,9 +38,11 @@ import org.hibernate.type.descriptor.java.AbstractJavaType;
import org.hibernate.type.descriptor.java.BasicPluralJavaType;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.java.MutabilityPlan;
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor;
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
import org.hibernate.type.internal.BasicTypeImpl;
import org.hibernate.type.spi.TypeConfiguration;
/**
@ -68,21 +70,15 @@ public class BasicCollectionJavaType<C extends Collection<E>, E> extends Abstrac
@Override
public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) {
final int preferredSqlTypeCodeForArray = indicators.getPreferredSqlTypeCodeForArray();
// Always determine the recommended type to make sure this is a valid basic java type
// (even though we only use this inside the if block, we want it to throw here if something wrong)
final JdbcType recommendedComponentJdbcType = componentJavaType.getRecommendedJdbcType( indicators );
final JdbcType jdbcType = indicators.getJdbcType( preferredSqlTypeCodeForArray );
if ( jdbcType instanceof ArrayJdbcType ) {
final TypeConfiguration typeConfiguration = indicators.getTypeConfiguration();
return ( (ArrayJdbcType) jdbcType ).resolveType(
typeConfiguration,
indicators.getDialect(),
recommendedComponentJdbcType,
ColumnTypeInformation.EMPTY
);
}
return jdbcType;
return getArrayJdbcType(
indicators.getTypeConfiguration(),
indicators.getDialect(),
indicators.getPreferredSqlTypeCodeForArray(),
new BasicTypeImpl<>( getElementJavaType(), componentJavaType.getRecommendedJdbcType( indicators ) ),
ColumnTypeInformation.EMPTY
);
}
public CollectionSemantics<C, E> getSemantics() {
@ -117,15 +113,8 @@ public class BasicCollectionJavaType<C extends Collection<E>, E> extends Abstrac
final BasicValueConverter<E, ?> valueConverter = elementType.getValueConverter();
if ( valueConverter == null ) {
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
);
}
final JdbcType arrayJdbcType =
getArrayJdbcType( typeConfiguration, dialect, Types.ARRAY, elementType, columnTypeInformation );
//noinspection unchecked,rawtypes
return new BasicCollectionType( elementType, arrayJdbcType, collectionJavaType );
};
@ -139,26 +128,39 @@ public class BasicCollectionJavaType<C extends Collection<E>, E> extends Abstrac
final JavaType<Object> relationalJavaType = typeConfiguration.getJavaTypeRegistry().resolveDescriptor(
Array.newInstance( valueConverter.getRelationalJavaType().getJavaTypeClass(), 0 ).getClass()
);
JdbcType arrayJdbcType = typeConfiguration.getJdbcTypeRegistry().getDescriptor( Types.ARRAY );
if ( arrayJdbcType instanceof ArrayJdbcType ) {
arrayJdbcType = ( (ArrayJdbcType) arrayJdbcType ).resolveType(
typeConfiguration,
dialect,
elementType,
columnTypeInformation
);
}
//noinspection unchecked,rawtypes
return new ConvertedBasicCollectionType<>(
return new ConvertedBasicCollectionType(
elementType,
arrayJdbcType,
getArrayJdbcType( typeConfiguration, dialect, Types.ARRAY, elementType, columnTypeInformation ),
collectionJavaType,
new CollectionConverter( valueConverter, collectionJavaType, relationalJavaType )
);
}
}
//TODO: copy/pasted from AbstractArrayJavaType
private static JdbcType getArrayJdbcType(
TypeConfiguration typeConfiguration,
Dialect dialect,
int preferredSqlTypeCodeForArray,
BasicType<?> elementType,
ColumnTypeInformation columnTypeInformation) {
final JdbcTypeRegistry jdbcTypeRegistry = typeConfiguration.getJdbcTypeRegistry();
final JdbcTypeConstructor arrayJdbcTypeConstructor =
jdbcTypeRegistry.getConstructor( preferredSqlTypeCodeForArray );
if ( arrayJdbcTypeConstructor != null ) {
return arrayJdbcTypeConstructor.resolveType(
typeConfiguration,
dialect,
elementType,
columnTypeInformation
);
}
else {
return jdbcTypeRegistry.getDescriptor( preferredSqlTypeCodeForArray );
}
}
@Override
public String extractLoggableRepresentation(C value) {
if ( value == null ) {

View File

@ -14,11 +14,8 @@ import java.sql.SQLException;
import java.sql.Types;
import org.hibernate.HibernateException;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.Size;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
import org.hibernate.type.BasicType;
import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions;
@ -39,30 +36,12 @@ import org.hibernate.type.spi.TypeConfiguration;
*/
public class ArrayJdbcType implements JdbcType {
public static final ArrayJdbcType INSTANCE = new ArrayJdbcType( ObjectJdbcType.INSTANCE );
private final JdbcType elementJdbcType;
public ArrayJdbcType(JdbcType elementJdbcType) {
this.elementJdbcType = elementJdbcType;
}
public JdbcType resolveType(
TypeConfiguration typeConfiguration,
Dialect dialect,
BasicType<?> elementType,
ColumnTypeInformation columnTypeInformation) {
return resolveType( typeConfiguration, dialect, elementType.getJdbcType(), columnTypeInformation );
}
public JdbcType resolveType(
TypeConfiguration typeConfiguration,
Dialect dialect,
JdbcType elementType,
ColumnTypeInformation columnTypeInformation) {
return new ArrayJdbcType( elementType );
}
@Override
public int getJdbcTypeCode() {
return Types.ARRAY;
@ -219,6 +198,6 @@ public class ArrayJdbcType implements JdbcType {
@Override
public String toString() {
return "ArrayTypeDescriptor(" + getFriendlyName() + ")";
return "ArrayTypeDescriptor";
}
}

View File

@ -0,0 +1,44 @@
/*
* 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.jdbc;
import org.hibernate.dialect.Dialect;
import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
import org.hibernate.type.BasicType;
import org.hibernate.type.spi.TypeConfiguration;
import java.sql.Types;
/**
* Factory for {@link ArrayJdbcType}.
*
* @author Gavin King
*/
public class ArrayJdbcTypeConstructor implements JdbcTypeConstructor {
public static final ArrayJdbcTypeConstructor INSTANCE = new ArrayJdbcTypeConstructor();
public JdbcType resolveType(
TypeConfiguration typeConfiguration,
Dialect dialect,
BasicType<?> elementType,
ColumnTypeInformation columnTypeInformation) {
return resolveType( typeConfiguration, dialect, elementType.getJdbcType(), columnTypeInformation );
}
public JdbcType resolveType(
TypeConfiguration typeConfiguration,
Dialect dialect,
JdbcType elementType,
ColumnTypeInformation columnTypeInformation) {
return new ArrayJdbcType( elementType );
}
@Override
public int getDefaultSqlTypeCode() {
return Types.ARRAY;
}
}

View File

@ -0,0 +1,50 @@
/*
* 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.jdbc;
import org.hibernate.dialect.Dialect;
import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
import org.hibernate.type.BasicType;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Factory for any {@link JdbcType} which is parameterized by
* a second {@code JdbcType}, the "element" type.
* <p>
* For example, {@link ArrayJdbcType} is parameterized by the
* type of its elements.
*
* @author Gavin King
*/
public interface JdbcTypeConstructor {
/**
* Called by {@link org.hibernate.type.descriptor.java.ArrayJavaType}
* and friends. Here we already know the type argument, which
* we're given as a {@link BasicType}.
*/
default JdbcType resolveType(
TypeConfiguration typeConfiguration,
Dialect dialect,
BasicType<?> elementType,
ColumnTypeInformation columnTypeInformation) {
return resolveType( typeConfiguration, dialect, elementType.getJdbcType(), columnTypeInformation );
}
/**
* Called from {@link Dialect#resolveSqlTypeDescriptor} when
* inferring {@link JdbcType}s from a JDBC {@code ResultSet}
* or when reverse-engineering a schema. Here we do not have
* a known {@link BasicType}.
*/
JdbcType resolveType(
TypeConfiguration typeConfiguration,
Dialect dialect,
JdbcType elementType,
ColumnTypeInformation columnTypeInformation);
int getDefaultSqlTypeCode();
}

View File

@ -15,6 +15,7 @@ import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.type.descriptor.JdbcTypeNameMapper;
import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor;
import org.hibernate.type.descriptor.jdbc.JdbcTypeFamilyInformation;
import org.hibernate.type.descriptor.jdbc.ObjectJdbcType;
import org.hibernate.type.descriptor.jdbc.internal.JdbcTypeBaseline;
@ -36,6 +37,7 @@ public class JdbcTypeRegistry implements JdbcTypeBaseline.BaselineTarget, Serial
private final TypeConfiguration typeConfiguration;
private final ConcurrentHashMap<Integer, JdbcType> descriptorMap = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Integer, JdbcTypeConstructor> descriptorConstructorMap = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, AggregateJdbcType> aggregateDescriptorMap = new ConcurrentHashMap<>();
public JdbcTypeRegistry(TypeConfiguration typeConfiguration) {
@ -91,7 +93,8 @@ public class JdbcTypeRegistry implements JdbcTypeBaseline.BaselineTarget, Serial
}
// see if the typecode is part of a known type family...
JdbcTypeFamilyInformation.Family family = JdbcTypeFamilyInformation.INSTANCE.locateJdbcTypeFamilyByTypeCode( jdbcTypeCode );
JdbcTypeFamilyInformation.Family family =
JdbcTypeFamilyInformation.INSTANCE.locateJdbcTypeFamilyByTypeCode( jdbcTypeCode );
if ( family != null ) {
for ( int potentialAlternateTypeCode : family.getTypeCodes() ) {
if ( potentialAlternateTypeCode != jdbcTypeCode ) {
@ -176,4 +179,16 @@ public class JdbcTypeRegistry implements JdbcTypeBaseline.BaselineTarget, Serial
|| JdbcTypeNameMapper.isStandardTypeCode( jdbcTypeCode )
|| JdbcTypeFamilyInformation.INSTANCE.locateJdbcTypeFamilyByTypeCode( jdbcTypeCode ) != null;
}
public JdbcTypeConstructor getConstructor(int jdbcTypeCode) {
return descriptorConstructorMap.get( jdbcTypeCode );
}
public void addTypeConstructor(int jdbcTypeCode, JdbcTypeConstructor jdbcTypeConstructor) {
descriptorConstructorMap.put( jdbcTypeCode, jdbcTypeConstructor );
}
public void addTypeConstructor(JdbcTypeConstructor jdbcTypeConstructor) {
addTypeConstructor( jdbcTypeConstructor.getDefaultSqlTypeCode(), jdbcTypeConstructor );
}
}