Spring事务 和 事务传播机制

        这里的 事务 和之前 MySQL的事务 一样,都是表示将⼀组操作封装成⼀个执⾏单元(封装到⼀起),要么全部成功,要么全部失败。

Spring 中事务的实现

1. 编程式事务(手动档)。

package com.example.transactiondemo.controller;

import com.example.transactiondemo.Service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;

    // 编程式事务
    @Autowired
    private DataSourceTransactionManager transactionManager;
    @Autowired
    private TransactionDefinition transactionDefinition;

    @RequestMapping("/del")
    public int del(Integer id) {
        if (id == null || id <= 0) return 0;
        // 1.开启事务
        TransactionStatus transactionStatus = null;
        int result = 0;
        try {
            transactionStatus = transactionManager.getTransaction(transactionDefinition);
            // 业务操作,删除用户
            result = userService.del(id);
            System.out.println("删除操作影响了:" + result + " 行");
            // 2. 提交事务/回滚事务
            transactionManager.commit(transactionStatus); // 提交事务
        } catch (Exception e) {
            if (transactionStatus != null) {
                transactionManager.rollback(transactionStatus); // 出现异常,回滚事务
            }
        }
        return result;
    }

}

运行结果:

2. 声明式事务(自动挡)。

package com.example.transactiondemo.controller;

import com.example.transactiondemo.Service.UserService;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user2")
public class UserController2 {

    @Autowired
    private UserService userService;

    @RequestMapping("del")
    @Transactional  // 在方法开始之前开启事务,方法正常执行结束之后提交事务,如果执行途中发生异常,则回滚事务
    public int del(@Param("id") Integer id) {
        if (id == null || id <= 0) {
            return 0;
        }
        int result = userService.del(id);
        System.out.println("删除操作影响了:" + result + " 行");
        return result;
    }

}

没有发生异常的情况下的运行结果:

1. 发生了异常情况下的运行结果(发生了回滚):

2. 但是这种代码的书写会导致程序直接异常而不能进行下一步了,为了更好解决这一问题就需要使用 try-catch 把错误代码包裹住:

        第 1 种是将异常抛出去,然后 spring 感知到了就自动帮我们实现回滚了,第 2 种是自己手动处理,因为 spring 会认为我们既然都用了 try-catch 了,那么就是知道会有异常发生了,从而不会帮我们自动实现回滚。

关于 @Transactional 

单元测试里:只要执行完都会进行回滚

普通类里:只有出现异常了才会进行回滚

参数的设置:

工作原理

通过切面,通过动态代理实现:

        @Transactional 是基于 AOP 实现的,AOP ⼜是使⽤动态代理实现的。如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理,如果⽬标对象没有实现了接⼝,会使用 CGLIB 动态代理。
        @Transactional 在开始执行业务之前,通过代理先开启事务,在执行成功之后再提交事务。如果中途遇到的异常,则回滚事务。

设置事务隔离级别

        Isolation.DEFAULT 是 spring 自己的,表示以连接的数据库的事务隔离级别为主。(Oracle 的默认隔离级别是 读已提交)
MySQL 事务隔离级别有 4 种:(默认的是 可重复读)

        ● 脏读:⼀个事务读取到了另⼀个事务修改的数据之后,后⼀个事务⼜进⾏了回滚操作,从⽽导致第⼀个事务读取的数据是错误的。
        ● 不可重复读:⼀个事务两次查询得到的结果不同,因为在两次查询中间,有另⼀个事务把数据修改了。
        ● 幻读:⼀个事务两次查询中得到的结果集不同,因为在两次查询中另⼀个事务有新增了⼀部分数据。

关于 spring boot 中事务会失效的情况

1.非public修饰的方法

2.timeout超时:当在 @Transactional 上,设置了一个较小的超时时间时,如果方法本身的执行时间超过了设置的 timeout 超时时间,那么就会导致本来应该正常插入数据的方法执行失败

3. 代码中有try/catch,也没进行手动回滚操作

4.当前数据库本身不支持事务

Spring 的事务传播机制

        事务传播机制就是当有多个事务线性调用时,其中一个环节的事务发生回滚了,那么上一级的事务 要不要 发生回滚。

种类

        1. Propagation.REQUIRED:默认的事务传播级别,它表示如果当前存在事务,则加入该事务;如果当前没有事务,则创建⼀个新的事务。
        2. Propagation.SUPPORTS:如果当前存在事务,则加⼊该事务;如果当前没有事务,则以⾮事务的⽅式继续运⾏。
        3. Propagation.MANDATORY:(mandatory:强制性)如果当前存在事务,则加⼊该事务;如果当前没有事务,则抛出异常。
        4. Propagation.REQUIRES_NEW:表示创建⼀个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部⽅法是否开启事务,Propagation.REQUIRES_NEW 修饰的内部⽅法会新开启⾃⼰的事务,且开启的事务相互独⽴,互不⼲扰。
        5. Propagation.NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
        6. Propagation.NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
        7. Propagation.NESTED: 如果当前存在事务,则创建⼀个事务作为当前事务的嵌套事务来运⾏;如果当前没有事务,则该取值等价于 PROPAGATION_REQUIRED。

根据是否支持当前事务分为 3 类

关于这三种的区别

  • 加入事务,Propagation.REQUIRED:在方法被调用时会尝试加入当前的事务,如果不存在事务,则创建一个新的事务。如果外部事务回滚,那么内部事务也会被回滚。如果只是内部进行回滚,那么外部事务也会回滚但会报错。
  • 嵌套事务,Propagation.NESTED:表示如果当前存在事务,则在嵌套事务中执行;如果当前没有事务,则创建一个新的事务并在其中执行。嵌套事务是独立于外部事务的子事务,它具有自己的保存点,并且可以独立于外部事务进行回滚。如果内部嵌套事务发生异常并回滚,它将会回滚到自己的保存点,而不影响外部事务。但如果外部的大事务回滚,会导致所有嵌套的小事务也回滚。和加入事务的区别就是有无保存点。
  • Propagation.REQUIRES_NEW:在一个调用链上的事务各自执行,互不干扰。
举例子

        假设有一个银行系统,其中包含转账的操作。

支持事务

  • 场景:用户 A 要向用户 B 转账 1000 元。
  • 过程:如果这个转账操作支持事务,那么在整个事务执行过程中,要么所有相关的数据库操作(如减少用户 A 的账户余额、增加用户 B 的账户余额等)都成功完成,事务被提交,转账成功;要么如果在其中任何一个操作出现问题(例如数据库故障、余额不足等),整个事务都会回滚,所有的操作都被撤销,就好像转账从未发生过一样。
  • 举例:在转账过程中,已经成功减少了用户 A 的余额,但在增加用户 B 的余额时出现了数据库错误。由于这是支持事务的操作,整个事务会回滚,用户 A 的余额会恢复到转账前的状态。

不支持事务

  • 场景:用户 A 要向用户 B 转账 1000 元。
  • 过程:如果转账操作不支持事务,那么每个数据库操作都是独立的。即使其中一个操作失败,之前已经成功完成的操作也不会被回滚。
  • 举例:在转账时先减少了用户 A 的余额,但增加用户 B 余额的操作失败了。由于不支持事务,用户 A 的余额已经减少,而用户 B 没有收到转账。

嵌套事务

  • 场景:假设在一个大的事务(如年度财务结算)中包含了用户 A 向用户 B 的转账这个小事务。
  • 过程:在嵌套事务中,如果内部的转账小事务失败并回滚,不会影响外部的大事务。但如果外部的大事务回滚,会导致所有嵌套的小事务也回滚。
  • 举例:年度财务结算事务中包含了多个转账操作,其中用户 A 向用户 B 的转账这个嵌套事务因为用户 B 的账户异常而回滚,不影响年度财务结算事务中的其他操作(小不影响大)。但如果年度财务结算事务因为某种原因整体回滚,那么包括这个转账在内的所有相关操作都会被撤销。(大会影响小)

总结

        1. 在 Spring 项⽬中使⽤事务,⽤两种⽅法⼿动操作和声明式⾃动提交,其中后者使⽤的最多,在⽅法上添加 @Transactional 就可以实现了。
        2. 设置事务的隔离级别 @Transactional(isolation = Isolation.SERIALIZABLE),Spring 中的事务隔离级别有 5 种。
        3. 设置事务的传播机制 @Transactional(propagation = Propagation.REQUIRED),Spring 中的事务传播级别有 7 种。
  • 16
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
事务隔离级别是指多个事务并发执行时,一个事务对其他事务的可见性和影响程度的控制。Spring框架支持四个事务隔离级别: 1. 未提交读(READ UNCOMMITTED):最低级别,一个事务可以读取未提交的数据,会导致脏读,不可重复读和幻读问题的出现。 2. 提交读(READ COMMITTED):一个事务只能读取已提交的数据,可以避免脏读问题,但可能导致不可重复读和幻读问题。 3. 可重复读(REPEATABLE READ):在一个事务中多次读取同一数据时,结果保持一致,避免了不可重复读问题。但仍然可能存在幻读问题。 4. 串行化(SERIALIZABLE):最高级别,通过确保事务串行执行来避免脏读、不可重复读和幻读问题。但会降低并发性能。 传播机制是指在调用多个事务方法时,如何处理事务传播。Spring框架提供七种传播行为: 1. REQUIRED:如果当前没有事务,就创建一个新事务;如果已存在事务,则加入该事务。 2. SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方法执行。 3. MANDATORY:强制要求存在当前事务,如果没有事务就抛出异常。 4. REQUIRES_NEW:创建一个新事务,并暂停当前事务(如果有)。 5. NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,则将其挂起。 6. NEVER:以非事务方式执行操作,如果当前存在事务,则抛出异常。 7. NESTED:在当前事务的控制下执行一个嵌套事务,如果不存在当前事务,则创建一个新事务。嵌套事务可以独立提交或回滚,但在外部事务提交时才会生效。 通过选择合适的事务隔离级别和传播机制,可以确保事务的数据一致性、安全性和性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值