Mybatis事务管理
Mybatis事务管理
在上一篇写了简单的SSM框架搭建相关链接,虽然简单快速,但是并不能真正的运用到实际的项目中,还有许多实际问题需要考虑,在接下来的几篇文章中我回讲解SSM中一些核心技术(我认为的核心技术)的讲解(其实就是学习笔记的啦),有什么不足或者错误的地方还请高手斧正!
我们会在接下来的两篇文章中分别讲解Mybatis和Spring
的事务管理。在学习事务管理之前,需要对数据库事务的知识有一定的掌握,例如:事务的定义、隔离级别、锁等概念,我把一些网上的概念性的文字材料进行一个整理,我会把几个参考资料链接放在文末,如果大家对数据库的这些知识还不是太了解,请直接移至结尾。
从这里开始默认已经掌握了资料里的事务相关的知识,其概念和特性也就不再一一描述,直接切入正题来讲解Mybatis的事务处理。
Mybatis的事务管理
Mybatis的事务管理一般在单独使用Mybatis时才会使用,在SSM整合之后,一般都是用Spring的事务管理,虽然我们使用的是SSM框架,但还是有必要详细的了解一下Mybatis的事务管理。
本文没有太多的应用代码,大部分都是对Mybatis接口的分析,文中的代码都能在mybatis-X.X.X.jar中找到。
Transaction接口
Mybatis的事务管理主要是由org.apache.ibatis.transaction.Transaction接口,以及他的两个实现类org.apache.ibatis.transaction.managed.ManagedTransaction和org.apache.ibatis.transaction.jdbc.JdbcTransaction。除此之外,Mybatis中还有另外一个接口org.apache.ibatis.transaction.TransactionFactory和两个实现类org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory和org.apache.ibatis.transaction.managed.ManagedTransactionFactory。代码不一一贴出,可以到mybatis-X.X.X.jar下,很轻松就能找到(如果你要是不知道mybatis-X.X.X.jar是什么,那你最好认真的学习Mybatis基础后在进行深入的学习)。
接下来看重点:Transaction接口。
... ...
public interface Transaction {
//获取数据库链接
Connection getConnection() throws SQLException;
//提交事务
void commit() throws SQLException;
//回滚事务
void rollback() throws SQLException;
//关闭数据库连接
void close() throws SQLException;
}
大家可以发现,不管是Transaction接口的实现类还是TransactionFactory接口的实现类,前缀都分为了Jdbc和Managed两个。这就引出Mybatis事务管理的两种机制:
- JDBC事务管理机制。
- MANAGED事务管理机制。
隔离级别的定义
相对于数据库的四种隔离级别,在Mybatis的隔离级别中,多定义了一个没有事务(NONE),其余的和数据库的四种隔离级别均相同。
... ...
public enum TransactionIsolationLevel {
//没有事务
NONE(0),
//已提交读(一般数据库中的默认隔离级别)
READ_COMMITTED(2),
//未提交读
READ_UNCOMMITTED(1),
//可重复读
REPEATABLE_READ(4),
//直译为序列化
SERIALIZABLE(8);
private final int level;
private TransactionIsolationLevel(int level) {
this.level = level;
}
public int getLevel() {
return this.level;
}
}
事务配置
对于Mybatis事务的配置,一般放在Mybatis的配置文件mybatis-config.xml中。其中定义的信息如下:
<!--Mybatis的事务管理,注意配置的顺序也是一门学问,environments的配置要在typeAliases之下,具体顺序看提示-->
<environments default="development">
<environment id="mysql">
<!--type决定事务管理机制-->
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/javafeng" />
<property name="username" value="root" />
<property name="password" value="root" />
</dataSource>
</environment>
</environments>
元素用来定义某数据库的信息,元素的type属性决定事务管理的机制(上述的JDBC和MANAGED)。在mybatis的配置文件中,对于集中元素的配置顺序有一定的要求,顺序出错的话配置文件就会报错,可以按照提示来调整。
事务工厂
Mybatis的事务创建交给事务工厂来完成,Mybatis在初始化时会根据节点的type属性来注册相应的事务工厂。
创建
private TransactionFactory transationManagerElement(XNode context) throws Exception
{
/*
Configuration在初始化的时候会根据transactionManager节点的type属性来创建相应的事务工厂
typeAliaRegistry.registerAlias("JDBC",JdbcTransactionFactory.class);
typeAliaRegistry.registerAlias("MANAGED",ManagedTransactionFactory.class);
*/
if(context != null)
{
String type = context.getStringAttribute("type");
Properties props = context.getChildrenAsProperties();
//从类型解决器中获取type对应的类型的实例,即JdbcTransactionFactory
TransactionFactory factory = (TransactionFactory)resolveClass(type).newInstance();
//设置JdbcTransactionFactory属性
factory.setProperties(props);
return factory;
} else
{
//抛出异常
throw new BuilderException("Environment declaration requires a TransactionFactory.");
}
}
详解
我们可以很容易的从事务工厂TransactionFactory中得到Transaction对象实例。接下来我们以JdbcTransaction的生成为例来看一下。直接上代码
... ...
public class JdbcTransactionFactory implements TransactionFactory {
public JdbcTransactionFactory() {
}
public void setProperties(Properties props) {
}
//通过指定的数据库链接(Connection)来创建事务(Transaction)
public Transaction newTransaction(Connection conn) {
return new JdbcTransaction(conn);
}
//通过数据源(DataSource),隔离级别(TransactionIsolationLevel)和是否自动提交来创建事务(Transaction)
public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
return new JdbcTransaction(ds, level, autoCommit);
}
}
如上就是JdbcTransactionFactory创建JDBC类型Transaction(也就是JdbcTransaction)的过程,这里只是JdbcTransaction的示例,ManagedTransactionFactory则会创建ManagedTransaction。
接下来就来看这两种机制的具体实现。
1. JdbcTransaction
JdbcTransaction直接使用JDBC的提交和回滚事务管理机制,它对于事务的实现依赖从数据源(DataSource)中获取的连接来管理事务,在调用getConnection()时才会取得connection对象。如果其自动提交属性(autocommit)被设置为开启状态(on)时,它会自动忽略提交(commit)和回滚(rollback)。并且它的事务操作都是使用java.sql.Connection上的提交(commit)和回滚(rollback)功能来完成的。JdbcTransaction相当于对java.sql.Connection事务处理的重新封装,事务管理均由java.sql.Connection完成。上代码
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);
}
}
如上可以看到,所有的实务操作均由connection对象来完成。
- ManagedTransaction
ManagedTransaction和JdbcTransaction就有很大的区别了,实际上ManagedTransaction对事物的处理任务全部交给了容器来完成(吐槽:万能背锅侠,啥啥都能干),即使调用了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());
}
}
}
如上可以看到,它的commit()和rollback()都nothing(对就是nothing,没有任何操作),正因为它吧整个事务的整个生命周期都交给容器,因此他不需要做任何操作。但它在默认情况下是可以关闭一个链接的,当然我们也可以通过设置closeConnection为false来让它不能关闭连接。
小结
- Transaction接口和他的一系列实现类。
- 事务工厂TransactionFactory。
- JdbcTransaction和ManagedTransaction解析。
参考资料
Mybatis的environments和properties
以上链接版权属作者所有,在此不一一鸣谢!
浙公网安备 33010602011771号