9允蜈、事務補充

當事務的傳播級別為SUPPORTS時冤吨,如果當前有事務蒿柳,則使用當前事務,如果當前沒有事務漩蟆,則以無事務方式執(zhí)行
這里的無事務方式執(zhí)行是指垒探,Spring不會提交事務,而且MyBatis也不會執(zhí)行commit怠李。
除非設(shè)置數(shù)據(jù)庫連接的auto-commit屬性為true圾叼,否則不能寫入數(shù)據(jù)

spring:
    datasource:
        auto-commit: true

1、propagation = Propagation.REQUIRED
在大多數(shù)情況下捺癞,我們使用默認隔離級別Propagation.REQUIRED夷蚊,當propagation = Propagation.REQUIRED時,數(shù)據(jù)庫連接由Spring事務管理器在DataSourceTransactionManager#doBegin中取得
doBegin會在org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction中被調(diào)用

org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin
protected void doBegin(Object transaction, TransactionDefinition definition) {
   DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
   Connection con = null;

   try {
      if (!txObject.hasConnectionHolder() ||
            txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
        // 由事務管理器從dataSource中獲取連接
         Connection newCon = obtainDataSource().getConnection();
         if (logger.isDebugEnabled()) {
            logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
         }
         txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
      }

      txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
      con = txObject.getConnectionHolder().getConnection();

      Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
      txObject.setPreviousIsolationLevel(previousIsolationLevel);

      // Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
      // so we don't want to do it unnecessarily (for example if we've explicitly
      // configured the connection pool to set it already).
      if (con.getauto-commit()) {
         txObject.setMustRestoreauto-commit(true);
         if (logger.isDebugEnabled()) {
            logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
         }
        // 當以有事務方式執(zhí)行時髓介,Spring會將數(shù)據(jù)庫連接的auto-commit屬性改為false
        // 所以當開啟新的事務或者說有事務方式執(zhí)行時惕鼓,auto-commit屬性會無效
        // 因為有事務,不論是當前創(chuàng)建新的事務唐础,或者是繼承自上一層的事務箱歧,都會是用的Spring獲取的連接,auto-commit都會被改成false
         con.setauto-commit(false);
      }

      prepareTransactionalConnection(con, definition);
      txObject.getConnectionHolder().setTransactionActive(true);

      int timeout = determineTimeout(definition);
      if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
         txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
      }

      // Bind the connection holder to the thread.
      if (txObject.isNewConnectionHolder()) {
         TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
      }
   }

   catch (Throwable ex) {
      if (txObject.isNewConnectionHolder()) {
         DataSourceUtils.releaseConnection(con, obtainDataSource());
         txObject.setConnectionHolder(null, false);
      }
      throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
   }
}

以有事務方式執(zhí)行時一膨,事務的提交呀邢,也是由Spring執(zhí)行
具體是在org.springframework.transaction.support.AbstractPlatformTransactionManager#processCommit中,具體不作分析了
還有一個問題豹绪。為什么有事務方式執(zhí)行時价淌,MyBatis不會提交事務?
相關(guān)邏輯在這里
org.mybatis.spring.SqlSessionTemplate.SqlSessionInterceptor

private class SqlSessionInterceptor implements InvocationHandler {
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    SqlSession sqlSession = getSqlSession(
        SqlSessionTemplate.this.sqlSessionFactory,
        SqlSessionTemplate.this.executorType,
        SqlSessionTemplate.this.exceptionTranslator);
    try {
      Object result = method.invoke(sqlSession, args);
        // isSqlSessionTransactional會判斷,當前執(zhí)行SQL的會話sqlSession和在TransactionSynchronizationManager中和當前線程綁定的會話是否是同一個
        // 如果是同一個蝉衣,則說明事務由Spring管理豺型,所以下面的sqlSession.commit(true);不會執(zhí)行
      if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
        // force commit even on non-dirty sessions because some databases require
        // a commit/rollback before calling close()
        sqlSession.commit(true);
      }
      return result;
    } catch (Throwable t) {
      Throwable unwrapped = unwrapThrowable(t);
      if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
        // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
        closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        sqlSession = null;
        Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
        if (translated != null) {
          unwrapped = translated;
        }
      }
      throw unwrapped;
    } finally {
      if (sqlSession != null) {
        closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
      }
    }
  }
}

