【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:父事务下创建子事务保存点;子事务回滚只回滚自己,父事务可以继续提交。
相关推荐
润乾软件1 小时前
Trae+SQLazy 编写复杂 SQL 实践
数据库·sql
会编程的吕洞宾1 小时前
Redis大Key拖垮线上?排查定位与治理实战
数据库·redis·bootstrap
ltl1 小时前
WiredTiger History Store:文档库里的第三种 MVCC
数据库
码农阿豪1 小时前
若依启动突然报 Redis MISCONF?一次从应用报错到磁盘爆满的完整排查记录
数据库·redis·bootstrap
ltl2 小时前
SQLite 查询计划器:没有 ANALYZE 时靠什么猜
数据库
ltl2 小时前
Redis maxmemory 策略:近似 LRU/LFU 与 MEMORY DOCTOR
数据库
leisoo80972 小时前
ig50数据落盘ClickHousevsTimescaleDBvsDuckDB实测对比 IG50免费开源股票数据API接口
开发语言·jvm·数据库·python·json
倔强的石头1063 小时前
数据库迁移工具从单机作业走向云端协同
数据库
FfHUCisI3 小时前
Golang 数据库连接池深度调优
android·数据库·golang