add ability to change column types to TableMigrator
This commit is contained in:
parent
fcb8e323b0
commit
736dfac693
|
@ -656,6 +656,17 @@ public class DB2Dialect extends Dialect {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlterColumnTypeString(String columnName, String columnType, String columnDefinition) {
|
||||
// would need multiple statements to 'set not null'/'drop not null', 'set default'/'drop default', 'set generated', etc
|
||||
return "alter column " + columnName + " set data type " + columnType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAlterColumnType() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(
|
||||
EntityMappingType rootEntityDescriptor,
|
||||
|
|
|
@ -32,6 +32,7 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
|
@ -192,40 +193,7 @@ import jakarta.persistence.TemporalType;
|
|||
import static java.lang.Math.ceil;
|
||||
import static java.lang.Math.log;
|
||||
import static org.hibernate.internal.util.StringHelper.parseCommaSeparatedString;
|
||||
import static org.hibernate.type.SqlTypes.ARRAY;
|
||||
import static org.hibernate.type.SqlTypes.BIGINT;
|
||||
import static org.hibernate.type.SqlTypes.BINARY;
|
||||
import static org.hibernate.type.SqlTypes.BLOB;
|
||||
import static org.hibernate.type.SqlTypes.BOOLEAN;
|
||||
import static org.hibernate.type.SqlTypes.CHAR;
|
||||
import static org.hibernate.type.SqlTypes.CLOB;
|
||||
import static org.hibernate.type.SqlTypes.DATE;
|
||||
import static org.hibernate.type.SqlTypes.DECIMAL;
|
||||
import static org.hibernate.type.SqlTypes.DOUBLE;
|
||||
import static org.hibernate.type.SqlTypes.FLOAT;
|
||||
import static org.hibernate.type.SqlTypes.INTEGER;
|
||||
import static org.hibernate.type.SqlTypes.LONG32NVARCHAR;
|
||||
import static org.hibernate.type.SqlTypes.LONG32VARBINARY;
|
||||
import static org.hibernate.type.SqlTypes.LONG32VARCHAR;
|
||||
import static org.hibernate.type.SqlTypes.NCHAR;
|
||||
import static org.hibernate.type.SqlTypes.NCLOB;
|
||||
import static org.hibernate.type.SqlTypes.NUMERIC;
|
||||
import static org.hibernate.type.SqlTypes.NVARCHAR;
|
||||
import static org.hibernate.type.SqlTypes.REAL;
|
||||
import static org.hibernate.type.SqlTypes.SMALLINT;
|
||||
import static org.hibernate.type.SqlTypes.TIME;
|
||||
import static org.hibernate.type.SqlTypes.TIMESTAMP;
|
||||
import static org.hibernate.type.SqlTypes.TIMESTAMP_UTC;
|
||||
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
||||
import static org.hibernate.type.SqlTypes.TIME_WITH_TIMEZONE;
|
||||
import static org.hibernate.type.SqlTypes.TINYINT;
|
||||
import static org.hibernate.type.SqlTypes.VARBINARY;
|
||||
import static org.hibernate.type.SqlTypes.VARCHAR;
|
||||
import static org.hibernate.type.SqlTypes.isFloatOrRealOrDouble;
|
||||
import static org.hibernate.type.SqlTypes.isIntegral;
|
||||
import static org.hibernate.type.SqlTypes.isNumericOrDecimal;
|
||||
import static org.hibernate.type.SqlTypes.isVarbinaryType;
|
||||
import static org.hibernate.type.SqlTypes.isVarcharType;
|
||||
import static org.hibernate.type.SqlTypes.*;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.JDBC_ESCAPE_END;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.JDBC_ESCAPE_START_DATE;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.JDBC_ESCAPE_START_TIME;
|
||||
|
@ -1358,10 +1326,21 @@ public abstract class Dialect implements ConversionContext {
|
|||
public boolean equivalentTypes(int typeCode1, int typeCode2) {
|
||||
return typeCode1==typeCode2
|
||||
|| isNumericOrDecimal(typeCode1) && isNumericOrDecimal(typeCode2)
|
||||
|| isIntegral(typeCode1) && isIntegral(typeCode2)
|
||||
|| isSmallOrTinyInt(typeCode1) && isSmallOrTinyInt(typeCode2) //special case for HHH-15288 migration
|
||||
// || isIntegral(typeCode1) && isIntegral(typeCode2)
|
||||
|| isFloatOrRealOrDouble(typeCode1) && isFloatOrRealOrDouble(typeCode2)
|
||||
|| isVarcharType(typeCode1) && isVarcharType(typeCode2)
|
||||
|| isVarbinaryType(typeCode1) && isVarbinaryType(typeCode2);
|
||||
|| isVarbinaryType(typeCode1) && isVarbinaryType(typeCode2)
|
||||
|| sameColumnType(typeCode1, typeCode2);
|
||||
}
|
||||
|
||||
private boolean sameColumnType(int typeCode1, int typeCode2) {
|
||||
try {
|
||||
return Objects.equals( columnType(typeCode1), columnType(typeCode2) );
|
||||
}
|
||||
catch (IllegalArgumentException iae) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2008,6 +1987,14 @@ public abstract class Dialect implements ConversionContext {
|
|||
return sb.toString();
|
||||
}
|
||||
|
||||
public String getAlterColumnTypeString(String columnName, String columnType, String columnDefinition) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean supportsAlterColumnType() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Slight variation on {@link #getCreateTableString}. Here, we have the
|
||||
* command used to create a table when there is no primary key and
|
||||
|
|
|
@ -590,6 +590,18 @@ public class H2Dialect extends Dialect {
|
|||
: super.getCascadeConstraintsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAlterColumnType() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlterColumnTypeString(String columnName, String columnType, String columnDefinition) {
|
||||
return "alter column " + columnName + " set data type " + columnType;
|
||||
// if only altering the type, no need to specify the whole definition
|
||||
// return "alter column " + columnName + " " + columnDefinition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCommentOn() {
|
||||
return true;
|
||||
|
|
|
@ -11,7 +11,6 @@ import java.sql.DatabaseMetaData;
|
|||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.PessimisticLockException;
|
||||
|
@ -855,6 +854,17 @@ public class MySQLDialect extends Dialect {
|
|||
return " drop index ";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlterColumnTypeString(String columnName, String columnType, String columnDefinition) {
|
||||
// no way to change just the column type, leaving other attributes intact
|
||||
return "modify column " + columnName + " " + columnDefinition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAlterColumnType() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LimitHandler getLimitHandler() {
|
||||
//also supports LIMIT n OFFSET m
|
||||
|
|
|
@ -15,8 +15,6 @@ import java.util.Locale;
|
|||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import jakarta.persistence.AttributeConverter;
|
||||
import jakarta.persistence.Converter;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.QueryTimeoutException;
|
||||
import org.hibernate.boot.model.TypeContributions;
|
||||
|
@ -51,7 +49,6 @@ import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
|
|||
import org.hibernate.internal.util.JdbcExceptionHelper;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.model.convert.spi.BasicValueConverter;
|
||||
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
||||
import org.hibernate.procedure.internal.StandardCallableStatementSupport;
|
||||
import org.hibernate.procedure.spi.CallableStatementSupport;
|
||||
|
@ -80,12 +77,9 @@ import org.hibernate.type.JavaObjectType;
|
|||
import org.hibernate.type.NullType;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
import org.hibernate.type.StandardBasicTypes;
|
||||
import org.hibernate.type.descriptor.java.BooleanJavaType;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.BooleanJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.OracleJsonBlobJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.NullJdbcType;
|
||||
|
@ -882,6 +876,16 @@ public class OracleDialect extends Dialect {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlterColumnTypeString(String columnName, String columnType, String columnDefinition) {
|
||||
return "modify " + columnName + " " + columnType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAlterColumnType() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SequenceSupport getSequenceSupport() {
|
||||
return OracleSequenceSupport.INSTANCE;
|
||||
|
|
|
@ -670,6 +670,17 @@ public class PostgreSQLDialect extends Dialect {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlterColumnTypeString(String columnName, String columnType, String columnDefinition) {
|
||||
// would need multiple statements to 'set not null'/'drop not null', 'set default'/'drop default', 'set generated', etc
|
||||
return "alter column " + columnName + " set data type " + columnType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAlterColumnType() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsValuesList() {
|
||||
return true;
|
||||
|
|
|
@ -74,7 +74,6 @@ import org.hibernate.type.descriptor.jdbc.XmlJdbcType;
|
|||
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
|
||||
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@ -1001,6 +1000,16 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlterColumnTypeString(String columnName, String columnType, String columnDefinition) {
|
||||
return "alter column " + columnName + " " + columnType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAlterColumnType() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NameQualifierSupport getNameQualifierSupport() {
|
||||
return NameQualifierSupport.BOTH;
|
||||
|
|
|
@ -365,4 +365,14 @@ public class SybaseDialect extends AbstractTransactSQLDialect {
|
|||
public CallableStatementSupport getCallableStatementSupport() {
|
||||
return jtdsDriver ? JTDSCallableStatementSupport.INSTANCE : super.getCallableStatementSupport();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlterColumnTypeString(String columnName, String columnType, String columnDefinition) {
|
||||
return "modify " + columnName + " " + columnType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAlterColumnType() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
package org.hibernate.engine.jdbc.internal;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import static org.hibernate.internal.util.StringHelper.isEmpty;
|
||||
|
@ -26,6 +27,9 @@ public class DDLFormatterImpl implements Formatter {
|
|||
*/
|
||||
public static final DDLFormatterImpl INSTANCE = new DDLFormatterImpl();
|
||||
|
||||
private static final Set<String> BREAKS = Set.of( "drop", "alter", "modify", "add", "references", "foreign", "on" );
|
||||
private static final Set<String> QUOTES = Set.of( "\"", "`", "]", "[", "'" );
|
||||
|
||||
@Override
|
||||
public String format(String sql) {
|
||||
if ( isEmpty( sql ) ) {
|
||||
|
@ -79,6 +83,7 @@ public class DDLFormatterImpl implements Formatter {
|
|||
final StringBuilder result = new StringBuilder( 60 ).append( INITIAL_LINE );
|
||||
final StringTokenizer tokens = new StringTokenizer( sql, " (,)'[]\"", true );
|
||||
|
||||
boolean first = true;
|
||||
boolean quoted = false;
|
||||
while ( tokens.hasMoreTokens() ) {
|
||||
final String token = tokens.nextToken();
|
||||
|
@ -86,11 +91,12 @@ public class DDLFormatterImpl implements Formatter {
|
|||
quoted = !quoted;
|
||||
}
|
||||
else if ( !quoted ) {
|
||||
if ( isBreak( token ) ) {
|
||||
if ( !first && isBreak( token ) ) {
|
||||
result.append( OTHER_LINES );
|
||||
}
|
||||
}
|
||||
result.append( token );
|
||||
first = false;
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
|
@ -135,19 +141,11 @@ public class DDLFormatterImpl implements Formatter {
|
|||
}
|
||||
|
||||
private static boolean isBreak(String token) {
|
||||
return "drop".equals( token )
|
||||
|| "add".equals( token )
|
||||
|| "references".equals( token )
|
||||
|| "foreign".equals( token )
|
||||
|| "on".equals( token );
|
||||
return BREAKS.contains( token );
|
||||
}
|
||||
|
||||
private static boolean isQuote(String tok) {
|
||||
return "\"".equals( tok )
|
||||
|| "`".equals( tok )
|
||||
|| "]".equals( tok )
|
||||
|| "[".equals( tok )
|
||||
|| "'".equals( tok );
|
||||
private static boolean isQuote(String token) {
|
||||
return QUOTES.contains( token );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
package org.hibernate.tool.schema.internal;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.hibernate.boot.Metadata;
|
||||
import org.hibernate.boot.model.naming.Identifier;
|
||||
|
@ -31,7 +30,6 @@ import org.hibernate.tool.schema.spi.SchemaFilter;
|
|||
import org.hibernate.tool.schema.spi.SchemaManagementException;
|
||||
import org.hibernate.tool.schema.spi.SchemaValidator;
|
||||
import org.hibernate.type.descriptor.JdbcTypeNameMapper;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
|
@ -160,22 +158,7 @@ public abstract class AbstractSchemaValidator implements SchemaValidator {
|
|||
Metadata metadata,
|
||||
ExecutionOptions options,
|
||||
Dialect dialect) {
|
||||
boolean typesMatch = dialect.equivalentTypes( column.getSqlTypeCode( metadata ), columnInformation.getTypeCode() )
|
||||
|| column.getSqlType( metadata.getDatabase().getTypeConfiguration(), dialect, metadata ).toLowerCase(Locale.ROOT)
|
||||
.startsWith( columnInformation.getTypeName().toLowerCase(Locale.ROOT) );
|
||||
if ( !typesMatch ) {
|
||||
// Try to resolve the JdbcType by type name and check for a match again based on that type code.
|
||||
// This is used to handle SqlTypes type codes like TIMESTAMP_UTC etc.
|
||||
final JdbcType jdbcType = dialect.resolveSqlTypeDescriptor(
|
||||
columnInformation.getTypeName(),
|
||||
columnInformation.getTypeCode(),
|
||||
columnInformation.getColumnSize(),
|
||||
columnInformation.getDecimalDigits(),
|
||||
metadata.getDatabase().getTypeConfiguration().getJdbcTypeRegistry()
|
||||
);
|
||||
typesMatch = dialect.equivalentTypes( column.getSqlTypeCode( metadata ), jdbcType.getDefaultSqlTypeCode() );
|
||||
}
|
||||
if ( !typesMatch ) {
|
||||
if ( !ColumnDefinitions.hasMatchingType( column, columnInformation, metadata, dialect ) ) {
|
||||
throw new SchemaManagementException(
|
||||
String.format(
|
||||
"Schema-validation: wrong column type encountered in column [%s] in " +
|
||||
|
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* 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.tool.schema.internal;
|
||||
|
||||
import org.hibernate.boot.Metadata;
|
||||
import org.hibernate.boot.model.relational.SqlStringGenerationContext;
|
||||
import org.hibernate.boot.spi.MetadataImplementor;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.jdbc.Size;
|
||||
import org.hibernate.mapping.Column;
|
||||
import org.hibernate.mapping.Constraint;
|
||||
import org.hibernate.mapping.Table;
|
||||
import org.hibernate.mapping.UniqueKey;
|
||||
import org.hibernate.tool.schema.extract.spi.ColumnInformation;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
|
||||
class ColumnDefinitions {
|
||||
|
||||
static boolean hasMatchingType(Column column, ColumnInformation columnInformation, Metadata metadata, Dialect dialect) {
|
||||
boolean typesMatch = dialect.equivalentTypes( column.getSqlTypeCode(metadata), columnInformation.getTypeCode() )
|
||||
|| stripArgs( column.getSqlType( metadata.getDatabase().getTypeConfiguration(), dialect, metadata ) )
|
||||
.equalsIgnoreCase( columnInformation.getTypeName() );
|
||||
if ( typesMatch ) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
// Try to resolve the JdbcType by type name and check for a match again based on that type code.
|
||||
// This is used to handle SqlTypes type codes like TIMESTAMP_UTC etc.
|
||||
final JdbcType jdbcType = dialect.resolveSqlTypeDescriptor(
|
||||
columnInformation.getTypeName(),
|
||||
columnInformation.getTypeCode(),
|
||||
columnInformation.getColumnSize(),
|
||||
columnInformation.getDecimalDigits(),
|
||||
metadata.getDatabase().getTypeConfiguration().getJdbcTypeRegistry()
|
||||
);
|
||||
return dialect.equivalentTypes( column.getSqlTypeCode(metadata), jdbcType.getDefaultSqlTypeCode() );
|
||||
}
|
||||
}
|
||||
|
||||
static boolean hasMatchingLength(Column column, ColumnInformation columnInformation, Metadata metadata, Dialect dialect) {
|
||||
final int actualSize = columnInformation.getColumnSize();
|
||||
if ( actualSize == 0 ) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
final Size size = column.getColumnSize( dialect, metadata );
|
||||
final Long requiredLength = size.getLength();
|
||||
final Integer requiredPrecision = size.getPrecision();
|
||||
return requiredLength != null && requiredLength == actualSize
|
||||
|| requiredPrecision != null && requiredPrecision == actualSize
|
||||
|| requiredPrecision == null && requiredLength == null;
|
||||
}
|
||||
}
|
||||
|
||||
static String getFullColumnDeclaration(
|
||||
Column column,
|
||||
Table table,
|
||||
Metadata metadata,
|
||||
Dialect dialect,
|
||||
SqlStringGenerationContext context) {
|
||||
StringBuilder definition = new StringBuilder();
|
||||
appendColumn( definition, column, table, metadata, dialect, context );
|
||||
return definition.toString();
|
||||
}
|
||||
|
||||
|
||||
static String getColumnDefinition(Column column, Table table, Metadata metadata, Dialect dialect) {
|
||||
StringBuilder definition = new StringBuilder();
|
||||
appendColumnDefinition( definition, column, table, metadata, dialect );
|
||||
appendComment( definition, column, dialect );
|
||||
return definition.toString();
|
||||
}
|
||||
|
||||
static void appendColumn(
|
||||
StringBuilder statement,
|
||||
Column column,
|
||||
Table table,
|
||||
Metadata metadata,
|
||||
Dialect dialect,
|
||||
SqlStringGenerationContext context) {
|
||||
statement.append( column.getQuotedName( dialect ) );
|
||||
appendColumnDefinition( statement, column, table, metadata, dialect );
|
||||
appendConstraints( statement, column, table, dialect, context );
|
||||
appendComment( statement, column, dialect );
|
||||
}
|
||||
|
||||
private static void appendConstraints(
|
||||
StringBuilder definition,
|
||||
Column column,
|
||||
Table table,
|
||||
Dialect dialect,
|
||||
SqlStringGenerationContext context) {
|
||||
if ( column.isUnique() && !table.isPrimaryKey( column ) ) {
|
||||
final String keyName = Constraint.generateName( "UK_", table, column);
|
||||
final UniqueKey uniqueKey = table.getOrCreateUniqueKey( keyName );
|
||||
uniqueKey.addColumn(column);
|
||||
definition.append( dialect.getUniqueDelegate().getColumnDefinitionUniquenessFragment( column, context ) );
|
||||
}
|
||||
|
||||
if ( dialect.supportsColumnCheck() && column.hasCheckConstraint() ) {
|
||||
definition.append( column.checkConstraint() );
|
||||
}
|
||||
}
|
||||
|
||||
private static void appendComment(StringBuilder definition, Column column, Dialect dialect) {
|
||||
final String columnComment = column.getComment();
|
||||
if ( columnComment != null ) {
|
||||
definition.append( dialect.getColumnComment( columnComment ) );
|
||||
}
|
||||
}
|
||||
|
||||
private static void appendColumnDefinition(
|
||||
StringBuilder definition,
|
||||
Column column,
|
||||
Table table,
|
||||
Metadata metadata,
|
||||
Dialect dialect) {
|
||||
final String columnType = getColumnType( column, metadata, dialect );
|
||||
if ( isIdentityColumn(column, table, metadata, dialect) ) {
|
||||
// to support dialects that have their own identity data type
|
||||
if ( dialect.getIdentityColumnSupport().hasDataTypeInIdentityColumn() ) {
|
||||
definition.append( ' ' ).append( columnType );
|
||||
}
|
||||
final String identityColumnString = dialect.getIdentityColumnSupport()
|
||||
.getIdentityColumnString( column.getSqlTypeCode(metadata) );
|
||||
definition.append( ' ' ).append( identityColumnString );
|
||||
}
|
||||
else {
|
||||
if ( column.hasSpecializedTypeDeclaration() ) {
|
||||
definition.append( ' ' ).append( column.getSpecializedTypeDeclaration() );
|
||||
}
|
||||
else if ( column.getGeneratedAs() == null || dialect.hasDataTypeBeforeGeneratedAs() ) {
|
||||
definition.append( ' ' ).append( columnType );
|
||||
}
|
||||
|
||||
final String defaultValue = column.getDefaultValue();
|
||||
if ( defaultValue != null ) {
|
||||
definition.append( " default " ).append( defaultValue );
|
||||
}
|
||||
|
||||
final String generatedAs = column.getGeneratedAs();
|
||||
if ( generatedAs != null) {
|
||||
definition.append( dialect.generatedAs( generatedAs ) );
|
||||
}
|
||||
|
||||
if ( column.isNullable() ) {
|
||||
definition.append( dialect.getNullColumnString( columnType ) );
|
||||
}
|
||||
else {
|
||||
definition.append( " not null" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static String getColumnType(Column column, Metadata metadata, Dialect dialect) {
|
||||
return column.getSqlType( metadata.getDatabase().getTypeConfiguration(), dialect, metadata );
|
||||
}
|
||||
|
||||
private static boolean isIdentityColumn(Column column, Table table, Metadata metadata, Dialect dialect) {
|
||||
// Try to find out the name of the primary key in case the dialect needs it to create an identity
|
||||
return isPrimaryKeyIdentity( table, metadata, dialect )
|
||||
&& column.getQuotedName( dialect ).equals( getPrimaryKeyColumnName( table, dialect ) );
|
||||
}
|
||||
|
||||
private static String getPrimaryKeyColumnName(Table table, Dialect dialect) {
|
||||
return table.hasPrimaryKey()
|
||||
? table.getPrimaryKey().getColumns().get(0).getQuotedName( dialect )
|
||||
: null;
|
||||
}
|
||||
|
||||
private static boolean isPrimaryKeyIdentity(Table table, Metadata metadata, Dialect dialect) {
|
||||
// TODO: this is the much better form moving forward as we move to metamodel
|
||||
//return hasPrimaryKey
|
||||
// && table.getPrimaryKey().getColumnSpan() == 1
|
||||
// && table.getPrimaryKey().getColumn( 0 ).isIdentity();
|
||||
MetadataImplementor metadataImplementor = (MetadataImplementor) metadata;
|
||||
return table.hasPrimaryKey()
|
||||
&& table.getIdentifierValue() != null
|
||||
&& table.getIdentifierValue().isIdentityColumn(
|
||||
metadataImplementor.getMetadataBuildingOptions().getIdentifierGeneratorFactory(),
|
||||
dialect
|
||||
);
|
||||
}
|
||||
|
||||
private static String stripArgs(String string) {
|
||||
int i = string.indexOf('(');
|
||||
return i>0 ? string.substring(0,i) : string;
|
||||
}
|
||||
}
|
|
@ -17,22 +17,20 @@ import org.hibernate.boot.model.relational.QualifiedName;
|
|||
import org.hibernate.boot.model.relational.QualifiedNameParser;
|
||||
import org.hibernate.boot.model.relational.QualifiedTableName;
|
||||
import org.hibernate.boot.model.relational.SqlStringGenerationContext;
|
||||
import org.hibernate.boot.spi.MetadataImplementor;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.dialect.aggregate.AggregateSupport;
|
||||
import org.hibernate.mapping.AggregateColumn;
|
||||
import org.hibernate.mapping.Column;
|
||||
import org.hibernate.mapping.Component;
|
||||
import org.hibernate.mapping.Constraint;
|
||||
import org.hibernate.mapping.Property;
|
||||
import org.hibernate.mapping.Table;
|
||||
import org.hibernate.mapping.UniqueKey;
|
||||
import org.hibernate.mapping.Value;
|
||||
import org.hibernate.sql.Template;
|
||||
import org.hibernate.tool.schema.spi.Exporter;
|
||||
|
||||
import static java.util.Collections.addAll;
|
||||
import static org.hibernate.internal.util.StringHelper.EMPTY_STRINGS;
|
||||
import static org.hibernate.tool.schema.internal.ColumnDefinitions.appendColumn;
|
||||
|
||||
/**
|
||||
* An {@link Exporter} for {@linkplain Table tables}.
|
||||
|
@ -100,97 +98,6 @@ public class StandardTableExporter implements Exporter<Table> {
|
|||
}
|
||||
}
|
||||
|
||||
static void appendColumn(
|
||||
StringBuilder statement,
|
||||
Column column,
|
||||
Table table,
|
||||
Metadata metadata,
|
||||
Dialect dialect,
|
||||
SqlStringGenerationContext context) {
|
||||
|
||||
statement.append( column.getQuotedName( dialect ) );
|
||||
|
||||
final String columnType = column.getSqlType( metadata.getDatabase().getTypeConfiguration(), dialect, metadata );
|
||||
if ( isIdentityColumn( column, table, metadata, dialect ) ) {
|
||||
// to support dialects that have their own identity data type
|
||||
if ( dialect.getIdentityColumnSupport().hasDataTypeInIdentityColumn() ) {
|
||||
statement.append( ' ' ).append( columnType );
|
||||
}
|
||||
final String identityColumnString = dialect.getIdentityColumnSupport()
|
||||
.getIdentityColumnString( column.getSqlTypeCode( metadata ) );
|
||||
statement.append( ' ' ).append( identityColumnString );
|
||||
}
|
||||
else {
|
||||
if ( column.hasSpecializedTypeDeclaration() ) {
|
||||
statement.append( ' ' ).append( column.getSpecializedTypeDeclaration() );
|
||||
}
|
||||
else if ( column.getGeneratedAs() == null || dialect.hasDataTypeBeforeGeneratedAs() ) {
|
||||
statement.append( ' ' ).append( columnType );
|
||||
}
|
||||
|
||||
final String defaultValue = column.getDefaultValue();
|
||||
if ( defaultValue != null ) {
|
||||
statement.append( " default " ).append( defaultValue );
|
||||
}
|
||||
|
||||
final String generatedAs = column.getGeneratedAs();
|
||||
if ( generatedAs != null) {
|
||||
statement.append( dialect.generatedAs( generatedAs ) );
|
||||
}
|
||||
|
||||
if ( column.isNullable() ) {
|
||||
statement.append( dialect.getNullColumnString(columnType) );
|
||||
}
|
||||
else {
|
||||
statement.append( " not null" );
|
||||
}
|
||||
}
|
||||
|
||||
if ( column.isUnique() && !table.isPrimaryKey(column) ) {
|
||||
final String keyName = Constraint.generateName( "UK_", table, column);
|
||||
final UniqueKey uk = table.getOrCreateUniqueKey( keyName );
|
||||
uk.addColumn(column);
|
||||
statement.append(
|
||||
dialect.getUniqueDelegate().getColumnDefinitionUniquenessFragment( column, context )
|
||||
);
|
||||
}
|
||||
|
||||
if ( dialect.supportsColumnCheck() && column.hasCheckConstraint() ) {
|
||||
statement.append( column.checkConstraint() );
|
||||
}
|
||||
|
||||
final String columnComment = column.getComment();
|
||||
if ( columnComment != null ) {
|
||||
statement.append( dialect.getColumnComment( columnComment ) );
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isIdentityColumn(Column column, Table table, Metadata metadata, Dialect dialect) {
|
||||
// Try to find out the name of the primary key in case the dialect needs it to create an identity
|
||||
return isPrimaryKeyIdentity( table, metadata, dialect )
|
||||
&& column.getQuotedName( dialect ).equals( getPrimaryKeyColumnName( table, dialect ) );
|
||||
}
|
||||
|
||||
private static String getPrimaryKeyColumnName(Table table, Dialect dialect) {
|
||||
return table.hasPrimaryKey()
|
||||
? table.getPrimaryKey().getColumns().get(0).getQuotedName( dialect )
|
||||
: null;
|
||||
}
|
||||
|
||||
private static boolean isPrimaryKeyIdentity(Table table, Metadata metadata, Dialect dialect) {
|
||||
// TODO: this is the much better form moving forward as we move to metamodel
|
||||
//return hasPrimaryKey
|
||||
// && table.getPrimaryKey().getColumnSpan() == 1
|
||||
// && table.getPrimaryKey().getColumn( 0 ).isIdentity();
|
||||
MetadataImplementor metadataImplementor = (MetadataImplementor) metadata;
|
||||
return table.hasPrimaryKey()
|
||||
&& table.getIdentifierValue() != null
|
||||
&& table.getIdentifierValue().isIdentityColumn(
|
||||
metadataImplementor.getMetadataBuildingOptions().getIdentifierGeneratorFactory(),
|
||||
dialect
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param table The table.
|
||||
* @param tableName The qualified table name.
|
||||
|
|
|
@ -24,7 +24,11 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
|
||||
import static org.hibernate.internal.util.collections.ArrayHelper.EMPTY_STRING_ARRAY;
|
||||
import static org.hibernate.tool.schema.internal.StandardTableExporter.appendColumn;
|
||||
import static org.hibernate.tool.schema.internal.ColumnDefinitions.getColumnDefinition;
|
||||
import static org.hibernate.tool.schema.internal.ColumnDefinitions.getColumnType;
|
||||
import static org.hibernate.tool.schema.internal.ColumnDefinitions.hasMatchingLength;
|
||||
import static org.hibernate.tool.schema.internal.ColumnDefinitions.hasMatchingType;
|
||||
import static org.hibernate.tool.schema.internal.ColumnDefinitions.getFullColumnDeclaration;
|
||||
|
||||
/**
|
||||
* A {@link TableMigrator} that only knows how to add new columns.
|
||||
|
@ -57,7 +61,7 @@ public class StandardTableMigrator implements TableMigrator {
|
|||
Table table,
|
||||
Dialect dialect,
|
||||
Metadata metadata,
|
||||
TableInformation tableInfo,
|
||||
TableInformation tableInformation,
|
||||
SqlStringGenerationContext sqlStringGenerationContext) throws HibernateException {
|
||||
|
||||
final String tableName = sqlStringGenerationContext.format( new QualifiedTableName(
|
||||
|
@ -66,22 +70,31 @@ public class StandardTableMigrator implements TableMigrator {
|
|||
table.getNameIdentifier() )
|
||||
);
|
||||
|
||||
final StringBuilder root = new StringBuilder( dialect.getAlterTableString( tableName ) )
|
||||
.append( ' ' )
|
||||
.append( dialect.getAddColumnString() );
|
||||
final String alterTable = dialect.getAlterTableString( tableName ) + ' ';
|
||||
|
||||
final List<String> results = new ArrayList<>();
|
||||
|
||||
for ( Column column : table.getColumns() ) {
|
||||
final ColumnInformation columnInfo = tableInfo.getColumn(
|
||||
final ColumnInformation columnInformation = tableInformation.getColumn(
|
||||
Identifier.toIdentifier( column.getName(), column.isQuoted() )
|
||||
);
|
||||
if ( columnInfo == null ) {
|
||||
if ( columnInformation == null ) {
|
||||
// the column doesn't exist at all.
|
||||
final StringBuilder alterTable = new StringBuilder( root.toString() ).append( ' ' );
|
||||
appendColumn( alterTable, column, table, metadata, dialect, sqlStringGenerationContext );
|
||||
alterTable.append( dialect.getAddColumnSuffixString() );
|
||||
results.add( alterTable.toString() );
|
||||
final String addColumn = dialect.getAddColumnString() + ' '
|
||||
+ getFullColumnDeclaration( column, table, metadata, dialect, sqlStringGenerationContext )
|
||||
+ dialect.getAddColumnSuffixString();
|
||||
results.add( alterTable + addColumn );
|
||||
}
|
||||
else if ( dialect.supportsAlterColumnType() ) {
|
||||
if ( !hasMatchingType( column, columnInformation, metadata, dialect )
|
||||
|| !hasMatchingLength( column, columnInformation, metadata, dialect ) ) {
|
||||
final String alterColumn = dialect.getAlterColumnTypeString(
|
||||
column.getQuotedName( dialect ),
|
||||
getColumnType( column, metadata, dialect ),
|
||||
getColumnDefinition( column, table, metadata, dialect )
|
||||
);
|
||||
results.add( alterTable + alterColumn );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
*/
|
||||
package org.hibernate.type;
|
||||
|
||||
import org.hibernate.Internal;
|
||||
|
||||
import java.sql.Types;
|
||||
|
||||
/**
|
||||
|
@ -614,6 +616,17 @@ public class SqlTypes {
|
|||
}
|
||||
}
|
||||
|
||||
@Internal
|
||||
public static boolean isSmallOrTinyInt(int typeCode) {
|
||||
switch ( typeCode ) {
|
||||
case SMALLINT:
|
||||
case TINYINT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the given typecode represent a SQL date, time, or timestamp type?
|
||||
* @param typeCode a JDBC type code from {@link Types}
|
||||
|
|
|
@ -15,7 +15,8 @@
|
|||
<id name="id">
|
||||
<generator class="sequence"/>
|
||||
</id>
|
||||
<property name="description"/>
|
||||
<property name="description"/>
|
||||
<property name="versionNumber"/>
|
||||
</class>
|
||||
|
||||
</hibernate-mapping>
|
||||
|
|
|
@ -16,7 +16,8 @@
|
|||
<generator class="sequence"/>
|
||||
</id>
|
||||
<property name="description"/>
|
||||
<property name="name"/>
|
||||
<property name="name"/>
|
||||
<property name="versionNumber"/>
|
||||
</class>
|
||||
|
||||
</hibernate-mapping>
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
<properties name="nameUK" unique="true">
|
||||
<property name="name"/>
|
||||
</properties>
|
||||
<property name="versionNumber"/>
|
||||
</class>
|
||||
|
||||
</hibernate-mapping>
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0"?>
|
||||
<!--
|
||||
~ 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>.
|
||||
-->
|
||||
<!DOCTYPE hibernate-mapping PUBLIC
|
||||
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
|
||||
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
|
||||
|
||||
<hibernate-mapping package="org.hibernate.orm.test.schemaupdate">
|
||||
|
||||
<class name="Version">
|
||||
<id name="id">
|
||||
<generator class="sequence"/>
|
||||
</id>
|
||||
<property name="description" length="500"/>
|
||||
<property name="versionNumber" type="long"/>
|
||||
</class>
|
||||
|
||||
</hibernate-mapping>
|
||||
|
|
@ -63,6 +63,10 @@ public class MigrationTest extends BaseUnitTestCase {
|
|||
v1metadata
|
||||
);
|
||||
|
||||
v1schemaUpdate.getExceptions().forEach(
|
||||
e -> System.out.println( e.getCause().getMessage() )
|
||||
);
|
||||
|
||||
assertEquals( 0, v1schemaUpdate.getExceptions().size() );
|
||||
|
||||
MetadataImplementor v2metadata = (MetadataImplementor) new MetadataSources( serviceRegistry )
|
||||
|
@ -74,11 +78,59 @@ public class MigrationTest extends BaseUnitTestCase {
|
|||
EnumSet.of( TargetType.DATABASE, TargetType.STDOUT ),
|
||||
v2metadata
|
||||
);
|
||||
|
||||
v2schemaUpdate.getExceptions().forEach(
|
||||
e -> System.out.println( e.getCause().getMessage() )
|
||||
);
|
||||
|
||||
assertEquals( 0, v2schemaUpdate.getExceptions().size() );
|
||||
|
||||
new SchemaExport().drop( EnumSet.of( TargetType.DATABASE ), v2metadata );
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleColumnTypeChange() {
|
||||
String resource1 = "org/hibernate/orm/test/schemaupdate/1_Version.hbm.xml";
|
||||
String resource4 = "org/hibernate/orm/test/schemaupdate/4_Version.hbm.xml";
|
||||
|
||||
MetadataImplementor v1metadata = (MetadataImplementor) new MetadataSources( serviceRegistry )
|
||||
.addResource( resource1 )
|
||||
.buildMetadata();
|
||||
|
||||
new SchemaExport().drop( EnumSet.of( TargetType.DATABASE ), v1metadata );
|
||||
|
||||
final SchemaUpdate v1schemaUpdate = new SchemaUpdate();
|
||||
v1schemaUpdate.execute(
|
||||
EnumSet.of( TargetType.DATABASE, TargetType.STDOUT ),
|
||||
v1metadata
|
||||
);
|
||||
|
||||
v1schemaUpdate.getExceptions().forEach(
|
||||
e -> System.out.println( e.getCause().getMessage() )
|
||||
);
|
||||
|
||||
assertEquals( 0, v1schemaUpdate.getExceptions().size() );
|
||||
|
||||
MetadataImplementor v2metadata = (MetadataImplementor) new MetadataSources( serviceRegistry )
|
||||
.addResource( resource4 )
|
||||
.buildMetadata();
|
||||
|
||||
final SchemaUpdate v2schemaUpdate = new SchemaUpdate();
|
||||
v2schemaUpdate.execute(
|
||||
EnumSet.of( TargetType.DATABASE, TargetType.STDOUT ),
|
||||
v2metadata
|
||||
);
|
||||
|
||||
v2schemaUpdate.getExceptions().forEach(
|
||||
e -> System.out.println( e.getCause().getMessage() )
|
||||
);
|
||||
|
||||
assertEquals( 0, v2schemaUpdate.getExceptions().size() );
|
||||
|
||||
new SchemaExport().drop( EnumSet.of( TargetType.DATABASE ), v2metadata );
|
||||
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 3_Version.hbm.xml contains a named unique constraint and an un-named
|
||||
|
|
|
@ -12,6 +12,7 @@ public class Version {
|
|||
long id;
|
||||
String name;
|
||||
String description;
|
||||
int versionNumber;
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
|
@ -34,4 +35,12 @@ public class Version {
|
|||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public void setVersionNumber(int versionNumber) {
|
||||
this.versionNumber = versionNumber;
|
||||
}
|
||||
|
||||
public int getVersionNumber() {
|
||||
return versionNumber;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue