Переход на спящий режим 5

Я переношу приложение с Hibernate 4.3 на Hibernate 5.0.1-Final. Я использую ImplicitNamingStrategyComponentPathImpl как hibernate.implicit_naming_strategy с Postgres 9.4.4, а моя компания использует hibernate.hbm2ddl.auto = update для развертывания (я знаю, что это плохая практика, но ничего не могу поделать)

Во время инициализации фабрики сеансов выдается следующая ошибка. Очевидно, сгенерированный псевдоним слишком длинный для 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

person ShellDragon    schedule 30.09.2015    source источник
comment
Пожалуйста, опубликуйте полную трассировку стека. И влияет ли изменение параметра hbm2ddl.auto?   -  person Lucky    schedule 30.09.2015
comment
@Lucky Это сработает, если я изменю hbm2ddl.auto на create. Но не может позволить себе сделать это в производстве, так как он удаляет и воссоздает объекты БД.   -  person ShellDragon    schedule 30.09.2015
comment
В PostgreSQL ограничение имени по умолчанию составляет 63 символа. Так что да, скорее всего дело в этом. Его изменение требует перекомпиляции.   -  person Craig Ringer    schedule 30.09.2015
comment
Ага. Я согласен с этим. Просто хочу знать, не жук ли это. Эта ошибка была похожа на пост на прошлой неделе с MySQL в качестве БД. Так что это должно быть проблемой с спящим режимом, а не с Postgres. Вот этот связанный вопрос stackoverflow.com/questions/32701093/, который также возникает при переходе на Hibernate 5. Поэтому временно вы можете использовать текущую версию спящего режима, чтобы не сталкиваться с этими проблемами.   -  person Lucky    schedule 30.09.2015
comment
Спасибо @CraigRinger. Я не собираюсь компилировать postgres. Все это работало на pre hibernate 5.0, и, вероятно, это как-то связано с реализацией Postgres94Dialect или ImplicitNamingStrategy. Ищите какие-либо обходные пути или чтобы узнать, является ли это известной ошибкой или чем-то подобным.   -  person ShellDragon    schedule 30.09.2015
comment
Я действительно настоятельно рекомендую не перекомпилировать PostgreSQL для изменения NAMEDATALEN в любом случае. Это будет настоящая боль, так как вы не сможете использовать пакеты, многие инструменты не будут работать и т. д. Думаю, я имел в виду, что вы в значительной степени застряли с этим ограничением.   -  person Craig Ringer    schedule 30.09.2015
comment
@CraigRinger Извини, приятель. Думаю, я не совсем правильно прочитал. Спасибо за разъяснения.   -  person ShellDragon    schedule 01.10.2015


Ответы (3)


Я исправил ситуацию с помощью пользовательского ImplicitNamingStrategy, который усекает сгенерированные Hibernate идентификаторы до 64 символов (максимальная длина для Postgres).

Предыдущие версии Hibernate(4.x) сталкивались с той же ошибкой, но просто игнорировали ее и продолжали инициализацию SessionFactory. Однако в Hibernate 5.x есть новый API-интерфейс начальной загрузки, который в таких случаях генерирует исключение SchemaManagementException и прерывает работу. Журналы 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

Спящий режим 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

  • Пользовательская стратегия неявного именования

    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() ) ) );
    
    }}
    
  • постоянство.xml

    <properties>
        <property name="hibernate.implicit_naming_strategy" value="org.foobar.persistence.PGConstrainedImplicitNamingStrategy"/>
    </properties>
    

Это совсем не масштабируемое решение, но оно помогает поддерживать работу шоу. Постоянным решением было бы явно указывать идентификаторы, чтобы спящий режим не генерировал очень длинные идентификаторы. - см. ответ, написанный мааартинус

person ShellDragon    schedule 05.10.2015

попробуйте следовать руководству по миграции в документации Hibernate по этой ссылке

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

person Spartan    schedule 30.09.2015
comment
Спасибо. Я попробую это, чтобы увидеть, не пропустил ли я что-нибудь важное. - person ShellDragon; 01.10.2015
comment
Выполнил все в руководстве по миграции. Не так много надежд. Тем временем вышла версия 5.0.2.Final. Я попробую и, возможно, зарегистрирую ошибку. - person ShellDragon; 04.10.2015

Решение OP может привести к коллизии (поэтому он называет его не масштабируемым, верно?). Явно указывать все идентификаторы мне кажется ужасной идеей. Я бы предложил один из следующих

  • предоставить Map<String, String> отображение всех слишком длинных имен на что-то более короткое
  • сократить все слишком длинные имена до POSTGRES_IDENTIFIER_MAXLENGTH - N и добавить N символов, сгенерированных из хэша отрезанной части, чтобы свести к минимуму вероятность коллизий
  • Используйте какую-нибудь функцию сокращения идентификатора, например {"Reference" -> "Ref", "Document" -> "Doc", ...}, и примените ее к своим идентификаторам до их обработки, чтобы получить RefDocVersion_RefDocSourceFileDescr... вместо referencedocumentversion_referencedocumentsourcefilesstoredescr....
  • Рассмотрите возможность использования сокращенных имен в самом коде. Это часто не рекомендуется, так как это легко приводит к непонятной чепухе, но ИМХО, это повышает читабельность при правильном использовании (используйте только пару сокращений и используйте их систематически; приведите их список).
person maaartinus    schedule 17.05.2016
comment
Согласен с тобой @maaartinus. Спасибо, что разбудили эту ветку, предложили более разумные решения и сделали мир лучше. - person ShellDragon; 17.05.2016