1. 程式人生 > >spring 同時配置hibernate and jdbc 事務

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

Java程式碼 複製程式碼 收藏程式碼
  1. HibernateTransactionObject txObject = new HibernateTransactionObject();;   
  2. txObject.setSavepointAllowed(isNestedTransactionAllowed(););;   
  3. if (TransactionSynchronizationManager.hasResource(getSessionFactory(););); {   
  4.     SessionHolder sessionHolder =   
  5.             (SessionHolder); TransactionSynchronizationManager.getResource(getSessionFactory(););;   
  6. if (logger.isDebugEnabled();); {   
  7.         logger.debug("Found thread-bound session [" + sessionHolder.getSession(); +   
  8. "] for Hibernate transaction");;   
  9.     }   
  10.     txObject.setSessionHolder(sessionHolder, false);;   
  11. if (getDataSource(); != null); {   
  12.         ConnectionHolder conHolder = (ConnectionHolder);   
  13.                 TransactionSynchronizationManager.getResource(getDataSource(););;   
  14.         txObject.setConnectionHolder(conHolder);;   
  15.     }   
  16. }   
  17. return txObject;  
  1. HibernateTransactionObject txObject = new HibernateTransactionObject();;  
  2. txObject.setSavepointAllowed(isNestedTransactionAllowed(););;  
  3. if (TransactionSynchronizationManager.hasResource(getSessionFactory(););); {  
  4.     SessionHolder sessionHolder =  
  5.             (SessionHolder); TransactionSynchronizationManager.getResource(getSessionFactory(););;  
  6.     if (logger.isDebugEnabled();); {  
  7.         logger.debug("Found thread-bound session [" + sessionHolder.getSession(); +  
  8.                 "] for Hibernate transaction");;  
  9.     }  
  10.     txObject.setSessionHolder(sessionHolder, false);;  
  11.     if (getDataSource(); != null); {  
  12.         ConnectionHolder conHolder = (ConnectionHolder);  
  13.                 TransactionSynchronizationManager.getResource(getDataSource(););;  
  14.         txObject.setConnectionHolder(conHolder);;  
  15.     }  
  16. }  
  17. return txObject;  


由此可以看出hibernateTransactionManager可以檢測到繫結在當前執行緒上的connection

HibernateTransactionManager#doBegin

Java程式碼 複製程式碼 收藏程式碼
  1. Connection con = session.connection();;   
  2. Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);;   
  3. txObject.setPreviousIsolationLevel(previousIsolationLevel);;   
  4. ...........   
  5. if (getDataSource(); != null); {   
  6.     ConnectionHolder conHolder = new ConnectionHolder(con);;   
  7. if (definition.getTimeout(); != TransactionDefinition.TIMEOUT_DEFAULT); {   
  8.         conHolder.setTimeoutInSeconds(definition.getTimeout(););;   
  9.     }   
  10. if (logger.isDebugEnabled();); {   
  11.         logger.debug("Exposing Hibernate transaction as JDBC transaction [" +   
  12.                 conHolder.getConnection(); + "]");;   
  13.     }   
  14.     TransactionSynchronizationManager.bindResource(getDataSource();, conHolder);;   
  15.     txObject.setConnectionHolder(conHolder);;   
  16. }   
  17. // bind the session holder to the thread
  18. if (txObject.isNewSessionHolder();); {   
  19.     TransactionSynchronizationManager.bindResource(getSessionFactory();, txObject.getSessionHolder(););;   
  20. }  
  1. Connection con = session.connection();;  
  2. Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);;  
  3. txObject.setPreviousIsolationLevel(previousIsolationLevel);;  
  4. ...........  
  5. if (getDataSource(); != null); {  
  6.     ConnectionHolder conHolder = new ConnectionHolder(con);;  
  7.     if (definition.getTimeout(); != TransactionDefinition.TIMEOUT_DEFAULT); {  
  8.         conHolder.setTimeoutInSeconds(definition.getTimeout(););;  
  9.     }  
  10.     if (logger.isDebugEnabled();); {  
  11.         logger.debug("Exposing Hibernate transaction as JDBC transaction [" +  
  12.                 conHolder.getConnection(); + "]");;  
  13.     }  
  14.     TransactionSynchronizationManager.bindResource(getDataSource();, conHolder);;  
  15.     txObject.setConnectionHolder(conHolder);;  
  16. }  
  17. // bind the session holder to the thread
  18. if (txObject.isNewSessionHolder();); {  
  19.     TransactionSynchronizationManager.bindResource(getSessionFactory();, txObject.getSessionHolder(););;  
  20. }  


由此可以看出,在真正啟動一個事務時,hbTxnManager會先把connection繫結到當前執行緒,再繫結session到當前執行緒,由TransactionSynchronizationManager統一管理。並且上面提到的connection是從session中取得的,也就是說,無論是jdbc dao還是hibernate dao本質上使用的是同一個database connection

因此得出結論:HibernateTransactionManager實際上是可以同時管理由JdbcTemplate或JdbcDaoSupport實現的dao以及HibernateTemplate或HibernateDaoSupport實現的事務的。


Rod Johnson的話:

引用 It is possible--and sometimes useful--to have coordinated transactions for both. Your JDBC transactions will be managed by the HibernateTransactionManager if you work with the same JDBC DataSource in the same transaction. That is, create the SessionFactory using Spring's SessionFactoryBean using the same DataSource that your JdbcTemplates use.

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的話:

引用 As Rod said, simply keep using HibernateTransactionManager, which auto-detects the DataSource used by Hibernate and seamlessly exposes Hibernate transactions as JDBC transactions for that DataSource. JDBC code that accesses the same DataSource via Spring will automatically participate in such transactions.

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.