Skip to content

Spring事务管理


问题引入

目前我们实现的新增员工功能中,操作了两次数据库,执行了两次 insert 操作

第一次:保存员工的基本信息到 emp 表中

第二次:保存员工的工作经历信息到 emp_expr 表中

问题分析

如果程序出现了异常,员工表 emp 数据保存成功了, 但是 emp_expr 员工工作经历信息表,数据保存失败了,这是不允许的

事务引入

使用事务来管理,保持一致性,二者为一个整体,要么成功,要么失败

基本介绍

事务是一组操作的集合,它是一个不可分割的工作单位。事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作 要么同时成功,要么同时失败

MySQL 的事务默认是自动提交的,也就是说,当执行一条 DML 语句,MySQL 会立即隐式的提交事务

MySQL 事务案例

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

需要在这组操作执行之前,先开启事务 ( 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;

Spring 事务管理

@Transactional 注解

(1)作用

就是在当前这个方法执行开始之前来开启事务,方法执行完毕之后提交事务。如果在这个方法执行的过程当中出现了异常,就会进行事务的回滚操作

(2)可定义的位置:业务层的方法上、类上、接口上

方法上:当前方法交给 spring 进行事务管理

类上:当前类中所有的方法都交由 spring 进行事务管理

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

(3)案例应用:新增员工

java
@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);
    }
}

配置事务管理日志

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

rollbackFor 属性

该属性是一个异常回滚属性

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

通过 rollbackFor 这个属性可以指定出现何种异常类型回滚事务

java
@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);
    }
}

propagation 属性

该属性是一个事务传播属性

什么是事务传播?

两个事务方法,一个 A 方法,一个 B 方法。在这两个方法上都添加了@Transactional 注解,就代表这两个方法都具有事务,而在 A 方法当中又去调用了 B 方法

所谓事务的传播行为,指的就是在 A 方法运行的时候,首先会开启一个事务,在 A 方法当中又调用了 B 方法, B 方法自身也具有事务,那么 B 方法在运行的时候,到底是加入到 A 方法的事务当中来,还是 B 方法在运行的时候新建一个事务?这个就涉及到了事务的传播行为

propagation 属性值


REQUIREDS_NEW 属性应用举例

在新增员工信息时,无论是成功还是失败,都要记录操作日志

java
@Service
public class EmpLogServiceImpl implements EmpLogService {

    @Autowired
    private EmpLogMapper empLogMapper;

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    @Override
    public void insertLog(EmpLog empLog) {
        empLogMapper.insert(empLog);
    }
}

-----------------------------------------------------------------------------------------

@Autowired
private EmpMapper empMapper;
@Autowired
private EmpExprMapper empExprMapper;
@Autowired
private EmpLogService empLogService;

@Transactional(rollbackFor = {Exception.class})
@Override
public void save(Emp emp) {
    try {
        //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);
        }
    } finally {
        //记录操作日志
        EmpLog empLog = new EmpLog(null, LocalDateTime.now(), emp.toString());
        empLogService.insertLog(empLog);
    }

}

事务的四大特性(ACID)

原子性(Atomicity)

原子性是指事务包装的一组 sql 是一个不可分割的工作单元,事务中的操作要么全部成功,要么全部失败

一致性(Consistency)

一个事务完成之后数据都必须处于一致性状态

(1)如果事务成功的完成,那么数据库的所有变化将生效

(2)如果事务执行出现错误,那么数据库的所有变化将会被回滚(撤销),返回到原始状态

隔离性(Isolation)

多个用户并发的访问数据库时,一个用户的事务不能被其他用户的事务干扰,多个并发的事务之间要相互隔离

一个事务的成功或者失败对于其他的事务是没有影响

持久性(Durability)

一个事务一旦被提交或回滚,它对数据库的改变将是永久性的,哪怕数据库发生异常,重启之后数据依然存在