MyBatis事务-----JdbcTransaction

Mybatis管理事务是分为两种方式:

(1)使用JDBC的事务管理机制,就是利用java.sql.Connection对象完成对事务的提交 
(2)使用MANAGED的事务管理机制,这种机制mybatis自身不会去实现事务管理,而是让程序的容器(JBOSS,WebLogic)来实现对事务的管理

JdbcTransaction

本文主要是学习第一种方式:

先来复习一下JDBC的事务: 
获取数据库连接:conncetion对象 
开始事务:conncetion.setAutoCommit(false),false表示不自动提交事务; 
事务提交:conncetion.commit(); 
事务回滚:conncetion.rollback();

例如下面的代码执行删除操作:

        //加载核心配置文件
        String resource = "sqlMapConfig.xml";
        InputStream in = Resources.getResourceAsStream(resource);
        //创建SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
        //创建SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //获得Mapper
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        userMapper.deleteUserById(31);
        //如果不提交,事务会自动回滚,无法插入数据到数据库
        sqlSession.commit();
        //释放资源
        sqlSession.close();

mybatis自动为我们开始了事务,而且设置为不自动提交事务。

但是事务是什么时候开启的呢,于是就去扒了一下源码。因为事务是包含在一次SqlSession会话中的,所以从创建会话开始分析,即SqlSessionFactory的openSession方法开始分析:

SqlSessionFactory是一个接口,它有两个实现类,DefaultSqlSessionFactory和SqlSessionManager,默认情况下使用的是DefaultSqlSessionFactory。 

DefaultSqlSessionFactory的openSession()方法:

public SqlSession openSession() {
        return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);
    }

调用了:openSessionFromDataSource方法:

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
        Transaction tx = null;
        DefaultSqlSession var8;
        try {
            //加载配置文件中的数据库信息
            Environment environment = this.configuration.getEnvironment();
            //创建事务工厂
            TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
            //实例化事务对象,传入数据源,事务等级,以及是否自动提交事务,但是通过查看源码发现,事务并没有在这里提交
            tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
            //这是一个执行的对象,是正操作数据库的对象。
            Executor executor = this.configuration.newExecutor(tx, execType);
            //实例化一个会话对象,并传入executor,当我们通过SqlSession向数据库发送指令时,最终都是调用的executor方法。
            var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
        } catch (Exception var12) {
            this.closeTransaction(tx);
            throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var12, var12);
        } finally {
            ErrorContext.instance().reset();
        }
        return var8;
    }

这里面有一个实例化了一个事务工厂TransactionFactory,TransactionFactory是一个接口:

它也有两个实现类:JdbcTransactionFactory和ManagedTransactionFactory,我们使用的是JDBC事务,所以使用JdbcTransactionFactory的newTransaction方法,创建JdbcTransaction。

查看JdbcTransaction源码:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.apache.ibatis.transaction.jdbc;

import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.session.TransactionIsolationLevel;
import org.apache.ibatis.transaction.Transaction;
import org.apache.ibatis.transaction.TransactionException;

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) {
        this.dataSource = ds;
        this.level = desiredLevel;
        this.autoCommmit = desiredAutoCommit;
    }

    public JdbcTransaction(Connection connection) {
        this.connection = connection;
    }

    //获取数据库连接
    public Connection getConnection() throws SQLException {
        if (this.connection == null) {
            this.openConnection();
        }

        return this.connection;
    }

    //事务提交
    public void commit() throws SQLException {
        if (this.connection != null && !this.connection.getAutoCommit()) {
            if (log.isDebugEnabled()) {
                log.debug("Committing JDBC Connection [" + this.connection + "]");
            }

            this.connection.commit();
        }

    }

    //事务回滚
    public void rollback() throws SQLException {
        if (this.connection != null && !this.connection.getAutoCommit()) {
            if (log.isDebugEnabled()) {
                log.debug("Rolling back JDBC Connection [" + this.connection + "]");
            }

            this.connection.rollback();
        }

    }

    //关闭资源
    public void close() throws SQLException {
        if (this.connection != null) {
            this.resetAutoCommit();
            if (log.isDebugEnabled()) {
                log.debug("Closing JDBC Connection [" + this.connection + "]");
            }

            this.connection.close();
        }

    }

    //设置事务是否自动提交
    protected void setDesiredAutoCommit(boolean desiredAutoCommit) {
        try {
            if (this.connection.getAutoCommit() != desiredAutoCommit) {
                if (log.isDebugEnabled()) {
                    log.debug("Setting autocommit to " + desiredAutoCommit + " on JDBC Connection [" + this.connection + "]");
                }

                this.connection.setAutoCommit(desiredAutoCommit);
            }

        } catch (SQLException var3) {
            throw new TransactionException("Error configuring AutoCommit.  Your driver may not support getAutoCommit() or setAutoCommit(). Requested setting: " + desiredAutoCommit + ".  Cause: " + var3, var3);
        }
    }

    //重置事务提交方式
    protected void resetAutoCommit() {
        try {
            if (!this.connection.getAutoCommit()) {
                if (log.isDebugEnabled()) {
                    log.debug("Resetting autocommit to true on JDBC Connection [" + this.connection + "]");
                }

                this.connection.setAutoCommit(true);
            }
        } catch (SQLException var2) {
            log.debug("Error resetting autocommit to true before closing the connection.  Cause: " + var2);
        }

    }

    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());
        }

        this.setDesiredAutoCommit(this.autoCommmit);
    }
}

