一、Mybatis事务概述
对于数据库事务而言,一般包括以下几个操作:创建、提交、回滚、关闭。MyBatis把这些抽象为Transaction接口: 接口定义如下:
接口定义了Connection连接、提交、回滚、关闭等功能。
Mybatis事务管理分为两种方式:
1、使用JDBC的事务管理机制:利用java.sql.Connection对象完成对事务的提交、回滚、关闭。
2、使用MANAGED的事务管理机制:这种方式Mybatis自身不会去实现事务管理,而是交给容器(Tomcat、JBOSS)去管理。
二、Mybatis事务使用
1、 事务配置:
我们在用Mybatis时,一般会用如下配置文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>
其子节点<transactionManager> 的type 会决定我们用什么类型的事务管理机制。
2、事务工厂的创建:
Mybatis的事务是交给TransactionFactory来创建,如果我们将<transactionManager>的type 配置为"JDBC",那么,在Mybatis初始化解析<environment>节点时,XMLConfigBuilder会根据type="JDBC"创建一个JdbcTransactionFactory工厂,其源码如下:
private TransactionFactory transactionManagerElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
Properties props = context.getChildrenAsProperties();
TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance();
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a TransactionFactory.");
}
如果type = "JDBC",则Mybatis会创建一个JdbcTransactionFactory ;如果type="MANAGED",则Mybatis会创建一个MangedTransactionFactory。
TransactionFactory接口:
创建Transaction有两个方法:一是通过Connection对象创建,另一个是通过数据源DataSource来创建。
看下JdbcTransactionFactory 创建过程,如下:
public class JdbcTransactionFactory implements TransactionFactory {
public void setProperties(Properties props) {
}
//根据给定的数据库连接Connection创建Transaction
public Transaction newTransaction(Connection conn) {
return new JdbcTransaction(conn);
}
// 根据DataSource、隔离级别和是否自动提交创建Transacion
public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
return new JdbcTransaction(ds, level, autoCommit);
}
}
3、事物管理的实现
我们以 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;
public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
dataSource = ds;
level = desiredLevel;
autoCommmit = desiredAutoCommit;
}
public JdbcTransaction(Connection connection) {
this.connection = connection;
}
public Connection getConnection() throws SQLException {
if (connection == null) {
openConnection();
}
return connection;
}
// 使用connection的commit()
public void commit() throws SQLException {
if (connection != null && !connection.getAutoCommit()) {
if (log.isDebugEnabled()) {
log.debug("Committing JDBC Connection [" + connection + "]");
}
connection.commit();
}
}
//使用connection的rollback()
public void rollback() throws SQLException {
if (connection != null && !connection.getAutoCommit()) {
if (log.isDebugEnabled()) {
log.debug("Rolling back JDBC Connection [" + connection + "]");
}
connection.rollback();
}
}
//使用connection的close()
public void close() throws SQLException {
if (connection != null) {
resetAutoCommit();
if (log.isDebugEnabled()) {
log.debug("Closing JDBC Connection [" + connection + "]");
}
connection.close();
}
}
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) {
throw new TransactionException("Error configuring AutoCommit. "
+ "Your driver may not support getAutoCommit() or setAutoCommit(). "
+ "Requested setting: " + desiredAutoCommit + ". Cause: " + e, e);
}
}
protected void resetAutoCommit() {
//select操作没有commit和rollback事务,一些数据库在select操作是会开启事务,一个变通方法是在关闭连接之前将autocommit设置为true。
try {
if (!connection.getAutoCommit()) {
if (log.isDebugEnabled()) {
log.debug("Resetting autocommit to true on JDBC Connection [" + connection + "]");
}
connection.setAutoCommit(true);
}
} catch (SQLException e) {
log.debug("Error resetting autocommit to true "
+ "before closing the connection. Cause: " + e);
}
}
protected void openConnection() throws SQLException {
if (log.isDebugEnabled()) {
log.debug("Opening JDBC Connection");
}
connection = dataSource.getConnection();
if (level != null) {
connection.setTransactionIsolation(level.getLevel());
}
setDesiredAutoCommit(autoCommmit);
}
}