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) ​

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