From a094d4c5d5c6f772a0c5d43ea71099f34ed2f182 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Thu, 25 Aug 2022 16:12:49 +0200 Subject: [PATCH] HHH-15463 Adjust JdbcType based on DDL capacity for varchar/varbinary like types --- .../mapping/basic/DurationMappingTests.java | 27 +++++- .../basic/InetAddressMappingTests.java | 21 +++-- .../community/dialect/InformixDialect.java | 5 ++ .../community/dialect/RDMSOS2200Dialect.java | 5 ++ .../dialect/SybaseAnywhereDialect.java | 5 ++ .../boot/model/internal/BasicValueBinder.java | 31 +++++-- .../internal/InferredBasicValueResolver.java | 20 +++-- .../process/spi/MetadataBuildingProcess.java | 4 + .../org/hibernate/dialect/DerbyDialect.java | 34 ++++---- .../java/org/hibernate/dialect/Dialect.java | 84 ++++++++++++++++--- .../org/hibernate/dialect/MySQLDialect.java | 24 +++--- .../hibernate/dialect/PostgreSQLDialect.java | 13 +++ .../PostgreSQLIntervalSecondJdbcType.java | 20 ++++- .../java/org/hibernate/type/SqlTypes.java | 35 ++++++++ .../type/descriptor/jdbc/BlobJdbcType.java | 50 +++++++++++ .../type/descriptor/jdbc/ClobJdbcType.java | 51 +++++++++++ .../type/descriptor/jdbc/JdbcType.java | 17 ++++ .../descriptor/jdbc/LongNVarcharJdbcType.java | 4 + .../descriptor/jdbc/LongVarcharJdbcType.java | 4 + .../type/descriptor/jdbc/NClobJdbcType.java | 51 +++++++++++ .../descriptor/jdbc/NVarcharJdbcType.java | 18 ++++ .../descriptor/jdbc/VarbinaryJdbcType.java | 23 ++++- .../type/descriptor/jdbc/VarcharJdbcType.java | 22 ++++- .../internal/Scale6IntervalSecondDdlType.java | 5 +- .../hibernate/orm/test/length/LengthTest.java | 17 ++++ 25 files changed, 517 insertions(+), 73 deletions(-) diff --git a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/DurationMappingTests.java b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/DurationMappingTests.java index f7239326db..55b8baaea4 100644 --- a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/DurationMappingTests.java +++ b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/DurationMappingTests.java @@ -7,6 +7,7 @@ package org.hibernate.userguide.mapping.basic; import java.time.Duration; + import jakarta.persistence.Entity; import jakarta.persistence.Id; import jakarta.persistence.Table; @@ -18,7 +19,9 @@ import org.hibernate.persister.entity.EntityPersister; import org.hibernate.type.SqlTypes; 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.spi.JdbcTypeRegistry; +import org.hibernate.type.spi.TypeConfiguration; import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.SessionFactory; @@ -50,15 +53,25 @@ public class DurationMappingTests { final JdbcType intervalType = jdbcTypeRegistry.getDescriptor(SqlTypes.INTERVAL_SECOND); final JdbcType realType; if (intervalType instanceof AdjustableJdbcType) { - realType = ((AdjustableJdbcType) intervalType).resolveIndicatedType( - () -> mappingMetamodel.getTypeConfiguration(), + realType = ( (AdjustableJdbcType) intervalType ).resolveIndicatedType( + new JdbcTypeIndicators() { + @Override + public TypeConfiguration getTypeConfiguration() { + return mappingMetamodel.getTypeConfiguration(); + } + + @Override + public int getColumnScale() { + return duration.getScale() == null ? JdbcTypeIndicators.NO_COLUMN_SCALE : duration.getScale(); + } + }, jdbcMapping.getJavaTypeDescriptor() ); } else { realType = intervalType; } - assertThat( jdbcMapping.getJdbcType(), is( realType)); + assertThat( jdbcMapping.getJdbcType(), is( realType ) ); scope.inTransaction( (session) -> { @@ -69,6 +82,14 @@ public class DurationMappingTests { scope.inTransaction( (session) -> session.find(EntityWithDuration.class, 1) ); + + scope.inTransaction( + (session) -> { + session.createQuery( "from EntityWithDuration e where e.duration = :param", EntityWithDuration.class ) + .setParameter( "param", Duration.ofHours( 3 ) ) + .getResultList(); + } + ); } @Entity(name = "EntityWithDuration") diff --git a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/InetAddressMappingTests.java b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/InetAddressMappingTests.java index 23bf24c009..a1911d1cb0 100644 --- a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/InetAddressMappingTests.java +++ b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/InetAddressMappingTests.java @@ -39,7 +39,7 @@ import static org.hamcrest.Matchers.is; public class InetAddressMappingTests { @Test - public void verifyMappings(SessionFactoryScope scope) { + public void verifyMappings(SessionFactoryScope scope) throws Exception { final MappingMetamodelImplementor mappingMetamodel = scope.getSessionFactory() .getRuntimeMetamodels() .getMappingMetamodel(); @@ -62,20 +62,27 @@ public class InetAddressMappingTests { } assertThat( jdbcMapping.getJdbcType(), is( realType)); + EntityWithInetAddress entity = new EntityWithInetAddress( 1, InetAddress.getLocalHost() ); scope.inTransaction( (session) -> { - try { - session.persist( new EntityWithInetAddress( 1, InetAddress.getLocalHost() ) ); - } - catch (UnknownHostException e) { - throw new RuntimeException( e ); - } + session.persist( entity ); } ); scope.inTransaction( (session) -> session.find( EntityWithInetAddress.class, 1) ); + + scope.inTransaction( + (session) -> { + session.createQuery( + "from EntityWithInetAddress e where e.address = :param", + EntityWithInetAddress.class + ) + .setParameter( "param", entity.address ) + .getResultList(); + } + ); } @Entity(name = "EntityWithInetAddress") diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixDialect.java index b5f91ec621..d7537e9a55 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixDialect.java @@ -168,6 +168,11 @@ public class InformixDialect extends Dialect { ); } + @Override + public boolean useMaterializedLobWhenCapacityExceeded() { + return false; + } + @Override public int getMaxVarbinaryLength() { //there's no varbinary type, only byte diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/RDMSOS2200Dialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/RDMSOS2200Dialect.java index 57e0629212..4167d983f8 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/RDMSOS2200Dialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/RDMSOS2200Dialect.java @@ -158,6 +158,11 @@ public class RDMSOS2200Dialect extends Dialect { } } + @Override + public boolean useMaterializedLobWhenCapacityExceeded() { + return false; + } + @Override public int getMaxVarbinaryLength() { //no varbinary type diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseAnywhereDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseAnywhereDialect.java index a2a2902e8c..51dcd7ab49 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseAnywhereDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseAnywhereDialect.java @@ -84,6 +84,11 @@ public class SybaseAnywhereDialect extends SybaseDialect { } } + @Override + public boolean useMaterializedLobWhenCapacityExceeded() { + return false; + } + @Override public void initializeFunctionRegistry(QueryEngine queryEngine) { super.initializeFunctionRegistry( queryEngine ); diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/BasicValueBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/BasicValueBinder.java index ff892f3d62..c5510e3e45 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/BasicValueBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/BasicValueBinder.java @@ -196,7 +196,17 @@ public class BasicValueBinder implements JdbcTypeIndicators { @Override public boolean isLob() { - return isLob; + if ( isLob ) { + return true; + } + + if ( explicitJdbcTypeAccess != null ) { + final JdbcType type = explicitJdbcTypeAccess.apply( getTypeConfiguration() ); + if ( type != null ) { + return type.isLob(); + } + } + return false; } @Override @@ -239,7 +249,16 @@ public class BasicValueBinder implements JdbcTypeIndicators { @Override public boolean isNationalized() { - return isNationalized; + if ( isNationalized ) { + return true; + } + if ( explicitJdbcTypeAccess != null ) { + final JdbcType type = explicitJdbcTypeAccess.apply( getTypeConfiguration() ); + if ( type != null ) { + return type.isNationalized(); + } + } + return false; } @@ -1093,11 +1112,11 @@ public class BasicValueBinder implements JdbcTypeIndicators { basicValue = new BasicValue( buildingContext, table ); - if ( isNationalized ) { + if ( isNationalized() ) { basicValue.makeNationalized(); } - if ( isLob ) { + if ( isLob() ) { basicValue.makeLob(); } @@ -1243,11 +1262,11 @@ public class BasicValueBinder implements JdbcTypeIndicators { basicValue.setTemporalPrecision( temporalPrecision ); } - if ( isLob ) { + if ( isLob() ) { basicValue.makeLob(); } - if ( isNationalized ) { + if ( isNationalized() ) { basicValue.makeNationalized(); } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/InferredBasicValueResolver.java b/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/InferredBasicValueResolver.java index d744376a80..75c22c9e63 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/InferredBasicValueResolver.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/InferredBasicValueResolver.java @@ -104,9 +104,13 @@ public class InferredBasicValueResolver { jdbcMapping = new SerializableType( explicitJavaType ); } else { - jdbcMapping = typeConfiguration.getBasicTypeRegistry().resolve( - explicitJavaType, - inferredJdbcType + jdbcMapping = resolveSqlTypeIndicators( + stdIndicators, + typeConfiguration.getBasicTypeRegistry().resolve( + explicitJavaType, + inferredJdbcType + ), + explicitJavaType ); } } @@ -206,9 +210,13 @@ public class InferredBasicValueResolver { // one, to create a mapping final JdbcType recommendedJdbcType = reflectedJtd.getRecommendedJdbcType( stdIndicators ); if ( recommendedJdbcType != null ) { - jdbcMapping = typeConfiguration.getBasicTypeRegistry().resolve( - reflectedJtd, - recommendedJdbcType + jdbcMapping = resolveSqlTypeIndicators( + stdIndicators, + typeConfiguration.getBasicTypeRegistry().resolve( + reflectedJtd, + recommendedJdbcType + ), + reflectedJtd ); } else if ( reflectedJtd instanceof SerializableJavaType diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java b/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java index 33694e38ed..d8eea6ef0a 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java @@ -450,6 +450,10 @@ public class MetadataBuildingProcess { jdbcTypeRegistry.addDescriptorIfAbsent( JsonJdbcType.INSTANCE ); jdbcTypeRegistry.addDescriptorIfAbsent( XmlAsStringJdbcType.INSTANCE ); + addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.MATERIALIZED_BLOB, SqlTypes.BLOB ); + addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.MATERIALIZED_CLOB, SqlTypes.CLOB ); + addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.MATERIALIZED_NCLOB, SqlTypes.NCLOB ); + final DdlTypeRegistry ddlTypeRegistry = typeConfiguration.getDdlTypeRegistry(); // Fallback to the biggest varchar DdlType when json is requested ddlTypeRegistry.addDescriptorIfAbsent( diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DerbyDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DerbyDialect.java index 480b60abdd..2f897bbe7e 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DerbyDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DerbyDialect.java @@ -168,49 +168,42 @@ public class DerbyDialect extends Dialect { super.registerColumnTypes( typeContributions, serviceRegistry ); final DdlTypeRegistry ddlTypeRegistry = typeContributions.getTypeConfiguration().getDdlTypeRegistry(); - //long varchar is the right type to use for lengths between 32_672 and 32_700 - int maxLongVarcharLength = 32_700; + int varcharDdlTypeCapacity = 32_672; ddlTypeRegistry.addDescriptor( - CapacityDependentDdlType.builder( VARBINARY, columnType( BLOB ), columnType( VARBINARY ), this ) - .withTypeCapacity( getMaxVarbinaryLength(), columnType( VARBINARY ) ) - .withTypeCapacity( maxLongVarcharLength, columnType( LONG32VARBINARY ) ) + CapacityDependentDdlType.builder( VARBINARY, columnType( LONG32VARBINARY ), columnType( VARBINARY ), this ) + .withTypeCapacity( varcharDdlTypeCapacity, columnType( VARBINARY ) ) .build() ); ddlTypeRegistry.addDescriptor( - CapacityDependentDdlType.builder( VARCHAR, columnType( CLOB ), columnType( VARCHAR ), this ) - .withTypeCapacity( getMaxVarcharLength(), columnType( VARCHAR ) ) - .withTypeCapacity( maxLongVarcharLength, columnType( LONG32VARCHAR ) ) + CapacityDependentDdlType.builder( VARCHAR, columnType( LONG32VARCHAR ), columnType( VARCHAR ), this ) + .withTypeCapacity( varcharDdlTypeCapacity, columnType( VARCHAR ) ) .build() ); ddlTypeRegistry.addDescriptor( - CapacityDependentDdlType.builder( NVARCHAR, columnType( CLOB ), columnType( NVARCHAR ), this ) - .withTypeCapacity( getMaxVarcharLength(), columnType( NVARCHAR ) ) - .withTypeCapacity( maxLongVarcharLength, columnType( LONG32VARCHAR ) ) + CapacityDependentDdlType.builder( NVARCHAR, columnType( LONG32VARCHAR ), columnType( NVARCHAR ), this ) + .withTypeCapacity( varcharDdlTypeCapacity, columnType( NVARCHAR ) ) .build() ); ddlTypeRegistry.addDescriptor( - CapacityDependentDdlType.builder( BINARY, columnType( BLOB ), columnType( VARBINARY ), this ) + CapacityDependentDdlType.builder( BINARY, columnType( LONG32VARBINARY ), columnType( VARBINARY ), this ) .withTypeCapacity( 254, "char($l) for bit data" ) - .withTypeCapacity( getMaxVarbinaryLength(), columnType( VARBINARY ) ) - .withTypeCapacity( maxLongVarcharLength, columnType( LONG32VARBINARY ) ) + .withTypeCapacity( varcharDdlTypeCapacity, columnType( VARBINARY ) ) .build() ); // This is the maximum size for the CHAR datatype on Derby ddlTypeRegistry.addDescriptor( - CapacityDependentDdlType.builder( CHAR, columnType( CLOB ), columnType( CHAR ), this ) + CapacityDependentDdlType.builder( CHAR, columnType( LONG32VARCHAR ), columnType( CHAR ), this ) .withTypeCapacity( 254, columnType( CHAR ) ) .withTypeCapacity( getMaxVarcharLength(), columnType( VARCHAR ) ) - .withTypeCapacity( maxLongVarcharLength, columnType( LONG32VARCHAR ) ) .build() ); ddlTypeRegistry.addDescriptor( - CapacityDependentDdlType.builder( NCHAR, columnType( CLOB ), columnType( NCHAR ), this ) + CapacityDependentDdlType.builder( NCHAR, columnType( LONG32NVARCHAR ), columnType( NCHAR ), this ) .withTypeCapacity( 254, columnType( NCHAR ) ) .withTypeCapacity( getMaxVarcharLength(), columnType( NVARCHAR ) ) - .withTypeCapacity( maxLongVarcharLength, columnType( LONG32NVARCHAR ) ) .build() ); } @@ -220,6 +213,11 @@ public class DerbyDialect extends Dialect { return 32_672; } + @Override + public int getMaxVarcharCapacity() { + return 32_700; + } + @Override public int getDefaultDecimalPrecision() { //this is the maximum allowed in Derby diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index be56888879..c921cad70c 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -171,6 +171,7 @@ 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.BlobJdbcType; import org.hibernate.type.descriptor.jdbc.ClobJdbcType; import org.hibernate.type.descriptor.jdbc.InstantAsTimestampJdbcType; import org.hibernate.type.descriptor.jdbc.InstantAsTimestampWithTimeZoneJdbcType; @@ -1396,29 +1397,35 @@ public abstract class Dialect implements ConversionContext { // by default, not much to do... registerColumnTypes( typeContributions, serviceRegistry ); final NationalizationSupport nationalizationSupport = getNationalizationSupport(); + final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration().getJdbcTypeRegistry(); if ( nationalizationSupport == NationalizationSupport.EXPLICIT ) { - typeContributions.contributeJdbcType( NCharJdbcType.INSTANCE ); - typeContributions.contributeJdbcType( NVarcharJdbcType.INSTANCE ); - typeContributions.contributeJdbcType( LongNVarcharJdbcType.INSTANCE ); - typeContributions.contributeJdbcType( NClobJdbcType.DEFAULT ); + jdbcTypeRegistry.addDescriptor( NCharJdbcType.INSTANCE ); + jdbcTypeRegistry.addDescriptor( NVarcharJdbcType.INSTANCE ); + jdbcTypeRegistry.addDescriptor( LongNVarcharJdbcType.INSTANCE ); + jdbcTypeRegistry.addDescriptor( NClobJdbcType.DEFAULT ); } if ( useInputStreamToInsertBlob() ) { - typeContributions.getTypeConfiguration().getJdbcTypeRegistry().addDescriptor( + jdbcTypeRegistry.addDescriptor( Types.CLOB, ClobJdbcType.STREAM_BINDING ); } if ( getTimeZoneSupport() == TimeZoneSupport.NATIVE ) { - typeContributions.contributeJdbcType( InstantAsTimestampWithTimeZoneJdbcType.INSTANCE ); + jdbcTypeRegistry.addDescriptor( InstantAsTimestampWithTimeZoneJdbcType.INSTANCE ); } else { - typeContributions.contributeJdbcType( InstantAsTimestampJdbcType.INSTANCE ); + jdbcTypeRegistry.addDescriptor( InstantAsTimestampJdbcType.INSTANCE ); } if ( supportsStandardArrays() ) { - typeContributions.contributeJdbcType( ArrayJdbcType.INSTANCE ); + jdbcTypeRegistry.addDescriptor( ArrayJdbcType.INSTANCE ); + } + if ( supportsMaterializedLobAccess() ) { + jdbcTypeRegistry.addDescriptor( SqlTypes.MATERIALIZED_BLOB, BlobJdbcType.MATERIALIZED ); + jdbcTypeRegistry.addDescriptor( SqlTypes.MATERIALIZED_CLOB, ClobJdbcType.MATERIALIZED ); + jdbcTypeRegistry.addDescriptor( SqlTypes.MATERIALIZED_NCLOB, NClobJdbcType.MATERIALIZED ); } } @@ -3816,6 +3823,32 @@ public abstract class Dialect implements ConversionContext { return true; } + /** + * Check whether the JDBC driver allows setting LOBs via {@link PreparedStatement#setBytes(int, byte[])}, + * {@link PreparedStatement#setNString(int, String)} or {@link PreparedStatement#setString(int, String)} APIs. + * + * @return {@code true} if LOBs can be set with the materialized APIs. + * @since 6.2 + */ + public boolean supportsMaterializedLobAccess() { + // Most drivers support this + return true; + } + + /** + * Whether to switch from {@code VARCHAR}-like types to {@link SqlTypes#MATERIALIZED_CLOB}, + * {@code NVARCHAR}-like types to {@link SqlTypes#MATERIALIZED_NCLOB} + * and {@code VARBINARY}-like types to {@link SqlTypes#MATERIALIZED_BLOB} types, + * when the requested size for a type exceeds the {@link #getMaxVarcharCapacity()}, {@link #getMaxNVarcharCapacity()} + * and {@link #getMaxVarbinaryCapacity()} respectively. + * + * @return {@code true} if materialized LOBs should be used for capacity exceeding types. + * @since 6.2 + */ + public boolean useMaterializedLobWhenCapacityExceeded() { + return supportsMaterializedLobAccess(); + } + /** * Modify the SQL, adding hints or comments, if necessary */ @@ -3895,7 +3928,7 @@ public abstract class Dialect implements ConversionContext { } /** - * The longest possible length of a {@link java.sql.Types#VARCHAR}-like column. + * The biggest size value that can be supplied as argument to a {@link java.sql.Types#VARCHAR}-like type. * For longer column lengths, use some sort of {@code text}-like type for the * column. */ @@ -3905,8 +3938,8 @@ public abstract class Dialect implements ConversionContext { } /** - * The longest possible length of a {@link java.sql.Types#NVARCHAR}-like column. - * For longer column lengths, use some sort of {@code text}-like type for the + * The biggest size value that can be supplied as argument to a {@link java.sql.Types#NVARCHAR}-like type. + * For longer column lengths, use some sort of {@code ntext}-like type for the * column. */ public int getMaxNVarcharLength() { @@ -3915,7 +3948,7 @@ public abstract class Dialect implements ConversionContext { } /** - * The longest possible length of a {@link java.sql.Types#VARBINARY}-like column. + * The biggest size value that can be supplied as argument to a {@link java.sql.Types#VARBINARY}-like type. * For longer column lengths, use some sort of {@code image}-like type for the * column. */ @@ -3924,6 +3957,33 @@ public abstract class Dialect implements ConversionContext { return getMaxVarcharLength(); } + /** + * The longest possible length of a {@link java.sql.Types#VARCHAR}-like column. + * For longer column lengths, use some sort of {@code clob}-like type for the + * column. + */ + public int getMaxVarcharCapacity() { + return getMaxVarcharLength(); + } + + /** + * The longest possible length of a {@link java.sql.Types#NVARCHAR}-like column. + * For longer column lengths, use some sort of {@code nclob}-like type for the + * column. + */ + public int getMaxNVarcharCapacity() { + return getMaxNVarcharLength(); + } + + /** + * The longest possible length of a {@link java.sql.Types#VARBINARY}-like column. + * For longer column lengths, use some sort of {@code blob}-like type for the + * column. + */ + public int getMaxVarbinaryCapacity() { + return getMaxVarbinaryLength(); + } + public long getDefaultLobLength() { return Size.DEFAULT_LOB_LENGTH; } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java index a3add17a42..c17e529afa 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java @@ -242,6 +242,12 @@ public class MySQLDialect extends Dialect { } } + @Override + public boolean useMaterializedLobWhenCapacityExceeded() { + // MySQL has no real concept of LOBs, so we can just use longtext/longblob with the materialized JDBC APIs + return false; + } + @Override protected String castType(int sqlTypeCode) { switch ( sqlTypeCode ) { @@ -304,9 +310,9 @@ public class MySQLDialect extends Dialect { "char", this ) - .withTypeCapacity( getMaxVarcharLength(), "varchar($l)" ) + .withTypeCapacity( getVarcharDdlTypeCapacity(), "varchar($l)" ) .withTypeCapacity( maxMediumLobLen, "mediumtext" ); - if ( getMaxVarcharLength() < maxLobLen ) { + if ( getVarcharDdlTypeCapacity() < maxLobLen ) { varcharBuilder.withTypeCapacity( maxLobLen, "text" ); } ddlTypeRegistry.addDescriptor( varcharBuilder.build() ); @@ -317,9 +323,9 @@ public class MySQLDialect extends Dialect { "char", this ) - .withTypeCapacity( getMaxVarcharLength(), "varchar($l)" ) + .withTypeCapacity( getVarcharDdlTypeCapacity(), "varchar($l)" ) .withTypeCapacity( maxMediumLobLen, "mediumtext" ); - if ( getMaxVarcharLength() < maxLobLen ) { + if ( getVarcharDdlTypeCapacity() < maxLobLen ) { nvarcharBuilder.withTypeCapacity( maxLobLen, "text" ); } ddlTypeRegistry.addDescriptor( nvarcharBuilder.build() ); @@ -330,9 +336,9 @@ public class MySQLDialect extends Dialect { "binary", this ) - .withTypeCapacity( getMaxVarbinaryLength(), "varbinary($l)" ) + .withTypeCapacity( getVarbinaryDdlTypeCapacity(), "varbinary($l)" ) .withTypeCapacity( maxMediumLobLen, "mediumblob" ); - if ( getMaxVarcharLength() < maxLobLen ) { + if ( getVarbinaryDdlTypeCapacity() < maxLobLen ) { varbinaryBuilder.withTypeCapacity( maxLobLen, "blob" ); } ddlTypeRegistry.addDescriptor( varbinaryBuilder.build() ); @@ -425,13 +431,11 @@ public class MySQLDialect extends Dialect { } } - @Override - public int getMaxVarcharLength() { + public int getVarcharDdlTypeCapacity() { return maxVarcharLength; } - @Override - public int getMaxVarbinaryLength() { + public int getVarbinaryDdlTypeCapacity() { return maxVarbinaryLength; } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java index 4114be729c..03944f2f83 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java @@ -261,6 +261,12 @@ public class PostgreSQLDialect extends Dialect { return 10_485_760; } + @Override + public int getMaxVarcharCapacity() { + // 1GB according to PostgreSQL docs + return 1_073_741_824; + } + @Override public int getMaxVarbinaryLength() { //postgres has no varbinary-like type @@ -1001,6 +1007,13 @@ public class PostgreSQLDialect extends Dialect { return false; } + @Override + public boolean supportsMaterializedLobAccess() { + // Prefer using text and bytea over oid (LOB), because oid is very restricted. + // If someone really wants a type bigger than 1GB, they should ask for it by using @Lob explicitly + return false; + } + @Override public boolean supportsTemporalLiteralOffset() { return true; diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLIntervalSecondJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLIntervalSecondJdbcType.java index b6842b67db..a9d45e3cce 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLIntervalSecondJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLIntervalSecondJdbcType.java @@ -17,6 +17,7 @@ import java.sql.Types; import java.time.Duration; import org.hibernate.HibernateException; +import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.internal.util.ReflectHelper; import org.hibernate.type.SqlTypes; import org.hibernate.type.descriptor.ValueBinder; @@ -29,7 +30,6 @@ 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.descriptor.jdbc.JdbcTypeIndicators; -import org.hibernate.type.spi.TypeConfiguration; /** * @author Christian Beikov @@ -104,8 +104,22 @@ public class PostgreSQLIntervalSecondJdbcType implements AdjustableJdbcType { @Override public JdbcType resolveIndicatedType(JdbcTypeIndicators indicators, JavaType domainJtd) { - // The default scale is 9 - if ( indicators.getColumnScale() == JdbcTypeIndicators.NO_COLUMN_SCALE || indicators.getColumnScale() > 6 ) { + final int scale; + if ( indicators.getColumnScale() == JdbcTypeIndicators.NO_COLUMN_SCALE ) { + scale = domainJtd.getDefaultSqlScale( + indicators.getTypeConfiguration() + .getServiceRegistry() + .getService( JdbcServices.class ) + .getDialect(), + this + ); + } + else { + scale = indicators.getColumnScale(); + } + if ( scale > 6 ) { + // Since the maximum allowed scale on PostgreSQL is 6 (microsecond precision), + // we have to switch to the numeric type if the value is greater return indicators.getJdbcType( indicators.resolveJdbcTypeCode( SqlTypes.NUMERIC ) ); } return this; diff --git a/hibernate-core/src/main/java/org/hibernate/type/SqlTypes.java b/hibernate-core/src/main/java/org/hibernate/type/SqlTypes.java index 9c19e9782e..21bc39f9ff 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/SqlTypes.java +++ b/hibernate-core/src/main/java/org/hibernate/type/SqlTypes.java @@ -10,6 +10,8 @@ import org.hibernate.Internal; import java.sql.Types; +import org.hibernate.Internal; + /** * Defines a list of constant type codes used to identify generic SQL types. * This is an extension of the standard JDBC-defined {@link Types}, defining @@ -446,6 +448,39 @@ public class SqlTypes { */ public static final int TIMESTAMP_UTC = 3003; + /** + * The constant in the Java programming language, sometimes referred to + * as a type code, that identifies the generic SQL type + * {@code MATERIALIZED_BLOB}. + * + * This type is used when JDBC access should use {@link #VARBINARY} semantics, + * but the {@link org.hibernate.type.descriptor.sql.DdlType} should be based on {@link #BLOB}. + */ + @Internal + public static final int MATERIALIZED_BLOB = 3004; + + /** + * The constant in the Java programming language, sometimes referred to + * as a type code, that identifies the generic SQL type + * {@code MATERIALIZED_CLOB}. + * + * This type is used when JDBC access should use {@link #VARCHAR} semantics, + * but the {@link org.hibernate.type.descriptor.sql.DdlType} should be based on {@link #CLOB}. + */ + @Internal + public static final int MATERIALIZED_CLOB = 3005; + + /** + * The constant in the Java programming language, sometimes referred to + * as a type code, that identifies the generic SQL type + * {@code MATERIALIZED_NCLOB}. + * + * This type is used when JDBC access should use {@link #NVARCHAR} semantics, + * but the {@link org.hibernate.type.descriptor.sql.DdlType} should be based on {@link #NCLOB}. + */ + @Internal + public static final int MATERIALIZED_NCLOB = 3006; + // Interval types /** diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/BlobJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/BlobJdbcType.java index 57c931c8b7..6a77d8801e 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/BlobJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/BlobJdbcType.java @@ -225,4 +225,54 @@ public abstract class BlobJdbcType implements JdbcType { } }; + public static final BlobJdbcType MATERIALIZED = new BlobJdbcType() { + @Override + public String toString() { + return "BlobTypeDescriptor(MATERIALIZED)"; + } + + @Override + public Class getPreferredJavaTypeClass(WrapperOptions options) { + return byte[].class; + } + + @Override + public BasicBinder getBlobBinder(final JavaType javaType) { + return new BasicBinder<>( javaType, this ) { + @Override + public void doBind(PreparedStatement st, X value, int index, WrapperOptions options) + throws SQLException { + st.setBytes( index, javaType.unwrap( value, byte[].class, options ) ); + } + + @Override + protected void doBind(CallableStatement st, X value, String name, WrapperOptions options) + throws SQLException { + st.setBytes( name, javaType.unwrap( value, byte[].class, options ) ); + } + }; + } + + @Override + public ValueExtractor getExtractor(final JavaType javaType) { + return new BasicExtractor<>( javaType, this ) { + @Override + protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException { + return javaType.wrap( rs.getBytes( paramIndex ), options ); + } + + @Override + protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException { + return javaType.wrap( statement.getBytes( index ), options ); + } + + @Override + protected X doExtract(CallableStatement statement, String name, WrapperOptions options) + throws SQLException { + return javaType.wrap( statement.getBytes( name ), options ); + } + }; + } + }; + } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/ClobJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/ClobJdbcType.java index 01294ff0f7..6883728b66 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/ClobJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/ClobJdbcType.java @@ -305,4 +305,55 @@ public abstract class ClobJdbcType implements AdjustableJdbcType { } }; + public static final ClobJdbcType MATERIALIZED = new ClobJdbcType() { + @Override + public String toString() { + return "ClobTypeDescriptor(MATERIALIZED)"; + } + + @Override + public Class getPreferredJavaTypeClass(WrapperOptions options) { + return String.class; + } + + @Override + public BasicBinder getClobBinder(final JavaType javaType) { + return new BasicBinder<>( javaType, this ) { + @Override + protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) + throws SQLException { + st.setString( index, javaType.unwrap( value, String.class, options ) ); + } + + @Override + protected void doBind(CallableStatement st, X value, String name, WrapperOptions options) + throws SQLException { + st.setString( name, javaType.unwrap( value, String.class, options ) ); + } + }; + } + + @Override + public ValueExtractor getExtractor(final JavaType javaType) { + return new BasicExtractor<>( javaType, this ) { + @Override + protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException { + return javaType.wrap( rs.getString( paramIndex ), options ); + } + + @Override + protected X doExtract(CallableStatement statement, int index, WrapperOptions options) + throws SQLException { + return javaType.wrap( statement.getString( index ), options ); + } + + @Override + protected X doExtract(CallableStatement statement, String name, WrapperOptions options) + throws SQLException { + return javaType.wrap( statement.getString( name ), options ); + } + }; + } + }; + } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JdbcType.java index d0a78218a6..44894a0632 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JdbcType.java @@ -192,6 +192,23 @@ public interface JdbcType extends Serializable { return false; } + default boolean isNationalized() { + return isNationalized( getJdbcTypeCode() ); + } + + static boolean isNationalized(int jdbcTypeCode) { + switch ( jdbcTypeCode ) { + case SqlTypes.NCHAR: + case SqlTypes.NVARCHAR: + case SqlTypes.LONGNVARCHAR: + case SqlTypes.LONG32NVARCHAR: + case SqlTypes.NCLOB: { + return true; + } + } + return false; + } + default boolean isInterval() { return SqlTypes.isIntervalType( getDefaultSqlTypeCode() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/LongNVarcharJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/LongNVarcharJdbcType.java index cbdf01d46d..777f2d454e 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/LongNVarcharJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/LongNVarcharJdbcType.java @@ -8,6 +8,7 @@ package org.hibernate.type.descriptor.jdbc; import java.sql.Types; +import org.hibernate.type.SqlTypes; import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; import org.hibernate.type.spi.TypeConfiguration; @@ -46,6 +47,9 @@ public class LongNVarcharJdbcType extends NVarcharJdbcType { if ( indicators.isLob() ) { jdbcTypeCode = indicators.isNationalized() ? Types.NCLOB : Types.CLOB; } + else if ( shouldUseMaterializedLob( indicators ) ) { + jdbcTypeCode = indicators.isNationalized() ? SqlTypes.MATERIALIZED_NCLOB : SqlTypes.MATERIALIZED_CLOB; + } else { jdbcTypeCode = indicators.isNationalized() ? Types.LONGNVARCHAR : Types.LONGVARCHAR; } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/LongVarcharJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/LongVarcharJdbcType.java index 3062d6873a..4f54cc7524 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/LongVarcharJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/LongVarcharJdbcType.java @@ -8,6 +8,7 @@ package org.hibernate.type.descriptor.jdbc; import java.sql.Types; +import org.hibernate.type.SqlTypes; import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; import org.hibernate.type.spi.TypeConfiguration; @@ -53,6 +54,9 @@ public class LongVarcharJdbcType extends VarcharJdbcType { if ( indicators.isLob() ) { jdbcTypeCode = indicators.isNationalized() ? Types.NCLOB : Types.CLOB; } + else if ( shouldUseMaterializedLob( indicators ) ) { + jdbcTypeCode = indicators.isNationalized() ? SqlTypes.MATERIALIZED_NCLOB : SqlTypes.MATERIALIZED_CLOB; + } else { jdbcTypeCode = indicators.isNationalized() ? Types.LONGNVARCHAR : Types.LONGVARCHAR; } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/NClobJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/NClobJdbcType.java index d6420d93fb..a8a4a56980 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/NClobJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/NClobJdbcType.java @@ -179,4 +179,55 @@ public abstract class NClobJdbcType implements JdbcType { }; } }; + + public static final NClobJdbcType MATERIALIZED = new NClobJdbcType() { + @Override + public String toString() { + return "NClobTypeDescriptor(MATERIALIZED)"; + } + + @Override + public Class getPreferredJavaTypeClass(WrapperOptions options) { + return String.class; + } + + @Override + public BasicBinder getNClobBinder(final JavaType javaType) { + return new BasicBinder<>( javaType, this ) { + @Override + protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) + throws SQLException { + st.setNString( index, javaType.unwrap( value, String.class, options ) ); + } + + @Override + protected void doBind(CallableStatement st, X value, String name, WrapperOptions options) + throws SQLException { + st.setNString( name, javaType.unwrap( value, String.class, options ) ); + } + }; + } + + @Override + public ValueExtractor getExtractor(final JavaType javaType) { + return new BasicExtractor<>( javaType, this ) { + @Override + protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException { + return javaType.wrap( rs.getNString( paramIndex ), options ); + } + + @Override + protected X doExtract(CallableStatement statement, int index, WrapperOptions options) + throws SQLException { + return javaType.wrap( statement.getNString( index ), options ); + } + + @Override + protected X doExtract(CallableStatement statement, String name, WrapperOptions options) + throws SQLException { + return javaType.wrap( statement.getNString( name ), options ); + } + }; + } + }; } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/NVarcharJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/NVarcharJdbcType.java index d6f98f26fd..6c79a4efbb 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/NVarcharJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/NVarcharJdbcType.java @@ -12,6 +12,9 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; +import org.hibernate.dialect.Dialect; +import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.type.SqlTypes; import org.hibernate.type.descriptor.ValueBinder; import org.hibernate.type.descriptor.ValueExtractor; import org.hibernate.type.descriptor.WrapperOptions; @@ -77,6 +80,9 @@ public class NVarcharJdbcType implements AdjustableJdbcType { if ( indicators.isLob() ) { jdbcTypeCode = indicators.isNationalized() ? Types.NCLOB : Types.CLOB; } + else if ( shouldUseMaterializedLob( indicators ) ) { + jdbcTypeCode = indicators.isNationalized() ? SqlTypes.MATERIALIZED_NCLOB : SqlTypes.MATERIALIZED_CLOB; + } else { jdbcTypeCode = indicators.isNationalized() ? Types.NVARCHAR : Types.VARCHAR; } @@ -84,6 +90,18 @@ public class NVarcharJdbcType implements AdjustableJdbcType { return jdbcTypeRegistry.getDescriptor( jdbcTypeCode ); } + protected boolean shouldUseMaterializedLob(JdbcTypeIndicators indicators) { + final Dialect dialect = indicators.getTypeConfiguration() + .getServiceRegistry() + .getService( JdbcServices.class ) + .getDialect(); + final long length = indicators.getColumnLength(); + final long maxLength = indicators.isNationalized() ? + dialect.getMaxNVarcharCapacity() : + dialect.getMaxVarcharCapacity(); + return length > maxLength && dialect.useMaterializedLobWhenCapacityExceeded(); + } + @Override public Class getPreferredJavaTypeClass(WrapperOptions options) { return String.class; diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/VarbinaryJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/VarbinaryJdbcType.java index 24e77279c9..e4cff7ebbb 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/VarbinaryJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/VarbinaryJdbcType.java @@ -12,6 +12,9 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; +import org.hibernate.dialect.Dialect; +import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.type.SqlTypes; import org.hibernate.type.descriptor.ValueBinder; import org.hibernate.type.descriptor.ValueExtractor; import org.hibernate.type.descriptor.WrapperOptions; @@ -75,9 +78,23 @@ public class VarbinaryJdbcType implements AdjustableJdbcType { @Override public JdbcType resolveIndicatedType(JdbcTypeIndicators indicators, JavaType domainJtd) { final JdbcTypeRegistry jdbcTypeRegistry = indicators.getTypeConfiguration().getJdbcTypeRegistry(); - return indicators.isLob() - ? jdbcTypeRegistry.getDescriptor( indicators.resolveJdbcTypeCode( Types.BLOB ) ) - : this; + if ( indicators.isLob() ) { + return jdbcTypeRegistry.getDescriptor( indicators.resolveJdbcTypeCode( SqlTypes.BLOB ) ); + } + else if ( shouldUseMaterializedLob( indicators ) ) { + return jdbcTypeRegistry.getDescriptor( indicators.resolveJdbcTypeCode( SqlTypes.MATERIALIZED_BLOB ) ); + } + return this; + } + + protected boolean shouldUseMaterializedLob(JdbcTypeIndicators indicators) { + final Dialect dialect = indicators.getTypeConfiguration() + .getServiceRegistry() + .getService( JdbcServices.class ) + .getDialect(); + final long length = indicators.getColumnLength(); + final long maxLength = dialect.getMaxVarbinaryCapacity(); + return length > maxLength && dialect.useMaterializedLobWhenCapacityExceeded(); } public ValueBinder getBinder(final JavaType javaType) { diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/VarcharJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/VarcharJdbcType.java index e0ad35f6fd..71fdbee6d2 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/VarcharJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/VarcharJdbcType.java @@ -12,6 +12,9 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; +import org.hibernate.dialect.Dialect; +import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.type.SqlTypes; import org.hibernate.type.descriptor.ValueBinder; import org.hibernate.type.descriptor.ValueExtractor; import org.hibernate.type.descriptor.WrapperOptions; @@ -65,9 +68,7 @@ public class VarcharJdbcType implements AdjustableJdbcType { } @Override - public JdbcType resolveIndicatedType( - JdbcTypeIndicators indicators, - JavaType domainJtd) { + public JdbcType resolveIndicatedType(JdbcTypeIndicators indicators, JavaType domainJtd) { assert domainJtd != null; final TypeConfiguration typeConfiguration = indicators.getTypeConfiguration(); @@ -77,6 +78,9 @@ public class VarcharJdbcType implements AdjustableJdbcType { if ( indicators.isLob() ) { jdbcTypeCode = indicators.isNationalized() ? Types.NCLOB : Types.CLOB; } + else if ( shouldUseMaterializedLob( indicators ) ) { + jdbcTypeCode = indicators.isNationalized() ? SqlTypes.MATERIALIZED_NCLOB : SqlTypes.MATERIALIZED_CLOB; + } else { jdbcTypeCode = indicators.isNationalized() ? Types.NVARCHAR : Types.VARCHAR; } @@ -84,6 +88,18 @@ public class VarcharJdbcType implements AdjustableJdbcType { return jdbcTypeRegistry.getDescriptor( indicators.resolveJdbcTypeCode( jdbcTypeCode ) ); } + protected boolean shouldUseMaterializedLob(JdbcTypeIndicators indicators) { + final Dialect dialect = indicators.getTypeConfiguration() + .getServiceRegistry() + .getService( JdbcServices.class ) + .getDialect(); + final long length = indicators.getColumnLength(); + final long maxLength = indicators.isNationalized() ? + dialect.getMaxNVarcharCapacity() : + dialect.getMaxVarcharCapacity(); + return length > maxLength && dialect.useMaterializedLobWhenCapacityExceeded(); + } + @Override public Class getPreferredJavaTypeClass(WrapperOptions options) { return String.class; diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/internal/Scale6IntervalSecondDdlType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/internal/Scale6IntervalSecondDdlType.java index 499d96f5c8..16103aa6a0 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/internal/Scale6IntervalSecondDdlType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/sql/internal/Scale6IntervalSecondDdlType.java @@ -18,11 +18,12 @@ public class Scale6IntervalSecondDdlType extends DdlTypeImpl { public Scale6IntervalSecondDdlType(String typeNamePattern, Dialect dialect) { super( SqlTypes.INTERVAL_SECOND, typeNamePattern, dialect ); } + @Override public String getTypeName(Long size, Integer precision, Integer scale) { - // The maximum scale for `interval second` is 6 unfortunately, so we have to use numeric by default + // The maximum scale for `interval second` is 6 unfortunately if ( scale == null || scale > 6 ) { - return DdlTypeImpl.replace( "numeric($p,$s)", size, precision, scale ); + throw new IllegalStateException( "Illegal attempt to use interval second type with scale > 6" ); } return super.getTypeName( size, precision, scale ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/length/LengthTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/length/LengthTest.java index 2e45161cb7..8b0671869c 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/length/LengthTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/length/LengthTest.java @@ -1,5 +1,10 @@ package org.hibernate.orm.test.length; +import org.hibernate.Length; +import org.hibernate.dialect.Dialect; +import org.hibernate.metamodel.mapping.BasicValuedMapping; +import org.hibernate.type.SqlTypes; + import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; @@ -12,6 +17,18 @@ import static org.junit.Assert.assertEquals; public class LengthTest { @Test public void testLength(SessionFactoryScope scope) { + final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect(); + final BasicValuedMapping mapping = (BasicValuedMapping) scope.getSessionFactory() + .getRuntimeMetamodels() + .getMappingMetamodel() + .getEntityDescriptor( WithLongStrings.class ) + .findAttributeMapping( "long32" ); + if ( dialect.useMaterializedLobWhenCapacityExceeded() && Length.LONG32 > dialect.getMaxVarcharCapacity() ) { + assertEquals( SqlTypes.CLOB, mapping.getJdbcMapping().getJdbcType().getJdbcTypeCode() ); + } + else { + assertEquals( SqlTypes.VARCHAR, mapping.getJdbcMapping().getJdbcType().getJdbcTypeCode() ); + } WithLongStrings strings = new WithLongStrings(); strings.longish = "hello world ".repeat(2500); strings.long16 = "hello world ".repeat(2700);