在getConnection()方法中调用了openConnection()方法,获取数据库连接,设置了等级,开启了事务。

回到上面的openSessionFromDataSource方法,创建好事务对象后,紧接着又实例化了两个对象:一个是Executor对象,一个的DefaultSqlSession对象,并将这个对象返回,并没有调用getConnection()方法,所以事务的开始并不在OpenSession方法中,那么我猜测,事务的开始,是在准备执行向数据库发送Sql语句的时候,于是回到最上面执行删除的代码中:
 

//加载核心配置文件
        String resource = "sqlMapConfig.xml";
        InputStream in = Resources.getResourceAsStream(resource);
        //创建SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
        //创建SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //获得Mapper
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        userMapper.deleteUserById(31);
        //如果不提交,事务会自动回滚,无法插入数据到数据库
        sqlSession.commit();
        //释放资源
        sqlSession.close();

在userMapper.deleteUserById(31)这里打了一下断点,然后一路追踪到了SimpleExecutor发doUpdate方法中,SimpleExecutor是Executor的实现类:

public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
        Statement stmt = null;

        int var6;
        try {
            Configuration configuration = ms.getConfiguration();
            StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, (ResultHandler)null, (BoundSql)null);
            stmt = this.prepareStatement(handler, ms.getStatementLog());
            var6 = handler.update(stmt);
        } finally {
            this.closeStatement(stmt);
        }

        return var6;
    }

然后查看:prepareStatement()方法:

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
        Connection connection = this.getConnection(statementLog);
        Statement stmt = handler.prepare(connection);
        handler.parameterize(stmt);
        return stmt;
    }

看到了getConnection,继续追进去:

进入BaseExecutor的getConnection方法:

 protected Connection getConnection(Log statementLog) throws SQLException {
        Connection connection = this.transaction.getConnection();
        return statementLog.isDebugEnabled() ? ConnectionLogger.newInstance(connection, statementLog, this.queryStack) : connection;
    }

Connection connection = this.transaction.getConnection();这一句调用了传入的transaction的getConnection方法, 
这里的transaction使我们在opensession方法中,实例化,并传入的。至此终于找到事务在哪开启的了。

分析了一大堆:其实只要记住,MyBatis使用JDBC进行事务管理的时候,会默认开始事务,并设置不自动提交事务
 

package com.xf.app1;

import com.xf.app1.mapper.UserMapper;
import com.xf.app1.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

public class Xml_SqlSessionFactory {

    public static void main(String[] args) {
        SqlSessionFactory sqlSessionFactory;
        SqlSession sqlSession = null;
        //使用类加载器加载mybatis的配置文件(它也加载关联的映射文件)
        try {
            //mybatis配置文件
            String resource = "com/xf/app1/mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            //创建能执行映射文件中sql的sqlSession
             sqlSession = sqlSessionFactory.openSession();

             //获取mapper方式
            UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
            User user1 =userMapper.selectUser(1L) ;
            System.out.println(user1);

            userMapper.deleteUserById(1L);//这里删除是不会提交到数据库的

            //通过具体路径
            User user = sqlSession.selectOne("com.xf.app1.mapper.UserMapper.selectUser", 1);
            System.out.println(user);

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (sqlSession != null) {
                sqlSession.close();
            }
        }

    }

}

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值