【Java EE】事务与事务的传播机制

文章目录

一、什么是事务

事务是一组数据库操作的集合,不可分割。这一组操作要么全部成功提交,要么全部失败回滚。

数据库原生事务三条指令:

sql 复制代码
--开启事务
start transaction;
--提交事务,全部操作生效
commit;
--回滚事务,撤销所有操作
rollback;

二、Spring的两种事务实现

Spring把数据库事务做封装,分为编程式事务声明式事务

1. 编程式事务

手动写代码控制事务开启、提交、回滚。

核心对象:

  • DataSourceTransactionManager:事务管理器,用来获取、提交、回滚事务
  • TransactionDefinition:事务属性
  • TransactionStatus:事务状态

示例代码:

java 复制代码
@Slf4j  
@RestController  
@RequestMapping("/user")  
public class UserController {  
    @Autowired  
    private UserInfoService userInfoService;  
  
    @Autowired  
    private DataSourceTransactionManager transactionManager;  
  
    @Autowired  
    private TransactionDefinition definition;  
  
    @RequestMapping("/registry")  
    public String registry(String name, String password) {  
        //1. 开启事务  
        // 创建副本  
        TransactionStatus status = transactionManager.getTransaction(definition);  
        //2. 用户注册  
        userInfoService.registryUser(name, password);  
        //3. 事务提交  
//        transactionManager.commit(status);  
//        log.info("用户注册成功");  
        //   回滚  
        transactionManager.rollback(status);  
        log.info("rollback");  
  
        return "registry success";  
    }  
}

缺点:代码侵入性强,业务代码和事务代码耦合,实际开发很少用。

2. 声明式事务@Transactional

基于AOP实现,注解驱动 ,不用手动写开启/提交/回滚代码。

进入方法自动开启事务;方法正常执行完毕自动提交;抛出未捕获异常自动回滚。

使用步骤:

  1. 引入spring‑tx依赖(mybatis自动添加)
xml 复制代码
<dependency>  
    <groupId>org.springframework</groupId>  
    <artifactId>spring-tx</artifactId>  
</dependency>
  1. public方法/类 上添加@Transactional

注意:

  1. 注解加在类上:类中所有public方法都生效
  2. 注解加在方法上:仅public方法生效;private/protected不会报错,但事务无效
  3. 建议加在Service业务层,一个业务会包含多次数据库操作。

示例:

java 复制代码
@Transactional
public String registry(String name,String password){
    userService.registryUser(name,password);
    int a = 10/0; //抛出运行时异常,事务自动回滚
    return "注册成功";
}

注意: 异常被try‑catch捕获,事务不会回滚

java 复制代码
@Transactional
public String registry(String name,String password){
    userService.registryUser(name,password);
    try {
        int a = 10/0;
    }catch (Exception e){
        e.printStackTrace();
    }
    //异常被捕获,Spring认为业务执行成功,事务直接提交,不会回滚
    return "注册成功";
}

两种解决方案

  1. catch之后重新抛出异常,接口返回500
java 复制代码
catch (Exception e){
    e.printStackTrace();
    throw e;
}
  1. 手动标记回滚,接口返回200
java 复制代码
catch (Exception e){
	TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}

三、@Transactional核心属性详解

1. rollbackFor:指定回滚异常

Spring事务默认只对RuntimeException运行时异常和Error执行回滚 ,普通受检异常不会触发回滚。如果想要对于某个指定的异常进行回滚就要使用rollbackFor.eoe

案例:抛出IOException,程序报错,但是数据依旧入库,事务没有回滚。

想要所有异常都触发回滚,配置rollbackFor = Exception.class

java 复制代码
@Transactional(rollbackFor = Exception.class)
public void test() throws IOException{
    //业务逻辑
}

2. isolation:事务隔离级别

隔离级别解决多个并发事务之间数据读取的问题,解决脏读、不可重复读、幻读。

MySQL原生4种隔离级别:

