天天看點

mybatis源碼解讀(四)——事務的配置

  上一篇部落格我們介紹了mybatis中關于 資料源的配置原理

,本篇部落格介紹mybatis的事務管理。

  對于事務,我們是在mybatis-configuration.xml 檔案中配置的:

  

mybatis源碼解讀(四)——事務的配置
  關于解析 <environments />标簽在上一篇資料源的配置我們已經介紹了,不了解的可以參考上篇部落格。

1、mybatis 支援的事務類圖

  mybatis 支援的所有事務的所有類都在如下包中:

mybatis源碼解讀(四)——事務的配置

  下面通過類圖來了解該包中所有類的關系:

mybatis源碼解讀(四)——事務的配置

2、mybatis 支援的兩種事務類型管理器

  通過配置檔案中的 type 屬性:

<transactionManager type="JDBC" />      

  我們可以配置不同的事務管理器來管理事務。在mybatis中支援兩種事務類型管理器,分别是:

  ①、type = "JDBC":這個配置就是直接使用了 JDBC 的送出和復原設定,它依賴于從資料源得到的連接配接來管理事務作用域。

  ②、type="MANAGED":這個配置幾乎沒做什麼。它從來不送出或復原一個連接配接,而是讓容器來管理事務的整個生命周期(比如 JEE 應用伺服器的上下文)。 預設情況下它會關閉連接配接,然而一些容器并不希望這樣,是以需要将 closeConnection 屬性設定為 false 來阻止它預設的關閉行為。例如:

1 <transactionManager type="MANAGED">
2   <property name="closeConnection" value="false"/>
3 </transactionManager>      

  注意:和資料源配置一樣,通常項目中我們不會單獨使用 mybatis 來管理事務。比如選擇架構 Spring +mybatis,這時候沒有必要配置事務管理器, 因為 Spring 子產品會使用自帶的管理器來覆寫前面的配置。

  再回頭看看在 mybatis 的 org.apache.ibatis.transaction 包下的所有類,也就是上面的類圖。mybatis的事務首先會定義一個事務接口 Transaction,

3、初始化事務管理器

  我們說事務(Transaction),一般是指要做的或所做的事情。在資料庫中,事務具有如下四個屬性:

  ①、原子性(atomicity):一個事務是一個不可分割的工作機關,事務中包括的諸操作要麼都做,要麼都不做。

  ②、一緻性(consistency):事務必須是使資料庫從一個一緻性狀态變到另一個一緻性狀态。一緻性與原子性是密切相關的。

  ③、隔離性(isolation):一個事務的執行不能被其他事務幹擾。即一個事務内部的操作及使用的資料對并發的其他事務是隔離的,并發執行的各個事務之間不能互相幹擾。

  ④、持久性(durability):持久性也稱永久性(permanence),指一個事務一旦送出,它對資料庫中資料的改變就應該是永久性的。接下來的其他操作或故障不應該對其有任何影響。

  這也就是常說的事務 ACID 特性。而在程式中,對于事務的操作通常是:

  1、建立事務(create)

  2、送出事務(commit)

  3、復原事務(rollback)

  4、關閉事務(close)

  在mybatis的 org.apache.ibatis.transaction 包下的 Transaction 接口便為我們定義了這一套操作:

mybatis源碼解讀(四)——事務的配置
mybatis源碼解讀(四)——事務的配置
mybatis源碼解讀(四)——事務的配置
1 /**
 2  *    Copyright 2009-2016 the original author or authors.
 3  *
 4  *    Licensed under the Apache License, Version 2.0 (the "License");
 5  *    you may not use this file except in compliance with the License.
 6  *    You may obtain a copy of the License at
 7  *
 8  *       http://www.apache.org/licenses/LICENSE-2.0
 9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16 package org.apache.ibatis.transaction;
17 
18 import java.sql.Connection;
19 import java.sql.SQLException;
20 
21 /**
22  * Wraps a database connection.
23  * Handles the connection lifecycle that comprises: its creation, preparation, commit/rollback and close. 
24  *
25  * @author Clinton Begin
26  */
27 public interface Transaction {
28 
29   /**
30    * Retrieve inner database connection
31    * @return DataBase connection
32    * @throws SQLException
33    */
34   Connection getConnection() throws SQLException;
35 
36   /**
37    * Commit inner database connection.
38    * @throws SQLException
39    */
40   void commit() throws SQLException;
41 
42   /**
43    * Rollback inner database connection.
44    * @throws SQLException
45    */
46   void rollback() throws SQLException;
47 
48   /**
49    * Close inner database connection.
50    * @throws SQLException
51    */
52   void close() throws SQLException;
53 
54   /**
55    * Get transaction timeout if set
56    * @throws SQLException
57    */
58   Integer getTimeout() throws SQLException;
59   
60 }      

