code-learning/mybatis/08-mybatis-事务模块.md

467 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 精尽 MyBatis 源码分析 —— 事务模块
# 1. 概述
本文,我们来分享 MyBatis 的事务模块,对应 `transaction` 包。如下图所示:[`transaction` 包](http://static.iocoder.cn/images/MyBatis/2020_01_19/01.png)
在 [《精尽 MyBatis 源码解析 —— 项目结构一览》](http://svip.iocoder.cn/MyBatis/intro) 中,简单介绍了这个模块如下:
> MyBatis 对数据库中的事务进行了抽象,其自身提供了**相应的事务接口和简单实现**。
>
> 在很多场景中MyBatis 会与 Spring 框架集成,并由 **Spring 框架管理事务**。
本文涉及的类如下图所示:[类图](http://static.iocoder.cn/images/MyBatis/2020_01_19/02.png)
下面,我们就一起来看看具体的源码实现。
# 2. Transaction
`org.apache.ibatis.transaction.Transaction` ,事务接口。代码如下:
```
// Transaction.java
public interface Transaction {
/**
* 获得连接
*
* Retrieve inner database connection
*
* @return DataBase connection
* @throws SQLException
*/
Connection getConnection() throws SQLException;
/**
* 事务提交
*
* Commit inner database connection.
*
* @throws SQLException
*/
void commit() throws SQLException;
/**
* 事务回滚
*
* Rollback inner database connection.
*
* @throws SQLException
*/
void rollback() throws SQLException;
/**
* 关闭连接
*
* Close inner database connection.
*
* @throws SQLException
*/
void close() throws SQLException;
/**
* 获得事务超时时间
*
* Get transaction timeout if set
*
* @throws SQLException
*/
Integer getTimeout() throws SQLException;
}
```
- 连接相关
- `#getConnection()` 方法,获得连接。
- `#close()` 方法,关闭连接。
- 事务相关
- `#commit()` 方法,事务提交。
- `#rollback()` 方法,事务回滚。
- `#getTimeout()` 方法,事务超时时间。实际上,目前这个方法都是空实现。
## 2.1 JdbcTransaction
`org.apache.ibatis.transaction.jdbc.JdbcTransaction` ,实现 Transaction 接口,基于 JDBC 的事务实现类。代码如下:
```
// JdbcTransaction.java
public class JdbcTransaction implements Transaction {
private static final Log log = LogFactory.getLog(JdbcTransaction.class);
/**
* Connection 对象
*/
protected Connection connection;
/**
* DataSource 对象
*/
protected DataSource dataSource;
/**
* 事务隔离级别
*/
protected TransactionIsolationLevel level;
/**
* 是否自动提交
*/
protected boolean autoCommit;
public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
dataSource = ds;
level = desiredLevel;
autoCommit = desiredAutoCommit;
}
public JdbcTransaction(Connection connection) {
this.connection = connection;
}
@Override
public Connection getConnection() throws SQLException {
// 连接为空,进行创建
if (connection == null) {
openConnection();
}
return connection;
}
@Override
public void commit() throws SQLException {
// 非自动提交,则执行提交事务
if (connection != null && !connection.getAutoCommit()) {
if (log.isDebugEnabled()) {
log.debug("Committing JDBC Connection [" + connection + "]");
}
connection.commit();
}
}
@Override
public void rollback() throws SQLException {
// 非自动提交。则回滚事务
if (connection != null && !connection.getAutoCommit()) {
if (log.isDebugEnabled()) {
log.debug("Rolling back JDBC Connection [" + connection + "]");
}
connection.rollback();
}
}
@Override
public void close() throws SQLException {
if (connection != null) {
// 重置连接为自动提交
resetAutoCommit();
if (log.isDebugEnabled()) {
log.debug("Closing JDBC Connection [" + connection + "]");
}
// 关闭连接
connection.close();
}
}
/**
* 设置指定的 autoCommit 属性
*
* @param desiredAutoCommit 指定的 autoCommit 属性
*/
protected void setDesiredAutoCommit(boolean desiredAutoCommit) {
try {
if (connection.getAutoCommit() != desiredAutoCommit) {
if (log.isDebugEnabled()) {
log.debug("Setting autocommit to " + desiredAutoCommit + " on JDBC Connection [" + connection + "]");
}
connection.setAutoCommit(desiredAutoCommit);
}
} catch (SQLException e) {
// Only a very poorly implemented driver would fail here,
// and there's not much we can do about that.
throw new TransactionException("Error configuring AutoCommit. "
+ "Your driver may not support getAutoCommit() or setAutoCommit(). "
+ "Requested setting: " + desiredAutoCommit + ". Cause: " + e, e);
}
}
/**
* 重置 autoCommit 属性
*/
protected void resetAutoCommit() {
try {
if (!connection.getAutoCommit()) {
// MyBatis does not call commit/rollback on a connection if just selects were performed.
// Some databases start transactions with select statements
// and they mandate a commit/rollback before closing the connection.
// A workaround is setting the autocommit to true before closing the connection.
// Sybase throws an exception here.
if (log.isDebugEnabled()) {
log.debug("Resetting autocommit to true on JDBC Connection [" + connection + "]");
}
connection.setAutoCommit(true);
}
} catch (SQLException e) {
if (log.isDebugEnabled()) {
log.debug("Error resetting autocommit to true "
+ "before closing the connection. Cause: " + e);
}
}
}
/**
* 获得 Connection 对象
*
* @throws SQLException 获得失败
*/
protected void openConnection() throws SQLException {
if (log.isDebugEnabled()) {
log.debug("Opening JDBC Connection");
}
// 获得连接
connection = dataSource.getConnection();
// 设置隔离级别
if (level != null) {
connection.setTransactionIsolation(level.getLevel());
}
// 设置 autoCommit 属性
setDesiredAutoCommit(autoCommit);
}
@Override
public Integer getTimeout() throws SQLException {
return null;
}
}
```
- 简单,胖友瞄一瞄。
## 2.2 ManagedTransaction
`org.apache.ibatis.transaction.managed.ManagedTransaction` ,实现 Transaction 接口,基于容器管理的事务实现类。代码如下:
```
// ManagedTransaction.java
public class ManagedTransaction implements Transaction {
private static final Log log = LogFactory.getLog(ManagedTransaction.class);
/**
* Connection 对象
*/
private Connection connection;
/**
* DataSource 对象
*/
private DataSource dataSource;
/**
* 事务隔离级别
*/
private TransactionIsolationLevel level;
/**
* 是否关闭连接
*
* 这个属性是和 {@link org.apache.ibatis.transaction.jdbc.JdbcTransaction} 不同的
*/
private final 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;
}
@Override
public Connection getConnection() throws SQLException {
// 连接为空,进行创建
if (this.connection == null) {
openConnection();
}
return this.connection;
}
@Override
public void commit() throws SQLException {
// Does nothing
}
@Override
public void rollback() throws SQLException {
// Does nothing
}
@Override
public void close() throws SQLException {
// 如果开启关闭连接功能,则关闭连接
if (this.closeConnection && this.connection != null) {
if (log.isDebugEnabled()) {
log.debug("Closing JDBC Connection [" + this.connection + "]");
}
this.connection.close();
}
}
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());
}
}
@Override
public Integer getTimeout() throws SQLException {
return null;
}
}
```
- 和 JdbcTransaction 相比,少了 `autoCommit` 属性,空实现 `#commit()``#rollback()` 方法。因此,事务的管理,交给了容器。
- 简单了解下就成,实际没用过这个。
## 2.3 SpringManagedTransaction
`org.mybatis.spring.transaction.SpringManagedTransaction` ,实现 Transaction 接口,基于 Spring 管理的事务实现类。实际真正在使用的,本文暂时不分享,感兴趣的胖友可以自己先愁一愁 [SpringManagedTransaction](https://github.com/eddumelendez/mybatis-spring/blob/master/src/main/java/org/mybatis/spring/transaction/SpringManagedTransaction.java) 。
# 3. TransactionFactory
`org.apache.ibatis.transaction.TransactionFactory` Transaction 工厂接口。代码如下:
```
// TransactionFactory.java
public interface TransactionFactory {
/**
* Sets transaction factory custom properties.
*
* 设置工厂的属性
*
* @param props 属性
*/
void setProperties(Properties props);
/**
* Creates a {@link Transaction} out of an existing connection.
*
* 创建 Transaction 事务
*
* @param conn Existing database connection
* @return Transaction
* @since 3.1.0
*/
Transaction newTransaction(Connection conn);
/**
* Creates a {@link Transaction} out of a datasource.
*
* 创建 Transaction 事务
*
* @param dataSource DataSource to take the connection from
* @param level Desired isolation level
* @param autoCommit Desired autocommit
* @return Transaction
* @since 3.1.0
*/
Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);
}
```
## 3.1 JdbcTransactionFactory
`org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory` ,实现 TransactionFactory 接口JdbcTransaction 工厂实现类。代码如下:
```
// JdbcTransactionFactory.java
public class JdbcTransactionFactory implements TransactionFactory {
@Override
public void setProperties(Properties props) {
}
@Override
public Transaction newTransaction(Connection conn) {
// 创建 JdbcTransaction 对象
return new JdbcTransaction(conn);
}
@Override
public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
// 创建 JdbcTransaction 对象
return new JdbcTransaction(ds, level, autoCommit);
}
}
```
- 胖友随便瞅一眼。
## 3.2 ManagedTransactionFactory
`org.apache.ibatis.transaction.managed.ManagedTransactionFactory` ,实现 TransactionFactory 接口ManagedTransaction 工厂实现类。代码如下:
```
// ManagedTransactionFactory.java
public class ManagedTransactionFactory implements TransactionFactory {
/**
* 是否关闭连接
*/
private boolean closeConnection = true;
@Override
public void setProperties(Properties props) {
// 获得是否关闭连接属性
if (props != null) {
String closeConnectionProperty = props.getProperty("closeConnection");
if (closeConnectionProperty != null) {
closeConnection = Boolean.valueOf(closeConnectionProperty);
}
}
}
@Override
public Transaction newTransaction(Connection conn) {
// 创建 ManagedTransaction 对象
return new ManagedTransaction(conn, closeConnection);
}
@Override
public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
// Silently ignores autocommit and isolation level, as managed transactions are entirely
// controlled by an external manager. It's silently ignored so that
// code remains portable between managed and unmanaged configurations.
// 创建 ManagedTransaction 对象
return new ManagedTransaction(ds, level, closeConnection);
}
}
```
## 3.3 SpringManagedTransactionFactory
`org.mybatis.spring.transaction.SpringManagedTransactionFactory` ,实现 TransactionFactory 接口SpringManagedTransaction 工厂实现类。实际真正在使用的,本文暂时不分享,感兴趣的胖友可以自己先愁一愁 [SpringManagedTransactionFactory](https://github.com/eddumelendez/mybatis-spring/blob/c5834f93bd4a5879f86854fe188957787e56ef95/src/main/java/org/mybatis/spring/transaction/SpringManagedTransactionFactory.java) 。
# 666. 彩蛋
水文一篇,开心。
参考和推荐如下文章:
- 祖大俊 [《Mybatis3.3.x技术内幕Mybatis事务管理将颠覆你心中目前对事务的理解](https://my.oschina.net/zudajun/blog/666764)
- 徐郡明 [《MyBatis 技术内幕》](https://item.jd.com/12125531.html) 的 [「2.7 Transaction」](https://svip.iocoder.cn/MyBatis/transaction-package/#) 小节