zoukankan      html  css  js  c++  java
  • mybatis源码解读(四)——事务的配置

      上一篇博客我们介绍了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 }

      

      

  • 相关阅读:
    UML
    Jenkins(Jenkins的安装配置)
    SpringCloud:Config配置中心
    IDEA将项目上传到GitHub
    JS—高阶函数
    项目中git操作
    互联网概述
    ESLint的extends
    vue3 + TS + eslint 实现代码格式化以及代码规范提示
    vue3基础使用
  • 原文地址:https://www.cnblogs.com/ysocean/p/9004790.html
Copyright © 2011-2022 走看看