spring中常用的事務管理有DataSourceTransactionManager 和HibernateTransactionManager,他們服務的對象不同,下面來簡單說明一下:
1、DataSourceTransactionManager:此事務管理器是針對傳統的JDBC進行事務管理,在spring中是對JdbcTemplate進行事務管理
2、HibernateTransactionManager:是對hibernate進行事務管理,當在spring中使用HibernateTemplate時,要使用此管理器。
但是當在service的一個方法中同時使用了JdbcTemplate和HibernateTemplate時,就要使用HibernateTransactionManager了,因爲當使用DataSourceTransactionManager時,JdbcTemplate和HibernateTemplate獲得的connection並不是同一個,也就沒辦法對service的方法進行事務管理了。
如果一個方法中既用了HibernateTemplate,又用了JdbcTemplate,應該怎麼配單實例的db事務呢(多例免談)用 DataSouceTransactionManager是不行的,而用HibernateTransactionManager就可以保證
原因的話看下它們源代碼,會發現HibernateTransactionManager中的處理可以保證SessionFactoryUtil和datasourceutil都能在一個事務裏取到同一個連接
原文如下=====================================================================
今天這邊報出一個問題,他在一個service方法裏面,用了jdbcdaosupport的dao又用了hibernateDaoSupport的dao,在spring裏面給service方法配上了事務,
但是通過MySQL的bin
log,發現這種不同的dao使用的連接id不是同一個,即jdbctemplate使用了一個鏈接,而hibernatetemplate使用了另外一個鏈接,這樣雖然兩種dao都是針對一個mysql實例,但卻沒法保證事務。
之前xd提過使用hibernateTransaction manager,可以保證混用時候的事務,但是卻不知道爲啥會這樣。我們這邊就以爲datasourcetransactionmanager也是一樣,但發現事實上不一樣。確實我們換成hibernateTransaction manager,兩種dao使用的connection就歸一了,真好,但是爲啥呢?
翻了半天spring的源代碼終於找到了。
以下是datasourceTransactionManager的doGetTransaction和doBegin代碼
- protected Object doGetTransaction() {
- //只是設定一個dataSource爲key的存放connection的threadlcal
- DataSourceTransactionObject txObject = new DataSourceTransactionObject();
- txObject.setSavepointAllowed(isNestedTransactionAllowed());
- ConnectionHolder conHolder =
- (ConnectionHolder) TransactionSynchronizationManager.getResource(this.dataSource);
- txObject.setConnectionHolder(conHolder, false);
- return txObject;
- }
- protected void doBegin(Object transaction, TransactionDefinition definition) {
- .....
- try {
- if (txObject.getConnectionHolder() == null ||
- txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
- Connection newCon = this.dataSource.getConnection();
- }
- ....
- //從datasource拿一個連接,放入thread生命週期的holder
- }
這就完了。
然後jdbctemplate會通過datasourceutil去拿這個holder裏面的connection
從而在一個事務裏使用這個連接。
但是hibernateTransactionManager呢:
- protected Object doGetTransaction() {
- HibernateTransactionObject txObject = new HibernateTransactionObject();
- txObject.setSavepointAllowed(isNestedTransactionAllowed());
- SessionHolder sessionHolder =
- (SessionHolder) TransactionSynchronizationManager.getResource(getSessionFactory());
- if (sessionHolder != null) {
- if (logger.isDebugEnabled()) {
- logger.debug("Found thread-bound Session [" +
- SessionFactoryUtils.toString(sessionHolder.getSession()) + "] for Hibernate transaction");
- }
- txObject.setSessionHolder(sessionHolder, false);
- }
- if (getDataSource() != null) {
- ConnectionHolder conHolder = (ConnectionHolder)
- TransactionSynchronizationManager.getResource(getDataSource());
- txObject.setConnectionHolder(conHolder);
- }
- return txObject;
- }
- //兩個holder都管!
- protected void doBegin(Object transaction, TransactionDefinition definition) {
- .....
- try {
- if (txObject.getSessionHolder() == null || txObject.getSessionHolder().isSynchronizedWithTransaction()) {
- Interceptor entityInterceptor = getEntityInterceptor();
- Session newSession = (entityInterceptor != null ?
- getSessionFactory().openSession(entityInterceptor) : getSessionFactory().openSession());
- if (logger.isDebugEnabled()) {
- logger.debug("Opened new Session [" + SessionFactoryUtils.toString(newSession) +
- "] for Hibernate transaction");
- }
- txObject.setSessionHolder(new SessionHolder(newSession), true);
- }
- .....
- //從sessionFactory拿個新session,也會產生一個新連接
- session = txObject.getSessionHolder().getSession();
- if (this.prepareConnection && isSameConnectionForEntireSession(session)) {
- // We're allowed to change the transaction settings of the JDBC Connection.
- if (logger.isDebugEnabled()) {
- logger.debug(
- "Preparing JDBC Connection of Hibernate Session [" + SessionFactoryUtils.toString(session) + "]");
- }
- //原來直接把session後面的connection也放入holder
- Connection con = session.connection();
- Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
- txObject.setPreviousIsolationLevel(previousIsolationLevel);
- }
所以如果使用hibernateTransactionManager的話,就完全可以保證SessionFactoryUtil和datasourceutil都能在一個事務裏取到同一個連接!所有的疑問煙消雲散了,
所以大家還是使用hibernateTransactionManager從而隨心所欲的使用jdbctemplate和hibernatetemplate吧
原文來自:http://www.pinhuba.com/spring/101108.htm
http://bjyzxxds.iteye.com/blog/427309