隔离级别 脏读 不可重复读 幻读
READ UNCOMMITTED读未提交 存在 存在 存在
READ COMMITTED读已提交 解决 存在 存在
REPEATABLE READ可重复读(MySQL默认) 解决 解决 存在
SERIALIZABLE串行化 解决 解决 解决

Spring提供5种隔离级别枚举:

  1. Isolation.DEFAULT默认,跟随数据库本身隔离级别
  2. Isolation.READ_UNCOMMITTED:读未提交
  3. Isolation.READ_COMMITTED:读已提交
  4. Isolation.REPEATABLE_READ:可重复读
  5. Isolation.SERIALIZABLE:串行化

使用示例:

java 复制代码
@Transactional(isolation = Isolation.READ_COMMITTED)

3. propagation:事务传播机制

事务隔离级别解决的是多个事务并发访问数据库;事务传播机制解决的是一个方法调用另一个带事务的方法时,事务该如何传递。

场景:A方法带@Transactional,调用同样带@Transactional的B方法。B是加入A的事务?还是新建独立事务?这就是传播机制控制。

Spring有7种传播行为:

传播枚举 说明
REQUIRED(默认) 如果当前有事务,就加入当前事务;没有事务新建事务
SUPPORTS 有事务就加入;没有事务就非事务运行
MANDATORY 必须存在外部事务,没有直接抛异常
REQUIRES_NEW 总是新建独立事务;外部存在事务就挂起外部事务
NOT_SUPPORTED 非事务运行;有外部事务就挂起外部事务
NEVER 非事务运行,如果存在外部事务直接抛异常
NESTED 存在外部事务,就创建嵌套子事务(保存点savepoint);无外部事务时等价REQUIRED

REQUIRED

controller层调用两个方法,这三个方法同属于一个事务

如果内部抛出异常,整个事务全部回滚

REQUIRES_NEW

内外都是独立事务,互不干扰。

外层调用内层,内层新开事务。如果内层异常回滚,不影响外层事务提交

场景:希望日志记录失败,不要影响主业务执行,就可以使用REQUIRES_NEW。

NESTED嵌套事务

基于数据库savepoint保存点实现,可以实现局部回滚

如果子事务2回滚,父事务和子事务1不会受到影响,可以正常提交;如果父事务回滚,所有子事务都不能正常提交。

NESTED 和 REQUIRED区别

  1. REQUIRED:多个方法合并为同一个事务,任意位置回滚,整体全部回滚,没有保存点。
  2. NESTED:父事务下创建子事务保存点;子事务回滚只回滚自己,父事务可以继续提交。
相关推荐
考虑考虑6 小时前
数据库中的EXISTS
运维·数据库·后端
Wang's Blog6 小时前
Java框架快速入门: Spring Security+OAuth2之数据库和实体类的RBAC改造
java·数据库·spring
梦想平凡7 小时前
百游棋牌源代码开发搭建教程(十):隔离部署、备份恢复与双端验收
java·前端·javascript·数据库·源代码管理
xcLeigh7 小时前
让大模型长出手脚,自己写SQL查数据库(Function Calling初探)
数据库·人工智能·sql·时序数据库·timechoai
yume_sibai8 小时前
06-Rust Web 开发实战(Axum 框架 + 数据库 + JWT 认证 + 中间件 + 部署)
前端·数据库·rust
AugustRed9 小时前
Neo4j 图数据库原理 + 应用场景简单介绍
数据库·neo4j
Gl�ria10 小时前
Redis 高可用架构对比
数据库·redis
IpdataCloud10 小时前
高可用的IP数据接口平台有哪些?从可用性、P99延迟到离线部署的评估框架(含代码)
数据库·tcp/ip·ip
呆萌很10 小时前
MySQL 数据库和表的管理操作(命令行)
数据库·mysql
IvorySQL11 小时前
当PostgreSQL“听懂”MySQL——协议兼容层的设计与实战
数据库·人工智能·postgresql