<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          MyBatis:事務接口的理解

          共 9412字,需瀏覽 19分鐘

           ·

          2021-09-03 17:10

          不點藍字關注,我們哪來故事?




          正文如下

          來源:my.oschina.net/zudajun/blog/666764

          1. MyBaits中的事務接口Transaction
          2. 事務工廠TransactionFactory
          3. Transaction的用法
          4. 你可能關心的有關事務的幾種特殊場景表現(xiàn)(重要)

          1.說到數據庫事務,人們腦海里自然不自然的就會浮現(xiàn)出事務的四大特性、四大隔離級別、七大傳播特性。四大還好說,問題是七大傳播特性是哪兒來的?是Spring在當前線程內,處理多個數據庫操作方法事務時所做的一種事務應用策略。事務本身并不存在什么傳播特性,不要混淆事務本身和Spring的事務應用策略。(當然,找工作面試時,還是可以巧妙的描述傳播特性的)

          2.一說到事務,人們可能又會想起create、begin、commit、rollback、close、suspend??蓪嶋H上,只有commit、rollback是實際存在的,剩下的create、begin、close、suspend都是虛幻的,是業(yè)務層或數據庫底層應用語意,而非JDBC事務的真實命令。

          create(事務創(chuàng)建):不存在。

          begin(事務開始):姑且認為存在于DB的命令行中,比如Mysql的start transaction命令,以及其他數據庫中的begin transaction命令。JDBC中不存在。

          close(事務關閉):不存在。應用程序接口中的close()方法,是為了把connection放回數據庫連接池中,供下一次使用,與事務毫無關系。

          suspend(事務掛起):不存在。Spring中事務掛起的含義是,需要新事務時,將現(xiàn)有的connection1保存起來(它還有尚未提交的事務),然后創(chuàng)建connection2,connection2提交、回滾、關閉完畢后,再把connection1取出來,完成提交、回滾、關閉等動作,保存connection1的動作稱之為事務掛起。在JDBC中,是根本不存在事務掛起的說法的,也不存在這樣的接口方法。

          因此,記住事務的三個真實存在的方法,不要被各種事務狀態(tài)名詞所迷惑,它們分別是:conn.setAutoCommit() 、conn.commit()conn.rollback() 。

          conn.close()含義為關閉一個數據庫連接,這已經不再是事務方法了。

          1. Mybaits中的事務接口Transaction

          public interface Transaction {
              Connection getConnection() throws SQLException;
              void commit() throws SQLException;
              void rollback() throws SQLException;
              void close() throws SQLException;
          }

          有了文章開頭的分析,當你再次看到close()方法時,千萬別再認為是關閉一個事務了,而是關閉一個conn連接,或者是把conn連接放回連接池內。

          事務類層次結構圖:

          (Made In Intellij Idea IDE)

          JdbcTransaction:單獨使用Mybatis時,默認的事務管理實現(xiàn)類,就和它的名字一樣,它就是我們常說的JDBC事務的極簡封裝,和編程使用mysql-connector-java-5.1.38-bin.jar事務驅動沒啥差別。其極簡封裝,僅是讓connection支持連接池而已。

          ManagedTransaction:含義為托管事務,空殼事務管理器,皮包公司。僅是提醒用戶,在其它環(huán)境中應用時,把事務托管給其它框架,比如托管給Spring,讓Spring去管理事務。

          org.apache.ibatis.transaction.jdbc.JdbcTransaction.java部分源碼。

          @Override
            public void close() throws SQLException {
              if (connection != null) {
                resetAutoCommit();
                if (log.isDebugEnabled()) {
                  log.debug("Closing JDBC Connection [" + connection + "]");
                }
                connection.close();
              }
            }

          面對上面這段代碼,我們不禁好奇,connection.close()之前,居然調用了一個resetAutoCommit(),含義為重置autoCommit屬性值。connection.close()含義為銷毀conn,既然要銷毀conn,為何還多此一舉的調用一個resetAutoCommit()呢?消失之前多喝口水,真的沒有必要。

          其實,原因是這樣的,connection.close()不意味著真的要銷毀conn,而是要把conn放回連接池,供下一次使用,既然還要使用,自然就需要重置AutoCommit屬性了。通過生成connection代理類,來實現(xiàn)重回連接池的功能。如果connection是普通的Connection實例,那么代碼也是沒有問題的,雙重支持。

          2. 事務工廠TransactionFactory

          顧名思義,一個生產JdbcTransaction實例,一個生產ManagedTransaction實例。兩個毫無實際意義的工廠類,除了new之外,沒有其他代碼。

          <transactionManager type="JDBC" />

          mybatis-config.xml配置文件內,可配置事務管理類型。


          ?? 點擊關注,可添加我微信??

          我是 Socket,堅持分享編程,算法,Java 等干貨教程


          3. Transaction的用法

          無論是SqlSession,還是Executor,它們的事務方法,最終都指向了Transaction的事務方法,即都是由Transaction來完成事務提交、回滾的。

          配一個簡單的時序圖。

          (Made In Visual Paradigm)

          代碼樣例:

          public static void main(String[] args) {
            SqlSession sqlSession = MybatisSqlSessionFactory.openSession();
            try {
             StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);

             Student student = new Student();
             student.setName("yy");
             student.setEmail("[email protected]");
             student.setDob(new Date());
             student.setPhone(new PhoneNumber("123-2568-8947"));

             studentMapper.insertStudent(student);
             sqlSession.commit();
            } catch (Exception e) {
             sqlSession.rollback();
            } finally {
             sqlSession.close();
            }
           }

          注:Executor在執(zhí)行insertStudent(student)方法時,與事務的提交、回滾、關閉毫無瓜葛(方法內部不會提交、回滾事務),需要像上面的代碼一樣,手動顯示調用commit()、rollback()、close()等方法。

          因此,后續(xù)在分析到類似insert()、update()等方法內部時,需要忘記事務的存在,不要試圖在insert()等方法內部尋找有關事務的任何方法。

          4. 你可能關心的有關事務的幾種特殊場景表現(xiàn)(重要)

          1. 一個conn生命周期內,可以存在無數多個事務。

                  // 執(zhí)行了connection.setAutoCommit(false),并返回
                  SqlSession sqlSession = MybatisSqlSessionFactory.openSession();
            try {
             StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);

             Student student = new Student();
             student.setName("yy");
             student.setEmail("[email protected]");
             student.setDob(new Date());
             student.setPhone(new PhoneNumber("123-2568-8947"));

             studentMapper.insertStudent(student);
             // 提交
             sqlSession.commit();

             studentMapper.insertStudent(student);
             // 多次提交
             sqlSession.commit();
            } catch (Exception e) {
                    // 回滾,只能回滾當前未提交的事務
             sqlSession.rollback();
            } finally {
             sqlSession.close();
            }

          對于JDBC來說,autoCommit=false時,是自動開啟事務的,執(zhí)行commit()后,該事務結束。以上代碼正常情況下,開啟了2個事務,向數據庫插入了2條數據。JDBC中不存在Hibernate中的session的概念,在JDBC中,insert了幾次,數據庫就會有幾條記錄,切勿混淆。而rollback(),只能回滾當前未提交的事務。

          2. autoCommit=false,沒有執(zhí)行commit(),僅執(zhí)行close(),會發(fā)生什么?

          try {
              studentMapper.insertStudent(student);
          finally {
              sqlSession.close();
          }

          就像上面這樣的代碼,沒有commit(),固執(zhí)的程序員總是好奇這樣的特例。

          insert后,close之前,如果數據庫的事務隔離級別是read uncommitted,那么,我們可以在數據庫中查詢到該條記錄。

          接著執(zhí)行sqlSession.close()時,經過SqlSession的判斷,決定執(zhí)行rollback()操作,于是,事務回滾,數據庫記錄消失。

          下面,我們看看org.apache.ibatis.session.defaults.DefaultSqlSession.java中的close()方法源碼。

            @Override
            public void close() {
              try {
                executor.close(isCommitOrRollbackRequired(false));
                dirty = false;
              } finally {
                ErrorContext.instance().reset();
              }
            }

          事務是否回滾,依靠isCommitOrRollbackRequired(false)方法來判斷。

            private boolean isCommitOrRollbackRequired(boolean force) {
              return (!autoCommit && dirty) || force;
            }

          在上面的條件判斷中,!autoCommit=true(取反當然是true了),force=false,最終是否回滾事務,只有dirty參數了,dirty含義為是否是臟數據。

            @Override
            public int insert(String statement, Object parameter) {
              return update(statement, parameter);
            }

            @Override
            public int update(String statement, Object parameter) {
              try {
                dirty = true;
                MappedStatement ms = configuration.getMappedStatement(statement);
                return executor.update(ms, wrapCollection(parameter));
              } catch (Exception e) {
                throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
              } finally {
                ErrorContext.instance().reset();
              }
            }

          源碼很明確,只要執(zhí)行update操作,就設置dirty=true。insert、delete最終也是執(zhí)行update操作。

          只有在執(zhí)行完commit()、rollback()、close()等方法后,才會再次設置dirty=false。

            @Override
            public void commit(boolean force) {
              try {
                executor.commit(isCommitOrRollbackRequired(force));
                dirty = false;
              } catch (Exception e) {
                throw ExceptionFactory.wrapException("Error committing transaction.  Cause: " + e, e);
              } finally {
                ErrorContext.instance().reset();
              }
            }

          因此,得出結論:autoCommit=false,但是沒有手動commit,在sqlSession.close()時,Mybatis會將事務進行rollback()操作,然后才執(zhí)行conn.close()關閉連接,當然數據最終也就沒能持久化到數據庫中了。

          3. autoCommit=false,沒有commit,也沒有close,會發(fā)生什么?

          studentMapper.insertStudent(student);

          干脆,就這一句話,即不commit,也不close。

          結論:insert后,jvm結束前,如果事務隔離級別是read uncommitted,我們可以查到該條記錄。jvm結束后,事務被rollback(),記錄消失。通過斷點debug方式,你可以看到效果。

          這說明JDBC驅動實現(xiàn),已經Kao慮到這樣的特例情況,底層已經有相應的處理機制了。這也超出了我們的探究范圍。

          但是,一萬個屌絲程序員會對你說:Don't do it like this. Go right way。

          警告:請按正確的try-catch-finally編程方式處理事務,若不從,本人概不負責后果。

          注:無參的openSession()方法,會自動設置autoCommit=false。

          總結:Mybatis的JdbcTransaction,和純粹的Jdbc事務,幾乎沒有差別,它僅是擴展支持了連接池的connection。另外,需要明確,無論你是否手動處理了事務,只要是對數據庫進行任何update操作(update、delete、insert),都一定是在事務中進行的,這是數據庫的設計規(guī)范之一。讀完本篇文章,是否顛覆了你心中目前對事務的理解呢?歡迎點評。

          -END-


          ↑ 點擊上方關注我  


          我是 Socket,堅持分享編程,算法,Java 等干貨教程


          一枚醫(yī)科大本科生,開源小作者,半吊子創(chuàng)業(yè)愛好者...

          半吊子的自己在試錯,不知道以后會干什么,但享受現(xiàn)在的試錯,試錯給我驚訝的生活


          喜歡公號的互動分享,感謝關注,路上遇見了你,同一小段時間之路,相伴 ~



          點個在看結對編程一把


          瀏覽 69
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  加勒比蜜桃日韩成人 | 北条麻妃成人 | 91超碰在线| 视频一区在线观看视频 | sese色|