Hibernate 4 -> 5 迁移:NamingStrategy 更改,未找到表

Hibernate 4 -> 5 migration: NamingStrategy changes, Tables not found

我想做什么

我正在尝试从 WildFly 8.2.0 迁移到 WildFly 10.0.0,这意味着我已经(并且想要)从 Hibernate 4.3 迁移到 Hibernate 5.0。

设置

Java 8u40 
Spring 4.1.9 
SQL Server 2012
Wildfly 8.2.0 -> Wildfly 10.0.0 
Hibernate 4.3.6 -> Hibernate 5.0.7

我已经在 SO 上阅读了关于此的 migration guide and i'm hit by the Naming Strategy changes. I many ,但我的似乎有点不同。 Hibernate 抱怨找不到 tables:

INFO [o.h.Version] HHH000412: Hibernate Core {5.0.7.Final}
INFO [o.h.cfg.Environment] HHH000206: hibernate.properties not found
INFO [o.h.cfg.Environment] HHH000021: Bytecode provider name : javassist
INFO [o.h.annotations.common.Version] HCANN000001: Hibernate Commons Annotations {5.0.1.Final}
INFO [o.h.dialect.Dialect] HHH000400: Using dialect: org.hibernate.dialect.SQLServerDialect
INFO [o.h.envers.boot.internal.EnversServiceImpl] Envers integration enabled? : true
INFO [o.h.validator.internal.util.Version] HV000001: Hibernate Validator 5.2.3.Final
INFO [o.h.tool.hbm2ddl.SchemaValidator] HHH000229: Running schema validator
INFO [o.h.t.s.e.i.InformationExtractorJdbcDatabaseMetaDataImpl] HHH000262: Table not found: SEC_AUTHORIZATION_RULES
INFO [o.h.t.s.e.i.InformationExtractorJdbcDatabaseMetaDataImpl] HHH000262: Table not found: SEC_USER
More tables not found ...
INFO [o.h.hql.internal.QueryTranslatorFactoryInitiator] (ServerService Thread Pool -- 62) HHH000397: Using ASTQueryTranslatorFactory

当我切换到 DEBUG 日志记录时,我看到他正在将实体绑定到正确的数据库 table:

DEBUG [o.h.c.a.EntityBinder] Bind entity com.company.user.User on table SEC_USER
DEBUG [o.h.c.Ejb3Column] Binding column: Ejb3Column{table=org.hibernate.mapping.Table(SEC_USER), mappingColumn=ID, insertable=true, updatable=true, unique=false}

令我感到奇怪的是该应用程序可以运行。在此 Table not founds 之后,它不会抱怨架构不正确。该应用程序有效。选择、插入、更新数据工作。

我通过 spring-orm 抽象配置了休眠模式:

@Bean(name = "myEmf")
@DependsOn({"dataSource", "flyway"})
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() {
    LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
    em.setDataSource(dataSource());
    em.setPackagesToScan(new String[]{"com.company.**.*"});
    em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
    em.setJpaProperties(additionalProperties());
    return em;
}

private Properties additionalProperties() {
  Properties propFile = propertiesFile();
  properties.setProperty("hibernate.hbm2ddl.auto", "validate");
  properties.setProperty("hibernate.dialect", "org.hibernate.dialect.SQLServer2012Dialect");
  properties.setProperty("hibernate.show_sql", "false");
  properties.setProperty("hibernate.format_sql", "true");
  properties.setProperty("hibernate.id.new_generator_mappings", "false");
  properties.setProperty("hibernate.use_sql_comments", "false");
  properties.setProperty("hibernate.implicit_naming_strategy", "legacy-jpa");
  return properties;
}

在这个相应的实体中,我有 table 名称和列名称明确命名:

@Entity
@Table(name = "SEC_USER")
public class User extends BaseEntity {

   @Column(name = "LOGIN", nullable = false, unique = true)
   private String login;

问题

我试过的

从 Hibernate 5 开始,NamingStrategy 的逻辑分为两个概念:隐式和物理。您已正确设置隐式命名策略,但尚未设置物理命名策略。 Hibernate 没有提供兼容的,所以你必须自己创建一个:

public class LegacyPhysicalNamingStrategy implements PhysicalNamingStrategy {
    private final Pattern pattern = Pattern.compile("(\p{Lower}+)(\p{Upper}+)");
    public Identifier toPhysicalCatalogName(Identifier name, JdbcEnvironment jdbcEnvironment) {
        return convert(name);
    }

    public Identifier toPhysicalSequenceName(Identifier name, JdbcEnvironment jdbcEnvironmen) {
        return convert(name);
    }

    public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment jdbcEnvironmen) {
        return convert(name);
    }

    public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment jdbcEnvironmen) {
        return convert(name);
    }

    public Identifier toPhysicalSchemaName(Identifier name, JdbcEnvironment jdbcEnvironmen) {
        return convert(name);
    }

    private Identifier convert(Identifier identifier) {
        if (identifier == null || identifier.getText().trim().isEmpty()) {
            return identifier;
        }

        String text = identifier.getText();
        text =  pattern.matcher(text).replaceAll("_").toLowerCase();
        return Identifier.toIdentifier(text, identifier.isQuoted());
    }

}

通过将 属性 hibernate.physical_naming_strategy 设置为上述 class 的完全限定 class 名称来更改为使用此名称。

通过使用最新的 SQL 服务器 JDBC 驱动程序解决了这个问题。 A 有一个 2012 年的旧版本。现在我从 https://www.microsoft.com/en-us/download/details.aspx?id=11774 下载并使用了最新的 JDBC 4.2 (sqljdbc_4.2.6420.100_enu.exe -> sqljdbc42.jar) 和它开始工作了。我什至可以还原 SQL 用户的默认架构更改。

我正在解决同样的问题,但使用 jtds 驱动程序。经过一些研究,我发现 SqlServerDialect 的休眠使用 sp_tables 存储过程来查找 table 的声明。在该 SP 中,第二个参数是模式名称,因此,如果它是 null table 搜索工作正常,如果它是空字符串 - 则不是。

Hibernate 在两种情况下将此参数设置为 null

  • if dialect method getNameQualifierSupport() returns NameQualifierSupport.CATALOG, but all SqlServerDialects return null
  • 如果驱动程序在 'supportsSchemasInTableDefinitions()' 方法中返回 false,但是 jtds returns true.

为了解决这个问题,我决定扩展 SqlServer2012Dialect end override getNameQualifierSupport() 方法。

public class SqlServer2012DialectWithCatalogOnly extends SQLServer2012Dialect {
    @Override
    public NameQualifierSupport getNameQualifierSupport() {
        return NameQualifierSupport.CATALOG;
    }
}

并将属性hibernate.dialect设置为新的classorg.company.SqlServer2012DialectWithCatalogOnly

希望这对您有所帮助...

我想你们都有兴趣知道,我已经记录了以下工单:

https://hibernate.atlassian.net/browse/HHH-11424

将 JTDS 更新为 Microsoft JDBC 驱动程序将为您提供一个实现 Connection.getSchema() 的驱动程序,显然, 解决问题,如所报告的.

但是,如果您确实更新了那个驱动程序,并且您依赖 default_schema 来有效地更改引用的位置表(这样它就不会查看当前用户的默认值 'dbo' 架构,例如)如果不提供自定义的 SchemaNameResolver,您仍然会很不走运。

所有这一切中真正令人烦恼的部分是在验证期间从未应用 implicitSchemaName。

我遇到了类似的问题,我是这样解决的:

jpaProperties.put("hibernate.hbm2ddl.jdbc_metadata_extraction_strategy", "individually")