2、當事務的傳播級別由REQUIRED改成SUPPORTS時
propagation = Propagation.SUPPORTS
在org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction中买乃,由于propagation = Propagation.SUPPORTS姻氨,不會創(chuàng)建新的事務。
DataSourceTransactionManager#doBegin也不會被調(diào)用剪验,所以auto-commit屬性不會被修改肴焊,那么連接是在在哪里取的呢?
答案是在org.apache.ibatis.executor.SimpleExecutor#prepareStatement中由MyBatis自己搞定了

org.apache.ibatis.executor.SimpleExecutor#prepareStatement
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
  Statement stmt;
  Connection connection = getConnection(statementLog);
  stmt = handler.prepare(connection, transaction.getTimeout());
  handler.parameterize(stmt);
  return stmt;
}

org.apache.ibatis.executor.BaseExecutor#getConnection
protected Connection getConnection(Log statementLog) throws SQLException {
  Connection connection = transaction.getConnection();
  if (statementLog.isDebugEnabled()) {
    return ConnectionLogger.newInstance(connection, statementLog, queryStack);
  } else {
    return connection;
  }
}

org.apache.ibatis.transaction.jdbc.JdbcTransaction#getConnection
@Override
public Connection getConnection() throws SQLException {
  if (connection == null) {
    openConnection();
  }
  return connection;
}

傳播級別為SUPPORTS時事務的提交有兩種可能
a功戚、auto-commit屬性為false娶眷,事務不會提交,或者說connection不會commit(SqlSessionInterceptor的isSqlSessionTransactional此時仍然返回true)
b啸臀、auto-commit屬性為true届宠,事務由java.sql.Connection各個實現(xiàn)類自己在執(zhí)行完SQL后自動提交了

3、不使用Spring事務乘粒,去掉@Transactionl注解
取數(shù)據(jù)庫連接和使用propagation = Propagation.SUPPORTS時相同
事務的提交也有兩種可能
a豌注、auto-commit屬性為false,事務由MyBatis提交(SqlSessionInterceptor的isSqlSessionTransactional此時返回false)
b灯萍、auto-commit屬性為true轧铁,事務由java.sql.Connection各個實現(xiàn)類自己在執(zhí)行完SQL后自動提交了
a和b兩種情況都是執(zhí)行一條SQL做一次commit。
在情況b中旦棉,SqlSessionInterceptor的isSqlSessionTransactional同樣返回false齿风,MyBatis為什么沒有提交事務?

SqlSessionInterceptor中sqlSession.commit(true)绑洛,最終調(diào)用的是org.apache.ibatis.transaction.jdbc.JdbcTransaction#commit

org.apache.ibatis.transaction.jdbc.JdbcTransaction#commit
@Override
public void commit() throws SQLException {
    // 當connection的auto-commit屬性為true時救斑,MyBatis不會自動commit
  if (connection != null && !connection.getauto-commit()) {
    if (log.isDebugEnabled()) {
      log.debug("Committing JDBC Connection [" + connection + "]");
    }
    connection.commit();
  }
}

最后一個問題。propagation = Propagation.REQUIRED時真屯,如果設(shè)置auto-commit屬性為true脸候,Spring會自動改成false,那么事務提交之后會將連接的自動提交屬性改回來嗎

答案是會

