做网站的备案/网络营销服务的特点有哪些
mybaits源码分析(三) 事务管理
前言:在mybaits源码分析(一) 核心执行流程事务这一块几乎是没讲到过。本篇主要讲解mybaits事务管理的实现机制,介绍mybaits事务设计接口和不同的实现,以及不同的事务实现JdbcTransaction 和 ManagedTransaction的具体实现不同,并且对mybaits配置文件解析,创建事务对象和实际使用的流程进行了分析。
一、概述:
对数据库的事务而言,应该具有以下几点:创建(create)、提交(commit)、回滚(rollback)、关闭(close)。对应地,MyBatis将事务抽象成了Transaction接口,其接口定义如下:
public interface Transaction {Connection getConnection() throws SQLException;void commit() throws SQLException;void rollback() throws SQLException;void close() throws SQLException;}
MyBatis的事务管理分为两种形式:
1) 使用JDBC事务管理(JdbcTransaction),即用java.sql.Connection完成事务的创建、提交、回滚、关闭。
2) 使用MANAGED事务管理(ManagedTransaction),这种机制MyBatis自身不会去实现事务管理,由提供数据源或链接的外层框架或容器实现。
二、事务相关类
1、主要类介绍
Transaction : 事务接口
TransactionFactory : 负责创建事务的工厂接口
JdbcTransactionFactory : 负责创建jdbc事务的工厂
JdbcTransaction:Jdbc事务实现
ManagedTransactionFactory:负责创建Managed事务的工厂
ManagedTransaction:Managed事务的实现
2、类图
3、JdbcTransaction的实现
JdbcTransaction直接使用JDB的提交和回滚事务管理机制,它依赖于从dataSource中取得链接connection,connection对象获取被延迟到调用getConnection,如果autocommit设置为on,那么它会忽略commit和rollback。
直观的讲,就是JdbcTransaction是使用java.sql.Connection的commit和rollback,JdbcTransaction相当于对java.sql.Connection的事务处理进行了一次包装,Transaction事务管理都是通过java.sql.Connection实现。
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;}public Connection getConnection() throws SQLException {if (connection == null) {openConnection();}return connection;}public void commit() throws SQLException {if (connection != null && !connection.getAutoCommit()) {if (log.isDebugEnabled()) {log.debug("Committing JDBC Connection [" + connection + "]");}connection.commit();}}public void rollback() throws SQLException {if (connection != null && !connection.getAutoCommit()) {if (log.isDebugEnabled()) {log.debug("Rolling back JDBC Connection [" + connection + "]");}connection.rollback();}}public void close() throws SQLException {if (connection != null) {resetAutoCommit();if (log.isDebugEnabled()) {log.debug("Closing JDBC Connection [" + connection + "]");}connection.close();}}// ..... 省略}
4、ManagedTransaction实现
ManagedTransaction让容器来管理事务Transaction的整个生命周期,意思就是说,使用ManagedTransaction的commit和rollback功能不会对事务有任何的影响,它什么都不会做,它将事务管理的权利移交给了容器来实现。
public class ManagedTransaction implements Transaction {private static final Log log = LogFactory.getLog(ManagedTransaction.class);private DataSource dataSource;private TransactionIsolationLevel level;private Connection connection;private 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;}public Connection getConnection() throws SQLException {if (this.connection == null) {openConnection();}return this.connection;}public void commit() throws SQLException {// Does nothing}public void rollback() throws SQLException {// Does nothing}public void close() throws SQLException {if (this.closeConnection && this.connection != null) {if (log.isDebugEnabled()) {log.debug("Closing JDBC Connection [" + this.connection + "]");}this.connection.close();}}
三、事务相关配置加载
1、配置文件
下面指定了一个mybaits的数据源的xml配置案例<environments default="development"><environment id="development"><!-- 事务:JDBC(简单jdbc的模式)或MANAGED-(不管理-比如交给spring)--><transactionManager type="JDBC"></transactionManager><!-- 配置数据源,采用mybatis连接池 --><dataSource type="POOLED"><property name="driver" value="${db.driver}" /><property name="url" value="${db.url}" /><property name="username" value="${db.username}" /><property name="password" value="${db.password}" /></dataSource></environment></environments>
2、我们从解析environments节点查看过程
Mybaits根据配置文件指定的事务管理器的类型字符串type(JDBC or MANAGED),来决定创建不同的事务工厂类, 并且把创建的事务工厂TransactionFactory和数据源包装到Environment对象中。
private void environmentsElement(XNode context) throws Exception {if (context != null) {if (environment == null) {environment = context.getStringAttribute("default"); // 默认的id}for (XNode child : context.getChildren()) {String id = child.getStringAttribute("id"); if (isSpecifiedEnvironment(id)) { // 当前环境的配置// 事务工厂 : 具体是根据transactionManager的type属性指定。TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));// 数据源工厂DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));DataSource dataSource = dsFactory.getDataSource();Environment.Builder environmentBuilder = new Environment.Builder(id).transactionFactory(txFactory).dataSource(dataSource);configuration.setEnvironment(environmentBuilder.build());}}}}
一个Configuration中只会创建一个Environment对象,一个Environment对象保存一个TransactionFactory和数据源。
四、事务对象的使用
上面讲了,我们解析配置,讲事务工厂类TransactionFactory和数据源包装到了Environment对象中,而Environment对象 是全局Configuration的一个属性,下面我们看看事务相关类的使用。
1、事务对象在那个时候创建?
我们知道创建一个SqlSession的时候,会对应的创建一个Executor,而Executor是我们实际执行增删改查的类,那么事务就是创建SqlSession的时候,包装到Executor内。我们直接看SqlSessionFactory的openSession的调用openSessionFromDataSource。
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {Transaction tx = null;try {final Environment environment = configuration.getEnvironment();// 得到事务工厂final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);// 创建事务,并且传入了数据源、事务隔离级别、是否自动提交等方法参数tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);// 创建执行器 final Executor executor = configuration.newExecutor(tx, execType);// 组装executor和configuration到DefaultSqlSessionreturn new DefaultSqlSession(configuration, executor, autoCommit);} catch (Exception e) {closeTransaction(tx); // may have fetched a connection so lets call close()throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);} finally {ErrorContext.instance().reset();}}
2、事务何时开启?如何提交回滚?
如果我们需要开启事务,那么需要设置在创建SqlSession的使用,调用 SqlSessionFactory.openSession(flase), 底层调用的是JDBC的
connection.setAutoCommit(desiredAutoCommit) 如果事务提交或回滚,我们调用SqlSessionFactory.close(),实际调用的是executor的close,然后executor会调用Trancation的close。
范例代码:SqlSession sqlSession = SqlSessionFactory.openSession(false);sqlSession.update('statementId',param);sqlSession.selectOne('queryStatementID',param);sqlSession.commit();sqlSession.close();