Web后端开发(事务管理)

        hello啊各位,今天我们来学习spring的事务管理

        事务管理

        首先我们来讲解spring的事务管理,我们会分为三部分来讲解,分别是事务回顾、Spring事务管理、事务进阶

        事务回顾

        接下来我们回顾一下什么是事务,事务是一组操作的集合,是一个不可分割的工作单位,这些操作要么成功,要么同时失败。那我们如何控制这组操作呢?此时就涉及到了事务的操作,事务操作分别如下:

  • 开启事务(一组操作开始前,开启事务):start transaction / begin;
  • 提交事务(这组操作全部成功后,提交事务):commit;
  • 回滚事务(中间任何一个操作出现异常,回滚事务):rollback。

以上便是事务的概念以及事务的基本操作,接下来我们便介绍一下Spring事务管理

        Spring事务管理

首先,我们完成一个小案例

解散部门:删除部门,同时删除该部门下的员工

        根据前几期我们的部门管理,我们之前只是删除了对应的部门信息,并没有删除对应的员工信息,所以我们接下来要完善删除部门的这一功能,第一步,我们要删除部门,第二步,我们要根据部门id,删除部门下的员工信息。

        我们对之前的代码进行修改,首先,修改实现类中的代码,然后再empmapper中添加根据部门id删除部门员工信息的方法

@Autowired
    private EmpMapper empMapper;

@Override
    public void delete(Integer id) {
        deptMapper.deleteByid(id); //根据ID删除部门数据

        empMapper.deleteByDeptId(id); //根据部门id删除该部门下的员工
    }
/**
     * 根据部门ID来删除部门下的员工数据
     * @param deptId
     */
    @Delete("delete from emp where dept_id = #{deptId}")
    void deleteByDeptId(Integer deptId)

以上便是删除部门优化后的代码了,记下来我们加入一个异常试一下

 @Override
    public void delete(Integer id) {
        deptMapper.deleteByid(id); //根据ID删除部门数据

        int i = 1/0; //模拟抛出异常
        empMapper.deleteByDeptId(id); //根据部门id删除该部门下的员工
    }

        我们运行后可以发现,即使程序运行抛出了异常,部门依然删除了,但是部门下的员工并没有删除,这就造成了数据的不一致

        即一旦出现异常,下面的代码便都不会执行。这时,我们要保证数据的一致性,那么就需要保证上面两步操作要么同时成功,要么同时失败。即让这两个操作处于同一事务当中。即在程序开始之前开启事务,在程序运行结束时提交/回滚事务。

        在spring框架中,spring已经把事务控制的代码封装成了一个注解。接下来我们就介绍一个@Transactional:一般情况下添加的业务层执行多次数据访问操作这类的增删改方法上

@Transactional

位置:业务(service)层的方法上、类上、接口上

作用:将当前方法交给spring进行事务管理,方法执行前,开启事务;方法执行成功,提交事务,出现异常,回滚事务。

此时我们只需要在service方法上加上此注解,同时在pop.yml中开启事务管理日志即可

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

此时,再次开启服务,会发现出现异常之后会进行回滚,将删除的部门恢复以保证数据的一致性

        事务进阶

        上面我们讲解了一个注解@Transactional,接下来我们就讲一下此注解的使用细节,即两个常见的属性:rollbackFor和propagation

rollbackFor

        默认情况下,只有出现RuntimeException才回滚异常。rolllbackFor属性用于控制出现何种异常类型,回滚事务。所以如果我们不是运行时异常,代码是不会回滚异常的,也就是说依旧会造成数据的不一致。

        此时,如果我们想要所有异常的属性都回滚的话,那么我们只需要声明rollbackFor的属性为Exception.class即可满足

