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 found
s 之后,它不会抱怨架构不正确。该应用程序有效。选择、插入、更新数据工作。
我通过 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;
问题
- 如何使 table 未找到的日志消息消失?
- 如果我有 table 个明确命名的名字,为什么它们会出现?
- 他为什么不抱怨列名?
- 为什么他看起来工作正常?
我试过的
- 正在将 Spring 4.1.9 升级到 4.2.5 says he has support for Hibernate 5
- 根据this
设置hibernate.implicit_naming_strategy为legacy-jpa
手动设置默认架构并分配角色 db_owner。请注意,在使用 hibernate 4 之前,我从来没有这样做过。
我对 hibernate 进行了一些调试,我在 InformationExtractorJdbcDatabaseMetaDataImpl.java 中发现 hibernate 看不到目录(不管是什么)和模式。至少我认为他应该看到架构。请参见下面的屏幕截图:目录和架构为空。
从 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")
我想做什么
我正在尝试从 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
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 found
s 之后,它不会抱怨架构不正确。该应用程序有效。选择、插入、更新数据工作。
我通过 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;
问题
- 如何使 table 未找到的日志消息消失?
- 如果我有 table 个明确命名的名字,为什么它们会出现?
- 他为什么不抱怨列名?
- 为什么他看起来工作正常?
我试过的
- 正在将 Spring 4.1.9 升级到 4.2.5 says he has support for Hibernate 5
- 根据this 设置hibernate.implicit_naming_strategy为legacy-jpa
手动设置默认架构并分配角色 db_owner。请注意,在使用 hibernate 4 之前,我从来没有这样做过。
我对 hibernate 进行了一些调试,我在 InformationExtractorJdbcDatabaseMetaDataImpl.java 中发现 hibernate 看不到目录(不管是什么)和模式。至少我认为他应该看到架构。请参见下面的屏幕截图:目录和架构为空。
从 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()
returnsNameQualifierSupport.CATALOG
, but all SqlServerDialects returnnull
- 如果驱动程序在 'supportsSchemasInTableDefinitions()' 方法中返回
false
,但是jtds
returnstrue
.
为了解决这个问题,我决定扩展 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")