"long" varchar/varbinary mappings

add Length class with useful constant values
well-defined mappings for "long" varchar/varbinary types
make LONGVARCHAR a synonym for VARCHAR with length=LONG32
make LONGVARBINARY a synonym for VARBINARY with length=LONG32
add Dialect.getMaxVarcharLength() + friends
make schema validator ignore the differences between string types
This commit is contained in:
Gavin 2021-12-10 14:33:11 +01:00 committed by Gavin King
parent f389952a9d
commit 0ca7a659b0
24 changed files with 419 additions and 132 deletions

View File

@ -109,7 +109,6 @@ public class SQLiteDialect extends Dialect {
registerColumnType( Types.BINARY, "blob" );
registerColumnType( Types.VARBINARY, "blob" );
registerColumnType( Types.LONGVARBINARY, "blob" );
uniqueDelegate = new SQLiteUniqueDelegate( this );
}

View File

@ -55,22 +55,18 @@ public class SybaseAnywhereDialect extends SybaseDialect {
registerColumnType( Types.TIMESTAMP, "timestamp" );
registerColumnType( Types.TIMESTAMP_WITH_TIMEZONE, "timestamp with time zone" );
final int maxStringLength = 32_767;
registerColumnType( Types.CHAR, maxStringLength, "char($l)" );
registerColumnType( Types.VARCHAR, maxStringLength, "varchar($l)" );
registerColumnType( Types.VARCHAR, "long varchar)" );
registerColumnType( Types.NCHAR, maxStringLength, "nchar($l)" );
registerColumnType( Types.NVARCHAR, maxStringLength, "nvarchar($l)" );
registerColumnType( Types.NVARCHAR, "long nvarchar)" );
//note: 'binary' is actually a synonym for 'varbinary'
registerColumnType( Types.BINARY, maxStringLength, "binary($l)" );
registerColumnType( Types.VARBINARY, maxStringLength, "varbinary($l)" );
registerColumnType( Types.VARBINARY, "long binary)" );
}
@Override
public int getMaxVarcharLength() {
return 32_767;
}
@Override
public SqlAstTranslatorFactory getSqlAstTranslatorFactory() {
return new StandardSqlAstTranslatorFactory() {

View File

@ -0,0 +1,67 @@
/*
* 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;
/**
* Defines a list of useful constant values that may be used
* to specify long column lengths in the JPA
* {@link jakarta.persistence.Column} annotation.
* <p>
* For example, {@code @Column(length=LONG16)} would specify
* that Hibernate should generate DDL with a column type
* capable of holding strings with 16-bit lengths.
*
* @see jakarta.persistence.Column#length()
*
* @author Gavin King
*/
public final class Length {
/**
* The default length for a column in JPA.
*
* @see jakarta.persistence.Column#length()
* @see org.hibernate.type.descriptor.java.JavaType#getDefaultSqlLength
*/
public static final int DEFAULT = 255;
/**
* Used to select a variable-length SQL type large
* enough to contain values of maximum length 32600.
* This arbitrary-looking number was chosen because
* some databases support variable-length types
* right up to a limit that is just slightly below
* 32767. (For some, the limit is 32672 characters.)
* <p>
* This is also the default length for a column
* declared using
* {@code @JdbcTypeCode(Types.LONGVARCHAR)} or
* {@code @JdbcTypeCode(Types.LONGVARBINARY)}.
*
* @see org.hibernate.type.descriptor.java.JavaType#getLongSqlLength
*/
public static final int LONG = 32_600;
/**
* The maximum length that fits in 16 bits.
* Used to select a variable-length SQL type large
* enough to contain values of maximum length 32767.
*/
public static final int LONG16 = Short.MAX_VALUE;
/**
* The maximum length of a Java string, that is,
* the maximum length that fits in 32 bits.
* Used to select a variable-length SQL type large
* enough to contain any Java string.
*/
public static final int LONG32 = Integer.MAX_VALUE;
/**
* The default length for a LOB column.
*
* @see org.hibernate.dialect.Dialect#getDefaultLobLength
*/
public static final int LOB_DEFAULT = 1_048_576;
private Length() {}
}

View File

@ -95,20 +95,23 @@ public class CockroachDialect extends Dialect {
registerColumnType( Types.TINYINT, "smallint" ); //no tinyint
//use 'string' instead of 'varchar'
registerColumnType( Types.VARCHAR, getMaxVarcharLength(), "string($l)");
registerColumnType( Types.VARCHAR, "string");
//no binary/varbinary
registerColumnType( Types.VARBINARY, "bytes" );
registerColumnType( Types.BINARY, "bytes" );
//no clob
registerColumnType( Types.LONGVARCHAR, "string" );
registerColumnType( Types.CLOB, "string" );
//no nchar/nvarchar
registerColumnType( Types.NCHAR, "string($l)" );
registerColumnType( Types.NVARCHAR, "string($l)" );
registerColumnType( Types.NVARCHAR, getMaxNVarcharLength(), "string($l)" );
registerColumnType( Types.NVARCHAR, "string");
//no nclob
registerColumnType( Types.LONGNVARCHAR, "string" );
registerColumnType( Types.NCLOB, "string" );
registerColumnType( SqlTypes.UUID, "uuid" );

View File

@ -101,14 +101,16 @@ public class DB2Dialect extends Dialect {
registerColumnType( Types.NUMERIC, "decimal($p,$s)" );
if ( getVersion().isBefore( 11 ) ) {
registerColumnType( Types.BINARY, "varchar($l) for bit data" ); //should use 'binary' since version 11
registerColumnType( Types.BINARY, 254, "char($l) for bit data" ); //should use 'binary' since version 11
registerColumnType( Types.VARBINARY, "varchar($l) for bit data" ); //should use 'varbinary' since version 11
registerColumnType( Types.BINARY, "varchar($l) for bit data" ); //should use 'binary' since version 11
registerColumnType( Types.VARBINARY, getMaxVarbinaryLength(), "varchar($l) for bit data" ); //should use 'varbinary' since version 11
//prior to DB2 11, the 'boolean' type existed,
//but was not allowed as a column type
registerColumnType( Types.BOOLEAN, "smallint" );
}
registerColumnType( Types.VARBINARY, "blob($l)" );
registerColumnType( Types.BLOB, "blob($l)" );
registerColumnType( Types.CLOB, "clob($l)" );
@ -117,7 +119,8 @@ public class DB2Dialect extends Dialect {
registerColumnType( Types.TIME_WITH_TIMEZONE, "time" );
// The long varchar data type was deprecated in DB2 and shouldn't be used anymore
registerColumnType( Types.LONGVARCHAR, "clob($l)" );
registerColumnType( Types.VARCHAR, "clob($l)" );
registerColumnType( Types.NVARCHAR, "nclob($l)" );
//not keywords, at least not in DB2 11,
//but perhaps they were in older versions?
@ -143,6 +146,11 @@ public class DB2Dialect extends Dialect {
return new DB2UniqueDelegate( this );
}
@Override
public int getMaxVarcharLength() {
return 32_672;
}
@Override
public DatabaseVersion getVersion() {
return version;

View File

@ -120,10 +120,11 @@ public class DerbyDialect extends Dialect {
registerColumnType( Types.NUMERIC, "decimal($p,$s)" );
registerColumnType( Types.BINARY, 254, "char($l) for bit data" );
registerColumnType( Types.BINARY, 32672, "varchar($l) for bit data" );
registerColumnType( Types.BINARY, getMaxVarbinaryLength(), "varchar($l) for bit data" );
registerColumnType( Types.BINARY, "long varchar for bit data" );
registerColumnType( Types.VARBINARY, 32672, "varchar($l) for bit data" );
registerColumnType( Types.VARBINARY, "long varchar for bit data" );
registerColumnType( Types.VARBINARY, getMaxVarbinaryLength(), "varchar($l) for bit data" );
registerColumnType( Types.VARBINARY, 32_700,"long varchar for bit data" );
registerColumnType( Types.VARBINARY, "blob($l)" );
registerColumnType( Types.BLOB, "blob($l)" );
registerColumnType( Types.CLOB, "clob($l)" );
@ -131,7 +132,8 @@ public class DerbyDialect extends Dialect {
registerColumnType( Types.TIMESTAMP, "timestamp" );
registerColumnType( Types.TIMESTAMP_WITH_TIMEZONE, "timestamp" );
registerColumnType( Types.LONGVARCHAR, "long varchar" );
registerColumnType( Types.VARCHAR, 32_700, "long varchar" );
registerColumnType( Types.VARCHAR, "clob($l)" );
registerDerbyKeywords();
@ -142,6 +144,11 @@ public class DerbyDialect extends Dialect {
getDefaultProperties().setProperty( Environment.STATEMENT_BATCH_SIZE, NO_BATCH );
}
@Override
public int getMaxVarcharLength() {
return 32_672;
}
@Override
public String getTypeName(int code, Size size) throws HibernateException {
if ( code == Types.CHAR ) {

View File

@ -221,7 +221,41 @@ public abstract class Dialect implements ConversionContext {
// constructors and factory methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
protected Dialect() {
this(true);
}
protected Dialect(boolean autoRegisterColumnTypes) {
uniqueDelegate = new DefaultUniqueDelegate( this );
sizeStrategy = new SizeStrategyImpl();
if (autoRegisterColumnTypes) {
registerDefaultColumnTypes();
}
registerHibernateTypes();
registerDefaultKeywords();
}
protected void registerDefaultColumnTypes() {
registerDefaultColumnTypes( getMaxVarcharLength(), getMaxNVarcharLength(), getMaxVarbinaryLength() );
}
/**
* Register an ANSI-standard column type for each JDBC type defined
* by {@link Types}. These mappings may be overridden by a concrete
* {@code Dialect} by calling {@link #registerColumnType(int,String)}
* from the constructor.
* <p>
* Note that {@link Types#LONGVARCHAR}, {@link Types#LONGNVARCHAR}
* and {@link Types#LONGVARBINARY} are considered synonyms for their
* non-{@code LONG} counterparts, with the only difference being that
* a different default length is used: {@link org.hibernate.Length#LONG}
* instead of {@link org.hibernate.Length#DEFAULT}. Concrete dialects
* should usually avoid registering mappings for these JDBC types.
*
* @param maxVarcharLength the maximum length of the {@link Types#VARCHAR} type
* @param maxNVarcharLength the maximum length of the {@link Types#NVARCHAR} type
* @param maxVarBinaryLength the maximum length of the {@link Types#VARBINARY} type
*/
protected void registerDefaultColumnTypes(int maxVarcharLength, int maxNVarcharLength, int maxVarBinaryLength) {
registerColumnType( Types.BOOLEAN, "boolean" );
registerColumnType( Types.TINYINT, "tinyint" );
@ -249,17 +283,19 @@ public abstract class Dialect implements ConversionContext {
registerColumnType( Types.TIME_WITH_TIMEZONE, "time with time zone" );
registerColumnType( Types.BINARY, "binary($l)" );
registerColumnType( Types.VARBINARY, "varbinary($l)" );
registerColumnType( Types.VARBINARY, maxVarBinaryLength, "varbinary($l)" );
registerColumnType( Types.BLOB, "blob" );
registerColumnType( Types.CHAR, "char($l)" );
registerColumnType( Types.VARCHAR, "varchar($l)" );
registerColumnType( Types.VARCHAR, maxVarcharLength, "varchar($l)" );
registerColumnType( Types.CLOB, "clob" );
registerColumnType( Types.NCHAR, "nchar($l)" );
registerColumnType( Types.NVARCHAR, "nvarchar($l)" );
registerColumnType( Types.NVARCHAR, maxNVarcharLength, "nvarchar($l)" );
registerColumnType( Types.NCLOB, "nclob" );
}
protected void registerHibernateTypes() {
// register hibernate types for default use in scalar sqlquery type auto detection
registerHibernateType( Types.BOOLEAN, StandardBasicTypes.BOOLEAN.getName() );
@ -298,11 +334,6 @@ public abstract class Dialect implements ConversionContext {
registerHibernateType( Types.DATE, StandardBasicTypes.DATE.getName() );
registerHibernateType( Types.TIME, StandardBasicTypes.TIME.getName() );
registerHibernateType( Types.TIMESTAMP, StandardBasicTypes.TIMESTAMP.getName() );
registerDefaultKeywords();
uniqueDelegate = new DefaultUniqueDelegate( this );
sizeStrategy = new SizeStrategyImpl();
}
protected void registerDefaultKeywords() {
@ -382,7 +413,7 @@ public abstract class Dialect implements ConversionContext {
/**
* Does the given JDBC type code represent some sort of
* string type?
* character string type?
* @param sqlType a JDBC type code from {@link Types}
*/
private static boolean isCharacterType(int sqlType) {
@ -399,6 +430,38 @@ public abstract class Dialect implements ConversionContext {
}
}
/**
* Does the given JDBC type code represent some sort of
* variable-length character string type?
* @param sqlType a JDBC type code from {@link Types}
*/
private static boolean isVarcharType(int sqlType) {
switch (sqlType) {
case Types.VARCHAR:
case Types.LONGVARCHAR:
case Types.NVARCHAR:
case Types.LONGNVARCHAR:
return true;
default:
return false;
}
}
/**
* Does the given JDBC type code represent some sort of
* variable-length binary string type?
* @param sqlType a JDBC type code from {@link Types}
*/
private static boolean isVarbinaryType(int sqlType) {
switch (sqlType) {
case Types.VARBINARY:
case Types.LONGVARBINARY:
return true;
default:
return false;
}
}
/**
* Render a SQL check condition for a column that represents a boolean value.
*/
@ -1021,12 +1084,20 @@ public abstract class Dialect implements ConversionContext {
/**
* Do the given JDBC type codes, as defined in {@link Types} represent
* essentially the same type in this dialect of SQL? The default
* implementation treats {@link Types#NUMERIC NUMERIC} and
* essentially the same type in this dialect of SQL?
* <p>
* The default implementation treats {@link Types#NUMERIC NUMERIC} and
* {@link Types#DECIMAL DECIMAL} as the same type, and
* {@link Types#FLOAT FLOAT}, {@link Types#REAL REAL}, and
* {@link Types#DOUBLE DOUBLE} as essentially the same type, since the
* ANSI SQL specification fails to meaningfully distinguish them.
* <p>
* The default implementation also treats {@link Types#VARCHAR VARCHAR},
* {@link Types#NVARCHAR NVARCHAR}, {@link Types#LONGVARCHAR LONGVARCHAR},
* and {@link Types#LONGNVARCHAR LONGNVARCHAR} as the same type, and
* {@link Types#VARBINARY BINARY} and
* {@link Types#LONGVARBINARY LONGVARBINARY} as the same type, since
* Hibernate doesn't really differentiate these types.
*
* @param typeCode1 the first JDBC type code
* @param typeCode2 the second JDBC type code
@ -1036,7 +1107,9 @@ public abstract class Dialect implements ConversionContext {
public boolean equivalentTypes(int typeCode1, int typeCode2) {
return typeCode1==typeCode2
|| isNumericOrDecimal(typeCode1) && isNumericOrDecimal(typeCode2)
|| isFloatOrRealOrDouble(typeCode1) && isFloatOrRealOrDouble(typeCode2);
|| isFloatOrRealOrDouble(typeCode1) && isFloatOrRealOrDouble(typeCode2)
|| isVarcharType(typeCode1) && isVarcharType(typeCode2)
|| isVarbinaryType(typeCode1) && isVarbinaryType(typeCode2);
}
private static boolean isNumericOrDecimal(int typeCode) {
@ -1152,6 +1225,10 @@ public abstract class Dialect implements ConversionContext {
String result = typeNames.get( code, size.getLength(), size.getPrecision(), size.getScale() );
if ( result == null ) {
switch ( code ) {
// these are no longer considered separate column types as such
// they're just used to indicate that JavaType.getLongSqlLength()
// should be used by default (and that's already handled by the
// time we get to here)
case Types.LONGVARCHAR:
return getTypeName( Types.VARCHAR, size );
case Types.LONGNVARCHAR:
@ -3467,6 +3544,36 @@ public abstract class Dialect implements ConversionContext {
return sizeStrategy;
}
/**
* The longest possible length of a {@link java.sql.Types#VARCHAR}-like column.
* For longer column lengths, use some sort of {@code text}-like type for the
* column.
*/
public int getMaxVarcharLength() {
//the longest possible length of a Java string
return Integer.MAX_VALUE;
}
/**
* 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
* column.
*/
public int getMaxNVarcharLength() {
//for most databases it's the same as for VARCHAR
return getMaxVarcharLength();
}
/**
* The longest possible length of a {@link java.sql.Types#VARBINARY}-like column.
* For longer column lengths, use some sort of {@code image}-like type for the
* column.
*/
public int getMaxVarbinaryLength() {
//for most databases it's the same as for VARCHAR
return getMaxVarcharLength();
}
public long getDefaultLobLength() {
return Size.DEFAULT_LOB_LENGTH;
}
@ -3634,7 +3741,7 @@ public abstract class Dialect implements ConversionContext {
switch (jdbcTypeCode) {
case Types.BIT:
// Use the default length for Boolean if we encounter the JPA default 255 instead
if ( javaType.getJavaTypeClass() == Boolean.class && length != null && length == 255 ) {
if ( javaType.getJavaTypeClass() == Boolean.class && length != null && length == Size.DEFAULT_LENGTH ) {
length = null;
}
size.setLength( javaType.getDefaultSqlLength( Dialect.this, jdbcType ) );
@ -3642,7 +3749,7 @@ public abstract class Dialect implements ConversionContext {
case Types.CHAR:
case Types.NCHAR:
// Use the default length for char and UUID if we encounter the JPA default 255 instead
if ( length != null && length == 255 ) {
if ( length != null && length == Size.DEFAULT_LENGTH ) {
if ( javaType.getJavaTypeClass() == Character.class || javaType.getJavaTypeClass() == UUID.class ) {
length = null;
}
@ -3654,7 +3761,7 @@ public abstract class Dialect implements ConversionContext {
case Types.BINARY:
case Types.VARBINARY:
// Use the default length for UUID if we encounter the JPA default 255 instead
if ( javaType.getJavaTypeClass() == UUID.class && length != null && length == 255 ) {
if ( javaType.getJavaTypeClass() == UUID.class && length != null && length == Size.DEFAULT_LENGTH ) {
length = null;
}
size.setLength( javaType.getDefaultSqlLength( Dialect.this, jdbcType ) );

View File

@ -128,7 +128,12 @@ public class H2Dialect extends Dialect {
// http://code.google.com/p/h2database/issues/detail?id=235
getDefaultProperties().setProperty( AvailableSettings.NON_CONTEXTUAL_LOB_CREATION, "true" );
registerColumnType( Types.VARCHAR, "varchar" );
registerColumnType( Types.NVARCHAR, "varchar" );
registerColumnType( Types.VARBINARY, "varbinary" );
registerColumnType( SqlTypes.ARRAY, "array" );
if ( version.isSameOrAfter( 1, 4, 32 ) ) {
this.sequenceInformationExtractor = version.isSameOrAfter( 1, 4, 201 )
? SequenceInformationExtractorLegacyImpl.INSTANCE
@ -240,6 +245,11 @@ public class H2Dialect extends Dialect {
CommonFunctionFactory.rownum( queryEngine );
}
@Override
public int getMaxVarcharLength() {
return 1_048_576;
}
@Override
public String currentTime() {
return useLocalTime ? "localtime" : super.currentTime();

View File

@ -47,12 +47,10 @@ public class HANAColumnStoreDialect extends AbstractHANADialect {
public HANAColumnStoreDialect(DatabaseVersion version) {
super( version );
if ( version.isSameOrAfter( 4 ) ) {
registerColumnType( Types.CHAR, "nvarchar(1)" );
registerColumnType( Types.VARCHAR, 5000, "nvarchar($l)" );
registerColumnType( Types.LONGVARCHAR, 5000, "nvarchar($l)" );
registerColumnType( Types.CHAR, "nvarchar($l)" );
registerColumnType( Types.VARCHAR, getMaxVarcharLength(), "nvarchar($l)" );
// for longer values map to clob/nclob
registerColumnType( Types.LONGVARCHAR, "nclob" );
registerColumnType( Types.VARCHAR, "nclob" );
registerColumnType( Types.CLOB, "nclob" );
@ -69,6 +67,11 @@ public class HANAColumnStoreDialect extends AbstractHANADialect {
}
}
@Override
public int getMaxVarcharLength() {
return 5000;
}
@Override
public DatabaseVersion getVersion(){
return version;

View File

@ -60,10 +60,6 @@ public class MariaDBDialect extends MySQLDialect {
this.version = version;
}
protected int getMaxVarcharLen() {
return getMySQLVersion().isBefore( 5 ) ? 255 : 65_534;
}
@Override
public DatabaseVersion getVersion() {
return version;

View File

@ -105,10 +105,12 @@ public class MySQLDialect extends Dialect {
}
public MySQLDialect(DatabaseVersion version, int characterSetBytesPerCharacter) {
super();
super(false);
this.version = version;
this.characterSetBytesPerCharacter = characterSetBytesPerCharacter;
registerDefaultColumnTypes();
String storageEngine = Environment.getProperties().getProperty( Environment.STORAGE_ENGINE );
if (storageEngine == null) {
storageEngine = System.getProperty( Environment.STORAGE_ENGINE );
@ -141,41 +143,40 @@ public class MySQLDialect extends Dialect {
registerColumnType(Types.TIMESTAMP_WITH_TIMEZONE, "timestamp($p)");
}
// max length for VARCHAR changed in 5.0.3
final int maxVarcharLen = getMaxVarcharLen();
registerColumnType( Types.VARCHAR, maxVarcharLen, "varchar($l)" );
registerColumnType( Types.VARBINARY, maxVarcharLen, "varbinary($l)" );
final int maxTinyLobLen = 255;
final int maxLobLen = 65_535;
final int maxMediumLobLen = 16_777_215;
final long maxLongLobLen = 4_294_967_295L;
//the maximum long LOB length is 4_294_967_295, bigger than any Java string
registerColumnType( Types.VARCHAR, maxLongLobLen, "longtext" );
registerColumnType( Types.VARCHAR, "longtext" );
registerColumnType( Types.VARCHAR, maxMediumLobLen, "mediumtext" );
if ( maxVarcharLen < maxLobLen ) {
if ( getMaxVarcharLength() < maxLobLen ) {
registerColumnType( Types.VARCHAR, maxLobLen, "text" );
}
registerColumnType( Types.VARBINARY, maxLongLobLen, "longblob" );
registerColumnType( Types.NVARCHAR, "longtext" );
registerColumnType( Types.NVARCHAR, maxMediumLobLen, "mediumtext" );
if ( getMaxNVarcharLength() < maxLobLen ) {
registerColumnType( Types.NVARCHAR, maxLobLen, "text" );
}
registerColumnType( Types.VARBINARY, "longblob" );
registerColumnType( Types.VARBINARY, maxMediumLobLen, "mediumblob" );
if ( maxVarcharLen < maxLobLen ) {
if ( getMaxVarbinaryLength() < maxLobLen ) {
registerColumnType( Types.VARBINARY, maxLobLen, "blob" );
}
registerColumnType( Types.BLOB, maxLongLobLen, "longblob" );
registerColumnType( Types.BLOB, "longblob" );
registerColumnType( Types.BLOB, maxMediumLobLen, "mediumblob" );
registerColumnType( Types.BLOB, maxLobLen, "blob" );
registerColumnType( Types.BLOB, maxTinyLobLen, "tinyblob" );
registerColumnType( Types.CLOB, maxLongLobLen, "longtext" );
registerColumnType( Types.CLOB, "longtext" );
registerColumnType( Types.CLOB, maxMediumLobLen, "mediumtext" );
registerColumnType( Types.CLOB, maxLobLen, "text" );
registerColumnType( Types.CLOB, maxTinyLobLen, "tinytext" );
registerColumnType( Types.NCLOB, "longtext" );
registerColumnType( Types.NCLOB, maxLongLobLen, "longtext" );
registerColumnType( Types.NCLOB, maxMediumLobLen, "mediumtext" );
registerColumnType( Types.NCLOB, maxLobLen, "text" );
registerColumnType( Types.NCLOB, maxTinyLobLen, "tinytext" );
@ -253,7 +254,9 @@ public class MySQLDialect extends Dialect {
return 4;
}
protected int getMaxVarcharLen() {
@Override
public int getMaxVarcharLength() {
// max length for VARCHAR changed in 5.0.3
if ( getMySQLVersion().isBefore( 5 ) ) {
return 255;
}
@ -272,6 +275,11 @@ public class MySQLDialect extends Dialect {
}
}
@Override
public int getMaxVarbinaryLength() {
return getMySQLVersion().isBefore( 5 ) ? 255 : 65_535;
}
@Override
public String getNullColumnString(String columnType) {
// Good job MySQL https://dev.mysql.com/doc/refman/8.0/en/timestamp-initialization.html

View File

@ -199,6 +199,19 @@ public class OracleDialect extends Dialect {
).setArgumentListSignature("(pattern, string[, start])");
}
@Override
public int getMaxVarcharLength() {
//with MAX_STRING_SIZE=EXTENDED, changes to 32_767
//TODO: provide a way to change this without a custom Dialect
return 4000;
}
@Override
public int getMaxVarbinaryLength() {
//with MAX_STRING_SIZE=EXTENDED, changes to 32_767
return 2000;
}
@Override
public SqlAstTranslatorFactory getSqlAstTranslatorFactory() {
return new StandardSqlAstTranslatorFactory() {
@ -525,16 +538,17 @@ public class OracleDialect extends Dialect {
protected void registerCharacterTypeMappings() {
if ( getVersion().isBefore( 9 ) ) {
registerColumnType( Types.VARCHAR, 4000, "varchar2($l)" );
registerColumnType( Types.VARCHAR, getMaxVarcharLength(), "varchar2($l)" );
registerColumnType( Types.VARCHAR, "clob" );
}
else {
registerColumnType( Types.CHAR, "char($l char)" );
registerColumnType( Types.VARCHAR, 4000, "varchar2($l char)" );
registerColumnType( Types.VARCHAR, getMaxVarcharLength(), "varchar2($l char)" );
registerColumnType( Types.VARCHAR, "clob" );
registerColumnType( Types.NVARCHAR, 4000, "nvarchar2($l)" );
registerColumnType( Types.NVARCHAR, getMaxNVarcharLength(), "nvarchar2($l)" );
registerColumnType( Types.NVARCHAR, "nclob" );
}
//note: the 'long' type is deprecated
}
protected void registerNumericTypeMappings() {
@ -576,10 +590,10 @@ public class OracleDialect extends Dialect {
}
protected void registerBinaryTypeMappings() {
registerColumnType( Types.BINARY, 2000, "raw($l)" );
registerColumnType( Types.BINARY, getMaxVarbinaryLength(), "raw($l)" );
registerColumnType( Types.BINARY, "blob" );
registerColumnType( Types.VARBINARY, 2000, "raw($l)" );
registerColumnType( Types.VARBINARY, getMaxVarbinaryLength(), "raw($l)" );
registerColumnType( Types.VARBINARY, "blob" );
}

View File

@ -132,13 +132,13 @@ public class PostgreSQLDialect extends Dialect {
//there are no nchar/nvarchar types in Postgres
registerColumnType( Types.NCHAR, "char($l)" );
registerColumnType( Types.NVARCHAR, "varchar($l)" );
registerColumnType( Types.NVARCHAR, getMaxNVarcharLength(), "varchar($l)" );
registerColumnType( Types.NVARCHAR, "text" );
//since there's no real difference between
//TEXT and VARCHAR, except for the length limit,
//we can just use 'text' for the "long" types
registerColumnType( Types.LONGVARCHAR, "text" );
registerColumnType( Types.LONGNVARCHAR, "text" );
// since there's no real difference between TEXT and VARCHAR,
// except for the length limit, we can just use 'text' for the
// "long" string types
registerColumnType( Types.VARCHAR, "text" );
registerColumnType( SqlTypes.INET, "inet" );
registerColumnType( SqlTypes.INTERVAL_SECOND, "interval second($s)" );
@ -163,6 +163,17 @@ public class PostgreSQLDialect extends Dialect {
getDefaultProperties().setProperty( Environment.NON_CONTEXTUAL_LOB_CREATION, "true" );
}
@Override
public int getMaxVarcharLength() {
return 10_485_760;
}
@Override
public int getMaxVarbinaryLength() {
//postgres has no varbinary-like type
return 0;
}
@Override
public String getTypeName(int code, Size size) throws HibernateException {
// The maximum scale for `interval second` is 6 unfortunately so we have to use numeric by default

View File

@ -66,7 +66,6 @@ import java.util.TimeZone;
import jakarta.persistence.TemporalType;
import static java.util.regex.Pattern.compile;
import static org.hibernate.query.TemporalUnit.NANOSECOND;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime;
@ -113,13 +112,10 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
exporter = new SqlServerSequenceExporter( this );
}
registerColumnType( Types.VARCHAR, 8000, "varchar($l)" );
registerColumnType( Types.NVARCHAR, 4000, "nvarchar($l)" );
registerColumnType( Types.VARBINARY, 8000, "varbinary($l)" );
if ( getVersion().isBefore( 9 ) ) {
registerColumnType( Types.VARBINARY, "image" );
registerColumnType( Types.VARCHAR, "text" );
registerColumnType( Types.NVARCHAR, "text" );
}
else {
@ -146,6 +142,16 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
registerKeyword( "key" );
}
@Override
public int getMaxVarcharLength() {
return 8000;
}
@Override
public int getMaxNVarcharLength() {
return 4000;
}
@Override
public DatabaseVersion getVersion() {
return version;

View File

@ -92,17 +92,20 @@ public class SpannerDialect extends Dialect {
//there is no time type of any kind
registerColumnType( Types.TIME, "timestamp" );
final int stringMaxLength = 2_621_440;
final int bytesMaxLength = 10_485_760;
registerColumnType( Types.CHAR, getMaxVarcharLength(), "string($l)" );
registerColumnType( Types.CHAR, "string(max)" );
registerColumnType( Types.VARCHAR, getMaxVarcharLength(), "string($l)" );
registerColumnType( Types.VARCHAR, "string(max)" );
registerColumnType( Types.CHAR, stringMaxLength, "string($l)" );
registerColumnType( Types.VARCHAR, stringMaxLength, "string($l)" );
registerColumnType( Types.NCHAR, getMaxNVarcharLength(), "string($l)" );
registerColumnType( Types.NCHAR, "string(max)" );
registerColumnType( Types.NVARCHAR, getMaxNVarcharLength(), "string($l)" );
registerColumnType( Types.NVARCHAR, "string(max)" );
registerColumnType( Types.NCHAR, stringMaxLength, "string($l)" );
registerColumnType( Types.NVARCHAR, stringMaxLength, "string($l)" );
registerColumnType( Types.BINARY, bytesMaxLength, "bytes($l)" );
registerColumnType( Types.VARBINARY, bytesMaxLength, "bytes($l)" );
registerColumnType( Types.BINARY, getMaxBytesLength(), "bytes($l)" );
registerColumnType( Types.BINARY, "bytes(max)" );
registerColumnType( Types.VARBINARY, getMaxBytesLength(), "bytes($l)" );
registerColumnType( Types.VARBINARY, "bytes(max)" );
registerColumnType( Types.CLOB, "string(max)" );
registerColumnType( Types.NCLOB, "string(max)" );
@ -114,6 +117,17 @@ public class SpannerDialect extends Dialect {
registerKeywords( info );
}
@Override
public int getMaxVarcharLength() {
//max is equivalent to 2_621_440
return 2_621_440;
}
public int getMaxBytesLength() {
//max is equivalent to 10_485_760
return 10_485_760;
}
@Override
public DatabaseVersion getVersion() {
return ZERO_VERSION;

View File

@ -99,12 +99,8 @@ public class SybaseASEDialect extends SybaseDialect {
}
}
// the maximum length of a VARCHAR or VARBINARY
// depends on the page size, and also on the
// version of Sybase ASE, and can be quite small,
// so use 'image' and 'text' as the "long" types
registerColumnType( Types.LONGVARBINARY, "image" );
registerColumnType( Types.LONGVARCHAR, "text" );
registerColumnType( Types.VARBINARY, "image" );
registerColumnType( Types.VARCHAR, "text" );
registerSybaseKeywords();
sizeStrategy = new SizeStrategyImpl() {
@ -127,6 +123,17 @@ public class SybaseASEDialect extends SybaseDialect {
};
}
@Override
public int getMaxVarcharLength() {
// the maximum length of a VARCHAR or VARBINARY
// column depends on the page size and ASE version
// and is actually a limit on the whole row length,
// not the individual column length -- anyway, the
// largest possible page size is 16k, so that's a
// hard upper limit
return 16_384;
}
private static boolean isAnsiNull(DatabaseMetaData databaseMetaData) {
if ( databaseMetaData != null ) {
try (java.sql.Statement s = databaseMetaData.getConnection().createStatement() ) {

View File

@ -6,6 +6,8 @@
*/
package org.hibernate.engine.jdbc;
import org.hibernate.Length;
import java.io.Serializable;
/**
@ -35,9 +37,9 @@ public class Size implements Serializable {
}
}
public static final long DEFAULT_LENGTH = 255;
public static final long LONG_LENGTH = 65_535;
public static final long DEFAULT_LOB_LENGTH = 1_048_576;
public static final long DEFAULT_LENGTH = Length.DEFAULT;
public static final long LONG_LENGTH = Length.LONG;
public static final long DEFAULT_LOB_LENGTH = Length.LOB_DEFAULT;
public static final int DEFAULT_PRECISION = 19;
public static final int DEFAULT_SCALE = 2;

View File

@ -18,7 +18,6 @@ import org.hibernate.boot.model.relational.internal.SqlStringGenerationContextIm
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.Table;
import org.hibernate.resource.transaction.spi.DdlTransactionIsolator;
import org.hibernate.tool.schema.extract.spi.ColumnInformation;
@ -48,12 +47,7 @@ public abstract class AbstractSchemaValidator implements SchemaValidator {
HibernateSchemaManagementTool tool,
SchemaFilter validateFilter) {
this.tool = tool;
if ( validateFilter == null ) {
this.schemaFilter = DefaultSchemaFilter.INSTANCE;
}
else {
this.schemaFilter = validateFilter;
}
this.schemaFilter = validateFilter == null ? DefaultSchemaFilter.INSTANCE : validateFilter;
}
@Override
@ -143,11 +137,9 @@ public abstract class AbstractSchemaValidator implements SchemaValidator {
);
}
final Iterator selectableItr = table.getColumnIterator();
while ( selectableItr.hasNext() ) {
final Selectable selectable = (Selectable) selectableItr.next();
if ( Column.class.isInstance( selectable ) ) {
final Column column = (Column) selectable;
final Iterator<Column> columnIter = table.getColumnIterator();
while ( columnIter.hasNext() ) {
final Column column = columnIter.next();
final ColumnInformation existingColumn = tableInformation.getColumn( Identifier.toIdentifier( column.getQuotedName() ) );
if ( existingColumn == null ) {
throw new SchemaManagementException(
@ -161,7 +153,6 @@ public abstract class AbstractSchemaValidator implements SchemaValidator {
validateColumnType( table, column, existingColumn, metadata, options, dialect );
}
}
}
protected void validateColumnType(
Table table,
@ -171,7 +162,8 @@ public abstract class AbstractSchemaValidator implements SchemaValidator {
ExecutionOptions options,
Dialect dialect) {
boolean typesMatch = dialect.equivalentTypes( column.getSqlTypeCode( metadata ), columnInformation.getTypeCode() )
|| column.getSqlType( dialect, metadata ).toLowerCase(Locale.ROOT).startsWith( columnInformation.getTypeName().toLowerCase(Locale.ROOT) );
|| column.getSqlType( dialect, metadata ).toLowerCase(Locale.ROOT)
.startsWith( columnInformation.getTypeName().toLowerCase(Locale.ROOT) );
if ( !typesMatch ) {
throw new SchemaManagementException(
String.format(
@ -186,14 +178,6 @@ public abstract class AbstractSchemaValidator implements SchemaValidator {
)
);
}
// this is the old Hibernate check...
//
// but I think a better check involves checks against type code and then the type code family, not
// just the type name.
//
// See org.hibernate.type.descriptor.sql.JdbcTypeFamilyInformation
// todo : this ^^
}
protected void validateSequence(Sequence sequence, SequenceInformation sequenceInformation) {

View File

@ -0,0 +1,25 @@
package org.hibernate.orm.test.length;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.Test;
import static org.junit.Assert.assertEquals;
@SessionFactory
@DomainModel(annotatedClasses = WithLongStrings.class)
public class LengthTest {
@Test
public void testLength(SessionFactoryScope scope) {
WithLongStrings strings = new WithLongStrings();
strings.long16 = "hello world ".repeat(2700);
strings.long32 = "hello world ".repeat(20000);
scope.inTransaction(s->s.persist(strings));
scope.inTransaction(s-> {
WithLongStrings strs = s.find(WithLongStrings.class, strings.id);
assertEquals(strs.long16, strings.long16);
assertEquals(strs.long32, strings.long32);
});
}
}

View File

@ -0,0 +1,21 @@
package org.hibernate.orm.test.length;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import static org.hibernate.Length.*;
@Entity
public class WithLongStrings {
@Id
@GeneratedValue
public int id;
@Column(length = LONG16)
public String long16;
@Column(length = LONG32)
public String long32;
}

View File

@ -16,7 +16,7 @@
<generator class="increment"/>
</id>
<property name="longByteArray" column="LONG_BYTE_ARRAY" type="image" length="15000"/>
<property name="longByteArray" column="LONG_BYTE_ARRAY" type="image" length="17000"/>
</class>
</hibernate-mapping>

View File

@ -53,7 +53,7 @@ public class GeneratedUuidTests {
// then changing
final GeneratedUuidEntity merged = scope.fromTransaction( (session) -> {
return (GeneratedUuidEntity) session.merge( created );
return session.merge( created );
} );
assertThat( merged ).isNotNull();

View File

@ -53,7 +53,8 @@ public class LongVarcharValidationTest implements ExecutionOptions {
@Parameterized.Parameters
public static Collection<String> parameters() {
return Arrays.asList(
new String[] {JdbcMetadaAccessStrategy.GROUPED.toString(), JdbcMetadaAccessStrategy.INDIVIDUALLY.toString()}
JdbcMetadaAccessStrategy.GROUPED.toString(),
JdbcMetadaAccessStrategy.INDIVIDUALLY.toString()
);
}

View File

@ -52,10 +52,8 @@ public class NumericValidationTest implements ExecutionOptions {
@Parameterized.Parameters
public static Collection<String> parameters() {
return Arrays.asList(
new String[] {
JdbcMetadaAccessStrategy.GROUPED.toString(),
JdbcMetadaAccessStrategy.INDIVIDUALLY.toString()
}
);
}