目录
事务进阶:Propagation 属性
1. 事务的传播行为(Propagation)
在 Spring 事务管理中,Propagation(传播行为)用于控制事务方法在调用另一个事务方法时应该如何处理事务。
2. Propagation 属性的几种类型
Spring 提供了 7 种事务传播行为,分别如下:
传播行为 | 描述 |
---|---|
REQUIRED(默认值) | 如果当前存在事务,则加入该事务;否则创建一个新的事务。 |
REQUIRES_NEW | 无论当前是否有事务,都会创建一个新的事务,新的事务独立于已有事务。 |
SUPPORTS | 如果当前存在事务,则加入该事务;如果没有事务,则以非事务方式运行。 |
NOT_SUPPORTED | 以非事务方式运行,如果当前存在事务,则挂起事务。 |
MANDATORY | 必须在一个已有事务中运行,如果当前没有事务,则抛出异常。 |
NEVER | 必须以非事务方式运行,如果当前存在事务,则抛出异常。 |
NESTED | 如果当前存在事务,则创建一个嵌套事务(内层事务可以独立回滚);如果没有事务,则等同于 REQUIRED。 |
3. 事务传播行为的应用场景
-
REQUIRED(默认值)
-
适用于大部分场景,如Service 方法默认采用 REQUIRED,确保调用链上的事务一致。
-
例如:
@Transactional(propagation = Propagation.REQUIRED) public void methodA() { methodB(); }
-
methodA()
调用methodB()
,两者属于同一个事务。
-
-
-
REQUIRES_NEW
-
适用于需要独立提交或回滚的操作,如日志记录:
@Transactional(propagation = Propagation.REQUIRES_NEW) public void saveLog() { // 日志存储 }
-
saveLog()
独立于调用者的事务,即使主事务回滚,日志也会被保存。
-
-
-
SUPPORTS
-
适用于既可以在事务环境下执行,也可以在非事务环境下执行的操作:
@Transactional(propagation = Propagation.SUPPORTS) public void methodC() { // 可选事务支持 }
-
如果
methodC()
由一个事务方法调用,则加入事务,否则以非事务方式执行。
-
-
-
NOT_SUPPORTED
-
适用于不希望事务影响某些操作,如执行非事务性的查询:
@Transactional(propagation = Propagation.NOT_SUPPORTED) public void methodD() { // 非事务方式运行 }
-
methodD()
以非事务方式运行,即使调用它的方法有事务。
-
-
-
MANDATORY
-
适用于必须有事务的情况,如某些业务逻辑必须依赖事务:
@Transactional(propagation = Propagation.MANDATORY) public void methodE() { // 需要事务支持,否则抛异常 }
-
如果
methodE()
在没有事务的情况下被调用,会抛出异常。
-
-
-
NEVER
-
适用于强制不使用事务的场景:
@Transactional(propagation = Propagation.NEVER) public void methodF() { // 强制非事务运行 }
-
如果
methodF()
在事务中调用,会抛出异常。
-
-
-
NESTED
-
适用于子方法需要部分回滚但主事务不受影响:
@Transactional(propagation = Propagation.NESTED) public void methodG() { // 嵌套事务 }
-
methodG()
创建一个嵌套事务,支持局部回滚。
-
-
4. Propagation 的使用示例
先准备好日志表以及部门操作日志类
4.1 部门操作日志表
create table dept_log
(
id int auto_increment comment '主键ID' primary key,
create_time datetime null comment '操作时间',
description varchar(300) null comment '操作描述'
) comment '部门操作日志表';
4.2 部门操作日志类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class DeptLog {
private Integer id;
private LocalDateTime createTime;
private String description;
}
4.3 DeptLogMapper
@Mapper
public interface DeptLogMapper {
@Insert("insert into dept_log(create_time,description) values(#{createTime},#{description})")
void insert(DeptLog log);
}
4.4 DeptLogService
public interface DeptLogService {
void insert(DeptLog deptLog);
}
@Service // 标记为 Spring 管理的 Bean
public class DeptLogServiceImpl implements DeptLogService {
@Autowired
private DeptLogMapper deptLogMapper;
@Transactional
@Override
public void insert(DeptLog deptLog) {
deptLogMapper.insert(deptLog);
}
}
4.5 DeptService
@Service
public class DeptServiceImpl implements DeptService {
@Autowired
private DeptMapper deptMapper;
@Autowired
private EmpMapper empMapper;
@Autowired
private DeptLogService deptLogService;
/*
删除部门
*/
@Transactional(rollbackFor = Exception.class)
@Override
public void delete(Integer id) {
try {
//1.删除部门
deptMapper.deleteById(id);
int i = 1/0; //模拟抛出异常
// if(true){throw new Exception("出错啦!")}
//2.根据部门id,删除部门下的员工信息
empMapper.deleteByDeptId(id);
} finally {
DeptLog deptLog = new DeptLog();
deptLog.setCreateTime(LocalDateTime.now());
deptLog.setDescription("执行了解散部门的操作,此次解散的是" + id + "号部门");
deptLogService.insert(deptLog);
}
}
}
4.6 安装高亮显示的插件
安装好之后重启一下IDEA
4.7 演示默认配置
发送删除部门请求,查看后台并标记高亮显示:
部门操作记录表没有变化:
4.8 演示修改配置
这时我们需要修改日志事务的传播行为,解散部门时,无论是成功还是失败,都要记录操作日志。
重新运行项目,再次发送删除部门请求。
这次即使删除的事务回滚了,日志记录的事务也没有回滚了:
5. Propagation 使用建议
-
一般业务逻辑:使用
REQUIRED
,保持事务一致性。 -
日志等独立操作:使用
REQUIRES_NEW
,避免事务回滚影响日志。 -
查询操作:使用
SUPPORTS
或NOT_SUPPORTED
,减少事务开销。 -
事务强制要求:使用
MANDATORY
,确保在事务上下文执行。 -
严格禁止事务:使用
NEVER
,确保操作不受事务管理。 -
局部回滚需求:使用
NESTED
,保证子事务可回滚但不影响主事务。
总结
-
Propagation
控制方法之间事务的传播方式,影响事务的创建、加入、挂起等行为。 -
选择合适的传播行为可提升事务控制的灵活性,优化事务管理策略。
-
开发中,大多数情况使用
REQUIRED
,日志等独立操作可使用REQUIRES_NEW
。
END
学习自:黑马程序员——JavaWeb课程