introduce TableMigrator and kill some code duplication

This commit is contained in:
Gavin 2022-12-13 15:02:34 +01:00 committed by Gavin King
parent f2576d6b87
commit 6348d9927e
9 changed files with 300 additions and 241 deletions

View File

@ -45,7 +45,6 @@ import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.ScrollMode; import org.hibernate.ScrollMode;
import org.hibernate.boot.TempTableDdlTransactionHandling; import org.hibernate.boot.TempTableDdlTransactionHandling;
import org.hibernate.boot.model.TypeContributions; import org.hibernate.boot.model.TypeContributions;
import org.hibernate.boot.model.convert.spi.ConverterRegistry;
import org.hibernate.boot.model.relational.AuxiliaryDatabaseObject; import org.hibernate.boot.model.relational.AuxiliaryDatabaseObject;
import org.hibernate.boot.model.relational.Sequence; import org.hibernate.boot.model.relational.Sequence;
import org.hibernate.boot.spi.SessionFactoryOptions; import org.hibernate.boot.spi.SessionFactoryOptions;
@ -153,7 +152,9 @@ import org.hibernate.tool.schema.internal.StandardIndexExporter;
import org.hibernate.tool.schema.internal.StandardSequenceExporter; import org.hibernate.tool.schema.internal.StandardSequenceExporter;
import org.hibernate.tool.schema.internal.StandardTableCleaner; import org.hibernate.tool.schema.internal.StandardTableCleaner;
import org.hibernate.tool.schema.internal.StandardTableExporter; import org.hibernate.tool.schema.internal.StandardTableExporter;
import org.hibernate.tool.schema.internal.StandardTableMigrator;
import org.hibernate.tool.schema.internal.StandardUniqueKeyExporter; import org.hibernate.tool.schema.internal.StandardUniqueKeyExporter;
import org.hibernate.tool.schema.internal.TableMigrator;
import org.hibernate.tool.schema.spi.Cleaner; import org.hibernate.tool.schema.spi.Cleaner;
import org.hibernate.tool.schema.spi.Exporter; import org.hibernate.tool.schema.spi.Exporter;
import org.hibernate.tool.schema.spi.SchemaManagementTool; import org.hibernate.tool.schema.spi.SchemaManagementTool;
@ -2488,12 +2489,17 @@ public abstract class Dialect implements ConversionContext {
private final StandardUniqueKeyExporter uniqueKeyExporter = new StandardUniqueKeyExporter( this ); private final StandardUniqueKeyExporter uniqueKeyExporter = new StandardUniqueKeyExporter( this );
private final StandardAuxiliaryDatabaseObjectExporter auxiliaryObjectExporter = new StandardAuxiliaryDatabaseObjectExporter( this ); private final StandardAuxiliaryDatabaseObjectExporter auxiliaryObjectExporter = new StandardAuxiliaryDatabaseObjectExporter( this );
private final StandardTemporaryTableExporter temporaryTableExporter = new StandardTemporaryTableExporter( this ); private final StandardTemporaryTableExporter temporaryTableExporter = new StandardTemporaryTableExporter( this );
private final StandardTableMigrator tableMigrator = new StandardTableMigrator( this );
private final StandardTableCleaner tableCleaner = new StandardTableCleaner( this ); private final StandardTableCleaner tableCleaner = new StandardTableCleaner( this );
public Exporter<Table> getTableExporter() { public Exporter<Table> getTableExporter() {
return tableExporter; return tableExporter;
} }
public TableMigrator getTableMigrator() {
return tableMigrator;
}
public Cleaner getTableCleaner() { public Cleaner getTableCleaner() {
return tableCleaner; return tableCleaner;
} }

View File

@ -869,10 +869,11 @@ public final class StringHelper {
* Return the interned form of a String, or null if the parameter is null. * Return the interned form of a String, or null if the parameter is null.
* <p> * <p>
* Use with caution: excessive interning is known to cause issues. * Use with caution: excessive interning is known to cause issues.
* Best to use only with strings which are known to be long lived constants, * Best to use only with strings which are known to be long-lived constants,
* and for which the chances of being actual duplicates is proven. * and for which the chances of being actual duplicates is proven.
* (Even better: avoid needing interning by design changes such as reusing * (Even better: avoid needing interning by design changes such as reusing
* the known reference) * the known reference)
*
* @param string The string to intern. * @param string The string to intern.
* @return The interned string. * @return The interned string.
*/ */

View File

@ -31,9 +31,9 @@ import org.hibernate.boot.model.relational.QualifiedTableName;
import org.hibernate.boot.model.relational.SqlStringGenerationContext; import org.hibernate.boot.model.relational.SqlStringGenerationContext;
import org.hibernate.boot.spi.InFlightMetadataCollector; import org.hibernate.boot.spi.InFlightMetadataCollector;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.tool.schema.extract.spi.ColumnInformation;
import org.hibernate.tool.schema.extract.spi.TableInformation; import org.hibernate.tool.schema.extract.spi.TableInformation;
import org.hibernate.tool.schema.internal.StandardTableMigrator;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import static java.util.Collections.unmodifiableList; import static java.util.Collections.unmodifiableList;
@ -160,6 +160,14 @@ public class Table implements Serializable, ContributableDatabaseObject {
return name; return name;
} }
public Identifier getSchemaIdentifier() {
return schema;
}
public Identifier getCatalogIdentifier() {
return catalog;
}
public String getQuotedName() { public String getQuotedName() {
return name == null ? null : name.toString(); return name == null ? null : name.toString();
} }
@ -432,88 +440,14 @@ public class Table implements Serializable, ContributableDatabaseObject {
} }
} }
@Deprecated(since = "6.2") @Remove
public Iterator<String> sqlAlterStrings( public Iterator<String> sqlAlterStrings(
Dialect dialect, Dialect dialect,
Metadata metadata, Metadata metadata,
TableInformation tableInfo, TableInformation tableInfo,
SqlStringGenerationContext sqlStringGenerationContext) throws HibernateException { SqlStringGenerationContext sqlStringGenerationContext) throws HibernateException {
final String tableName = sqlStringGenerationContext.format( new QualifiedTableName( catalog, schema, name ) ); return StandardTableMigrator.sqlAlterStrings(this, dialect, metadata, tableInfo, sqlStringGenerationContext )
.iterator();
final StringBuilder root = new StringBuilder( dialect.getAlterTableString( tableName ) )
.append( ' ' )
.append( dialect.getAddColumnString() );
final List<String> results = new ArrayList<>();
for ( Column column : getColumns() ) {
final ColumnInformation columnInfo = tableInfo.getColumn(
Identifier.toIdentifier( column.getName(), column.isQuoted() )
);
if ( columnInfo == null ) {
// the column doesn't exist at all.
final StringBuilder alter = new StringBuilder( root.toString() )
.append( ' ' )
.append( column.getQuotedName( dialect ) );
final String columnType = column.getSqlType(
metadata.getDatabase().getTypeConfiguration(),
dialect,
metadata
);
if ( column.hasSpecializedTypeDeclaration() ) {
alter.append( ' ' ).append( column.getSpecializedTypeDeclaration() );
}
else if ( column.getGeneratedAs() == null || dialect.hasDataTypeBeforeGeneratedAs() ) {
alter.append(' ').append(columnType);
}
final String defaultValue = column.getDefaultValue();
if ( defaultValue != null ) {
alter.append( " default " ).append( defaultValue );
}
final String generatedAs = column.getGeneratedAs();
if ( generatedAs != null) {
alter.append( dialect.generatedAs( generatedAs ) );
}
if ( column.isNullable() ) {
alter.append( dialect.getNullColumnString( columnType ) );
}
else {
alter.append( " not null" );
}
if ( column.isUnique() && !isPrimaryKey( column ) ) {
String keyName = Constraint.generateName( "UK_", this, column );
UniqueKey uk = getOrCreateUniqueKey( keyName );
uk.addColumn( column );
alter.append( dialect.getUniqueDelegate()
.getColumnDefinitionUniquenessFragment( column, sqlStringGenerationContext ) );
}
if ( column.hasCheckConstraint() && dialect.supportsColumnCheck() ) {
alter.append( column.checkConstraint() );
}
final String columnComment = column.getComment();
if ( columnComment != null ) {
alter.append( dialect.getColumnComment( columnComment ) );
}
alter.append( dialect.getAddColumnSuffixString() );
results.add( alter.toString() );
}
}
if ( results.isEmpty() ) {
log.debugf( "No alter strings for table : %s", getQuotedName() );
}
return results.iterator();
} }
public boolean isPrimaryKey(Column column) { public boolean isPrimaryKey(Column column) {

View File

@ -186,14 +186,14 @@ public abstract class AbstractSchemaMigrator implements SchemaMigrator {
final Set<String> exportIdentifiers = CollectionHelper.setOfSize( 50 ); final Set<String> exportIdentifiers = CollectionHelper.setOfSize( 50 );
final Database database = metadata.getDatabase(); final Database database = metadata.getDatabase();
Exporter<AuxiliaryDatabaseObject> auxiliaryExporter = dialect.getAuxiliaryDatabaseObjectExporter();
// Drop all AuxiliaryDatabaseObjects // Drop all AuxiliaryDatabaseObjects
for ( AuxiliaryDatabaseObject auxiliaryDatabaseObject : database.getAuxiliaryDatabaseObjects() ) { for ( AuxiliaryDatabaseObject auxiliaryDatabaseObject : database.getAuxiliaryDatabaseObjects() ) {
if ( auxiliaryDatabaseObject.appliesToDialect( dialect ) ) { if ( auxiliaryDatabaseObject.appliesToDialect( dialect ) ) {
applySqlStrings( applySqlStrings(
true, true,
dialect.getAuxiliaryDatabaseObjectExporter() auxiliaryExporter.getSqlDropStrings( auxiliaryDatabaseObject, metadata, sqlGenerationContext ),
.getSqlDropStrings( auxiliaryDatabaseObject, metadata, sqlGenerationContext ),
formatter, formatter,
options, options,
targets targets
@ -207,7 +207,7 @@ public abstract class AbstractSchemaMigrator implements SchemaMigrator {
&& auxiliaryDatabaseObject.appliesToDialect( dialect ) ) { && auxiliaryDatabaseObject.appliesToDialect( dialect ) ) {
applySqlStrings( applySqlStrings(
true, true,
auxiliaryDatabaseObject.sqlCreateStrings( sqlGenerationContext ), auxiliaryExporter.getSqlCreateStrings( auxiliaryDatabaseObject, metadata, sqlGenerationContext ),
formatter, formatter,
options, options,
targets targets
@ -246,18 +246,10 @@ public abstract class AbstractSchemaMigrator implements SchemaMigrator {
if ( options.getSchemaFilter().includeNamespace( namespace ) ) { if ( options.getSchemaFilter().includeNamespace( namespace ) ) {
for ( Sequence sequence : namespace.getSequences() ) { for ( Sequence sequence : namespace.getSequences() ) {
if ( contributableInclusionFilter.matches( sequence ) ) { if ( contributableInclusionFilter.matches( sequence ) ) {
checkExportIdentifier( sequence, exportIdentifiers ); checkExportIdentifier( sequence, exportIdentifiers);
final SequenceInformation sequenceInformation = final SequenceInformation sequenceInformation = existingDatabase.getSequenceInformation( sequence.getName() );
existingDatabase.getSequenceInformation( sequence.getName() );
if ( sequenceInformation == null ) { if ( sequenceInformation == null ) {
applySqlStrings( applySequence( sequence, dialect, metadata, formatter, options, sqlGenerationContext, targets );
false,
dialect.getSequenceExporter()
.getSqlCreateStrings( sequence, metadata, sqlGenerationContext ),
formatter,
options,
targets
);
} }
} }
} }
@ -269,17 +261,12 @@ public abstract class AbstractSchemaMigrator implements SchemaMigrator {
if ( options.getSchemaFilter().includeNamespace( namespace ) ) { if ( options.getSchemaFilter().includeNamespace( namespace ) ) {
final NameSpaceTablesInformation nameSpaceTablesInformation = tablesInformation.get( namespace ); final NameSpaceTablesInformation nameSpaceTablesInformation = tablesInformation.get( namespace );
for ( Table table : namespace.getTables() ) { for ( Table table : namespace.getTables() ) {
if ( ! options.getSchemaFilter().includeTable( table ) ) { if ( options.getSchemaFilter().includeTable( table ) && contributableInclusionFilter.matches( table ) ) {
continue; final TableInformation tableInformation = nameSpaceTablesInformation.getTableInformation( table );
} if ( tableInformation == null || tableInformation.isPhysicalTable() ) {
if ( ! contributableInclusionFilter.matches( table ) ) { applyForeignKeys( table, tableInformation, dialect, metadata, formatter, options,
continue; sqlGenerationContext, targets );
} }
final TableInformation tableInformation = nameSpaceTablesInformation.getTableInformation( table );
if ( tableInformation == null || tableInformation.isPhysicalTable() ) {
applyForeignKeys( table, tableInformation, dialect, metadata, formatter, options,
sqlGenerationContext, targets );
} }
} }
} }
@ -287,10 +274,10 @@ public abstract class AbstractSchemaMigrator implements SchemaMigrator {
// Create after-table AuxiliaryDatabaseObjects // Create after-table AuxiliaryDatabaseObjects
for ( AuxiliaryDatabaseObject auxiliaryDatabaseObject : database.getAuxiliaryDatabaseObjects() ) { for ( AuxiliaryDatabaseObject auxiliaryDatabaseObject : database.getAuxiliaryDatabaseObjects() ) {
if ( auxiliaryDatabaseObject.beforeTablesOnCreation() && auxiliaryDatabaseObject.appliesToDialect( dialect )) { if ( auxiliaryDatabaseObject.beforeTablesOnCreation() && auxiliaryDatabaseObject.appliesToDialect( dialect ) ) {
applySqlStrings( applySqlStrings(
true, true,
auxiliaryDatabaseObject.sqlCreateStrings( sqlGenerationContext ), auxiliaryExporter.getSqlCreateStrings( auxiliaryDatabaseObject, metadata, sqlGenerationContext ),
formatter, formatter,
options, options,
targets targets
@ -299,6 +286,23 @@ public abstract class AbstractSchemaMigrator implements SchemaMigrator {
} }
} }
private static void applySequence(
Sequence sequence,
Dialect dialect,
Metadata metadata,
Formatter formatter,
ExecutionOptions options,
SqlStringGenerationContext sqlGenerationContext,
GenerationTarget... targets) {
applySqlStrings(
false,
dialect.getSequenceExporter().getSqlCreateStrings( sequence, metadata, sqlGenerationContext ),
formatter,
options,
targets
);
}
protected void createTable( protected void createTable(
Table table, Table table,
Dialect dialect, Dialect dialect,
@ -327,12 +331,8 @@ public abstract class AbstractSchemaMigrator implements SchemaMigrator {
GenerationTarget... targets) { GenerationTarget... targets) {
applySqlStrings( applySqlStrings(
false, false,
table.sqlAlterStrings( dialect.getTableMigrator()
dialect, .getSqlAlterStrings( table, metadata, tableInformation, sqlGenerationContext ),
metadata,
tableInformation,
sqlGenerationContext
),
formatter, formatter,
options, options,
targets targets
@ -576,17 +576,4 @@ public abstract class AbstractSchemaMigrator implements SchemaMigrator {
} }
} }
} }
private static void applySqlStrings(
boolean quiet,
Iterator<String> sqlStrings,
Formatter formatter,
ExecutionOptions options,
GenerationTarget... targets) {
if ( sqlStrings != null ) {
while ( sqlStrings.hasNext() ) {
applySqlString( quiet, sqlStrings.next(), formatter, options, targets );
}
}
}
} }

View File

@ -6,6 +6,7 @@
*/ */
package org.hibernate.tool.schema.internal; package org.hibernate.tool.schema.internal;
import org.hibernate.Incubating;
import org.hibernate.boot.Metadata; import org.hibernate.boot.Metadata;
import org.hibernate.boot.model.naming.Identifier; import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.relational.QualifiedNameParser; import org.hibernate.boot.model.relational.QualifiedNameParser;
@ -24,6 +25,7 @@ import java.util.stream.Collectors;
* *
* @author Gavin King * @author Gavin King
*/ */
@Incubating
public class StandardTableCleaner implements Cleaner { public class StandardTableCleaner implements Cleaner {
protected final Dialect dialect; protected final Dialect dialect;

View File

@ -7,7 +7,6 @@
package org.hibernate.tool.schema.internal; package org.hibernate.tool.schema.internal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import org.hibernate.MappingException; import org.hibernate.MappingException;
@ -20,19 +19,22 @@ import org.hibernate.boot.model.relational.QualifiedTableName;
import org.hibernate.boot.model.relational.SqlStringGenerationContext; import org.hibernate.boot.model.relational.SqlStringGenerationContext;
import org.hibernate.boot.spi.MetadataImplementor; import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.mapping.Column; import org.hibernate.mapping.Column;
import org.hibernate.mapping.Constraint; import org.hibernate.mapping.Constraint;
import org.hibernate.mapping.Table; import org.hibernate.mapping.Table;
import org.hibernate.mapping.UniqueKey; import org.hibernate.mapping.UniqueKey;
import org.hibernate.tool.schema.spi.Exporter; import org.hibernate.tool.schema.spi.Exporter;
import static java.util.Collections.addAll;
import static org.hibernate.internal.util.StringHelper.EMPTY_STRINGS;
/** /**
* An {@link Exporter} for {@linkplain Table tables}. * An {@link Exporter} for {@linkplain Table tables}.
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class StandardTableExporter implements Exporter<Table> { public class StandardTableExporter implements Exporter<Table> {
protected final Dialect dialect; protected final Dialect dialect;
public StandardTableExporter(Dialect dialect) { public StandardTableExporter(Dialect dialect) {
@ -44,39 +46,17 @@ public class StandardTableExporter implements Exporter<Table> {
Table table, Table table,
Metadata metadata, Metadata metadata,
SqlStringGenerationContext context) { SqlStringGenerationContext context) {
final QualifiedName tableName = new QualifiedNameParser.NameParts( final QualifiedName tableName = getTableName(table);
Identifier.toIdentifier( table.getCatalog(), table.isCatalogQuoted() ),
Identifier.toIdentifier( table.getSchema(), table.isSchemaQuoted() ),
table.getNameIdentifier()
);
try { try {
String formattedTableName = context.format( tableName ); final String formattedTableName = context.format( tableName );
StringBuilder createTable =
final StringBuilder createTable =
new StringBuilder( tableCreateString( table.hasPrimaryKey() ) ) new StringBuilder( tableCreateString( table.hasPrimaryKey() ) )
.append( ' ' ) .append( ' ' )
.append( formattedTableName ) .append( formattedTableName )
.append( " (" ); .append( " (" );
boolean isPrimaryKeyIdentity = table.hasPrimaryKey()
&& table.getIdentifierValue() != null
&& table.getIdentifierValue().isIdentityColumn(
( (MetadataImplementor) metadata ).getMetadataBuildingOptions()
.getIdentifierGeneratorFactory(),
dialect
);
// TODO: this is the much better form moving forward as we move to metamodel
//boolean isPrimaryKeyIdentity = hasPrimaryKey
// && table.getPrimaryKey().getColumnSpan() == 1
// && table.getPrimaryKey().getColumn( 0 ).isIdentity();
// Try to find out the name of the primary key in case the dialect needs it to create an identity
String pkColName = null;
if ( table.hasPrimaryKey() ) {
pkColName = table.getPrimaryKey().getColumns().get(0).getQuotedName( dialect );
}
boolean isFirst = true; boolean isFirst = true;
for ( Column column : table.getColumns() ) { for ( Column column : table.getColumns() ) {
if ( isFirst ) { if ( isFirst ) {
@ -85,75 +65,10 @@ public class StandardTableExporter implements Exporter<Table> {
else { else {
createTable.append( ", " ); createTable.append( ", " );
} }
appendColumn( createTable, column, table, metadata, dialect, context );
String colName = column.getQuotedName( dialect );
createTable.append( colName );
if ( isPrimaryKeyIdentity && colName.equals( pkColName ) ) {
// to support dialects that have their own identity data type
if ( dialect.getIdentityColumnSupport().hasDataTypeInIdentityColumn() ) {
createTable.append( ' ' ).append(
column.getSqlType( metadata.getDatabase().getTypeConfiguration(), dialect, metadata )
);
}
String identityColumnString = dialect.getIdentityColumnSupport()
.getIdentityColumnString( column.getSqlTypeCode(metadata) );
createTable.append( ' ' ).append( identityColumnString );
}
else {
final String columnType = column.getSqlType(
metadata.getDatabase().getTypeConfiguration(),
dialect,
metadata
);
if ( column.hasSpecializedTypeDeclaration() ) {
createTable.append( ' ' ).append( column.getSpecializedTypeDeclaration() );
}
else if ( column.getGeneratedAs() == null || dialect.hasDataTypeBeforeGeneratedAs() ) {
createTable.append( ' ' ).append( columnType );
}
String defaultValue = column.getDefaultValue();
if ( defaultValue != null ) {
createTable.append( " default " ).append( defaultValue );
}
String generatedAs = column.getGeneratedAs();
if ( generatedAs != null) {
createTable.append( dialect.generatedAs( generatedAs ) );
}
if ( column.isNullable() ) {
createTable.append( dialect.getNullColumnString( columnType ) );
}
else {
createTable.append( " not null" );
}
}
if ( column.isUnique() && !table.isPrimaryKey( column ) ) {
String keyName = Constraint.generateName( "UK_", table, column );
UniqueKey uk = table.getOrCreateUniqueKey( keyName );
uk.addColumn( column );
createTable.append(
dialect.getUniqueDelegate()
.getColumnDefinitionUniquenessFragment( column, context )
);
}
if ( dialect.supportsColumnCheck() && column.hasCheckConstraint() ) {
createTable.append( column.checkConstraint() );
}
String columnComment = column.getComment();
if ( columnComment != null ) {
createTable.append( dialect.getColumnComment( columnComment ) );
}
} }
if ( table.hasPrimaryKey() ) { if ( table.hasPrimaryKey() ) {
createTable.append( ", " ) createTable.append( ", " ).append( table.getPrimaryKey().sqlConstraintString( dialect ) );
.append( table.getPrimaryKey().sqlConstraintString( dialect ) );
} }
createTable.append( dialect.getUniqueDelegate().getTableCreationUniqueConstraintsFragment( table, context ) ); createTable.append( dialect.getUniqueDelegate().getTableCreationUniqueConstraintsFragment( table, context ) );
@ -168,20 +83,108 @@ public class StandardTableExporter implements Exporter<Table> {
applyTableTypeString( createTable ); applyTableTypeString( createTable );
List<String> sqlStrings = new ArrayList<>(); final List<String> sqlStrings = new ArrayList<>();
sqlStrings.add( createTable.toString() ); sqlStrings.add( createTable.toString() );
applyComments( table, formattedTableName, sqlStrings ); applyComments( table, formattedTableName, sqlStrings );
applyInitCommands( table, sqlStrings, context ); applyInitCommands( table, sqlStrings, context );
return sqlStrings.toArray( EMPTY_STRINGS );
return sqlStrings.toArray(StringHelper.EMPTY_STRINGS);
} }
catch (Exception e) { catch (Exception e) {
throw new MappingException( "Error creating SQL create commands for table : " + tableName, e ); throw new MappingException( "Error creating SQL create commands for table : " + tableName, e );
} }
} }
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 table The table.
* @param tableName The qualified table name. * @param tableName The qualified table name.
@ -202,12 +205,16 @@ public class StandardTableExporter implements Exporter<Table> {
protected void applyComments(Table table, String formattedTableName, List<String> sqlStrings) { protected void applyComments(Table table, String formattedTableName, List<String> sqlStrings) {
if ( dialect.supportsCommentOn() ) { if ( dialect.supportsCommentOn() ) {
if ( table.getComment() != null ) { if ( table.getComment() != null ) {
sqlStrings.add( "comment on table " + formattedTableName + " is '" + table.getComment() + "'" ); sqlStrings.add( "comment on table "
+ formattedTableName
+ " is '" + table.getComment() + "'" );
} }
for ( Column column : table.getColumns() ) { for ( Column column : table.getColumns() ) {
String columnComment = column.getComment(); String columnComment = column.getComment();
if ( columnComment != null ) { if ( columnComment != null ) {
sqlStrings.add( "comment on column " + formattedTableName + '.' + column.getQuotedName( dialect ) + " is '" + columnComment + "'" ); sqlStrings.add( "comment on column "
+ formattedTableName + '.' + column.getQuotedName( dialect )
+ " is '" + columnComment + "'" );
} }
} }
} }
@ -215,7 +222,7 @@ public class StandardTableExporter implements Exporter<Table> {
protected void applyInitCommands(Table table, List<String> sqlStrings, SqlStringGenerationContext context) { protected void applyInitCommands(Table table, List<String> sqlStrings, SqlStringGenerationContext context) {
for ( InitCommand initCommand : table.getInitCommands( context ) ) { for ( InitCommand initCommand : table.getInitCommands( context ) ) {
Collections.addAll( sqlStrings, initCommand.getInitCommands() ); addAll( sqlStrings, initCommand.getInitCommands() );
} }
} }
@ -226,9 +233,7 @@ public class StandardTableExporter implements Exporter<Table> {
protected void applyTableCheck(Table table, StringBuilder buf) { protected void applyTableCheck(Table table, StringBuilder buf) {
if ( dialect.supportsTableCheck() ) { if ( dialect.supportsTableCheck() ) {
for (String constraint : table.getCheckConstraints() ) { for (String constraint : table.getCheckConstraints() ) {
buf.append( ", check (" ) buf.append( ", check (" ).append( constraint ).append( ')' );
.append( constraint )
.append( ')' );
} }
} }
} }
@ -244,19 +249,19 @@ public class StandardTableExporter implements Exporter<Table> {
if ( dialect.supportsIfExistsBeforeTableName() ) { if ( dialect.supportsIfExistsBeforeTableName() ) {
buf.append( "if exists " ); buf.append( "if exists " );
} }
buf.append( context.format( getTableName( table ) ) )
.append( dialect.getCascadeConstraintsString() );
if ( dialect.supportsIfExistsAfterTableName() ) {
buf.append( " if exists" );
}
return new String[] { buf.toString() };
}
final QualifiedName tableName = new QualifiedNameParser.NameParts( private static QualifiedName getTableName(Table table) {
return new QualifiedNameParser.NameParts(
Identifier.toIdentifier( table.getCatalog(), table.isCatalogQuoted() ), Identifier.toIdentifier( table.getCatalog(), table.isCatalogQuoted() ),
Identifier.toIdentifier( table.getSchema(), table.isSchemaQuoted() ), Identifier.toIdentifier( table.getSchema(), table.isSchemaQuoted() ),
table.getNameIdentifier() table.getNameIdentifier()
); );
buf.append( context.format( tableName ) )
.append( dialect.getCascadeConstraintsString() );
if ( dialect.supportsIfExistsAfterTableName() ) {
buf.append( " if exists" );
}
return new String[] { buf.toString() };
} }
} }

View File

@ -0,0 +1,94 @@
/*
* 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.HibernateException;
import org.hibernate.Incubating;
import org.hibernate.Internal;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.relational.QualifiedTableName;
import org.hibernate.boot.model.relational.SqlStringGenerationContext;
import org.hibernate.dialect.Dialect;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Table;
import org.hibernate.tool.schema.extract.spi.ColumnInformation;
import org.hibernate.tool.schema.extract.spi.TableInformation;
import org.jboss.logging.Logger;
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;
/**
* A {@link TableMigrator} that only knows how to add new columns.
*
* @author Gavin King
*/
@Incubating
public class StandardTableMigrator implements TableMigrator {
private static final Logger log = Logger.getLogger( Table.class );
protected final Dialect dialect;
public StandardTableMigrator(Dialect dialect) {
this.dialect = dialect;
}
@Override
public String[] getSqlAlterStrings(
Table table,
Metadata metadata,
TableInformation tableInfo,
SqlStringGenerationContext sqlStringGenerationContext) {
return sqlAlterStrings( table, dialect, metadata, tableInfo, sqlStringGenerationContext )
.toArray( EMPTY_STRING_ARRAY );
}
@Internal
public static List<String> sqlAlterStrings(
Table table,
Dialect dialect,
Metadata metadata,
TableInformation tableInfo,
SqlStringGenerationContext sqlStringGenerationContext) throws HibernateException {
final String tableName = sqlStringGenerationContext.format( new QualifiedTableName(
Identifier.toIdentifier( table.getCatalog(), table.isCatalogQuoted() ),
Identifier.toIdentifier( table.getSchema(), table.isSchemaQuoted() ),
table.getNameIdentifier() )
);
final StringBuilder root = new StringBuilder( dialect.getAlterTableString( tableName ) )
.append( ' ' )
.append( dialect.getAddColumnString() );
final List<String> results = new ArrayList<>();
for ( Column column : table.getColumns() ) {
final ColumnInformation columnInfo = tableInfo.getColumn(
Identifier.toIdentifier( column.getName(), column.isQuoted() )
);
if ( columnInfo == 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() );
}
}
if ( results.isEmpty() ) {
log.debugf( "No alter strings for table : %s", table.getQuotedName() );
}
return results;
}
}

View File

@ -0,0 +1,28 @@
/*
* 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.mapping.Table;
import org.hibernate.tool.schema.extract.spi.TableInformation;
/**
* An object that produces an {@code alter table} statements
* needed to update the definition of a table.
*
* @author Gavin King
*
* @since 6.2
*/
public interface TableMigrator {
String[] getSqlAlterStrings(
Table table,
Metadata metadata,
TableInformation tableInfo,
SqlStringGenerationContext sqlStringGenerationContext);
}

View File

@ -18,6 +18,8 @@ import java.util.Collection;
* An object that produces the SQL required to truncate the tables in a schema. * An object that produces the SQL required to truncate the tables in a schema.
* *
* @author Gavin King * @author Gavin King
*
* @since 6.2
*/ */
@Incubating @Incubating
public interface Cleaner { public interface Cleaner {