
事务管理对于企业应用来说是至关重要的,即使出现异常情况,它也可以保证数据的一致性。
事务: 是数据库操作的最小工作单元,是作为单个逻辑工作单元执行的一系列操作;这些操作作为一个整体一起向系统提交,要么都执行、要么都不执行;事务是一组不可再分割的操作集合(工作逻辑单元)。
Spring支持 编程式事务管理 和 声明式事务管理 两种方式。
编程式事务Spring推荐使用TransactionTemplate。
声明式事务管理建立在AOP之上 的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。
声明式事务唯一不足地方是只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。
注解参数如下
事务传播类型。默认为 Propagation.REQUIRED
可选值 | 描述 |
|---|---|
Propagation.REQUIRED | 支持当前事务,如果不存在则创建一个新事务。这是事务注释的默认设置 |
SUPPORTS | 支持当前事务,如果不存在则以非事务方式执行。 |
MANDATORY | 支持当前事务,如果不存在则抛出异常。 |
REQUIRES_NEW | 创建一个新事务,如果存在则暂停当前事务 |
NOT_SUPPORTED | 以非事务方式执行,如果存在则暂停当前事务 |
NEVER | 以非事务方式执行,如果存在事务则抛出异常 |
NESTED | 如果当前事务存在,则在嵌套事务中执行,否则行为类似于 REQUIRED。 |
事务的超时时间(以秒为单位)。默认为底层事务系统的默认超时(30秒)。
同timeout,此事务的超时时间(以秒为单位)。默认为底层事务系统的默认超时。
事务隔离级别。默认为 Isolation.DEFAULT
脏读 : 一个事务读取到另一事务未提交的更新数据 不可重复读 : 在同一事务中, 多次读取同一数据返回的结果有所不同, 换句话说, 后续读取可以读到另一事务已提交的更新数据. 相反, "可重复读"在同一事务中多次 读取数据时, 能够保证所读数据一样, 也就是后续读取不能读到另一事务已提交的更新数据 幻读 : 一个事务读到另一个事务已提交的insert数据
可选值 | 描述 |
|---|---|
Isolation.DEFAULT | 使用底层数据存储的默认隔离级别。所有其他级别对应于 JDBC 隔离级别。 |
Isolation.READ_UNCOMMITTED | 读取未提交数据(会出现脏读、不可重复读、幻读) 基本不使用 |
Isolation.READ_COMMITTED | 读取已提交数据(会出现不可重复读和幻读) |
Isolation.REPEATABLE_READ | 可重复读(会出现幻读) |
Isolation.SERIALIZABLE | 最高隔离级别,不允许事务并发执行,而必须串行化执行,最安全,不可能出现更新、脏读、不可重复读、幻读 |
MYSQL: 默认为REPEATABLE_READ级别
SQLSERVER: 默认为READ_COMMITTED
设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。
设置需要进行回滚的异常类,当方法中抛出指定异常,则进行事务回滚。可以是1个异常活多个异常类,必须是Throwable的子类,默认情况下,事务将在 RuntimeException 和 Error 上回滚。
指定单一异常类:@Transactional(rollbackFor=RuntimeException.class)
指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class})
同上边rollbackFor, 只不过可以是完全限定类名的子字符串,目前不支持通配符。例如,“ServletException”的值将匹配 javax.servlet.ServletException 及其子类。
指定单一异常类名称:@Transactional(rollbackForClassName=“RuntimeException”)
指定多个异常类名称:@Transactional(rollbackForClassName={“RuntimeException”,“Exception”})
设置哪些异常类型 不能 导致事务回滚,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚。匹配异常类及其子类。
指定单一异常类:@Transactional(noRollbackFor=RuntimeException.class)
指定多个异常类:@Transactional(noRollbackFor={RuntimeException.class, Exception.class})
指示哪些异常类型不得导致事务回滚。可以是完全限定类名的子字符串
指定单一异常类名称:@Transactional(noRollbackForClassName=“RuntimeException”)
指定多个异常类名称:@Transactional(noRollbackForClassName={“RuntimeException”,“Exception”})
@Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。
虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。
默认情况下,只有 来自外部的方法调用 才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰。
@Transactional(readOnly = true)
public class UserServiceImpl implements UserService {
public void insertAll(List<User> list) {
// do something
}
//方法上注解属性会覆盖类注解上的相同属性
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void updateUser(User user) {
// do something
}
}@Autowired
private TransactionTemplate transactionTemplate;
@Autowired
private DataSourceTransactionManager dataSourceTransactionManager;
public void insert(){
// 开启一个只读事务
transactionTemplate.setReadOnly(true);
TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionTemplate);
try{
//do something
//提交
dataSourceTransactionManager.commit(transactionStatus);
}catch(Exception ex){
//回滚
dataSourceTransactionManager.rollback(transactionStatus);
}
}
public void insert2(){
transactionTemplate.execute(status -> {
//数据库新增
dao.insert(2);
dao.insert(3);
return new Object();
});
}