JAVA-Web后端学习8 事务

へんなの へんなの,へんなの へんなの

事务介绍

就像我们在实战中对于员工数据的存储一样,有个问题我们需要注意:存储员工数据时会分别存储员工的个人基本信息和员工的工作经历,如果前者存储成功而后者存储失败该怎么办?

是的,这是在实际开发中不允许的。因为二者是一体,属于同一个业务操作,必须保证同时成功或者都失败才行,否则会造成数据库数据的不一致

事务:一套操作的集合,是一个不可分割的单位。事务会把所有的操作作为一个整体一起向系统提交或者撤销操作,也就是说,这些操作要么全部成功,要么全部失败

一般情况下,默认MySQL的事务是自动提交的,也就是说当执行一条DML语句,MySQL会隐式地自动提交事务

事务操作

MYSQL中的事务操作如下:

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

-- 提交事务操作
commit;

-- 撤销事务操作,回滚 (只要有一项失败)
rollback;

事务控制

回看这段代码

@Override
public void insertNewEmpData(EmpData empData) {
    //1.保存员工个人基本信息
    empData.setCreateTime(LocalDateTime.now());
    empData.setUpdateTime(LocalDateTime.now());
    empData.setPassword("123456");
    empMapper.insertNewEmp(empData);
    //2.保存员工工作经历
    List<EmpExprData> empExprDataList=empData.getExprList();
    if(!CollectionUtils.isEmpty(empExprDataList)) {
        for(EmpExprData empExprData : empExprDataList) {
            empExprData.setEmpId(empData.getId());
        }
        System.out.println("Now we get It ! empExprMapper.insertNewEmpExpr!");
        empExprMapper.insertNewEmpExpr(empExprDataList);
    }
}

我们需要保证1和2要么都成功,要么都失败,也就是使用事务控制

Spring事务管理

Spring的事务管理封装在了@Transactional注解中,作用是将当前方法交给spring进行事务管理,方法执行前开启事务,执行完毕则提交事务,否则回滚事务

@Transactional注解的位置在逻辑业务层(Service)的方法上、类上、接口上

一般推荐@Transactional注解添加在对于数据库进行多次增删改查的方法上

在application.yml的配置文件中添加如下语句,用于输出Spring事务管理的底层日志

logging:
  level:
    org.springframework.jdbc.support.JdbcTransactionManager: debug

接下来,我们在之前的实战代码中添加上该注解

@Transactional
@Override
public void insertNewEmpData(EmpData empData) {
    empData.setCreateTime(LocalDateTime.now());
    empData.setUpdateTime(LocalDateTime.now());
    empData.setPassword("123456");
    empMapper.insertNewEmp(empData);
    List<EmpExprData> empExprDataList=empData.getExprList();
    if(!CollectionUtils.isEmpty(empExprDataList)) {
        for(EmpExprData empExprData : empExprDataList) {
            empExprData.setEmpId(empData.getId());
        }
        System.out.println("Now we get It ! empExprMapper.insertNewEmpExpr!");
        empExprMapper.insertNewEmpExpr(empExprDataList);
    }
}

控制台相关信息如下,可以看到相应的事务操作:
image

那么如果出现异常会怎么办,比如将代码修改为如下:

@Service
public class EmpServiceImpl implements EmpService {

    @Autowired
    private EmpMapper empMapper;
    private EmpExprMapper empExprMapper;

    @Transactional
    @Override
    public void insertNewEmpData(EmpData empData) {
        empData.setCreateTime(LocalDateTime.now());
        empData.setUpdateTime(LocalDateTime.now());
        empData.setPassword("123456");
        empMapper.insertNewEmp(empData);
        List<EmpExprData> empExprDataList=empData.getExprList();
        if(!CollectionUtils.isEmpty(empExprDataList)) {
            for(EmpExprData empExprData : empExprDataList) {
                empExprData.setEmpId(empData.getId());
            }
            System.out.println("Now we get It ! empExprMapper.insertNewEmpExpr!");
            empExprMapper.insertNewEmpExpr(empExprDataList);
        }
    }

}

这是一个常见的错误,此时@Autowired只会给empMapper进行DI注入而没有给empExprMapper进行DI注入,从而导致后者没有实体对象

接下来执行,控制台输出信息如下:
image

可以看到只有员工基本数据表的插入操作执行了,而工作经历表的没有执行,而失败之后事务执行了回滚(rollback)操作。

这时去查看相关的数据表,发现都没有新数据注入
image
image

