Java Spring事务,事务的传播机制

文章目录

事务

  1. 事务:同时执行多个操作,要么同时成功,要么同时失败
    事务具有原子性

比如:转账

  1. 数据库执行事务

使用Spring实现事务

  1. Spring对于事务的实现

    (1) 编程式实现(和数据库是一样的)

    手动开启事务

    手动提交事务/手动回滚事务

    (2) 声明式事务(利用自动开启事务和提交事务),推荐使用这种方式

    通过注解来实现

  2. 对事务的操作,必然要使用数据库

  3. MyBatis版本不匹配

  4. 如果引入了MyBatis的依赖,就需要配置数据库

    引入MyBatis依赖之所以感觉"需要"配置数据库,是因为Spring Boot的自动配置机制被触发,它试图为你创建一个完整可用的MyBatis环境,而这个环境的基石------数据源(DataSource)------必须由你通过配置文件来提供必要的信息。

没有配置,自动配置就无法完成它的工作,从而导致应用启动失败。

java 复制代码
spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/trans_test?characterEncoding=utf8&useSSL=false
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
  configuration: # 配置打印 MyBatis⽇志
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    map-underscore-to-camel-case: true #配置驼峰⾃动转换

Spring 编程式事务(了解)

  1. Spring 手动操作事务和上面 MySQL 操作事务类似, 有 3 个重要操作步骤:

    • 开启事务(获取事务)

    • 提交事务

    • 回滚事务

  2. SpringBoot 内置了两个对象:

    (1) DataSourceTransactionManager 事务管理器. 用来获取事务(开启事务), 提交或回滚事务

    (2) TransactionDefinition 是事务的属性, 在获取事务的时候需要将TransactionDefinition 传递进去从而获得一个事务 TransactionStatus

  3. 观察事务回滚和提交时,日志的差异

Spring 声明式事务 @Transactional

  1. @Transactional 自动完成事务的提交,回滚,关闭等操作
  2. @Transactional 什么时候回滚??

    @Transactional只会检查程序是否出错了,出错了就回滚,
    没有出错或者是捕获了异常就不回滚,
    捕获了异常,但是重新抛出了会回滚,
    捕获了异常,手动回滚
    @Transactional只关心你是否解决掉异常了,不关注你的内部代码,没有解决掉异常就会回滚
java 复制代码
package com.example.trans.demos.controller;

import com.example.trans.demos.service.UserService;
import lombok.extern.slf4j.Slf4j;
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.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.beans.Transient;

@Slf4j
@RequestMapping("/trans")
@RestController
public class TranController {
    @Autowired
    private UserService userService;

    // 正常处理,没有错误
    @Transactional
    @RequestMapping("/registry")
    public String registry(String userName,String password){
        Integer result = userService.insertUser(userName,password);
        log.info("用户插入成功,result " + result);

        return "注册成功";
    }

    // 有异常,没有处理
    @Transactional
    @RequestMapping("/t2")
    public String registry3(String userName,String password){
        Integer result = userService.insertUser(userName,password);
        int a = 10 / 0;
        log.info("用户插入成功,result " + result);

        return "注册成功";
    }

    // 抛出异常并且捕获了异常
    @Transactional
    @RequestMapping("/t3")
    public String registry2(String userName,String password) {
        Integer result = userService.insertUser(userName, password);
        try {
            int a = 10 / 0;
        } catch (Exception e){
            log.info("程序出错");
        }
        log.info("用户插入成功,result " + result);

        return "注册成功";
    }

    // 程序报错,重新抛出异常
    // 事务回滚
    @Transactional
    @RequestMapping("/t4")
    public String registry4(String userName,String password) {
        Integer result = userService.insertUser(userName, password);
        try {
            int a = 10 / 0;
        } catch (Exception e){
            log.info("程序出错");
            throw e;
        }
        log.info("用户插入成功,result " + result);

        return "注册成功";
    }

    // 程序报错
    // 手动回滚
    @Transactional
    @RequestMapping("/t5")
    public String registry5(String userName,String password) {
        Integer result = userService.insertUser(userName, password);
        try {
            int a = 10 / 0;
        } catch (Exception e){
            log.info("程序出错");
            // 当前事务回滚
            // 当前的事务的状态设置为回滚
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        log.info("用户插入成功,result " + result);

        return "注册成功";
    }
}

@Transactional

  1. @Transactional 默认处理error和运行时异常才会回滚,遇到非运行时异常就会提交
  1. 遇到了异常,并且处理了这个异常,但是抛出了其他异常

  2. @Transactional可以设置哪些异常去回滚

  3. @Transactional的底层:

事务的隔离级别

  1. 读已提交
  2. 可重复读

Spring的事务隔离级别

Spring 中事务隔离级别有5 种:

  1. Isolation.DEFAULT : 以连接的数据库的事务隔离级别为主.
  2. Isolation.READ_UNCOMMITTED : 读未提交, 对应SQL标准中 READ UNCOMMITTED
  3. Isolation.READ_COMMITTED : 读已提交,对应SQL标准中 READ COMMITTED
  4. Isolation.REPEATABLE_READ : 可重复读, 对应SQL标准中 REPEATABLE READ
  5. Isolation.SERIALIZABLE : 串行化, 对应SQL标准中 SERIALIZABLE

可以自己设置隔离级别:
Spring 中事务隔离级别可以通过 @Transactional 中的 isolation 属性进行设置

java 复制代码
@Transactional(isolation = Isolation.READ_COMMITTED)
@RequestMapping("/r3")
public String r3(String name,String password) throws IOException {
//... 代码省略
return "r3";
}

事务传播机制

  1. 事务传播机制: 多个事务方法存在调用关系时, 事务是如何在这些方法间进行传播的

比如:

有两个方法A, B都被 @Transactional 修饰, A方法调用B方法

A方法运行时, 会开启一个事务。 当A调用B时, B方法本身也有事务, 此时B方法运行时, 是加入A的事务, 还是创建⼀个新的事务呢?

这个就涉及到了事务的传播机制。

  1. 事务的传播机制:

举个例子:

比如有人结婚了,需要买一套房子

事务传播机制的使用

  1. REQUIRED(默认值)

  2. REQUIRES_NEW

  3. Required 调用的事务,如果成功,整体就成功了,如果其中一个失败,整体失败

    如果当前存在事务,就加入事务。如果当前没有事务,则创建一个新的事务

  4. Required_new

  5. 嵌套事务

  6. nested和required的区别:

总结

相关推荐
我命由我123454 小时前
Spring Cloud - Spring Cloud 声明式接口调用(Fiegn 声明式接口调用概述、Fiegn 使用)
java·后端·spring·spring cloud·微服务·架构·java-ee
摸鱼的老谭4 小时前
Java学习之旅第三季-17:Lambda表达式
java·lambda表达式·1024程序员节
摸鱼的老谭4 小时前
Java学习之旅第三季-18:方法引用
java·方法引用
lang201509285 小时前
Spring依赖注入与配置全解析
java·spring
百锦再5 小时前
破茧成蝶:全方位解析Java学习难点与征服之路
java·python·学习·struts·kafka·maven·intellij-idea
羊锦磊5 小时前
[ Redis ] SpringBoot集成使用Redis(补充)
java·数据库·spring boot·redis·spring·缓存·json
golang学习记5 小时前
Go slog 日志打印最佳实践指南
开发语言·后端·golang
新手村领路人5 小时前
python opencv gpu加速 cmake msvc cuda编译问题和设置
开发语言·python·opencv
dengzhenyue5 小时前
C# 初级编程
开发语言·c#