View Code

  同時,mybatis為了擷取事務采用了工廠模式,定義了一個工廠接口:TransactionFactory

mybatis源碼解讀(四)——事務的配置

  通過實作該工廠接口,mybatis提供了兩種不同的事務管理器:

mybatis源碼解讀(四)——事務的配置

  這兩種事務管理器的擷取也是采用了工廠模式。下面我們來分别看看這兩種事務管理器。

4、JdbcTransaction

  當在配置檔案中配置:type = "JDBC"時,就是用 JdbcTransaction 來管理事務。使用了 JDBC 的送出和復原設定,它依賴于從資料源得到的連接配接來管理事務作用域。

  代碼如下:

1 public class JdbcTransaction implements Transaction {
  2 
  3   private static final Log log = LogFactory.getLog(JdbcTransaction.class);
  4   //資料庫連接配接
  5   protected Connection connection;
  6   //資料源
  7   protected DataSource dataSource;
  8   //隔離級别
  9   protected TransactionIsolationLevel level;
 10   //是否自動送出
 11   protected boolean autoCommmit;
 12 
 13   public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
 14     dataSource = ds;
 15     level = desiredLevel;
 16     autoCommmit = desiredAutoCommit;
 17   }
 18 
 19   public JdbcTransaction(Connection connection) {
 20     this.connection = connection;
 21   }
 22 
 23   @Override
 24   public Connection getConnection() throws SQLException {
 25     if (connection == null) {
 26       openConnection();
 27     }
 28     return connection;
 29   }
 30 
 31   //調用connection.commit()來實作
 32   @Override
 33   public void commit() throws SQLException {
 34     if (connection != null && !connection.getAutoCommit()) {
 35       if (log.isDebugEnabled()) {
 36         log.debug("Committing JDBC Connection [" + connection + "]");
 37       }
 38       connection.commit();
 39     }
 40   }
 41 
 42   //調用connection.rollback()來實作
 43   @Override
 44   public void rollback() throws SQLException {
 45     if (connection != null && !connection.getAutoCommit()) {
 46       if (log.isDebugEnabled()) {
 47         log.debug("Rolling back JDBC Connection [" + connection + "]");
 48       }
 49       connection.rollback();
 50     }
 51   }
 52 
 53   //調用connection.close()來實作
 54   @Override
 55   public void close() throws SQLException {
 56     if (connection != null) {
 57       resetAutoCommit();
 58       if (log.isDebugEnabled()) {
 59         log.debug("Closing JDBC Connection [" + connection + "]");
 60       }
 61       connection.close();
 62     }
 63   }
 64 
 65   protected void setDesiredAutoCommit(boolean desiredAutoCommit) {
 66     try {
 67       //事務送出狀态不一緻
 68       if (connection.getAutoCommit() != desiredAutoCommit) {
 69         if (log.isDebugEnabled()) {
 70           log.debug("Setting autocommit to " + desiredAutoCommit + " on JDBC Connection [" + connection + "]");
 71         }
 72         connection.setAutoCommit(desiredAutoCommit);
 73       }
 74     } catch (SQLException e) {
 75       // Only a very poorly implemented driver would fail here,
 76       // and there's not much we can do about that.
 77       throw new TransactionException("Error configuring AutoCommit.  "
 78           + "Your driver may not support getAutoCommit() or setAutoCommit(). "
 79           + "Requested setting: " + desiredAutoCommit + ".  Cause: " + e, e);
 80     }
 81   }
 82 
 83   protected void resetAutoCommit() {
 84     try {
 85       if (!connection.getAutoCommit()) {
 86         // MyBatis does not call commit/rollback on a connection if just selects were performed.
 87         // Some databases start transactions with select statements
 88         // and they mandate a commit/rollback before closing the connection.
 89         // A workaround is setting the autocommit to true before closing the connection.
 90         // Sybase throws an exception here.
 91         if (log.isDebugEnabled()) {
 92           log.debug("Resetting autocommit to true on JDBC Connection [" + connection + "]");
 93         }
 94         connection.setAutoCommit(true);
 95       }
 96     } catch (SQLException e) {
 97       if (log.isDebugEnabled()) {
 98         log.debug("Error resetting autocommit to true "
 99           + "before closing the connection.  Cause: " + e);
100       }
101     }
102   }
103 
104   protected void openConnection() throws SQLException {
105     if (log.isDebugEnabled()) {
106       log.debug("Opening JDBC Connection");
107     }
108     connection = dataSource.getConnection();
109     if (level != null) {
110       connection.setTransactionIsolation(level.getLevel());
111     }
112     setDesiredAutoCommit(autoCommmit);
113   }
114 
115   @Override
116   public Integer getTimeout() throws SQLException {
117     return null;
118   }
119   
120 }      

