YSOcean 阅读(34) 评论(0)

  上一篇博客我们介绍了mybatis中关于数据源的配置原理,本篇博客介绍mybatis的事务管理。

  对于事务,我们是在mybatis-configuration.xml 文件中配置的:

  

  关于解析 <environments />标签在上一篇数据源的配置我们已经介绍了,不了解的可以参考上篇博客。

1、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 接口便为我们定义了这一套操作:

  

 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提供了两种不同的事务管理器:

  

  这两种事务管理器的获取也是采用了工厂模式。下面我们来分别看看这两种事务管理器。

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 }