DataSourceTransactionManager.doBegin片段      
if (con.getauto-commit()) {
            // 這里將DataSourceTransactionObject的mustRestoreAutoCommit屬性改成true讨跟,表示事務提交之后要重置connection的auto-commit屬性為true
         txObject.setMustRestoreauto-commit(true);
         if (logger.isDebugEnabled()) {
            logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
         }```
        // 當以有事務方式執(zhí)行時纪他,Spring會將數(shù)據(jù)庫連接的auto-commit屬性改為false
        // 所以當開啟新的事務或者說有事務方式執(zhí)行時,auto-commit屬性會無效
        // 因為有事務晾匠,不論是當前創(chuàng)建新的事務茶袒,或者是繼承自上一層的事務,都會是用的Spring獲取的連接凉馆,auto-commit都會被改成false
         con.setauto-commit(false);
      }

具體調(diào)用路徑是

org.springframework.transaction.support.AbstractPlatformTransactionManager#processCommit
org.springframework.transaction.support.AbstractPlatformTransactionManager#cleanupAfterCompletion
org.springframework.jdbc.datasource.DataSourceTransactionManager#doCleanupAfterCompletion

protected void doCleanupAfterCompletion(Object transaction) {
   DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;

   // Remove the connection holder from the thread, if exposed.
   if (txObject.isNewConnectionHolder()) {
      TransactionSynchronizationManager.unbindResource(obtainDataSource());
   }

   // Reset connection.
   Connection con = txObject.getConnectionHolder().getConnection();
   try {
        // 這里DataSourceTransactionObject的mustRestoreAutoCommit屬性為true薪寓,修改連接auto-commit屬性為true
      if (txObject.isMustRestoreAutoCommit()) {
         con.setAutoCommit(true);
      }
      DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel());
   }
   catch (Throwable ex) {
      logger.debug("Could not reset JDBC Connection after transaction", ex);
   }

   if (txObject.isNewConnectionHolder()) {
      if (logger.isDebugEnabled()) {
         logger.debug("Releasing JDBC Connection [" + con + "] after transaction");
      }
      DataSourceUtils.releaseConnection(con, this.dataSource);
   }

   txObject.getConnectionHolder().clear();
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末亡资,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子向叉,更是在濱河造成了極大的恐慌锥腻,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件母谎,死亡現(xiàn)場離奇詭異瘦黑,居然都是意外死亡,警方通過查閱死者的電腦和手機奇唤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門幸斥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人咬扇,你說我怎么就攤上這事甲葬。” “怎么了懈贺?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵经窖,是天一觀的道長。 經(jīng)常有香客問我梭灿,道長画侣,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任胎源,我火速辦了婚禮棉钧,結(jié)果婚禮上屿脐,老公的妹妹穿的比我還像新娘涕蚤。我一直安慰自己,他們只是感情好的诵,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布万栅。 她就那樣靜靜地躺著,像睡著了一般西疤。 火紅的嫁衣襯著肌膚如雪烦粒。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天代赁,我揣著相機與錄音扰她,去河邊找鬼。 笑死芭碍,一個胖子當著我的面吹牛徒役,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播窖壕,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼忧勿,長吁一口氣:“原來是場噩夢啊……” “哼杉女!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起鸳吸,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤熏挎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后晌砾,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體坎拐,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年养匈,在試婚紗的時候發(fā)現(xiàn)自己被綠了廉白。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡乖寒,死狀恐怖猴蹂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情楣嘁,我是刑警寧澤磅轻,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站逐虚,受9級特大地震影響聋溜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜叭爱,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一撮躁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧买雾,春花似錦把曼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽茸歧。三九已至拐邪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留震叮,地道東北人。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓鳍鸵,卻偏偏與公主長得像苇瓣,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子权纤,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 1. 簡介 1.1 什么是 MyBatis 钓简? MyBatis 是支持定制化 SQL乌妒、存儲過程以及高級映射的優(yōu)秀的...
    笨鳥慢飛閱讀 5,429評論 0 4
  • 原文鏈接:https://docs.spring.io/spring-boot/docs/1.4.x/refere...
    pseudo_niaonao閱讀 4,677評論 0 9
  • 什么是事務? 事務(Transaction)外邓,一般是指要做的或所做的事情撤蚊。在計算機術(shù)語中是指訪問并可能更新數(shù)據(jù)庫中...
    青青子衿zq閱讀 7,928評論 0 2
  • java事務的處理 轉(zhuǎn) https://www.cnblogs.com/Bonker/p/5417967.html...
    小小的Jobs閱讀 1,364評論 0 1
  • 一、背景介紹 Spring 框架應該是每一個人 javaer 都必須接觸和學習的技術(shù)损话,Spring 公司所提供的各...
    小王學java閱讀 513評論 0 0