Switch to sleep mode 5

I port the 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 to deploy (I know this is bad practice, but can not help)

During the initialization of the factory session, it throws the following error. Apparently, the generated alias is too long for Postgres. How do we get around this situation? I tried assigning an @Table(name=..) annotation to get around this, but it gets worse as every relation from this point gets stuck.

 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 
+4
java postgresql hibernate
source share
3 answers

I examined the situation with a custom ImplicitNamingStrategy which truncates Hibernate-generated identifiers to 64 characters (maximum length for Postgres).

Previous versions of Hibernate (4.x) encountered the same error, but they simply ignore it and continue to initialize SessionFactory. However, Hibernate 5.x has a new boot strap API that throws a SchemaManagementException in such cases and interrupts operation. Hibernate logs from my test scripts are inserted below for reference.

Hibernate 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

 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) 

Decision

  • 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> 

This is not a scalable solution, but it helps keep the show running. The permanent solution is to explicitly provide identifiers so that hibernate does not generate really long identifiers. - see the answer written by Maaartinus

+4
source share

try following the migration guide in the hibernation documentation in this link

https://github.com/hibernate/hibernate-orm/blob/5.0/migration-guide.adoc

+3
source share

OP's solution can lead to a collision (why does he call it not scalable, right?). Explicitly supplying all identifiers sounds like a terrible idea to me. I would suggest one of the following

  • provide Map<String, String> matching all overlap names with something short
  • reduce all overlap names to POSTGRES_IDENTIFIER_MAXLENGTH - N and add N characters generated from the hash of the cut part, so the chance of collision is minimized
  • Use some identifier abbreviation function, for example {"Reference" -> "Ref", "Document" -> "Doc", ...} , and apply it to your identifiers before they are processed so that you get RefDocVersion_RefDocSourceFileDescr... instead of referencedocumentversion_referencedocumentsourcefilesstoredescr...
  • Consider using abbreviated names in your code. This is often advised against, since it easily leads to incomprehensible non-meanings, but IMHO improves readability when used correctly (use only a few abbreviations and use them systematically, list them).
+2
source share

All Articles