问题
I changed the sessionFactory bean creation in an existing application because of various reasons from :
<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>
to:
<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>
this is my newly added persistence-unit:
<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>
After that I received in the transaction block in many Junit integration tests an InvalidComponentException. For example:
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 );
}
}
} );
the exception is thrown at:
catch ( InvalidComponentException e ) { fail ( "Invalid Component: " + 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)
...
the Problem was at sessionFactory.getCurrentSession ().save ( c ); instead i had to call:
Session session = sessionFactory.getCurrentSession ();
session.save ( c );
session.flush ();
Now I know that the HibernateTransactionManager works diffrent then the JpaTransactionManager. But if i have to call the session.flush() manually everytime, that is a pain and preformance killer. notably I´m getting the same problem when I try to remove an objekt. I have to merge then remove or split into 2 transactions.
How do I have to configurate the JpaTransactionManager to solve this problem without adding session.flush()? I tried in my newly added persistence.xml, but that didn´t help. I would be thankful and It would be great for any help.
I´ve just checked what my actual flushmode is, with both variants, and it is AUTO.
And I've tried to call em.setFlushMode(FlushModeType.COMMIT)
but the flushmode is not changed at all it is allways AUTO and with ((Session)em.getDelegate()).setFlushMode(FlushMode.MANUAL)
I'm getting this exception:
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:
Notice that you give us an incomplete stacktrace since you only print the exception message, and the assertion error, but do not show us the real exception stack (do e.printStackTrace() or something in your catch). Anyway this looks like to be a business, non technical/hibernate/jpa exception so i don't think it will bring any solution.
If it works with flush() this probably means that you are now in flushmode=MANUAL, while you were probably in flushmode=AUTO.
The available options for Hibernate are described here: org.hibernate.FlushMode Btw it seems the manual flushmode is not part of the JPA specification: javax.persistence.FlushModeType
So you may try to check what is you actual flushmode. For exemple by doing:
em.getFlushMode()
You should also check that result:
((Session)em.getDelegage()).getFlushMode()
Because you are importing an Hibernate config file hibernate.cfg.xml. Perhaps there are some bugs in hibernate that do not set an appropriate flushmode or something in some specific cases like yours.
Check also if your flushmode is consistent on all your application (when you don't explicitly modify it...), because if you are using Spring @Transactionnal attributes with "readonly=true" or some things like that, the hibernate flushmode is automagically set to manual. And if you don't use these annotations it's perhaps a good idea instead of handling transactions in transaction callback system from 2003.
Actually it seems that there is a JpaTemplate.
See this code: http://massapi.com/source/apache-camel-2.5.0/components/camel-jpa/src/main/java/org/apache/camel/component/jpa/JpaTemplateTransactionStrategy.java.html
According to the Javadoc, TransactionTemplate is done for dealing with low-level resources like JDBC. You should use the template that fits to your needs or you may have strange side effects.
来源:https://stackoverflow.com/questions/8503820/migrating-from-hibernatetransactionmanager-to-jpatransactionmanager