5、ManagedTransaction

  ManagedTransaction的代碼實作幾乎都是一個空的方法,它選擇讓容器來管理事務的整個生命周期(比如 JEE 應用伺服器的上下文)。

1 /**
  2  *    Copyright 2009-2016 the original author or authors.
  3  *
  4  *    Licensed under the Apache License, Version 2.0 (the "License");
  5  *    you may not use this file except in compliance with the License.
  6  *    You may obtain a copy of the License at
  7  *
  8  *       http://www.apache.org/licenses/LICENSE-2.0
  9  *
 10  *    Unless required by applicable law or agreed to in writing, software
 11  *    distributed under the License is distributed on an "AS IS" BASIS,
 12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  *    See the License for the specific language governing permissions and
 14  *    limitations under the License.
 15  */
 16 package org.apache.ibatis.transaction.managed;
 17 
 18 import java.sql.Connection;
 19 import java.sql.SQLException;
 20 import javax.sql.DataSource;
 21 
 22 import org.apache.ibatis.logging.Log;
 23 import org.apache.ibatis.logging.LogFactory;
 24 import org.apache.ibatis.session.TransactionIsolationLevel;
 25 import org.apache.ibatis.transaction.Transaction;
 26 
 27 /**
 28  * {@link Transaction} that lets the container manage the full lifecycle of the transaction.
 29  * Delays connection retrieval until getConnection() is called.
 30  * Ignores all commit or rollback requests.
 31  * By default, it closes the connection but can be configured not to do it.
 32  *
 33  * @author Clinton Begin
 34  *
 35  * @see ManagedTransactionFactory
 36  */
 37 public class ManagedTransaction implements Transaction {
 38 
 39   private static final Log log = LogFactory.getLog(ManagedTransaction.class);
 40 
 41   private DataSource dataSource;
 42   private TransactionIsolationLevel level;
 43   private Connection connection;
 44   private boolean closeConnection;
 45 
 46   public ManagedTransaction(Connection connection, boolean closeConnection) {
 47     this.connection = connection;
 48     this.closeConnection = closeConnection;
 49   }
 50 
 51   public ManagedTransaction(DataSource ds, TransactionIsolationLevel level, boolean closeConnection) {
 52     this.dataSource = ds;
 53     this.level = level;
 54     this.closeConnection = closeConnection;
 55   }
 56 
 57   @Override
 58   public Connection getConnection() throws SQLException {
 59     if (this.connection == null) {
 60       openConnection();
 61     }
 62     return this.connection;
 63   }
 64 
 65   @Override
 66   public void commit() throws SQLException {
 67     // Does nothing
 68   }
 69 
 70   @Override
 71   public void rollback() throws SQLException {
 72     // Does nothing
 73   }
 74 
 75   @Override
 76   public void close() throws SQLException {
 77     if (this.closeConnection && this.connection != null) {
 78       if (log.isDebugEnabled()) {
 79         log.debug("Closing JDBC Connection [" + this.connection + "]");
 80       }
 81       this.connection.close();
 82     }
 83   }
 84 
 85   protected void openConnection() throws SQLException {
 86     if (log.isDebugEnabled()) {
 87       log.debug("Opening JDBC Connection");
 88     }
 89     this.connection = this.dataSource.getConnection();
 90     if (this.level != null) {
 91       this.connection.setTransactionIsolation(this.level.getLevel());
 92     }
 93   }
 94 
 95   @Override
 96   public Integer getTimeout() throws SQLException {
 97     return null;
 98   }
 99 
100 }      

作者:

YSOcean

出處:

http://www.cnblogs.com/ysocean/

本文版權歸作者所有,歡迎轉載,但未經作者同意不能轉載,否則保留追究法律責任的權利。