事务管理:确保数据一致性与业务完整性

摘要 :本文围绕事务管理展开,先回顾事务基本概念与操作,后深入探讨Spring事务管理。通过具体案例剖析事务管理在实际应用中的问题及解决方案,详细介绍@Transactional注解及其属性rollbackForpropagation的使用。

关键词 :事务管理;Spring框架;@Transactional注解;事务传播行为

参考资料黑马程序员day13 完整项目请从第10天开始看

一、事务管理概述

1.1 事务回顾

事务是一组不可分割的操作集合,作为一个整体向数据库提交或撤销请求,确保这组操作要么全部成功,要么全部失败。其具体操作包含以下三步:

  1. 开启事务 :在一组操作开始前执行,指令为start transaction / begin
  2. 提交事务 :当所有操作成功完成后,使用commit指令提交。
  3. 回滚事务 :若操作过程中出现异常,通过rollback指令回滚事务。

1.2 Spring事务管理

1.2.1 案例

以解散部门为例,需求是删除部门信息的同时删除该部门下的所有员工数据。

  1. 代码实现

DeptServiceImpl

java 复制代码
@Slf4j
@Service
public class DeptServiceImpl implements DeptService {
    @Autowired
    private DeptMapper deptMapper;
    @Autowired
    private EmpMapper empMapper;

    //根据部门id,删除部门信息及部门下的所有员工
    @Override
    public void delete(Integer id) {
        //根据部门id删除部门信息
        deptMapper.deleteById(id);

        //删除部门下的所有员工信息
        empMapper.deleteByDeptId(id);
    }
}

DeptMapper

java 复制代码
@Mapper
public interface DeptMapper {
    /**
     * 根据id删除部门信息
     * @param id   部门id
     */
    @Delete("delete from dept where id = #{id}")
    void deleteById(Integer id);
}

EmpMapper

java 复制代码
@Mapper
public interface EmpMapper {
    //根据部门id删除部门下所有员工
    @Delete("delete from emp where dept_id=#{deptId}")
    public int deleteByDeptId(Integer deptId);
}
  1. 测试情况 :正常运行时,dept表和emp表中的相关数据均被删除。但当在DeptServiceImpldelete方法中添加异常模拟代码(如int i = 1/0;)后,部门信息被删除,而部门下的员工数据未删除,导致数据不一致。
1.2.2 原因分析

上述问题产生的原因在于,删除部门操作先执行且成功,随后的异常导致后续删除员工数据的操作未执行。要保证数据一致性,需确保解散部门的两个业务操作要么同时成功,要么同时失败,这可通过事务来实现。在Spring框架中,可借助@Transactional注解简化事务控制代码。

1.2.3 @Transactional注解

@Transactional注解用于控制事务,在方法执行前开启事务,执行完毕后提交事务,若执行过程中出现异常则回滚事务。通常在业务层使用(spring的三层业务结构是:表现层cotroller、业务逻辑层service、数据访问层Dao),因为业务层的一个业务功能可能涉及多个数据访问操作,通过在业务层控制事务,可将这些操作纳入同一事务范围。

@Transactional注解可置于方法、类或接口上:

  • 方法:仅当前方法由Spring进行事务管理。
  • :当前类中的所有方法都由Spring管理事务。
  • 接口:接口下所有实现类的所有方法均由Spring管理事务。

DeptServiceImpldelete方法上添加@Transactional注解后,再次测试,由于异常事务回滚,部门和员工数据均未被删除,保证了数据一致性。同时,可在application.yml配置文件中开启事务管理日志,以便查看事务相关信息:

yaml 复制代码
#spring事务管理日志
logging:
  level:
    org.springframework.jdbc.support.JdbcTransactionManager: debug

1.3 事务进阶

1.3.1 rollbackFor

在业务方法上添加@Transactional注解实现事务管理时,默认情况下 ,只有RuntimeException(运行时异常)会触发事务回滚。

例如:

  • 当业务方法中抛出除0的算数运算异常(运行时异常)时,事务会回滚。
  • 但当抛出Exception(编译时异常)时,事务不会回滚。

若希望所有异常都能触发事务回滚,可通过配置@Transactional注解的rollbackFor属性指定异常类型。如:

java 复制代码
@Slf4j
@Service
public class DeptServiceImpl implements DeptService {
    @Autowired
    private DeptMapper deptMapper;
    @Autowired
    private EmpMapper empMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(Integer id) {
        //根据部门id删除部门信息
        deptMapper.deleteById(id);

        //模拟:异常发生
        int num = id / 0;

        //删除部门下的所有员工信息
        empMapper.deleteByDeptId(id);
    }
}

重新启动服务测试删除部门操作,可发现由于异常事务回滚,部门未被删除。由此可见,在Spring事务管理中,默认仅运行时异常会回滚事务,若要回滚指定类型异常,可通过rollbackFor属性指定。

