该章将介绍Mybatis对事务的封装,事务模块所在的包位置如下:
各个类之间的关系图如下:
1、Transaction接口如下
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;
}
2、其实现类JdbcTransaction实现Transaction接口其中定义字段如下
/**
* connection 对象
*/
protected Connection connection;
/**
* 数据源
*/
protected DataSource dataSource;
/**
* 数据库隔离级别
*/
protected TransactionIsolationLevel level;
/**
* 是否自动提交
*/
protected boolean autoCommit;
在JdbcTransaction的构造函数中首先会初始化connection之外的三个字段,而connection是通过getConnection() 方法延时初始化化。
构造器如下:
public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
dataSource = ds;
level = desiredLevel;
autoCommit = desiredAutoCommit;
}
getConnection()方法如下
public Connection getConnection() throws SQLException {
// 如果connection为空,则创建
if (connection == null) {
openConnection();
}
return connection;
}
openConnection()方法主要是从数据源中获取连接并设置相关属性,如下
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);
}
setDesiredAutoCommit() 方法如下
protected void setDesiredAutoCommit(boolean desiredAutoCommit) {
try {
// 将事务提交方式设置为 desiredAutoCommit
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);
}
}
事务提交方法如下:
public void commit() throws SQLException {
// connection 不为 null 并且 connection不是自动提交
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();
}
}
关闭connection链接方法如下:
public void close() throws SQLException {
if (connection != null) {
// 将connection重置为自动提交
resetAutoCommit();
if (log.isDebugEnabled()) {
log.debug("Closing JDBC Connection [" + connection + "]");
}
// 关闭connection
connection.close();
}
}
resetAutoCommit()方法如下:
protected void resetAutoCommit() {
try {
if (!connection.getAutoCommit()) {
// MyBatis does not call commit/rollback on a connection if just selects were performed.
// 当执行查询时,mybatis不需要提交或者回滚
// 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);
}
}
}
3.ManagedTransaction 的实现更加简单,它同样依赖其中的 dataSource 宇段获取连接, 但其commit()、 rollback()方法都是空实现,事务的提交和回滚都是依靠容器管理的。Managed Transaction 中通过 closeConnection 字段的值控制数据库连接 关闭行为。比较简单可自行看一下源码
4.TranscationFactory定义如下:
public interface TransactionFactory {
/**
* 设置工厂属性(运用了java8中的新特性,在接口中允许default方法)
* Sets transaction factory custom properties.
* @param props
*/
default void setProperties(Properties props) {
// NOP
}
/**
*
* 创建transaction
* Creates a {@link Transaction} out of an existing connection.
* @param conn Existing database connection
* @return Transaction
* @since 3.1.0
*/
Transaction newTransaction(Connection conn);
/**
* Creates a {@link Transaction} out of a datasource.
* @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);
}
而TransactionFactory的实现类为JdbcTransactionFactory和ManagedTransactionFactory,主要是为了创建JdbcTranscation 和 ManagedTransaction,比较简单,就不贴出来代码了。至此Transaction模块分析完毕。
参考文献 《Mybatis技术内幕》