postgresql 迁移到 Hibernate 5
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/32864327/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Migrating to Hibernate 5
提问by ShellDragon
I am migrating an application from Hibernate 4.3 to Hibernate 5.0.1-Final
I use ImplicitNamingStrategyComponentPathImpl
as my hibernate.implicit_naming_strategy
with Postgres 9.4.4 and my company uses hibernate.hbm2ddl.auto = update
for deployment ( I know it is a bad practice but cant help it)
我正在将应用程序从 Hibernate 4.3 迁移到 Hibernate 5.0.1-Final 我ImplicitNamingStrategyComponentPathImpl
用作我hibernate.implicit_naming_strategy
的 Postgres 9.4.4 并且我的公司hibernate.hbm2ddl.auto = update
用于部署(我知道这是一个不好的做法,但无能为力)
While the session factory initializes, it throws the below error. Apparently the generated alias is too long for Postgres. How do we go about this situation? I have tried assigning @Table(name=..)
annotation to work around this it but it is getting worse as every relationship from that point gets screwd.
当会话工厂初始化时,它会抛出以下错误。显然,生成的别名对于 Postgres 来说太长了。我们如何处理这种情况?我曾尝试分配@Table(name=..)
注释来解决这个问题,但随着从那时起的每一个关系都被搞砸,情况变得越来越糟。
Caused by: org.hibernate.tool.schema.spi.SchemaManagementException: Unable to execute schema management to JDBC target [create table public.ReferenceDocumentVersion_ReferenceDocumentSourceFilesStoreDescriptor (ReferenceDocumentVersion_unid uuid not null, sourceFilesStore_filesDescriptorMap_unid uuid not null, filesDescriptorMap_KEY text not null, primary key (ReferenceDocumentVersion_unid, filesDescriptorMap_KEY))]
at org.hibernate.tool.schema.internal.TargetDatabaseImpl.accept(TargetDatabaseImpl.java:59)
at org.hibernate.tool.schema.internal.SchemaMigratorImpl.applySqlString(SchemaMigratorImpl.java:371)
at org.hibernate.tool.schema.internal.SchemaMigratorImpl.applySqlStrings(SchemaMigratorImpl.java:360)
at org.hibernate.tool.schema.internal.SchemaMigratorImpl.createTable(SchemaMigratorImpl.java:181)
at org.hibernate.tool.schema.internal.SchemaMigratorImpl.doMigrationToTargets(SchemaMigratorImpl.java:134)
at org.hibernate.tool.schema.internal.SchemaMigratorImpl.doMigration(SchemaMigratorImpl.java:59)
at org.hibernate.tool.hbm2ddl.SchemaUpdate.execute(SchemaUpdate.java:129)
at org.hibernate.tool.hbm2ddl.SchemaUpdate.execute(SchemaUpdate.java:97)
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:481)
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:444)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:802)
... 29 more
Caused by: org.postgresql.util.PSQLException: ERROR: relation "referencedocumentversion_referencedocumentsourcefilesstoredescr" already exists
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2182)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1911)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:173)
at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:618)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:454)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeUpdate(AbstractJdbc2Statement.java:382)
at org.apache.tomcat.dbcp.dbcp.DelegatingStatement.executeUpdate(DelegatingStatement.java:228)
at org.apache.tomcat.dbcp.dbcp.DelegatingStatement.executeUpdate(DelegatingStatement.java:228)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at net.bull.javamelody.JdbcWrapper.doExecute(JdbcWrapper.java:404)
at net.bull.javamelody.JdbcWrapper$StatementInvocationHandler.invoke(JdbcWrapper.java:129)
at net.bull.javamelody.JdbcWrapper$DelegatingInvocationHandler.invoke(JdbcWrapper.java:286)
at com.sun.proxy.$Proxy93.executeUpdate(Unknown Source)
at org.hibernate.tool.schema.internal.TargetDatabaseImpl.accept(TargetDatabaseImpl.java:56)
... 39 more
采纳答案by ShellDragon
I have addressed the situation with a custom ImplicitNamingStrategy
that truncates Hibernate generated identifiers to 64 chars (MAX length for Postgres).
我已经通过将ImplicitNamingStrategy
Hibernate 生成的标识符截断为 64 个字符(Postgres 的最大长度)的自定义解决了这种情况。
Previous versions of Hibernate(4.x) have encountered the same error but they just ignores it and proceeds with initializing the SessionFactory. However, Hibernate 5.x has a new boot strap API which throws a SchemaManagementException in such cases and aborts. Hibernate logs from my test scenarios are pasted below for reference.
以前版本的 Hibernate(4.x) 遇到了同样的错误,但他们只是忽略它并继续初始化 SessionFactory。但是,Hibernate 5.x 有一个新的引导程序 API,它在这种情况下会抛出 SchemaManagementException 并中止。下面粘贴了我的测试场景中的 Hibernate 日志以供参考。
Hibernate 4.X
休眠 4.X
INFO: HHH000396: Updating schema
Oct 04, 2015 1:38:00 PM org.hibernate.tool.hbm2ddl.DatabaseMetadata getTableMetadata
INFO: HHH000262: Table not found: ReferenceDocumentVersionEntityWithAReallyReallyReallyLongNameBeyondPostGres
Oct 04, 2015 1:38:00 PM org.hibernate.tool.hbm2ddl.DatabaseMetadata getTableMetadata
INFO: HHH000262: Table not found: ReferenceDocumentVersionEntityWithAReallyReallyReallyLongNameBeyondPostGres
Oct 04, 2015 1:38:00 PM org.hibernate.tool.hbm2ddl.DatabaseMetadata getTableMetadata
INFO: HHH000262: Table not found: ReferenceDocumentVersionEntityWithAReallyReallyReallyLongNameBeyondPostGres
Oct 04, 2015 1:38:00 PM org.hibernate.tool.hbm2ddl.SchemaUpdate execute
ERROR: HHH000388: Unsuccessful: create table ReferenceDocumentVersionEntityWithAReallyReallyReallyLongNameBeyondPostGres (unid uuid not null, path text, primary key (unid))
Oct 04, 2015 1:38:00 PM org.hibernate.tool.hbm2ddl.SchemaUpdate execute
ERROR: ERROR: relation "referencedocumentversionentitywithareallyreallyreallylongnamebe" already exists
Oct 04, 2015 1:38:00 PM org.hibernate.tool.hbm2ddl.SchemaUpdate execute
INFO: HHH000232: Schema update complete
Hibernate 5.0.2.Final
休眠 5.0.2.Final
Oct 04, 2015 1:39:16 PM org.hibernate.tool.hbm2ddl.SchemaUpdate execute
INFO: HHH000228: Running hbm2ddl schema update
Oct 04, 2015 1:39:16 PM org.hibernate.tool.schema.extract.internal.InformationExtractorJdbcDatabaseMetaDataImpl processGetTableResults
INFO: HHH000262: Table not found: ReferenceDocumentVersionEntityWithAReallyReallyReallyLongNameBeyondPostGres
Oct 04, 2015 1:39:16 PM org.hibernate.tool.schema.extract.internal.InformationExtractorJdbcDatabaseMetaDataImpl processGetTableResults
INFO: HHH000262: Table not found: ReferenceDocumentVersionEntityWithAReallyReallyReallyLongNameBeyondPostGres
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.813 sec <<< FAILURE!
testApp(org.foobar.AppTest) Time elapsed: 0.788 sec <<< ERROR!
javax.persistence.PersistenceException: [PersistenceUnit: org.foobar.persistence.default] Unable to build Hibernate SessionFactory
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.persistenceException(EntityManagerFactoryBuilderImpl.java:877)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:805)
at org.hibernate.jpa.HibernatePersistenceProvider.createEntityManagerFactory(HibernatePersistenceProvider.java:58)
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:55)
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:39)
at org.foobar.AppTest.testApp(AppTest.java:18)
Solution
解决方案
Custom ImplicitNamingStrategy
package org.foobar.persistence; import org.hibernate.boot.model.naming.Identifier; import org.hibernate.boot.model.naming.ImplicitNamingStrategyComponentPathImpl; import org.hibernate.boot.spi.MetadataBuildingContext; public class PGConstrainedImplicitNamingStrategy extends ImplicitNamingStrategyComponentPathImpl { private static final int POSTGRES_IDENTIFIER_MAXLENGTH = 63; public static final PGConstrainedImplicitNamingStrategy INSTANCE = new PGConstrainedImplicitNamingStrategy(); public PGConstrainedImplicitNamingStrategy() { } @Override protected Identifier toIdentifier(String stringForm, MetadataBuildingContext buildingContext) { return buildingContext.getMetadataCollector() .getDatabase() .getJdbcEnvironment() .getIdentifierHelper() .toIdentifier( stringForm.substring( 0, Math.min( POSTGRES_IDENTIFIER_MAXLENGTH, stringForm.length() ) ) ); }}
persistence.xml
<properties> <property name="hibernate.implicit_naming_strategy" value="org.foobar.persistence.PGConstrainedImplicitNamingStrategy"/> </properties>
自定义隐式命名策略
package org.foobar.persistence; import org.hibernate.boot.model.naming.Identifier; import org.hibernate.boot.model.naming.ImplicitNamingStrategyComponentPathImpl; import org.hibernate.boot.spi.MetadataBuildingContext; public class PGConstrainedImplicitNamingStrategy extends ImplicitNamingStrategyComponentPathImpl { private static final int POSTGRES_IDENTIFIER_MAXLENGTH = 63; public static final PGConstrainedImplicitNamingStrategy INSTANCE = new PGConstrainedImplicitNamingStrategy(); public PGConstrainedImplicitNamingStrategy() { } @Override protected Identifier toIdentifier(String stringForm, MetadataBuildingContext buildingContext) { return buildingContext.getMetadataCollector() .getDatabase() .getJdbcEnvironment() .getIdentifierHelper() .toIdentifier( stringForm.substring( 0, Math.min( POSTGRES_IDENTIFIER_MAXLENGTH, stringForm.length() ) ) ); }}
持久化文件
<properties> <property name="hibernate.implicit_naming_strategy" value="org.foobar.persistence.PGConstrainedImplicitNamingStrategy"/> </properties>
This is not a scalable solution at all but helps to keep the show running. The permanent solution would be to explicitly supply identifiers so that hibernate does not generate really long identifiers.- see the answer written by maaartinus
这根本不是一个可扩展的解决方案,但有助于保持节目的运行。永久的解决方案是显式提供标识符,以便 hibernate 不会生成非常长的标识符。- 见maaartinus写的答案
回答by Spartan
try to follow the Migration guide in Hibernate Documentation in this link
尝试按照此链接中的 Hibernate 文档中的迁移指南进行操作
https://github.com/hibernate/hibernate-orm/blob/5.0/migration-guide.adoc
https://github.com/hibernate/hibernate-orm/blob/5.0/migration-guide.adoc
回答by maaartinus
The OP's solution may lead to collision (that's why he calls it not scalable, right?). Explicitly supplying allidentifiers sound like a terrible idea to me. I'd suggest one of the following
OP 的解决方案可能会导致冲突(这就是为什么他称其为不可扩展的,对吗?)。显式提供所有标识符对我来说听起来是个糟糕的主意。我建议以下之一
- provide a
Map<String, String>
mapping all overlong names to something shorter - shorten all overlong names to
POSTGRES_IDENTIFIER_MAXLENGTH - N
and appendN
characters generated from the hash of the cut away part, so the probability of collisions gets minimized - Use some identifier abbreviating function like
{"Reference" -> "Ref", "Document" -> "Doc", ...}
and apply it to your identifiers before they get processed, so that you getRefDocVersion_RefDocSourceFileDescr...
instead ofreferencedocumentversion_referencedocumentsourcefilesstoredescr...
. - Consider using abbreviated names in you code itself. This is often advised against, as it easily leads to incomprehensible non-sense, but IMHO it increasesreadability when used right (use only a couple of abbreviations and use them systematically; provide a list of them).
- 提供
Map<String, String>
所有超长名称到较短名称的映射 - 将所有过长的名称缩短为
POSTGRES_IDENTIFIER_MAXLENGTH - N
并附加N
从切掉部分的哈希生成的字符,因此冲突的可能性最小化 - 使用一些标识符缩写函数,例如
{"Reference" -> "Ref", "Document" -> "Doc", ...}
在它们被处理之前将它应用到你的标识符上,这样你就会得到RefDocVersion_RefDocSourceFileDescr...
而不是referencedocumentversion_referencedocumentsourcefilesstoredescr...
. - 考虑在您的代码中使用缩写名称。通常不建议这样做,因为它很容易导致难以理解的无意义,但恕我直言,如果使用得当,它会增加可读性(仅使用几个缩写并系统地使用它们;提供它们的列表)。