PS:rollbackFor参数用来指定哪些异常会触发事务回滚。 rollbackFor = Exception.class 表明所有 Exception 类型的异常都会触发回滚,这其中包含了受检查异常(Checked Exception)。

1.3.3 propagation
1.3.3.1 介绍

propagation属性用于配置事务的传播行为,即当一个事务方法调用另一个事务方法时,后者应如何进行事务控制。例如,有AB两个事务方法,均添加@Transactional注解,若A方法调用B方法,事务传播行为决定B方法是加入A方法的事务,还是新建一个事务。

通过在@Transactional注解后指定propagation属性来控制事务传播行为,常见的事务传播行为如下:

属性值 含义
REQUIRED 【默认值】需要事务,有则加入,无则创建新事务
REQUIRES_NEW 需要新事务,无论有无,总是创建新事务
SUPPORTS 支持事务,有则加入,无则在无事务状态中运行
NOT_SUPPORTED 不支持事务,在无事务状态下运行,如果当前存在已有事务,则挂起当前事务
MANDATORY 必须有事务,否则抛异常
NEVER 必须没事务,否则抛异常

实际应用中,重点关注REQUIRED(默认值)和REQUIRES_NEW

1.3.3.2 案例

以解散部门并记录操作日志为例,具体步骤如下:

  1. 准备工作

创建数据库表dept_log

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

引入实体类DeptLogMapper接口DeptLogMapper、业务接口DeptLogService及业务实现类DeptLogServiceImpl

  1. 代码实现
java 复制代码
@Slf4j
@Service
//@Transactional //当前业务实现类中的所有的方法,都添加了spring事务管理机制
public class DeptServiceImpl implements DeptService {
    @Autowired
    private DeptMapper deptMapper;
    @Autowired
    private EmpMapper empMapper;
    @Autowired
    private DeptLogService deptLogService;

    //根据部门id,删除部门信息及部门下的所有员工
    @Override
    @Log
    @Transactional(rollbackFor = Exception.class)
    public void delete(Integer id) throws Exception {
        try {
            //根据部门id删除部门信息
            deptMapper.deleteById(id);
            //模拟:异常
            if (true) {
                throw new Exception("出现异常了~~~");
            }
            //删除部门下的所有员工信息
            empMapper.deleteByDeptId(id);
        } finally {
            //不论是否有异常,最终都要执行的代码:记录日志
            DeptLog deptLog = new DeptLog();
            deptLog.setCreateTime(LocalDateTime.now());
            deptLog.setDescription("执行了解散部门的操作,此时解散的是" + id + "号部门");
            //调用其他业务类中的方法
            deptLogService.insert(deptLog);
        }
    }
    //省略其他代码...
}
  1. 测试与分析
    • 测试情况 :重新启动SpringBoot服务,测试删除3号部门,程序发生Exception异常,执行事务回滚,dept_log表中未记录日志数据。
    • 原因分析delete操作开启一个事务,insert操作默认事务传播行为为REQUIRED,即加入delete操作的事务。由于同一事务中的操作要么同时成功,要么同时失败,异常发生时事务回滚,导致insert操作也被回滚。
  2. 解决方案
    DeptLogServiceImpl类的insert方法上添加@Transactional(propagation = Propagation.REQUIRES_NEW)表示无论是否存在事务,都创建新事务并独立运行。 重启服务再次测试删除3号部门,insert方法运行完毕后事务立即提交,即使外部delete方法所在事务出现异常,已提交的insert事务也不会回滚。
相关推荐
爱喝酸奶的桃酥1 分钟前
MYSQL数据库集群高可用和数据监控平台
java·数据库·mysql
唐僧洗头爱飘柔952739 分钟前
【SSM-SSM整合】将Spring、SpringMVC、Mybatis三者进行整合;本文阐述了几个核心原理知识点,附带对应的源码以及描述解析
java·spring·mybatis·springmvc·动态代理·ioc容器·视图控制器
骑牛小道士1 小时前
Java基础 集合框架 Collection接口和抽象类AbstractCollection
java
alden_ygq1 小时前
当java进程内存使用超过jvm设置大小会发生什么?
java·开发语言·jvm
triticale1 小时前
【Java】网络编程(Socket)
java·网络·socket
淘源码d2 小时前
什么是ERP?ERP有哪些功能?小微企业ERP系统源码,SpringBoot+Vue+ElementUI+UniAPP
java·源码·erp·erp源码·企业资源计划·企业erp·工厂erp
源码方舟2 小时前
【基于ALS模型的教育视频推荐系统(Java实现)】
java·python·算法·音视频
Mcworld8572 小时前
整数分解JAVA
java·开发语言
小南家的青蛙2 小时前
LeetCode面试题 01.09 字符串轮转
java·leetcode
秋野酱3 小时前
基于javaweb的SpringBoot爱游旅行平台设计和实现(源码+文档+部署讲解)
java·spring boot·后端