本文将会介绍Mybatis的事务管理机制的原理,首先介绍下事务管理的特质、在Mybatis中是如何创建事务、事务有哪几种类型、不同事务的源码实现、并比较其中的不同、并总结事务的原理。最后分享学习事务常见的小知识。
事务的概述
对于数据库事务,具有如下几种特质:
- 创建(
create
) - 提交(
commit
) - 回滚(
rollback
) -
关闭(
close
)
对应地,MyBatis将事务抽象成了Transaction
接口,源码如下:
可以看出,这个接口中包含最基本 的
getConnection、commit、rollback、close
方法,任何实现对事物管理都需要实现这几个方法。
在
Mybatis
中实现事务的管理分为如下两种:
-
JDBC
的事务管理机制:即利用java.sql.Connection
对象完成对事务的提交(commit()
)、回滚(rollback()
)、关闭(close()
)等 -
MANAGED
的事务管理机制:这种机制MyBatis
自身不会去实现事务管理,而是让程序的容器如(JBOSS
,Weblogic
)来实现对事务的管理
事务的创建
Mybatis
在初始化的时候,会加载解析Mybatis
的xml配置文件,在xml文件中若配置了事务管理的类型,<transactionManager>
的type
配置为"JDBC"
,那么,在MyBatis
初始化解析<environment>
节点时,会根据
-
type="JDBC"
创建一个JdbcTransactionFactory
工厂,JdbcTransactionFactory
能够创建JDBC
类型的事务管理机制 -
type="MANAGED"
创建一个MangedTransactionFactory
工厂,MangedTransactionFactory
能够创建MANAGED
类型的事务管理机制
源码截图如下:
下面我们来看看他们的实现细节:
JdbcTransaction
的创建
JdbcTransactionFactory
类会根据 DataSource
、隔离级别、是否自动提交 这三个参数创建Transacion
,也可以根据给定的数据库连接Connection
创建Transaction
JdbcTransactionFactory
的源码如下:
public class JdbcTransactionFactory implements TransactionFactory {
public void setProperties(Properties props) {
}
/**
* 根据给定的数据库连接Connection创建Transaction
* @param conn Existing database connection
* @return
*/
public Transaction newTransaction(Connection conn) {
return new JdbcTransaction(conn);
}
/**
* 根据DataSource、隔离级别和是否自动提交创建Transacion
*
* @param ds
* @param level Desired isolation level
* @param autoCommit Desired autocommit
* @return
*/
public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
return new JdbcTransaction(ds, level, autoCommit);
}
}
JdbcTransaction
的实现
JdbcTransaction
直接使用JDBC
的提交和回滚事务管理机制 。它依赖与从dataSource
中取得的连接connection
来管理transaction
的作用域,connection
对象的获取被延迟到调用getConnection()
方法。如果autocommit
设置为on,开启状态的话,它会忽略commit
和rollback
。
直观地讲,就是JdbcTransaction
是使用的java.sql.Connection
上的commit
和rollback
功能,JdbcTransaction
只是相当于对java.sql.Connection
事务处理进行了一次包装(wrapper),Transaction
的事务管理都是通过java.sql.Connection
实现的。
JdbcTransaction
的源码如下,快速阅读的读者只需要看本人加注释的部分即可:
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;
//根据 DataSource、隔离级别、是否自动提交 三个参数创建Transacion
public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
this.dataSource = ds;
this.level = desiredLevel;
this.autoCommmit = desiredAutoCommit;
}
//根据给定的数据库连接Connection创建Transaction
public JdbcTransaction(Connection connection) {
this.connection = connection;
}
public Connection getConnection() throws SQLException {
if(this.connection == null) {
this.openConnection();
}
return this.connection;
}
//使用 java.sql.Connection 的 commit
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();
}
}
//使用 java.sql.Connection 的 rollback
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();
}
}
//使用 java.sql.Connection 的 close
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) {
if(log.isDebugEnabled()) {
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);
}
public Integer getTimeout() throws SQLException {
return null;
}
}
总结:
- 当使用DataSource创建数据库连接时,数据库的事务隔离级别使用DataSource默认的事务隔离级别
- 当使用 DataSource、隔离级别、是否自动提交 三个参数创建
JdbcTransaction
时,会使用传入的参数来设定隔离级别和是否自动提交- 对select不进行事务控制
JdbcTransaction
的事务,原理上就是封装了一层JDBC
的方法
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) {
this.openConnection();
}
return this.connection;
}
//不做任何处理
public void commit() throws SQLException {
}
//不做任何处理
public void rollback() throws SQLException {
}
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());
}
}
public Integer getTimeout() throws SQLException {
return null;
}
}
关于事务的隔离级别
先对不同隔离级别涉及到的名词解释:
• 脏读: 对于两个事物 T1、T2,T1 读取了已经被 T2 更新但还没有被提交的字段。之后, 若 T2 回滚,T1读取的内容就是临时且无效的。
• 不可重复读: 对于两个事物 T1、T2, T1 读取了一个字段, 然后 T2 更新了该字段。 之后, T1再次读取同一个字段, 值就不同了。
• 幻读: 对于两个事物 T1、T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中插入了一些新的行。 如果 T1 再次读取同一个表, 就会多出数据
具体的隔离级别定义:
- READ UNCOMMITTED(读未提交数据) :允许事务读取未被其他事务提交的变更,脏读、不可重复读和幻读的问题都会出现
- READ COMMITED(读已提交数据) :只允许事务读取已经被其他事务提交的变更,可以避免脏读,但不可重复读和幻读问题仍然会出现
- REPEATABLE READ(可重复读) :确保事务可以多次从一个字段中读取相同的值,在这个事务持续期间,禁止其他事务对这个字段进行更新,可以避免脏读和不可重复读,但幻读的问题依然存在
- SERIALIZABLE(串行化) :确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其他事务对该表执行插入、更新和删除操作,所有并发问题都可以避免,但性能十分低
Oracle
默认的事务隔离级别为: READ COMMITED(读已提交数据)
Mysql
默认的事务隔离级别为: REPEATABLE READ(可重复读)
以上就是《Mybatis原理--事务管理》的全部内容,如有不正确的地方,请读者指正,互相学习,共同进步,谢谢。