对于那些使用 Grails 3 和 gorm-hibernate5 来解决这个问题的人,我根据 Graeme 对 grails-data-mapping #880 的评论找到了一个解决方案。
我实现了一个自定义 SchemaManagementTool 并将其添加到应用程序配置中:
hibernate.schema_management_tool = CustomSchemaManagementTool
Hibernate SchemaManagementTool 最终将原始 SQL 命令委托给 GenerationTarget(通常为 GenerationTargetToDatabase),因此我们的目标是提供我们自己的 GenerationTarget。
如果我们可以覆盖 HibernateSchemaManagementTool.buildGenerationTargets,这将是最简单的,但不幸的是,这没有公开。相反,我们需要开发自己的 SchemaCreator 和 SchemaDropper 并在 CustomSchemaManagementTool 中返回它们:
class CustomSchemaManagementTool extends HibernateSchemaManagementTool {
@Override
SchemaCreator getSchemaCreator(Map options) {
return new CustomSchemaCreator(this, getSchemaFilterProvider(options).getCreateFilter())
}
@Override
SchemaDropper getSchemaDropper(Map options) {
return new CustomSchemaDropper(this, getSchemaFilterProvider(options).getDropFilter())
}
// We unfortunately copy this private method from HibernateSchemaManagementTool
private SchemaFilterProvider getSchemaFilterProvider(Map options) {
final Object configuredOption = (options == null) ? null : options.get(AvailableSettings.HBM2DDL_FILTER_PROVIDER)
return serviceRegistry.getService(StrategySelector.class).resolveDefaultableStrategy(
SchemaFilterProvider.class,
configuredOption,
DefaultSchemaFilterProvider.INSTANCE
)
}
}
对于 SchemaCreator 和 SchemaDropper 实现,我们可以分别覆盖 doCreation 和 doDrop。这些本质上是从 Hibernate 实现中复制的,但使用的是 CustomGenerationTarget 而不是 GenerationTargetToDatabase:
class CustomSchemaCreator extends SchemaCreatorImpl {
private final HibernateSchemaManagementTool tool
CustomSchemaCreator(HibernateSchemaManagementTool tool, SchemaFilter schemaFilter) {
super(tool, schemaFilter)
this.tool = tool
}
@Override
void doCreation(Metadata metadata, ExecutionOptions options, SourceDescriptor sourceDescriptor, TargetDescriptor targetDescriptor) {
final JdbcContext jdbcContext = tool.resolveJdbcContext( options.getConfigurationValues() )
final GenerationTarget[] targets = new GenerationTarget[ targetDescriptor.getTargetTypes().size() ]
targets[0] = new CustomGenerationTarget(tool.getDdlTransactionIsolator(jdbcContext), true)
super.doCreation(metadata, jdbcContext.getDialect(), options, sourceDescriptor, targets)
}
}
class CustomSchemaDropper extends SchemaDropperImpl {
private final HibernateSchemaManagementTool tool
CustomSchemaDropper(HibernateSchemaManagementTool tool, SchemaFilter schemaFilter) {
super(tool, schemaFilter)
this.tool = tool
}
@Override
void doDrop(Metadata metadata, ExecutionOptions options, SourceDescriptor sourceDescriptor, TargetDescriptor targetDescriptor) {
final JdbcContext jdbcContext = tool.resolveJdbcContext( options.getConfigurationValues() )
final GenerationTarget[] targets = new GenerationTarget[ targetDescriptor.getTargetTypes().size() ]
targets[0] = new CustomGenerationTarget(tool.getDdlTransactionIsolator(jdbcContext), true)
super.doDrop(metadata, options, jdbcContext.getDialect(), sourceDescriptor, targets)
}
}
在这种情况下,我对创建和删除使用相同的 CustomGenerationTarget,但您可以轻松地将其拆分为不同的类。现在我们终于通过扩展 GenerationTargetToDatabase 并覆盖 accept 方法获得了回报。通过仅在要保留的 SQL 语句上调用 super.accept,您可以过滤掉不需要的 DDL 语句。
class CustomGenerationTarget extends GenerationTargetToDatabase {
CustomGenerationTarget(DdlTransactionIsolator ddlTransactionIsolator, boolean releaseAfterUse) {
super(ddlTransactionIsolator, releaseAfterUse)
}
@Override
void accept(String command) {
if (shouldAccept(command))
super.accept(command)
}
boolean shouldAccept(String command) {
// Custom filtering logic here, e.g.:
if (command =~ /references legacy\.xyz/)
return false
return true
}
}
这不是最优雅的解决方案,但您可以完成工作。
我还尝试提供自己的 SchemaFilterProvider(以及自定义的 SchemaFilter)。不幸的是,这只允许过滤表/命名空间 - 而不是外键。