spring 同時配置hibernate and jdbc 事務
在專案中同時用到了JDBC和hibernate,分別配置了各自的事務,同時配置了不同的tx:annotation-driven。
但是在實際使用中,只有配置在前的tx:annotation-driven起了作用,另外一個tx:annotation-driven不起作用。
============
給一個TransactionProxyFactoryBean(使用DataSourceTransactionManager)配置的service加上@Transactional
(用HibernateTransactionManager宣告的),則會使用DataSourceTransactionManager事務
===============
最近開發中,遇到了這樣一個問題。
1.系統中,事務是有spring管理的。
2.系統中,即用到了JDBC,又用到了Hibernate。
3.spring管理了jdbc事務,也管理了Hibernate事務。
如上3點所述,存在這樣的情況:
配置了jdbc事務的service,注入了配置了hibernate事務的service。這時,執行的時候,系統就會丟擲異常:
org.springframework.transaction.IllegalTransactionStateException: Pre-bound JDBC connection found - HibernateTransactionManager does not support running within DataSourceTransactionManager if told to manage the DataSource itself. It is recommended to use a single HibernateTransactionManager for all transactions on a single DataSource, no matter whether Hibernate or JDBC access.
但是,由配置了hibernate事務的service,注入了配置了jdbc事務的service。就能正常執行。
不知道大家有沒有遇到過類似的問題。一起討論一下。
================
最近摸索了一下Hibernate與JDBC(iBATIS)事務整合問題,算是有點收穫,寫出來和大家討論一下吧。。。
一般大家都會使用spring宣告型事務 transactionAttributes 為 PROPAGATION_REQUIRED
Hibernate 使用 HibernateTransactionManager 、JDBC(iBATIS) 使用 DataSourceTransactionManager
當需要將它們整合到一個事務中的時候
普通的做法是配置統一的DataSource, Hibernate與JDBC(iBATIS) 都使用HibernateTransactionManager
------------------------------------------------------
Hibernate與JDBC(iBATIS) 都使用DataSourceTransactionManager又可不可以呢?
普遍的看法是 NO! 認為DataSourceTransactionManager根本就不處理Hibernate的session,事務當然無法實現。。。
但事實是否真的如此呢?
Juergen Hoeller:
Just if you proxy your JDBC DataSource with a TransactionAwareDataSourceProxy (available since Spring 1.1 RC1) and pass that proxy to your LocalSessionFactoryBean, you could use DataSourceTransactionManager in combination with Hibernate.
也就是說配置 sessionFactory 的 useTransactionAwareDataSource 為 true
Hibernate與JDBC(iBATIS) 都使用 DataSourceTransactionManager 同樣可以保證事務
原理就是保證了 connection 的唯一性。
樓主以及二樓的朋友的論斷錯誤倒也罷了,那種肯定的,結論性總結的態度很容易誤導初學者的學習和理解。
提個小小的建議:下結論前要進行充分的考證,我們技術工作者尤其需要嚴謹的態度。需要用證據來說話。
jdo dao和jdbc dao能否在同一個事務裡這我不太清楚。因為我沒用過jdo daosupport。
但是jdbc daosupport和hibernate daosupport卻能被wrap到同一個事務裡。成立需要幾點條件:
1、使用同一個datasource
2、事務交由hibernateTransactionManager管理
3、相關dao以及service需要使用runtime exception體系,使用spring提供的exception可以,自己封裝設計的runtime exception體系也行。
與此相關的事務程式碼片斷在HibernateTransactionManager類中。最好可以把DatasourceTransactionManager和HibernateTransactionManager對比來看。
在此貼上幾個原始碼片斷,多餘的我就不解釋了。相信大家一看自明。
HibernateTransactionManager#doGetTransaction
- HibernateTransactionObject txObject = new HibernateTransactionObject();;
- txObject.setSavepointAllowed(isNestedTransactionAllowed(););;
- if (TransactionSynchronizationManager.hasResource(getSessionFactory(););); {
- SessionHolder sessionHolder =
- (SessionHolder); TransactionSynchronizationManager.getResource(getSessionFactory(););;
- if (logger.isDebugEnabled();); {
- logger.debug("Found thread-bound session [" + sessionHolder.getSession(); +
- "] for Hibernate transaction");;
- }
- txObject.setSessionHolder(sessionHolder, false);;
- if (getDataSource(); != null); {
- ConnectionHolder conHolder = (ConnectionHolder);
- TransactionSynchronizationManager.getResource(getDataSource(););;
- txObject.setConnectionHolder(conHolder);;
- }
- }
- return txObject;
- HibernateTransactionObject txObject = new HibernateTransactionObject();;
- txObject.setSavepointAllowed(isNestedTransactionAllowed(););;
- if (TransactionSynchronizationManager.hasResource(getSessionFactory(););); {
- SessionHolder sessionHolder =
- (SessionHolder); TransactionSynchronizationManager.getResource(getSessionFactory(););;
- if (logger.isDebugEnabled();); {
- logger.debug("Found thread-bound session [" + sessionHolder.getSession(); +
- "] for Hibernate transaction");;
- }
- txObject.setSessionHolder(sessionHolder, false);;
- if (getDataSource(); != null); {
- ConnectionHolder conHolder = (ConnectionHolder);
- TransactionSynchronizationManager.getResource(getDataSource(););;
- txObject.setConnectionHolder(conHolder);;
- }
- }
- return txObject;
由此可以看出hibernateTransactionManager可以檢測到繫結在當前執行緒上的connection
HibernateTransactionManager#doBegin
- Connection con = session.connection();;
- Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);;
- txObject.setPreviousIsolationLevel(previousIsolationLevel);;
- ...........
- if (getDataSource(); != null); {
- ConnectionHolder conHolder = new ConnectionHolder(con);;
- if (definition.getTimeout(); != TransactionDefinition.TIMEOUT_DEFAULT); {
- conHolder.setTimeoutInSeconds(definition.getTimeout(););;
- }
- if (logger.isDebugEnabled();); {
- logger.debug("Exposing Hibernate transaction as JDBC transaction [" +
- conHolder.getConnection(); + "]");;
- }
- TransactionSynchronizationManager.bindResource(getDataSource();, conHolder);;
- txObject.setConnectionHolder(conHolder);;
- }
- // bind the session holder to the thread
- if (txObject.isNewSessionHolder();); {
- TransactionSynchronizationManager.bindResource(getSessionFactory();, txObject.getSessionHolder(););;
- }
- Connection con = session.connection();;
- Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);;
- txObject.setPreviousIsolationLevel(previousIsolationLevel);;
- ...........
- if (getDataSource(); != null); {
- ConnectionHolder conHolder = new ConnectionHolder(con);;
- if (definition.getTimeout(); != TransactionDefinition.TIMEOUT_DEFAULT); {
- conHolder.setTimeoutInSeconds(definition.getTimeout(););;
- }
- if (logger.isDebugEnabled();); {
- logger.debug("Exposing Hibernate transaction as JDBC transaction [" +
- conHolder.getConnection(); + "]");;
- }
- TransactionSynchronizationManager.bindResource(getDataSource();, conHolder);;
- txObject.setConnectionHolder(conHolder);;
- }
- // bind the session holder to the thread
- if (txObject.isNewSessionHolder();); {
- TransactionSynchronizationManager.bindResource(getSessionFactory();, txObject.getSessionHolder(););;
- }
由此可以看出,在真正啟動一個事務時,hbTxnManager會先把connection繫結到當前執行緒,再繫結session到當前執行緒,由TransactionSynchronizationManager統一管理。並且上面提到的connection是從session中取得的,也就是說,無論是jdbc dao還是hibernate dao本質上使用的是同一個database connection
因此得出結論:HibernateTransactionManager實際上是可以同時管理由JdbcTemplate或JdbcDaoSupport實現的dao以及HibernateTemplate或HibernateDaoSupport實現的事務的。
Rod Johnson的話:
The only issue to watch, of course, is that you may be invalidating your Hibernate cache by JDBC changes. Generally I find it best to use JDBC to update only tables that don't have Hibernate mappings.
Juergen Hoeller的話:
Note that you must specify the DataSource for Hibernate via LocalSessionFactoryBean's "dataSource" property to allow HibernateTransactionManager to auto-detect it. Alternatively, you can explicitly pass the DataSource to HibernateTransactionManager's "dataSource" property.