Переход с HibernateTransactionManager на JpaTransactionManager
Я изменил создание bean-компонента sessionFactory в существующем приложении по разным причинам:
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mappingLocations" ref="hibernateMappingFiles" />
<property name="hibernateProperties" ref="hibernateProperties" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
чтобы:
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="spring-jpa" />
</bean>
<bean id="sessionFactory" factory-bean="entityManagerFactory" factory-method="getSessionFactory" />
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
это мой недавно добавленный модуль персистентности:
<persistence-unit name="spring-jpa">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect" />
<property name="hibernate.show_sql" value="false" />
<property name="hibernate.format_sql" value="true" />
<property name="hibernate.hbm2ddl.auto" value="none" />
<property name="javax.persistence.validation.mode" value="none" />
<property name="hibernate.jdbc.use_streams_for_binary"
value="true" />
<property name="hibernate.jdbc.fetch_size" value="100" />
<property name="hibernate.current_session_context_class" value="org.springframework.orm.hibernate3.SpringSessionContext" />
<property name="hibernate.ejb.cfgfile" value="/hibernate.cfg.xml" />
</properties>
</persistence-unit>
После этого во многих интеграционных тестах Junit я получил в блоке транзакций исключение InvalidComponentException. Например:
createFolder ( "/examples/inheritance" );
transactionTemplate.execute ( new TransactionCallbackWithoutResult () {
@Override
protected void doInTransactionWithoutResult ( TransactionStatus status ) {
for ( Resource r : Arrays.asList ( abstractComponent, concreteAComponent, concreteBComponent ) ) {
ComponentVersion c;
try {
c = componentService.validateAssignValues ( new ModelSource ( loadResource ( r ) ), null, TechnicalVersion.INITIAL_VERSION,
ModelVersion.INITIAL_VERSION, ModelVersionIncrement.MAJOR );
} catch ( InvalidComponentException e ) {
fail ( "Invalid Component: " + e.getMessage () );
return;
} catch ( IOException e ) {
fail ( "IOException: " + e.getMessage () );
return;
}
sessionFactory.getCurrentSession ().save ( c.getModelElement () );
sessionFactory.getCurrentSession ().save ( c );
}
}
} );
исключение выдается в:
catch (InvalidComponentException e) {fail ("Неверный компонент: " + e.getMessage ());
java.lang.AssertionError: Invalid Component: The inherited component does not exist: name=/examples/inheritance/AbstractComponent
at org.junit.Assert.fail(Assert.java:93)
at info.server.component.FindComponentHierarchyTest$1.doInTransactionWithoutResult(FindComponentHierarchyTest.java:83)
at org.springframework.transaction.support.TransactionCallbackWithoutResult.doInTransaction(TransactionCallbackWithoutResult.java:33)
...
Проблема была в sessionFactory.getCurrentSession ().save ( c); вместо этого я должен был позвонить:
Session session = sessionFactory.getCurrentSession ();
session.save ( c );
session.flush ();
Теперь я знаю, что HibernateTransactionManager работает иначе, чем JpaTransactionManager. Но если мне нужно каждый раз вызывать session.flush() вручную, это убивает боль и преформ. в частности, у меня возникает та же проблема, когда я пытаюсь удалить объект. Я должен объединить, а затем удалить или разделить на 2 транзакции.
Как мне настроить JpaTransactionManager для решения этой проблемы без добавления session.flush()? Я попробовал в своем недавно добавленном файле persistence.xml, но это не помогло. Я был бы благодарен, и было бы здорово за любую помощь.
Я только что проверил, каков мой настоящий режим сброса, с обоими вариантами, и это АВТО. И я пытался позвонить em.setFlushMode(FlushModeType.COMMIT)
но флешмод вообще не меняется, он всегда АВТО и с ((Session)em.getDelegate()).setFlushMode(FlushMode.MANUAL)
Я получаю это исключение:
org.hibernate.SessionException: Session is closed!
at org.hibernate.impl.AbstractSessionImpl.errorIfClosed(AbstractSessionImpl.java:72)
at org.hibernate.impl.SessionImpl.setFlushMode(SessionImpl.java:1423)
at info.novatec.np.server.component.FindComponentHierarchyTest.importComponents(FindComponentHierarchyTest.java:78)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:27)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:48)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
at org.junit.runners.ParentRunner.run(ParentRunner.java:292)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
1 ответ
Обратите внимание, что вы даете нам неполную трассировку стека, поскольку вы печатаете только сообщение об исключении и ошибку подтверждения, но не показывает нам реальный стек исключений (выполните e.printStackTrace() или что-то еще в вашем улове). В любом случае, похоже, что это бизнес, а не техническое /hibernate/jpa исключение, поэтому я не думаю, что это принесет какое-либо решение.
Если он работает с flush(), это, вероятно, означает, что вы сейчас находитесь в режиме flushmode=MANUAL, а вы, вероятно, находитесь в режиме flushmode=AUTO.
Доступные опции для Hibernate описаны здесь: org.hibernate.FlushMode Кстати, кажется, что ручной режим сброса не является частью спецификации JPA: javax.persistence.FlushModeType
Таким образом, вы можете попробовать проверить, какой у вас режим. Например, выполнив:
em.getFlushMode()
Вы также должны проверить этот результат:
((Session)em.getDelegage()).getFlushMode()
Потому что вы импортируете конфигурационный файл Hibernate hibernate.cfg.xml. Возможно, в hibernate есть некоторые ошибки, которые не устанавливают соответствующий flushmode или что-то в некоторых конкретных случаях, таких как ваш.
Проверьте также, является ли ваш flushmode одинаковым для всех ваших приложений (если вы явно не изменяете его...), потому что, если вы используете атрибуты Spring @Transactionnal с "readonly=true" или что-то в этом роде, режим hibernate flushmode автоматически устанавливается на ручной. И если вы не используете эти аннотации, возможно, это хорошая идея вместо обработки транзакций в системе обратного вызова транзакций с 2003 года.
На самом деле кажется, что есть JpaTemplate.
Смотрите этот код: http://massapi.com/source/apache-camel-2.5.0/components/camel-jpa/src/main/java/org/apache/camel/component/jpa/JpaTemplateTransactionStrategy.java.html
Согласно Javadoc, TransactionTemplate предназначен для работы с низкоуровневыми ресурсами, такими как JDBC. Вы должны использовать шаблон, который соответствует вашим потребностям, или у вас могут быть странные побочные эффекты.