propagation

        接下来我们来讲解事务管理注解@Transactional的第二个属性propagation,propagation是用来配置事务的传播行为的,所谓事务的传播行为,指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行事务控制。

        即有两个方法,一个a,一个b,这两个方法上面都加了@transactional,即代表这两个方法都具有事务,而在a方法中调用b方法,b方法在运行时到底是加入到a方法中,还是说b方法在运行时新建一个新的事务。此时就涉及到了事务的传播行为,我们要想控制事务的传播行为,就要在注解后面加一个(Propagation = Propagation.REQUIRED),接下来我们就来介绍一下默认的传播行为。

Web后端开发(事务管理)_第1张图片

        接下来我们就通过一个案例来演示一下propagation,案例需求:解散部门时,无论成功还是失败,都要记录操作日志

步骤:

  1. 解散部门:删除部门,删除部门下的员工
  2. 记录日志到数据表中

接下来我们就来添加代码,首先,需要在数据库中再创建一个表

-- 表结构 --
create table dept_log(
                         id int auto_increment comment '主键ID' primary key,
                         create_time datetime null comment '操作时间',
                         description varchar(300) null comment '操作描述'
)comment '部门操作日志表';

然后优化impl

@Transactional
    @Override
    public void delete(Integer id) throws Exception {
        try {
            deptMapper.deleteByid(id); //根据ID删除部门数据
            int i = 1/0; //模拟抛出异常
        /*if (true){
            throw new Exception("出错啦....");
        }*/
            empMapper.deleteByDeptId(id); //根据部门id删除该部门下的员工

            
        } finally {
            //记录日志到数据表中
            DeptLog deptLog = new DeptLog();
            deptLog.setCreateTime(LocalDateTime.now());
            deptLog.setDescription("执行了解散部门的操作,此次解散的是"+id+"号部门");
            deptLogService.insert(deptLog);
        }
    }

紧接着是一些基础代码的书写,包括接口和类的创建

package com.ittaotao.service;

import com.ittaotao.pojo.DeptLog;

public interface DeptLogService {

    void insert(DeptLog deptLog);

}
package com.ittaotao.service.impl;

import com.ittaotao.mapper.DeptLogMapper;
import com.ittaotao.pojo.DeptLog;
import com.ittaotao.service.DeptLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class DeptLogServiceImpl implements DeptLogService {

    @Autowired
    private DeptLogMapper deptLogMapper;


    @Transactional //(propagation = Propagation.REQUIRES_NEW)
    @Override
    public void insert(DeptLog deptLog) {
        deptLogMapper.insert(deptLog);
    }
}
package com.ittaotao.mapper;

import com.ittaotao.pojo.DeptLog;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface DeptLogMapper {

    @Insert("insert into dept_log(create_time, description) values (#{createTime},#{description})")
    void insert(DeptLog log);
}

        我们开启服务,在前端进行删除部门的操作,此时因为有异常存在并没有删除成功,我们打开表结构,却发现日志并没有记录进去,再次打开控制台查看。

Web后端开发(事务管理)_第2张图片

        我们可以发现delete和insert默认公用的是一个事务,即使我们没有配置,但事务传播也是有默认值的,即required,那么我们此时应该将其修改

@Transactional (propagation = Propagation.REQUIRES_NEW)
    @Override
    public void insert(DeptLog deptLog) {
        deptLogMapper.insert(deptLog);
    }

即无论我当前调用这个方法时是否有事务,我调用insert这个方法时都会开启一个新事务

此时重新执行,可以发现数据表中已经有数据了

Web后端开发(事务管理)_第3张图片

最后我们进行一个小结

事务的传播行为属性大部分时间只需要两个

  • REQUIRED:大部分情况下都是用该传播行为即可
  • REQUIRES_NEW:当我们不希望事务之间相互影响时,可以使用该传播行为。比如:下订单前需要记录日志,不论订单保存成功与否,都需要保证日志记录能够记录成功

        以上便是我们这篇博客的全部内容了,下篇博客将为大家讲解javaweb的第二大核心知识点——AOP操作,老规矩,如有问题还请各位评论区多多指教,感谢感谢!!!

你可能感兴趣的:(java,学习,spring,boot)