Migrating to Hibernate 5
Asked Answered
G

3

4

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)

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.

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
Graft answered 30/9, 2015 at 10:56 Comment(7)
Please post the full stacktrace. And does changing the hbm2ddl.auto parameter has any effect?Engadine
@Engadine It works if I change hbm2ddl.auto to create. But can not afford to do that in production since it drops and recreates the db objects.Graft
In PostgreSQL the name limit defaults to 63 characters. So yes, that's likely to be the issue. Changing it requires a recompile.Indwell
Yeah. I agree with that. Just wanna know if that's a bugger. This bug was similar to a post last week with MySQL as DB. So I this should be an issue with hibernate and not with Postgres. Here's that related question #32701593 which is also caused when migrating to Hibernate 5. So temporarily you can use your current version of hibernate in order to not run into these issues.Engadine
Thanks @CraigRinger. I am not looking forward to compile postgres. The whole thing was working on pre hibernate 5.0 and probably this has something to do with Postgres94Dialect or the ImplicitNamingStrategy implementation. Looking for any work around or to see if it is a known bug or something in similar lines.Graft
I really strongly recommend not recompiling PostgreSQL to change NAMEDATALEN anyway. It'd be a real pain as you'd be unable to use packages, lots of tools wouldn't work, etc. I guess what I meant was "you're pretty much stuck with that limit"Indwell
@CraigRinger Sorry mate. I think I didn't read it quite right. Thanks for clarifying.Graft
G
4

I have addressed the situation with a custom ImplicitNamingStrategy that truncates Hibernate generated identifiers to 64 chars (MAX length for 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

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)

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>
    

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

Graft answered 5/10, 2015 at 7:4 Comment(0)
I
3

try to follow the Migration guide in Hibernate Documentation in this link

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

Indistinct answered 30/9, 2015 at 15:15 Comment(2)
Thanks. I will try that to see if I missed anything vital.Graft
Followed everything in the migration guide. Not much hope. Meanwhile 5.0.2.Final was released. I'll have a shot at it and possibly file a bug.Graft
D
2

The OP's solution may lead to collision (that's why he calls it not scalable, right?). Explicitly supplying all identifiers sound like a terrible idea to me. I'd suggest one of the following

  • provide a Map<String, String> mapping all overlong names to something shorter
  • shorten all overlong names to POSTGRES_IDENTIFIER_MAXLENGTH - N and append N 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 get RefDocVersion_RefDocSourceFileDescr... instead of referencedocumentversion_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 increases readability when used right (use only a couple of abbreviations and use them systematically; provide a list of them).
Disruption answered 17/5, 2016 at 2:57 Comment(1)
Agree with you @maaartinus. Thanks for waking this thread up, providing smarter solutions and making the world a better place.Graft

© 2022 - 2024 — McMap. All rights reserved.