目录
- 一、事务简介
- 二、Spring事务的实现
-
- [2.1 事务的操作](#2.1 事务的操作)
- [2.2 分类](#2.2 分类)
-
- [2.2.1 Spring编程式事务](#2.2.1 Spring编程式事务)
- [2.2.2 Spring 声明式事务 @Transactional](#2.2.2 Spring 声明式事务 @Transactional)
-
- [2.2.2.1 @Transactional 详解](#2.2.2.1 @Transactional 详解)
-
- [2.2.2.1.1 rollbackFor](#2.2.2.1.1 rollbackFor)
- [2.2.2.1.2 Isolation](#2.2.2.1.2 Isolation)
- [2.2.2.1.3 propagation](#2.2.2.1.3 propagation)

一、事务简介
事务:事务是⼀组操作的集合,是⼀个不可分割的操作。事务会把所有的操作作为⼀个整体,⼀起操作。所以这组操作要么同时成功,要么同时失败。
二、Spring事务的实现
2.1 事务的操作
事务的操作主要有三步:
- 开启事start transaction / begin (⼀组操作前开启事务)
- 提交事务: commit (这组操作全部成功,提交事务)
- 回滚事务: rollback (这组操作中间任何⼀个操作出现异常,回滚事务)
2.2 分类
Spring 中的事务操作分为两类:
- 编程式事务(⼿动写代码操作事务)
- 声明式事务(利⽤注解⾃动开启和提交事务)
2.2.1 Spring编程式事务
SpringBoot 内置了两个对象:
DataSourceTransactionManager
事务管理器。⽤来获取事务(开启事务),提交或回滚事务TransactionDefinition
是事务的属性,在获取事务的时候需要将 TransactionDefinition 传递进去从⽽获得⼀个事务 TransactionStatus
示例:
java
package com.example.springtransdemo.controller;
import com.example.springtransdemo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/user")
@RestController
public class UserController {
@Autowired
private UserService userService;
//事务属性
@Autowired
private TransactionDefinition definition;
//事务管理器
@Autowired
private DataSourceTransactionManager transactionManager;
@RequestMapping("/registry")
public String registry(String name,String password) {
//开启事务
TransactionStatus transaction = transactionManager.getTransaction(definition);
//⽤⼾注册
userService.registryUser(name, password);
//提交事务
transactionManager.commit(transaction);
//回滚事务
//transactionManager.rollback(transaction);
return "注册成功";
}
}
当我们不提交时可以在数据库中添加字段,回滚后就不会在数据库中添加字段,提交和回滚方法不能在同一个事务中调用。
2.2.2 Spring 声明式事务 @Transactional
使用这个注解需要添加依赖,但是如果已经添加了MyBatis的依赖,那么就包含了下面需要引入的依赖的内容。
xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
注解的应用,在需要使用到事务的方法上加上注解,就会自动提交事务状态。当发⽣了没有处理的error和RunTimeException异常(默认情况下)的时候,就会自动回滚。
例如上面的代码就可以改为:
java
@RequestMapping("/registry")
@Transactional
public String registry(String name,String password) {
//⽤⼾注册
userService.registryUser(name, password);
return "注册成功";
}
那么当我们就是要在捕获处理了异常后,还是将事务进行回滚时,我们就可以手动回滚使用TransactionAspectSupport
类下使用currentTransactionStatus
方法获取状态,使用isRollbackOnly
方法回滚。
java
@RequestMapping("/registry")
@Transactional
public String registry(String name,String password) {
//⽤⼾注册
userService.registryUser(name, password);
try{
int a = 10 / 0;
} catch (Exception e) {
log.error("错误");
TransactionAspectSupport.currentTransactionStatus().isRollbackOnly();
}
return "注册成功";
}
2.2.2.1 @Transactional 详解
修饰范围:
- 修饰类时,就是修饰类中的所用访问修饰限定符为
public
的方法。 - 修饰方法时,只能修饰访问修饰限定符为
public
的方法。
@Transactiona 常见三大属性:
- rollbackFor:异常回滚属性。指定能够触发事务回滚的异常类型。可以指定多个异常类型。
- Isolation:事务的隔离级别。默认值为 Isolation.DEFAULT。
- propagation:事务的传播机制。默认值为 Propagation.REQUIRED 。
2.2.2.1.1 rollbackFor
@Transactional 默认只在遇到运⾏时异常RunTimeException和Error时才会回滚。
例如执行下面的操作的时候,事务不会回滚,数据库中还是会添加数据。
java
@RequestMapping("/registry")
@Transactional
public String registry(String name,String password) throws IOException {
//⽤⼾注册
userService.registryUser(name, password);
if(true) {
throw new IOException();
}
return "注册成功";
}
但是我们加上属性rollbackFor就会在发生rollbackFor指定的异常的时候回滚。
java
@RequestMapping("/registry")
@Transactional(rollbackFor = Exception.class)
public String registry(String name,String password) throws IOException {
//⽤⼾注册
userService.registryUser(name, password);
if(true) {
throw new IOException();
}
return "注册成功";
}
还有与rollbackFor相似作用的属性:
- rollbackFor :使用class作为值,在发生该异常的时候回滚。
- rollbackForClassName:使用类名作为值,在发生该异常的时候回滚。
- noRollbackFor :使用class作为值,在发生该异常的时候不回滚。
- noRollbackForClassName:使用类名作为值,在发生该异常的时候不回滚。
当我们同时使用@Transactionsl的noRollbackFor和rollbackFor对同一异常类处理,后使用的会覆盖前面的。例如@Transactional(rollbackFor = IOException.class, noRollbackFor = IOException.class)
生效的是noRollbackFor = IOException.class
。
2.2.2.1.2 Isolation
Spring事务的隔离级别有以下几种值:
- DEFAULT:数据库是什么隔离级别,这个方法就还是什么隔离级别。
剩下的都对应MySQL的隔离级别。
- 读未提交(READ_UNCOMMITTED):读未提交,也叫未提交读。该隔离级别的事务可以看到其他事务中
未提交的数据,会有脏读问题。 - 读提交(READ_COMMITTED):读已提交,也叫提交读。该隔离级别的事务能读取到已经提交事务的数据,该隔离级别不会有脏读的问题。
- 可重复读(REPEATABLE_READ):事务不会读到其他事务对已有数据的修改,即使其他事务已提交。也就可以确保同⼀事务多次查询的结果⼀致,但是其他事务新插⼊的数据,是可以感知到的,这也就引发了幻读问题。
- 串⾏化(SERIALIZABLE):序列化,事务最⾼隔离级别。它会强制事务排序,使之不会发⽣冲突,从⽽解
决了脏读,不可重复读和幻读问题,但因为执⾏效率低,所以真正使⽤的场景并不多。
2.2.2.1.3 propagation
事务的传播机制有以下七种。
我们以A方法调用B方法为例解释:
- Propagation.REQUIRED :默认的事务传播级别,如果当前存在事务,则加⼊该事务,如果当前没有事务,则创建⼀个新的事务。如果A方法有事务,B直接就加入,A没有事务B创建一个事务。
- Propagation.SUPPORTS :如果当前存在事务,则加⼊该事务。如果当前没有事务,则以⾮事务的⽅式继续运⾏。如果A方法有事务,B直接就加入,A没有事务B也没有事务。
- Propagation.MANDATORY :强制性。如果当前存在事务,则加⼊该事务。如果当前没有事务,则抛出异常。如果A方法有事务,B直接就加入,A没有事务抛异常。
- Propagation.REQUIRES_NEW :创建⼀个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部⽅法是否开启事务,Propagation.REQUIRES_NEW 修饰的内部⽅法都会新开启⾃⼰的事务,且开启的事务相互独⽴,互不⼲扰。如果A方法有事务,B创建一个事务,两个事务互不影响,A没有事务,B也会创建一个事务。
- Propagation.NOT_SUPPORTED :以⾮事务⽅式运⾏,如果当前存在事务,则把当前事务挂起(不⽤)。如果A方法有事务,B创建一个事务,使用B创建的事务,A没有事务B也没有。
- Propagation.NEVER :以⾮事务⽅式运⾏,如果当前存在事务,则抛出异常。如果A方法有事务,抛异常,A没有事务B也没有。
- Propagation.NESTED :如果当前存在事务,则创建⼀个事务作为当前事务的嵌套事务来运⾏。如果当前没有事务,则该取值等价于 PROPAGATION_REQUIRED 。如果A方法有事务,B创建一个事务,A的事务相当于B的父事务,A没有事务,B也会创建一个事务。
传播行为 | 当前有事务 | 当前无事务 |
---|---|---|
REQUIRED | 加入事务 | 新建事务 |
SUPPORTS | 加入事务 | 非事务执行 |
MANDATORY | 加入事务 | 抛出异常 |
REQUIRES_NEW | 挂起当前事务并新建事务 | 新建事务 |
NOT_SUPPORTED | 挂起当前事务并以非事务执行 | 非事务执行 |
NEVER | 抛出异常 | 非事务执行 |
NESTED | 创建嵌套事务 | 新建事务 |