Spring事务管理详解(传播属性、隔离级别)
(一)事务的概述
事务指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。事务在数据库的增删改操作中最为常见。事务具有ACID的特性,即原子性、一致性、隔离性、持久性。通过JDBC首先来了解一下事务的运行原理:
(二)JDBC中的事务
事务的操作主要由三大模块,即事务的开启、事务的提交和事务的回滚。
首先写一段jdbc操作事务的代码,这里用到的数据库名为user,只有name和age两个属性:
public class ConnectionUtil {
public final static String DB_DRIVER="com.mysql.jdbc.Driver";
public final static String DB_URL="jdbc:mysql://localhost:3306/transactiontest";
public final static String DB_USER="root";
public final static String DB_PWD="123456";
public static Connection getConnection() throws ClassNotFoundException, SQLException {
Connection conn=null;
Class.forName(DB_DRIVER);
conn= DriverManager.getConnection(DB_URL,DB_USER,DB_PWD);
System.out.println("数据库连接成功");
return conn;
}
}
public class TransactionTest1 {
public static void main(String[] args) {
Connection conn=null;
try {
conn=ConnectionUtil.getConnection();
//1.开启事务
conn.setAutoCommit(false);
insertTest1(conn);
//2.提交事务
conn.commit();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
try {
//3.事务的回滚
conn.rollback();
System.out.println("rollback success");
} catch (SQLException ex) {
System.out.println("rollback failed");
}
}finally {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
private static void insertTest1(Connection conn) throws SQLException {
PreparedStatement stat=conn.prepareStatement("insert into user(name) values (?)");
stat.setString(1,"sdxb");
stat.executeUpdate();
System.out.println("insert success");
stat.close();
}
}
代码第七行关闭自动提交表示事务的开启位置,进行一系列操作后(增删改查),事务提交。如果此时没有报错,事务提交成功,业务完成。如果此时出现报错,就会进入事务的回滚,回到最初的状态,维持事务的一致性。
(三)Spring中的事务
Spring实现事务的方式有两种,编程式事务管理和声明式事务管理
编程式事务管理在Spring中有两种实现方式:使用TransactionTemplate和直接使用PlatformTransactionManager。
声明式事务管理的其中一个代表就是@Transactional 注解,随着Spring更新中逐步抛弃了配置文件的使用,使用@Transactional注解管理事务成了很常用的方式。
@Transactional注解的使用很简单,在需要添加事务的方法上增加@Transactional注解
参数名称 | 功能描述 |
readOnly | 该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。例如:@Transactional(readOnly=true) |
rollbackFor | 该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。例如: 指定单一异常类:@Transactional(rollbackFor=RuntimeException.class) 指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class}) |
rollbackForClassName | 该属性用于设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。例如: 指定单一异常类名称:@Transactional(rollbackForClassName="RuntimeException") 指定多个异常类名称:@Transactional(rollbackForClassName={"RuntimeException","Exception"}) |
noRollbackFor | 该属性用于设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚。例如: 指定单一异常类:@Transactional(noRollbackFor=RuntimeException.class) 指定多个异常类:@Transactional(noRollbackFor={RuntimeException.class, Exception.class}) |
noRollbackForClassName | 该属性用于设置不需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚。例如: 指定单一异常类名称:@Transactional(noRollbackForClassName="RuntimeException") 指定多个异常类名称: @Transactional(noRollbackForClassName={"RuntimeException","Exception"}) |
propagation | 该属性用于设置事务的传播行为,具体取值可参考表6-7。 例如:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true) |
isolation | 该属性用于设置底层数据库的事务隔离级别,事务隔离级别用于处理多事务并发的情况,通常使用数据库的默认隔离级别即可,基本不需要进行设置 |
timeout | 该属性用于设置事务的超时秒数,默认值为-1表示永不超时 |
在@Transactional的参数中,尤其需要注意的是传播属性(propagation)和隔离级别(isolation):
(四)Spring事务的传播属性
事务的传播属性只在Spring中存在,在数据库中的事务中不存在传播属性的说法
PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。(默认)
PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED--如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。
4.1 重要传播属性的解释:
假设有ServiceA.MethodA(),在其内部调用ServiceB.MethodB()
Spring默认使用PROPAGATION_REQUIRED传播属性,当ServiceA.MethodA()运行时,开启一个事务,此时ServiceB.MethodB()方法发现已经存在一个事务,就不会再开启事务,因此不管哪一个方法报错,都会回调。
新建事务,如果当前存在事务,把当前事务挂起。当ServiceA.MethodA()运行时,开启一个事务A。当运行ServiceB.MethodB()时,把事务A挂起,然后开启事务B。就算事务A发生回滚,事务B依然能正常提交。总结起来就是:外部事务不会影响内部事务的提交和回滚。
如果当前存在事务,则在嵌套事务内执行。关于嵌套需要首先了解检查点的概念:当在事务中设置检查点后,如果发生回滚,会回滚到设置检查点的位置,而不是回滚整个事务。嵌套事务就使用了检查点Savepoint。当ServiceA.MethodA()运行时,开启一个事务A。当运行ServiceB.MethodB()时,开启一个子事务B,它将取得一个 savepoint. 如果这个事务B失败, 将回滚到此 savepoint,而不会影响整个事务。总结一句话就是内部事务不会影响外部事务的提交和回滚。
(五)Spring事务的隔离级别
1. ISOLATION_DEFAULT: 这是一个 PlatfromTransactionManager 默认的隔离级别,使用数据库默认的事务隔离级别.
2. ISOLATION_READ_UNCOMMITTED:读未提交。这是事务最低的隔离级别,这种隔离级别会产生脏读,不可重复读和幻像读。
3. ISOLATION_READ_COMMITTED:读已提交,ORACLE默认隔离级别,有幻读以及不可重复读风险。
4. ISOLATION_REPEATABLE_READ: 可重复读,解决不可重复读的隔离级别,但还是有幻读风险。
5. ISOLATION_SERIALIZABLE 串行化,最高的事务隔离级别,不管多少事务,挨个运行完一个事务的所有子事务之后才可以执行另外一个事务里面的所有子事务,解决脏读、不可重复读和幻读。