I am migrating a legacy Spring 3, Hibernate 3, JTA on JBoss 5 application to the latest versions (Spring 4.1.0.RELEASE, Hibernate 4.3.6.Final, JBoss Wildfly 8.1). It seems that Spring 4.1.0.RELEASE and Hibernate 4.3.6.Final do NOT work together in supporting transactions for write operations with the LocalSessionFactoryBean and the HibernateTransactionManager as configured below. Read-only get operations appear to be working ok.
To migrate, org.springframework.orm.hibernate3.support.HibernateDaoSupport has been updated to org.springframework.orm.hibernate4.support.HibernateDaoSupport. The code in question is trying to save with getHibernateTemplate().saveOrUpdate(myObject); where myObject is the object to save (that works in Spring3 + Hibernate 3). The code compiles but at runtime I see the code throw an exception for the call at:
Questions:
Is the opening/closing of Hibernate sessions triggered by the getSessionFactory().getCurrentSession() call an issue (performance or otherwise)? If so, is there something in the configuration that can be set to avoid it?
HibernateTemplate always sets the newly opened session to FlushMode.MANUAL while handling the exception. And, in the debugger, I see that this fails the check for write operations at:
Note that setting getHibernateTemplate().setCheckWriteOperations(false); bypasses the Spring check but the getHibernateTemplate().saveOrUpdate(myObject) call silently fails in the Hibernate code without throwing any exceptions and nothing gets written to the database. What config change(s) do I need to make to get the write operations to commit?
Bean Definitions:
Here're the relevant bean definition snippets from the application-context.xml Spring config file:
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<tx:annotation-driven/>
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean" lazy-init="false">
<property name="jndiName" value="java:jboss/datasources/jdbc/my-srvr"/>
<property name="cache">
<value>false</value>
</property>
<property name="proxyInterface">
<value>javax.sql.DataSource</value>
</property>
</bean>
<!-- Hibernate SessionFactory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" lazy-init="true">
<property name="dataSource" ref="dataSource"/>
<property name="mappingResources">
<list>
<value>com/mydomain/dao/Hib.hib.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</prop>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.generate_statistics">false</prop>
<!-- JTA -->
<prop key="hibernate.transaction.factory_class">org.hibernate.engine.transaction.internal.jta.JtaTransactionFactory</prop>
<prop key="hibernate.flushMode">AUTO</prop>
<prop key="jta.UserTransaction">java:jboss/UserTransaction</prop>
<prop key="jta.TransactionManager">java:jboss/TransactionManager</prop>
<prop key="hibernate.transaction.jta.platform">org.hibernate.engine.transaction.jta.platform.internal.JBossAppServerJtaPlatform</prop>
<prop key="hibernate.current_session_context_class">org.hibernate.context.internal.JTASessionContext</prop>
<!--prop key="hibernate.transaction.manager_lookup_class">
org.hibernate.transaction.JBossTransactionManagerLookup
</prop-->
<!-- Turn caching off to focus on JTA issues-->
<prop key="hibernate.cache.use_second_level_cache">false</prop>
<prop key="hibernate.cache.use_query_cache">false</prop>
<!--prop key="hibernate.cache.provider_class">net.sf.ehcache.hibernate.SingletonEhCacheProvider</prop-->
<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop>
<prop key="net.sf.ehcache.configurationResourceName">sample-ehcache.xml</prop>
</props>
</property>
<!--No equivalent class in Spring4; comment out for now-->
<!--property name="eventListeners">
<map>
<entry key="merge">
<bean class="org.springframework.orm.hibernate3.support.IdTransferringMergeEventListener"/>
</entry>
</map>
</property-->
</bean>
Note: An important change from the legacy bean definition is the change from org.springframework.transaction.jta.JtaTransactionManager to org.springframework.orm.hibernate4.HibernateTransactionManager.
JNDI View
Once deployed, the JNDI View in JBoss Wildfly is as below (of course the object references change every deployment):
java:jboss
TransactionManager TransactionManagerDelegate@49e6e9c8
TransactionSynchronizationRegistry TransactionSynchronizationRegistryImple@40cd0746
UserTransaction UserTransaction
jaas java:jboss/jaas/ Context proxy
So I finally got the write operations to work in the legacy code. Here are the steps I followed to get the code working:
Ensure that you are using the Hibernate specific transaction manager org.springframework.orm.hibernate4.HibernateTransactionManager and NOT the generic org.springframework.transaction.jta.JtaTransactionManager
Verify that the annotations are enabled.
Add the @Transactional annotation (org.springframework.transaction.annotation.Transactional) to the methods where you perform the save/update/delete etc operations. The legacy code worked without needing this annotation but now, for the latest versions, it is required to enable write operations.
In my case, I got a bunch of auto-wiring issues as soon as I added the annotation. The root cause turned out to be that some implementation classes, not interfaces, were being used to auto-wire properties at the @Service level classes. Changing the references to use the interfaces fixed that issue. You can read more about it on other threads such as this one.
I had to search and repeat the steps to fix all such instances in the legacy code. Note that setting the FlushMode to AUTO globally via OpenSessionInViewFilter is not a clean solution; there is a good reason why Spring sets the FlushMode to MANUAL by default. Spring makes the necessary runtime tweaks to support write operations when the @Transactional annotation is present. I debugged all the way to the Hibernate org.hibernate.engine.transaction.internal.TransactionCoordinatorImpl class and the Jta synchronization works fine with the setup above. Hope this helps anyone stuck in trying to migrate legacy code to the latest versions.
来源:https://stackoverflow.com/questions/26637847/spring4-hibernate4-jta-write-operations-fail