Mybatis管理事务是分为两种方式:
(1)使用JDBC的事务管理机制,就是利用java.sql.Connection对象完成对事务的提交
(2)使用MANAGED的事务管理机制,这种机制mybatis自身不会去实现事务管理,而是让程序的容器(JBOSS,WebLogic)来实现对事务的管理
JdbcTransaction
本文主要是学习第一种方式:
先来复习一下JDBC的事务:
获取数据库连接:conncetion对象
开始事务:conncetion.setAutoCommit(false),false表示不自动提交事务;
事务提交:conncetion.commit();
事务回滚:conncetion.rollback();
当我们使用MyBatis进行增删改操作的时候,如果不提交事务,就没有办法更新数据库,因为会自动回滚,这表明,mybatis自动为我们开始了事务,而且设置为不自动提交事务。
例如下面的代码执行删除操作:
//加载核心配置文件
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();
打印了一下日志:
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 809762318.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@3043fe0e]
DEBUG [main] - ==> Preparing: delete from user where id = ?
DEBUG [main] - ==> Parameters: 31(Integer)
DEBUG [main] - <== Updates: 1
DEBUG [main] - Committing JDBC Connection [com.mysql.jdbc.JDBC4Connection@3043fe0e]
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@3043fe0e]
DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@3043fe0e]
DEBUG [main] - Returned connection 809762318 to pool.
从日志中发现,在执行删除操作的时候,MyBatis显示通过JDBC连接了数据库,然后设置了自动提交为false,这也证明了,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进行事务管理的时候,会默认开始事务,并设置不自动提交事务