目录
前言
一、事务
1. 事务的概念
2. Spring 中的事务
1. 编程式事务
2. 声明式事务
二、 @Transactional
1. rollbackFor
2. isolation
3. Spring 事务的传播机制
4. Spring 事务传播机制演示
1. required
2. requires_new
3. nested
4. required 和 nested 的区别
前言
本文介绍了Spring框架中的事务管理机制。主要内容包括:1. 事务基本概念,即一组操作的原子性执行;2. Spring提供的两种事务实现方式:编程式事务(手动管理)和声明式事务(通过@Transactional注解);3. @Transactional注解的核心属性:rollbackFor(异常回滚规则)、isolation(隔离级别)和propagation(传播机制);4. 重点讲解了7种事务传播机制及其应用场景,特别是REQUIRED、REQUIRES_NEW和NESTED的区别。文章通过代码示例展示了不同传播机制下的行为差异,帮助开发者根据业务需求选择合适的事务管理策略。
一、事务
1. 事务的概念
事务是一组操作的集合,是一个不可分割的操作;
事务会把组内的操作视作一个整体,如果所有操作全部成功,事务进行提交,如果有一个或者多个步骤失败,就会将成功的操作也进行回滚,实现整体上全部成功,或者全部不成功的功能;
事务的操作有 3 个步骤:
- 1. 开启事务;
- 2. 提交事务;
- 3. 回滚事务;
2. Spring 中的事务
Spring 中的事务操作分为两类:
- 编程式事务:手动编程,操作事务;
- 声明式事务:利用注解,自动开启,提交或者回滚事务;
1. 编程式事务
DataSourceTransactionManager:事务管理器,用来获取事务,开启事务,提交事务,回滚事务;
TransactionDefinition:事务的属性,在获取事务的时候,将 TransactionDefinition 传递进去,从而获得一个事务 TransactionStatus;
手动实现事务的提交和回滚:
package com.example.trans.controller;
import com.example.trans.service.UserInfoService;
import lombok.extern.slf4j.Slf4j;
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.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("/user")
public class UserInfoController {@Autowiredprivate DataSourceTransactionManager dataSourceTransactionManager;@Autowiredprivate TransactionDefinition transactionDefinition;@Autowiredprivate UserInfoService userInfoService;@RequestMapping("/register")public Boolean register(String userName, String password){// 1. 参数校验log.info("/user/register接收到参数:userName: {}, password: {}", userName, password);if(!StringUtils.hasLength("userName") || !StringUtils.hasLength(password)){return false;}// 2. 开启事务TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);// 3. 调用 servicetry{Integer result = userInfoService.insertUserInfo(userName, password);if(result <= 0){return false;}}catch (Exception e){return false;}// 4. 提交事务
// dataSourceTransactionManager.commit(transactionStatus);// 5. 回滚事务dataSourceTransactionManager.rollback(transactionStatus);return true;}
}
2. 声明式事务
使用 @Transactional 注解,自动实现事务的开启,提交和回滚;
package com.example.trans.controller;
import com.example.trans.service.UserInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
@Slf4j
@RestController
@RequestMapping("/user2")
public class UserInfoController2 {@Autowiredprivate UserInfoService userInfoService;// @Transactional 自动实现事务的开启,提交或者回滚@Transactional@RequestMapping("/register")public Boolean register(String userName, String password) throws IOException {// 1. 参数校验log.info("/user/register接收到参数:userName: {}, password: {}", userName, password);if(!StringUtils.hasLength("userName") || !StringUtils.hasLength(password)){return false;}// 2. 调用 servicetry{Integer result = userInfoService.insertUserInfo(userName, password);int n = 10 / 0;if(result <= 0){return false;}}catch (Exception e){// 手动回滚TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
// return false;}return true;}
}
@Transactional 可以用来修饰方法和类:
- 修饰方法时,只有修饰被 public 修饰的方法才生效;
- 修饰类时,对类中的所有被 public 修饰的方法生效;
事务提交和回滚的情况分析:
当所有操作执行成功,事务提交;
当发生运行时异常或者错误时,且异常没有被捕获,事务会自动回滚;
如果发生运行时异常且异常被捕获,则事务提交;
如果发生的是受查异常,则事务会提交;
如果发生运行时异常后,捕获到了异常,仍然想要实现回滚:可以再次抛出异常,也可以手动回滚;
如果发生的是受查异常,仍然想要回滚,可以设置 @Transactional 的属性 rollbackFor 为 Exception.class 类型;
二、 @Transactional
@Transactional 常见的三个属性:
rollbackFor:异常回滚属性,能够指定多个异常类型,发生指定类型的异常时,事务会实现回滚;
Isolation:事务的隔离级别;
propagation:事务的传播机制;
1. rollbackFor
package com.example.trans.controller;
import com.example.trans.service.UserInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
@Slf4j
@RestController
@RequestMapping("/user2")
public class UserInfoController2 {@Autowiredprivate UserInfoService userInfoService;// @Transactional 自动实现事务的开启,提交或者回滚@Transactional(rollbackFor = Exception.class)@RequestMapping("/register")public Boolean register(String userName, String password) throws IOException {// 1. 参数校验log.info("/user/register接收到参数:userName: {}, password: {}", userName, password);if(!StringUtils.hasLength("userName") || !StringUtils.hasLength(password)){return false;}// 2. 调用 servicetry{Integer result = userInfoService.insertUserInfo(userName, password);int n = 10 / 0;if(result <= 0){return false;}}catch (Exception e){throw new IOException();}return true;}
}
设置 rollbackFor 的类型为 Exception.class 后,只要发生异常,就会实现回滚;
2. isolation
MySQL 有 4 种隔离级别,分别是:读未提交,读已提交,可重复度和串行化;
读未提交:事务 A 写数据时,事务 B 可以读到,如果事务 A 之后又修改了数据,事务 B 读到的就是脏数据,也称为脏读;
读已提交:当事务 A 提交后,事务 B 能读到,事务 C 又修改了当初事务 A 提交的数据,此时事务 B 再读,发现数据前后不一致了,称为不可重复读;
可重复读:当事务 A 提交后,事务 B 不能读到事务 A 产生的数据,但是当 事务 B 想要插入数据时,发现数据行的 ID 已经被占用,事务 B 能感知到数据发生变化,但是读不到,称为幻读;
串行化: 把事务进行排序,一个一个执行,完全没有并发;
MySQL 的默认隔离界别时可重复读;Spring 的默认隔离级别是采用数据库设置的隔离级别;
package org.springframework.transaction.annotation;
public enum Isolation {DEFAULT(-1),READ_UNCOMMITTED(1),READ_COMMITTED(2),REPEATABLE_READ(4),SERIALIZABLE(8);private final int value;private Isolation(int value) {this.value = value;}public int value() {return this.value;}
}
3. Spring 事务的传播机制
传播机制:当多个事务方法存在调用关系,事务在这些方法的传播方式;
事务的隔离机制解决的是多个事务同时调用一个数据的问题;
事务的传播机制解决的是一个事务在多个方法中传递的问题;
@Transactional 可以设置 propagation 属性,设置事务的传播机制;
传播机制有 7 种:
package org.springframework.transaction.annotation;
public enum Propagation {REQUIRED(0),SUPPORTS(1),MANDATORY(2),REQUIRES_NEW(3),NOT_SUPPORTED(4),NEVER(5),NESTED(6);private final int value;private Propagation(int value) {this.value = value;}public int value() {return this.value;}
}
Propagation.REQUIRED:默认事务传播级别,如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务;
Propagation.REQUIRES_NEW:不管当前是否存在事务(如果有事务则挂起),都要创建一个新的事务;
Propagation.NESTED:如果当前存在一个事务,则创建一个新的事务,作为嵌套事务运行,如果嵌套事务发生异常,且异常被 catch 住了,则不影响原有事务,如果当前不存在事务,则创建一个新的事务运行;
Propagation.SUPPORTS:如果当前存在事务,则加入该事务,如果当前不存在事务,则以非事务的方式运行;
Propagation.NOT_SUPPORTED:如果当前存在事务,则挂起,以非事务的方式运行;
Propagation.MANDATORY:如果当前不存在事务,则抛出异常;
Propagation.NEVER:如果当前存在事务,则抛出异常;
4. Spring 事务传播机制演示
1. required
用户注册接口:
package com.example.trans.controller;
import com.example.trans.service.LogInfoService;
import com.example.trans.service.UserInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
@Slf4j
@RestController
@RequestMapping("/user2")
public class UserInfoController2 {@Autowiredprivate UserInfoService userInfoService;@Autowiredprivate LogInfoService logInfoService;// @Transactional 自动实现事务的开启,提交或者回滚@Transactional(rollbackFor = Exception.class, isolation = Isolation.REPEATABLE_READ)@RequestMapping("/register")public Boolean register(String userName, String password) throws IOException {// 1. 参数校验log.info("/user/register接收到参数:userName: {}, password: {}", userName, password);if(!StringUtils.hasLength("userName") || !StringUtils.hasLength(password)){return false;}// 2. 调用 servicetry{Integer result = userInfoService.insertUserInfo(userName, password);Integer result2 = logInfoService.insertLogInfo(userName, "用户注册");if(result <= 0 || result2 <= 0){return false;}}catch (Exception e){// 手动回滚TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}return true;}
}
UserInfoService:
package com.example.trans.service;
import com.example.trans.mapper.UserInfoMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserInfoService {@Autowiredprivate UserInfoMapper userInfoMapper;@Transactional(propagation = Propagation.REQUIRED)public Integer insertUserInfo(String userName, String password) {return userInfoMapper.insertUserInfo(userName, password);}
}
LogInfoService:
package com.example.trans.service;
import com.example.trans.mapper.LogInfoMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
public class LogInfoService {@Autowiredprivate LogInfoMapper logInfoMapper;@Transactional(propagation = Propagation.REQUIRED)public Integer insertLogInfo(String userName, String op) {return logInfoMapper.insertLogInfo(userName, op);}
}
成功运行时:

LogInfoService 抛出异常:事务不会被提交
package com.example.trans.service;
import com.example.trans.mapper.LogInfoMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
public class LogInfoService {@Autowiredprivate LogInfoMapper logInfoMapper;@Transactional(propagation = Propagation.REQUIRED)public Integer insertLogInfo(String userName, String op) {int n = 10 / 0;return logInfoMapper.insertLogInfo(userName, op);}
}

2. requires_new
注册接口代码不变,更改 UserInfoService 和 LogInfoService 的传播机制为 requires_new:

UserInfo 提交成功,LogInfo 没有提交;
3. nested
注册接口代码不变,更改 UserInfoService 和 LogInfoService 的传播机制为 nested;
捕获 LogInfoService 中的异常,并手动回滚事务:
package com.example.trans.service;
import com.example.trans.mapper.LogInfoMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
@Service
public class LogInfoService {@Autowiredprivate LogInfoMapper logInfoMapper;@Transactional(propagation = Propagation.NESTED)public Integer insertLogInfo(String userName, String op) {try{int n = 10 / 0;}catch(Exception e){// 手动回滚TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}return logInfoMapper.insertLogInfo(userName, op);}
}
测试:事务提交,LogInfo 实现了手动回滚;

4. required 和 nested 的区别
如果事务全部执行成功,二者是相同的;
如果事务一部分执行成功,required 加入事务会使整个事务全部回滚,nested 如果 catch 住异常,能手动实现部分回滚,不影响整个事务的提交;