事务控制主要三步操作:开启事务、提交事务/回滚事务。

  • 需要在这组操作执行之前,先开启事务 ( start transaction; / begin;)。
  • 所有操作如果全部都执行成功,则提交事务 ( commit; )。
  • 如果这组操作中,有任何一个操作执行失败,都应该回滚事务 ( rollback )。

那接下来,我们就可以将添加员工的业务操作,进行事务管理。 具体的SQL如下:

-- 开启事务
start transaction; / begin;

-- 1. 保存员工基本信息
insert into emp values (39, 'Tom', '123456', '汤姆', 1, '13300001111', 1, 4000, '1.jpg', '2023-11-01', 1, now(), now());

-- 2. 保存员工的工作经历信息
insert into emp_expr(emp_id, begin, end, company, job) values (39,'2019-01-01', '2020-01-01', '百度', '开发'),                                                                                                       (39,'2020-01-10', '2022-02-01', '阿里', '架构');

-- 提交事务(全部成功)
commit;

-- 回滚事务(有一个失败)
rollback;

事务管理的场景,是非常多的,比如:

  • 银行转账
  • 下单扣减库存


注解:@Transactional
作用:就是在当前这个方法执行开始之前来开启事务,方法执行完毕之后提交事务。如果在这个方法执行的过程当中出现了异常,就会进行事务的回滚操作。
位置:业务层的方法上、类上、接口上

  • 方法上:当前方法交给spring进行事务管理
  • 类上:当前类中所有的方法都交由spring进行事务管理
  • 接口上:接口下所有的实现类当中所有的方法都交给spring 进行事务管理


可以在application.yml配置文件中开启事务管理日志,这样就可以在控制看到和事务相关的日志信息了

#spring事务管理日志
logging: 
  level: 
    org.springframework.jdbc.support.JdbcTransactionManager: debug

事务回滚

@Transactional注解当中的两个常见的属性:

  • 异常回滚的属性:rollbackFor
  • 事务传播行为:propagation

我们在之前编写的业务方法上添加了@Transactional注解,来实现事务管理。

@Transactional
@Override
public void save(Emp emp) {
    //1.补全基础属性
    emp.setCreateTime(LocalDateTime.now());
    emp.setUpdateTime(LocalDateTime.now());
    //2.保存员工基本信息
    empMapper.insert(emp);

    
int i = 1/0;
//3. 保存员工的工作经历信息 - 批量 Integer empId = emp.getId(); List<EmpExpr> exprList = emp.getExprList(); if(!CollectionUtils.isEmpty(exprList)){ exprList.forEach(empExpr -> empExpr.setEmpId(empId)); empExprMapper.insertBatch(exprList); } }

以上业务功能save方法在运行时,会引发除0的算术运算异常(运行时异常),出现异常之后,由于我们在方法上加了@Transactional注解进行事务管理,所以发生异常会执行rollback回滚操作,从而保证事务操作前后数据是一致的。

我们修改业务功能代码,在模拟异常的位置上直接抛出Exception异常(编译时异常)

@Transactional
@Override
public void save(Emp emp) {
    //1.补全基础属性
    emp.setCreateTime(LocalDateTime.now());
    emp.setUpdateTime(LocalDateTime.now());
    //2.保存员工基本信息
    empMapper.insert(emp);
        
    
//模拟:异常发生 if(true){ throw new Exception("出现异常了~~~"); }
//3. 保存员工的工作经历信息 - 批量 Integer empId = emp.getId(); List<EmpExpr> exprList = emp.getExprList(); if(!CollectionUtils.isEmpty(exprList)){ exprList.forEach(empExpr -> empExpr.setEmpId(empId)); empExprMapper.insertBatch(exprList); } }


说明:在service中向上抛出一个Exception编译时异常之后,由于是controller调用service,所以在controller中要有异常处理代码,此时我们选择在controller中继续把异常向上抛。

事务提交,没有回滚

我们看到数据库的事务居然提交了,并没有进行回滚。

通过以上测试可以得出一个结论:


默认情况下,只有出现RuntimeException(运行时异常)才会回滚事务。

假如我们想让所有的异常都回滚,需要来配置@Transactional注解当中的rollbackFor属性,通过rollbackFor这个属性可以指定出现何种异常类型回滚事务。

@Transactional(rollbackFor = Exception.class)
@Override
public void save(Emp emp) throws Exception {
    //1.补全基础属性
    emp.setCreateTime(LocalDateTime.now());
    emp.setUpdateTime(LocalDateTime.now());
    //2.保存员工基本信息
    empMapper.insert(emp);
        
    //int i = 1/0;
    if(true){
        throw new Exception("出异常啦....");
    }
        
    //3. 保存员工的工作经历信息 - 批量
    Integer empId = emp.getId();
    List<EmpExpr> exprList = emp.getExprList();
    if(!CollectionUtils.isEmpty(exprList)){
        exprList.forEach(empExpr -> empExpr.setEmpId(empId));
        empExprMapper.insertBatch(exprList);
    }
}

事务回滚


结论:

  • 在Spring的事务管理中,默认只有运行时异常 RuntimeException才会回滚。
  • 如果还需要回滚指定类型的异常,可以通过rollbackFor属性来指定。

最后修改:2025 年 09 月 10 日
如果觉得我的文章对你有用,请随意赞赏