• <noscript id="e0iig"><kbd id="e0iig"></kbd></noscript>
  • <td id="e0iig"></td>
  • <option id="e0iig"></option>
  • <noscript id="e0iig"><source id="e0iig"></source></noscript>
  • Mybatis源碼學習之事務管理(八)

    標簽: 事務管理

    簡述

    在實際開發中,數據庫事務的控制是一件非常重要的工作,本文將學習Mybatis對事務的管理機制。在Mybatis中基于接口 Transaction 將事務分為兩種,一種是JdbcTransaction, 另一種是ManagedTransaction,接下來本文將從源碼的角度解讀 Transaction 接口的不同實現及其區別。
    image
    MyBatis將事務抽象成 Transaction 接口,該接口包含了數據庫事務應有的操作,包括創建(create)、提交(commit)、回滾(rollback)、關閉(close),如下類圖

    image

    MyBatis的事務管理分為兩種形式:

    一、使用JDBC的事務管理機制:利用java.sql.Connection對象完成對事務的提交(commit())、回滾(rollback())、關閉(close())等

    二、使用MANAGED的事務管理機制:這種機制是讓容器如(JBOSS,Weblogic)來實現對事務的管理

    image

    Mybatis事務的使用

    通常我們會通過mybatis.xml配置事務的使用機制,如下 使用JDBC事務管理機制

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
      PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
      "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
      <environments default="development">
        <environment id="development">
          <transactionManager type="JDBC"/>
          <dataSource type="POOLED">
            <property name="driver" value="${driver}"/>
            <property name="url" value="${url}"/>
            <property name="username" value="${username}"/>
            <property name="password" value="${password}"/>
          </dataSource>
        </environment>
      </environments>
    </configuration>

    Mybatis事務工廠創建

    MyBatis事務的創建是交給TransactionFactory 事務工廠來創建的,如果我們將的type 配置為”JDBC”,那么,在MyBatis初始化解析節點時,會根據type=”JDBC”創建一個JdbcTransactionFactory工廠,源碼如下:

    /**
         * 解析<transactionManager>節點,創建對應的TransactionFactory
         *
         * @param context xml配置節點信息
         * @return TransactionFactory
         */
        private TransactionFactory transactionManagerElement(XNode context) throws Exception {
            if (context != null) {
                //獲取事務管理機制的類型
                String type = context.getStringAttribute("type");
                Properties props = context.getChildrenAsProperties();
                //根據事務管理機制類型創建對應的事務管理工廠
                TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance();
                factory.setProperties(props);
                return factory;
            }
            throw new BuilderException("Environment declaration requires a TransactionFactory.");
        }

    如代碼所示,如果type = “JDBC”,則MyBatis會創建一個JdbcTransactionFactory.class 實例;如果type=”MANAGED”,則MyBatis會創建一個MangedTransactionFactory.class實例。

    ### Mybatis 事務工廠TransactionFactory
    事務TransactionFactory定義了創建Transaction的兩個方法:

    一個是通過指定的Connection對象創建Transaction,

    另外是通過數據源DataSource來創建Transaction。

    與JDBC 和 MANAGED兩種Transaction相對應,TransactionFactory有兩個對應的實現的子類:

    image

    ### Mybatis 事務創建
    通過上文的學習,很清楚的知道Transaction對象的創建很容易 通過事務工廠TransactionFactory得到。這里我們以JdbcTransaction為例,看一下JdbcTransactionFactory是怎樣生成JdbcTransaction的,代碼如下:

    /**
     * JdbcTransaction事務工廠,創建JdbcTransaction實例
     *
     * @author kaifeng
     * @author Clinton Begin
     * @see JdbcTransaction
     */
    public class JdbcTransactionFactory implements TransactionFactory {
    
        @Override
        public void setProperties(Properties props) {
        }
    
        /**
         * 根據給定的數據庫連接conn創建JdbcTransaction實例
         *
         * @param conn 數據庫連接對象
         */
        @Override
        public Transaction newTransaction(Connection conn) {
            return new JdbcTransaction(conn);
        }
    
        /**
         * 根據給定的數據源conn創建JdbcTransaction實例
         *
         * @param ds         數據源
         * @param level      事務隔離級別
         * @param autoCommit 事務是否自動提交
         */
        @Override
        public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
            return new JdbcTransaction(ds, level, autoCommit);
        }
    }
    

    Mybatis JDBC事務

    JdbcTransaction是直接使用JDBC的提交和回滾事務管理機制 。它從dataSource中取得連接connection 來管理transaction 的作用域,connection對象的獲取被延遲到調用getConnection()方法。
    JdbcTransaction相當于對java.sql.Connection事務處理進行了包裝(wrapper)。

    JdbcTransaction的代碼實現如下:

    /**
     * JDBC 事務實現
     *
     * @author kaifeng
     * @author Clinton Begin
     * @see JdbcTransactionFactory
     */
    public class JdbcTransaction implements Transaction {
    
        private static final Log log = LogFactory.getLog(JdbcTransaction.class);
    
        /**
         * 數據庫連接對象
         */
        protected Connection connection;
    
        /**
         * 數據源對象
         */
        protected DataSource dataSource;
    
        /**
         * 事務隔離級別
         */
        protected TransactionIsolationLevel level;
    
        /**
         * 事務是否自動提交
         */
        protected boolean autoCommmit;
    
        public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
            dataSource = ds;
            level = desiredLevel;
            autoCommmit = desiredAutoCommit;
        }
    
        public JdbcTransaction(Connection connection) {
            this.connection = connection;
        }
    
        @Override
        public Connection getConnection() throws SQLException {
            if (connection == null) {
                openConnection();
            }
            return connection;
        }
    
        /**
         * 事務提交,通過connection.commit()
         */
        @Override
        public void commit() throws SQLException {
            if (connection != null && !connection.getAutoCommit()) {
                if (log.isDebugEnabled()) {
                    log.debug("Committing JDBC Connection [" + connection + "]");
                }
                connection.commit();
            }
        }
    
        /**
         * 事務回滾,通過connection.rollback();
         */
        @Override
        public void rollback() throws SQLException {
            if (connection != null && !connection.getAutoCommit()) {
                if (log.isDebugEnabled()) {
                    log.debug("Rolling back JDBC Connection [" + connection + "]");
                }
                connection.rollback();
            }
        }
    
        /**
         * 關閉數據庫連接
         */
        @Override
        public void close() throws SQLException {
            if (connection != null) {
                resetAutoCommit();
                if (log.isDebugEnabled()) {
                    log.debug("Closing JDBC Connection [" + connection + "]");
                }
                connection.close();
            }
        }
    
        protected void setDesiredAutoCommit(boolean desiredAutoCommit) {
            try {
                if (connection.getAutoCommit() != desiredAutoCommit) {
                    if (log.isDebugEnabled()) {
                        log.debug("Setting autocommit to " + desiredAutoCommit + " on JDBC Connection [" + connection + "]");
                    }
                    connection.setAutoCommit(desiredAutoCommit);
                }
            } catch (SQLException e) {
                // Only a very poorly implemented driver would fail here,
                // and there's not much we can do about that.
                throw new TransactionException("Error configuring AutoCommit.  "
                        + "Your driver may not support getAutoCommit() or setAutoCommit(). "
                        + "Requested setting: " + desiredAutoCommit + ".  Cause: " + e, e);
            }
        }
    
        protected void resetAutoCommit() {
            try {
                if (!connection.getAutoCommit()) {
                    // MyBatis does not call commit/rollback on a connection if just selects were performed.
                    // Some databases start transactions with select statements
                    // and they mandate a commit/rollback before closing the connection.
                    // A workaround is setting the autocommit to true before closing the connection.
                    // Sybase throws an exception here.
                    if (log.isDebugEnabled()) {
                        log.debug("Resetting autocommit to true on JDBC Connection [" + connection + "]");
                    }
                    connection.setAutoCommit(true);
                }
            } catch (SQLException e) {
                if (log.isDebugEnabled()) {
                    log.debug("Error resetting autocommit to true "
                            + "before closing the connection.  Cause: " + e);
                }
            }
        }
    
        protected void openConnection() throws SQLException {
            if (log.isDebugEnabled()) {
                log.debug("Opening JDBC Connection");
            }
            connection = dataSource.getConnection();
            if (level != null) {
                connection.setTransactionIsolation(level.getLevel());
            }
            setDesiredAutoCommit(autoCommmit);
        }
    
        @Override
        public Integer getTimeout() throws SQLException {
            return null;
        }
    
    }
    

    Mybatis Managed事務

    ManagedTransaction是讓容器來管理事務Transaction的整個生命周期,使用ManagedTransaction的commit和rollback功能不會對事務有任何的影響,什么都不會做,它將事務管理的權利移交給了容器來實現。

    如下Managed的實現代碼:

    /**
     * 讓容器來管理事務的整個生命周期,ManagedTransaction的操作不會對數據庫產生影響,默認情況下它的數據庫連接是關閉的
     *
     * @author kaifeng
     * @author Clinton Begin
     * @see ManagedTransactionFactory
     */
    public class ManagedTransaction implements Transaction {
    
        private static final Log log = LogFactory.getLog(ManagedTransaction.class);
    
        /**
         * 數據源
         */
        private DataSource dataSource;
    
        /**
         * 事務隔離級別
         */
        private TransactionIsolationLevel level;
    
        /**
         * 數據庫連接對象
         */
        private Connection connection;
    
        /**
         * 是否關閉數據庫連接
         */
        private final boolean closeConnection;
    
        public ManagedTransaction(Connection connection, boolean closeConnection) {
            this.connection = connection;
            this.closeConnection = closeConnection;
        }
    
        public ManagedTransaction(DataSource ds, TransactionIsolationLevel level, boolean closeConnection) {
            this.dataSource = ds;
            this.level = level;
            this.closeConnection = closeConnection;
        }
    
        @Override
        public Connection getConnection() throws SQLException {
            if (this.connection == null) {
                openConnection();
            }
            return this.connection;
        }
    
        /**
         * 事務提交,不做任何操作
         */
        @Override
        public void commit() throws SQLException {
            // Does nothing
        }
    
        /**
         * 事務回滾,不做任何操作
         */
        @Override
        public void rollback() throws SQLException {
            // Does nothing
        }
    
        /**
         * 事務關閉,不做任何操作
         */
        @Override
        public void close() throws SQLException {
            if (this.closeConnection && this.connection != null) {
                if (log.isDebugEnabled()) {
                    log.debug("Closing JDBC Connection [" + this.connection + "]");
                }
                this.connection.close();
            }
        }
    
        protected void openConnection() throws SQLException {
            if (log.isDebugEnabled()) {
                log.debug("Opening JDBC Connection");
            }
            this.connection = this.dataSource.getConnection();
            if (this.level != null) {
                this.connection.setTransactionIsolation(this.level.getLevel());
            }
        }
    
        @Override
        public Integer getTimeout() throws SQLException {
            return null;
        }
    
    }
    

    在實踐中,MyBatis通常會與Spring集成使用,數據庫的事務是交給Spring進行管理的,在后文我們會介紹Transaction接口的另一個實現—SpringManagedTransaction

    版權聲明:本文為u010647035原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
    本文鏈接:https://blog.csdn.net/u010647035/article/details/81589222

    智能推薦

    requests實現全自動PPT模板

    http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...

    Linux C系統編程-線程互斥鎖(四)

    互斥鎖 互斥鎖也是屬于線程之間處理同步互斥方式,有上鎖/解鎖兩種狀態。 互斥鎖函數接口 1)初始化互斥鎖 pthread_mutex_init() man 3 pthread_mutex_init (找不到的情況下首先 sudo apt-get install glibc-doc sudo apt-get install manpages-posix-dev) 動態初始化 int pthread_...

    統計學習方法 - 樸素貝葉斯

    引入問題:一機器在良好狀態生產合格產品幾率是 90%,在故障狀態生產合格產品幾率是 30%,機器良好的概率是 75%。若一日第一件產品是合格品,那么此日機器良好的概率是多少。 貝葉斯模型 生成模型與判別模型 判別模型,即要判斷這個東西到底是哪一類,也就是要求y,那就用給定的x去預測。 生成模型,是要生成一個模型,那就是誰根據什么生成了模型,誰就是類別y,根據的內容就是x 以上述例子,判斷一個生產出...

    styled-components —— React 中的 CSS 最佳實踐

    https://zhuanlan.zhihu.com/p/29344146 Styled-components 是目前 React 樣式方案中最受關注的一種,它既具備了 css-in-js 的模塊化與參數化優點,又完全使用CSS的書寫習慣,不會引起額外的學習成本。本文是 styled-components 作者之一 Max Stoiber 所寫,首先總結了前端組件化樣式中的最佳實踐原則,然后在此基...

    猜你喜歡

    基于TCP/IP的網絡聊天室用Java來實現

    基于TCP/IP的網絡聊天室實現 開發工具:eclipse 開發環境:jdk1.8 發送端 接收端 工具類 運行截圖...

    19.vue中封裝echarts組件

    19.vue中封裝echarts組件 1.效果圖 2.echarts組件 3.使用組件 按照組件格式整理好數據格式 傳入組件 home.vue 4.接口返回數據格式...

    劍指Offer39-調整數組順序使奇數位于偶數前面

    一開始想著用冒泡排序的方法來做,但是bug還是很多,后來看了評論區答案,發現直接空間換時間是最簡單的,而且和快排的寫法是類似的。...

    【一只蒟蒻的刷題歷程】【藍橋杯】歷屆試題 九宮重排 (八數碼問題:BFS+集合set)

    資源限制 時間限制:1.0s 內存限制:256.0MB 問題描述 如下面第一個圖的九宮格中,放著 1~8 的數字卡片,還有一個格子空著。與空格子相鄰的格子中的卡片可以移動到空格中。經過若干次移動,可以形成第二個圖所示的局面。 我們把第一個圖的局面記為:12345678. 把第二個圖的局面記為:123.46758 顯然是按從上到下,從左到右的順序記錄數字,空格記為句點。 本題目的任務是已知九宮的初態...

    dataV組件容器寬高發生變化后,組件不會自適應解決方法

    項目中需要大屏幕數據展示,于是使用了dataV組件,但是使用是發現拖動瀏覽器邊框,dataV組件顯示異常,如圖: 于是查了官網,官網的解釋如下:   于是按照官網的意思編寫代碼: 于是可以自適應了...

    精品国产乱码久久久久久蜜桃不卡