而如果没有添加相关的事务注解,再来执行同样的问题
image
可以看到有一句话Closing non transactional SqlSession 表示这是正在关闭一个非事务性 SqlSession
此时查看数据库
image
image
可以看到员工个人基本数据表被更新,而工作经历数据表没有被更新,这显然是不对的

Spring事务管理的进阶操作

rollbackFor

rollbackFor属性用于控制出现何种异常类型时进行事务回滚

@Transactional(rollbackFor={Exception.class})

propagation

事务控制中的propagation(传播机制)是指当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行事务控制的机制。

事务传播机制定义了在多个事务方法相互调用时,事务应该如何传播和管理。它解决了当一个事务方法调用另一个事务方法时,新方法的事务是加入现有事务、创建新事务,还是以非事务方式执行的问题。

乍一听有点抽象,不过没关系,使用案例代码介绍一下
还是之前的代码,这一次添加一个员工添加日志记录方法,不论添加该员工数据成功还是失败,都要调用该方法记录日志到一个日志数据库中

@Transactional
@Override
public void insertNewEmpData(EmpData empData) {
    try {
      empData.setCreateTime(LocalDateTime.now());
      empData.setUpdateTime(LocalDateTime.now());
      empData.setPassword("123456");
      empMapper.insertNewEmp(empData);
      List<EmpExprData> empExprDataList=empData.getExprList();
      if(!CollectionUtils.isEmpty(empExprDataList)) {
          for(EmpExprData empExprData : empExprDataList) {
              empExprData.setEmpId(empData.getId());
          }
          System.out.println("Now we get It ! empExprMapper.insertNewEmpExpr!");
          empExprMapper.insertNewEmpExpr(empExprDataList);
      }
    } finally {
      EmpLogInsert();
    }
}

//这是另一个类的方法
@Transactional
void EmpLogInsert(){
    //记录日志
    ...
}

我们为了保证调用insertNewEmpData方法时无论插入员工数据成功与否都要调用EmpLogInsert方法,使用了try...finally关键字修饰,并且在后者也添加了@Transactional事务注解

但是如果运行的话就会发现EmpLogInsert方法在insertNewEmpData方法时无论插入员工数据出现异常时会执行,但是也会因为后者的回滚而导致无法完全记录日志到数据库中去

这因为事务控制的传播机制默认定义如下:

@Transaction(prapagation=Propagation.REQUIRED)

Propagation.REQUIRED是Spring默认的事务传播行为,意思是:如果当前存在事务,则加入该事务;如果当前不存在事务,则创建一个新的事务。

也就是说EmpLogInsert方法的事务在insertNewEmpData方法的调用中会加入后者的事务,而前者的事务则不会被创建,因此后者失败的话连带前者也会被回滚

所以需要定义如下:

@Transaction(prapagation=Propagation.REQUIRES_NEW)

Propagation.REQUIRES_NEW表示新事务与原有事务完全独立,互不影响。无论当前是否存在事务,都创建一个新的事务;如果当前存在事务,则挂起当前事务。

把上面的代码完整修改后如下:

@Transaction
@Override
public void insertNewEmpData(EmpData empData) {
    try {
      empData.setCreateTime(LocalDateTime.now());
      empData.setUpdateTime(LocalDateTime.now());
      empData.setPassword("123456");
      empMapper.insertNewEmp(empData);
      List<EmpExprData> empExprDataList=empData.getExprList();
      if(!CollectionUtils.isEmpty(empExprDataList)) {
          for(EmpExprData empExprData : empExprDataList) {
              empExprData.setEmpId(empData.getId());
          }
          System.out.println("Now we get It ! empExprMapper.insertNewEmpExpr!");
          empExprMapper.insertNewEmpExpr(empExprDataList);
      }
    } finally {
      EmpLogInsert();
    }
}

//这是另一个类的方法
@Transactional(prapagation=Propagation.REQUIRES_NEW)
void EmpLogInsert(){
    //记录日志
    ...
}

除了REQUIREDREQUIRES_NEW两种行为之外,一共有如下行为:

image

事务的四大特性

事务存在四大特性,称为ACID特性,详细如下:

原子性(Atomicity) 事务是不可分割的最小单元,要么全部成功,要么全部失败
一致性(Consistency) 事务完成时必须使所有数据都保持一致状态
隔离性(Isolation) 数据库系统提供的隔离机制,保证事务在不收外部并发操作的独立环境下运行
持久性(Durability) 事务一旦提交或者回滚,对于数据库造成的影响是永久的

posted @ 2026-03-23 16:41  tcswuzb  阅读(1)  评论(0)    收藏  举报