Spring Boot 升级到Sping Boot 3(Hibernate 6)后使用自定义SchemaFilterProvider的“Schema-validation:missing table”

txu3uszq  于 2023-11-17  发布在  Spring
关注(0)|答案(1)|浏览(204)

当升级到Sping Boot 3和Hibernate 6时,似乎属性hibernate.hbm2ddl.schema_filter_provider无法正常工作。我有一个SchemaFilterProvider的自定义实现,它将my_table从验证中排除,我设置hibernate.hbm2ddl.schema_filter_provider属性以使用它。我得到这个错误:org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: missing table [my_table]
调试后,我发现DefaultSchemaFilter.INSTANCE正在使用,上面的属性没有被考虑:
验证在这里完成:

protected void validateTables(
            Metadata metadata,
            DatabaseInformation databaseInformation,
            ExecutionOptions options,
            ContributableMatcher contributableInclusionFilter,
            Dialect dialect, Namespace namespace) {

        final NameSpaceTablesInformation tables = databaseInformation.getTablesInformation( namespace );
        for ( Table table : namespace.getTables() ) {
            if ( options.getSchemaFilter().includeTable( table )
                    && table.isPhysicalTable()
                    && contributableInclusionFilter.matches( table ) ) {
                validateTable(
                        table,
                        tables.getTableInformation( table ),
                        metadata,
                        options,
                        dialect
                );
            }
        }
    }

字符串
options在这里创建:

public static ExecutionOptions buildExecutionOptions(
            final Map<String,Object> configurationValues,
            final ExceptionHandler exceptionHandler) {
        return buildExecutionOptions(
                configurationValues,
                DefaultSchemaFilter.INSTANCE,
                exceptionHandler
        );
    }


这意味着DefaultSchemaFilter.INSTANCE将始终处于使用状态。
Hibernate有一个没有响应的公开bug:https://hibernate.atlassian.net/browse/HBX-2476
如何将my_table从验证中排除?

ddrv8njm

ddrv8njm1#

自从https://hibernate.atlassian.net/browse/HBX-2476被打开以来已经快一年了,它仍然没有解决。我要分享一个讨厌的解决方法,至少对我有用,直到bug被修复。

临时解决方案

使用反射,设置(静态final)DefaultSchemaFilter.INSTANCE的值以引用在早期Hibernate版本中使用的自定义SchemaFilter的示例。这样,当Hibernate将使用“硬编码”DefaultSchemaFilter.INSTANCE作为过滤器时,它实际上将使用我们的自定义过滤器实现。请注意,自定义实现extends DefaultSchemaFilter
例如,假设我们有这样一个自定义实现,它跳过了PostgreSQL临时表的验证:

public class CustomSchemaFilter extends DefaultSchemaFilter {

    public static final CustomSchemaFilter INSTANCE = new CustomSchemaFilter();

    @Override
    public boolean includeTable( Table table ) {
        return !"pg_temp".equals(table.getSchema());
    }
}

字符串
我们需要设置DefaultSchemaFilter.INSTANCE = CustomSchemaFilter.INSTANCE
反射代码的具体实现取决于Java版本。对于Java 17,基于https://stackoverflow.com/a/56043252/9061851,我使用了以下代码:

try {
    Field defaultSchemaFilterInstanceField = DefaultSchemaFilter.class.getDeclaredField("INSTANCE");
    MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(Field.class, MethodHandles.lookup());
    VarHandle modifiers = lookup.findVarHandle(Field.class, "modifiers", int.class);
    modifiers.set(defaultSchemaFilterInstanceField, defaultSchemaFilterInstanceField.getModifiers() & ~Modifier.FINAL);
    defaultSchemaFilterInstanceField.setAccessible(true);
    defaultSchemaFilterInstanceField.set(null, CustomSchemaFilter.INSTANCE);
} catch (NoSuchFieldException | IllegalAccessException e) {
    throw new RuntimeException(e);
}


但是,这需要将--add-opens=java.base/java.lang.reflect=ALL-UNNAMED添加到JVM运行参数中。
这段代码应该在Hibernate运行其验证之前调用。最简单的选择是在加载Sping Boot 之前从main()调用它。

提示:这只是一个临时的解决方案。

相关问题