Spring Boot中的事务是如何实现的?懂吗?

news2024/11/27 4:15:59

SpringBoot中的事务管理,用得好,能确保数据的一致性和完整性;用得不好,可能会给性能带来不小的影响哦。

基本使用

在SpringBoot中,事务的使用非常简洁。首先,得感谢Spring框架提供的@Transactional注解,这个小东西可以说是非常强大了。

让我们先看一个基础的例子:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Transactional
    public void createUser(String name) {
        User user = new User(name);
        userRepository.save(user);
        // 这里假设有其他的逻辑操作
    }
}

在这个例子中,我们通过@Transactional注解标记了createUser方法。这意味着,当这个方法被调用时,Spring会为我们自动创建一个事务。如果方法正常执行完毕,事务就会提交;如果遇到异常,事务就会回滚,确保数据的一致性。

最近无意间获得一份阿里大佬写的刷题笔记,一下子打通了我的任督二脉,进大厂原来没那么难。

这是大佬写的, 7701页的BAT大佬写的刷题笔记,让我offer拿到手软

开启事务

虽然我们已经看到了如何使用@Transactional,但是你知道Spring是如何开启事务的吗?其实,当我们使用@Transactional注解时,Spring会通过AOP(面向切面编程)在运行时创建代理对象,来管理事务的开启和关闭。这个过程对我们来说是透明的,但了解其背后的机制对于深入理解Spring事务是很有帮助的。

事务回滚

默认情况下,如果被@Transactional注解的方法抛出了运行时异常(RuntimeException)或者Error,Spring就会回滚事务。但是,如果你想让事务在遇到非运行时异常时也回滚,可以这样做:

@Transactional(rollbackFor = Exception.class)
public void createUserWithRollbackForException(String name) throws Exception {
    // ...
}

性能优化

事务虽好,但也不是没有成本的。在某些高并发场景下,过多的事务操作可能会成为性能瓶颈。为了优化性能,我们可以通过以下几种方式:

  1. 减少事务范围:尽量让事务只包含那些必须要在同一事务中完成的操作。
  2. 只读事务:如果事务只涉及到数据的读取,可以将事务标记为只读,这样可以帮助数据库优化事务处理。
@Transactional(readOnly = true)
public User findUserById(Long id) {
    return userRepository.findById(id).orElse(null);
}

失效场景

在使用Spring事务的时候,有些情况可能会导致事务失效,比如:

  1. 自调用问题:在同一个类中,一个非事务方法调用事务方法,事务是不会起作用的。
  2. 异常处理:如果你在事务方法中捕获了所有异常,并没有重新抛出,事务是不会回滚的。

使用场景

事务通常用在需要保证一系列操作要么全部成功,要么全部失败的场景,比如:

  • 用户注册时,需要同时创建用户记录和用户的初始数据。
  • 订单支付时,需要更新订单状态和用户的账户余额。

代码示例

让我们再看一个例子,模拟用户转账的场景:

@Transactional
public void transfer(Long fromId, Long toId, BigDecimal amount) {
    User fromUser = userRepository.findById(fromId).orElseThrow();
    User toUser = userRepository.findById(toId).orElseThrow();
    
    fromUser.setBalance(fromUser.getBalance().subtract(amount));
    toUser.setBalance(toUser.getBalance().add(amount));
    
    userRepository.save(fromUser);
    userRepository.save(toUser);
}

在这个例子中,我们通过事务确保了转账操作的原子性。如果在转账过程中发生任何异常,比如余额不足,整个操作都会回滚,保证账户的数据一致性。

SpringBoot中事务管理的一些更高级和具体的应用场景

示例1:声明式事务的传播行为

Spring事务的传播行为定义了事务方法之间的交互方式。举个例子,我们来看REQUIREDREQUIRES_NEW传播行为的区别。

@Service
public class AccountService {

    @Autowired
    private TransferService transferService;

    @Transactional(propagation = Propagation.REQUIRED)
    public void methodA() {
        // 这里的操作在methodA的事务范围内
        transferService.methodB();
        // 如果methodB出错,整个methodA都会回滚
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void methodB() {
        // 这里的操作有自己的独立事务
        // 即使methodA失败了,methodB的操作还是会提交
    }
}

示例2:编程式事务管理

除了声明式事务,Spring还支持编程式事务管理,这在某些复杂的场景下非常有用。

@Service
public class ComplexService {

    @Autowired
    private TransactionTemplate transactionTemplate;

    public void executeComplexLogic() {
        transactionTemplate.execute(new TransactionCallback<Void>() {
            @Override
            public Void doInTransaction(TransactionStatus status) {
                // 这里是你的业务逻辑
                // 如果需要回滚,可以调用 status.setRollbackOnly();
                return null;
            }
        });
    }
}

示例3:事务的隔离级别

事务的隔离级别决定了一个事务可能受其他并发事务影响的程度。比如,我们来看看如何设置隔离级别:

@Transactional(isolation = Isolation.SERIALIZABLE)
public void transferMoney(Long fromAccountId, Long toAccountId, BigDecimal amount) {
    // 这个方法会以最高的隔离级别运行,以避免并发事务带来的问题
    // 但是性能可能会受影响
}

示例4:事务超时设置

在某些长时间运行的事务中,你可能需要设置事务的超时时间,以避免长时间占用资源。

@Transactional(timeout = 10) // 10秒超时
public void processLargeData() {
    // 这个方法如果运行超过10秒,事务会被标记为回滚
}

示例5:事务回滚的条件自定义

有时候,你可能需要自定义事务回滚的条件。比如,只在特定的异常出现时才回滚。

@Transactional(rollbackFor = {CustomException.class})
public void updateUserDetails(User user) throws CustomException {
    // 这个方法只在CustomException抛出时才回滚
    // 其他异常不会触发回滚
}

示例6:嵌套事务

嵌套事务允许在一个事务内部开始一个新的事务。如果内部事务失败,它会回滚到它开始的状态,而不影响外部事务。

@Transactional
public void parentMethod() {
    // 父事务的操作...

    try {
        nestedMethod();
    } catch (Exception e) {
        // 处理内部事务异常,父事务可以继续
    }

    // 父事务的其他操作...
}

@Transactional(propagation = Propagation.NESTED)
public void nestedMethod() {
    // 嵌套事务的操作...
}

示例7:声明式事务与异常处理

处理声明式事务时,异常的处理方式至关重要。下面是一个常见的错误处理方式。

@Transactional
public void updateUser() {
    try {
        // 更新用户数据的操作...

    } catch (Exception e) {
        // 捕获异常,这将导致事务不回滚
    }
}

在这个例子中,由于异常被捕获并没有重新抛出,事务将不会回滚,这可能会导致数据的不一致性。

示例8:使用事务同步管理器

在某些情况下,你可能需要直接与事务同步管理器进行交互,以获取当前事务的状态信息。

public void complexBusinessLogic() {
    boolean isCurrentTransactionActive = TransactionSynchronizationManager.isActualTransactionActive();
    if (isCurrentTransactionActive) {
        // 执行依赖于当前事务的操作...
    }
}

示例9:异步方法与事务

异步方法和事务一起使用时需要特别小心,因为异步方法通常会在不同的线程中运行,这可能会导致事务管理出现问题。

@Async
@Transactional
public Future<String> asyncMethodWithTransaction() {
    // 异步操作,但事务可能不会按预期工作
    // 因为它可能在不同的线程中执行
    return new AsyncResult<>("Done");
}

示例10:事务日志记录

在某些业务场景中,你可能需要记录事务的执行情况,特别是在事务提交或回滚时。

@Transactional
public void transactionalMethodWithLogging() {
    // 事务操作...

    TransactionSynchronizationManager.registerSynchronization(
        new TransactionSynchronizationAdapter() {
            @Override
            public void afterCommit() {
                // 记录事务提交后的日志
            }

            @Override
            public void afterCompletion(int status) {
                if (status == TransactionSynchronization.STATUS_ROLLED_BACK) {
                    // 记录事务回滚的日志
                }
            }
        }
    );
}

通过这些示例,你可以看到Spring事务管理在不同场景下的应用。

理解这些复杂场景对于能够在实际开发中灵活运用Spring事务管理至关重要。

记住,每个场景都有其特殊性,选择正确的事务策略可以帮助你避免许多常见的问题。

核心要点

  1. 基本使用:使用@Transactional注解来声明事务,这是Spring提供的一种声明式事务管理方式。
  2. 事务传播行为:Spring事务的传播行为定义了事务之间的相互作用,如REQUIREDREQUIRES_NEWNESTED等,这决定了事务是否共享或独立。
  3. 事务的隔离级别:隔离级别(如READ_COMMITTEDSERIALIZABLE等)控制事务之间的可见性,防止诸如脏读、不可重复读、幻读等问题。
  4. 事务的回滚规则:默认情况下,Spring仅在运行时异常发生时回滚事务。可通过rollbackFor自定义回滚条件。
  5. 超时和只读设置:可以设置事务的超时时间和声明只读事务,以优化性能和资源利用。

高级应用场景

  1. 编程式事务管理:通过TransactionTemplate或直接使用PlatformTransactionManager来手动管理事务。
  2. 嵌套事务:通过NESTED传播行为实现嵌套事务,内部事务失败不影响外部事务。
  3. 异步和事务:异步方法中使用事务需要特别注意,由于执行线程的不同,可能影响事务的管理。
  4. 事务同步管理:使用TransactionSynchronizationManager进行事务的细粒度控制,如在事务提交或回滚后执行特定操作。
  5. 异常处理与事务回滚:异常处理在事务中非常重要,不当的异常处理可能导致事务不回滚,引起数据不一致。

实际应用建议

  • 合理设计事务范围:避免将大量操作包含在单一事务中,以减少资源锁定时间和提高性能。
  • 注意异常处理:确保适当的异常抛出,以触发事务回滚。
  • 避免在异步方法中使用事务:或者确保你理解如何在多线程环境下正确管理事务。
  • 谨慎使用嵌套事务:它们可能会增加复杂性和性能开销。
  • 监控和调优:在生产环境中监控事务的性能,根据需要调整事务策略和配置。

总之,SpringBoot中的事务管理是一个强大但需要谨慎使用的工具。

理解它的工作原理和应用场景,可以帮助你更有效地管理数据一致性和应用性能。

记住,每个应用的需求不同,所以在使用事务时,总是要考虑到你的具体场景和需求。

最后说一句(求关注,求赞,别白嫖我)

最近无意间获得一份阿里大佬写的刷题笔记,一下子打通了我的任督二脉,进大厂原来没那么难。

这是大佬写的, 7701页的BAT大佬写的刷题笔记,让我offer拿到手软

项目文档&视频:

项目文档 & 视频

本文,已收录于,我的技术网站 ddkk.com,有大厂完整面经,工作技术,架构师成长之路,等经验分享

求一键三连:点赞、分享、收藏

点赞对我真的非常重要!在线求赞,加个关注我会非常感激

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1300819.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

为 Compose MultiPlatform 添加 C/C++ 支持(1):在 kotlin 中使用 cinterop 实现与 C/C++ 互操作

前言 在安卓中我们可以使用 jvm 提供的 jni 方便的编写 C/C 代码并与 java/kotlin 互操作。 但是当使用 kotlin MultiPlatform 时想要调用 C/C 代码将变得麻烦甚至是不可用&#xff0c;因为对于 Android 和 Desktop 来说依旧使用的是 jvm &#xff0c;所以只要稍微适配一下也…

Android画布Canvas绘图scale,Kotlin

Android画布Canvas绘图scale&#xff0c;Kotlin <?xml version"1.0" encoding"utf-8"?> <androidx.appcompat.widget.LinearLayoutCompat xmlns:android"http://schemas.android.com/apk/res/android"xmlns:app"http://schemas.…

【密码学基础】Diffie-Hellman密钥交换协议

DH介绍 Diffie-Hellman密钥协议算法是一种确保共享密钥安全穿越不安全网络的方法。 这个机制的巧妙在于需要安全通信的双方可以用这个方法确定对称密钥&#xff0c;然后可以用这个密钥进行加密和解密。 但是注意&#xff0c;这个密钥交换协议 只能用于密钥的交换&#xff0c;而…

【Table/SQL Api】Flink Table/SQL Api表转流读取MySQL

引入依赖 jdbc依赖 flink-connector-jdbc mysql-jdbc-driver 操作mysql数据库 <!-- Flink-Connector-Jdbc --><dependency><groupId>org.apache.flink</groupId><artifactId>flink-connector-jdbc_${scala.binary.version}</artifactId>…

深度学习——第4.1章 深度学习的数学基础

第4章 深度学习的数学基础 目录 4.1 向量 4.2 求和符号 4.3 累乘符号 4.4 导数 4.5 偏导数 4.6 矩阵 4.7 指数函数和对数函数 注意&#xff1a;4.6和4.7位于4.2章 第4章 深度学习的数学基础 本章总结一下机器学习所需的数学知识&#xff0c;同时介绍如何在Python中使用…

PolarCTF网络安全2023冬季个人挑战赛 WEB方向题解 WriteUp

完工&#xff0c;最后CB链没时间打了&#xff0c;估计也不怎么打得出来&#xff0c;今天一边在打polar一边弄服务外包赛&#xff0c;好累呜呜呜。 Polar 冬 干正则&#xff08;WEB&#xff09; 直接给了源码 parse_str()&#xff1a;将字符串解析成多个变量 payload&#xff…

基于JavaWeb+SSM+Vue马拉松报名系统微信小程序的设计和实现

基于JavaWebSSMVue马拉松报名系统微信小程序的设计和实现 源码获取入口Lun文目录前言主要技术系统设计功能截图订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 源码获取入口 Lun文目录 1系统概述 1 1.1 研究背景 1 1.2研究目的 1 1.3系统设计思想 1 2相关技术 2 2.…

23 秋 计网真题回忆

总体 计网整体部分还是以全龙老师的PPT和王道的考研题和课后题考的部分很细 都是PPT上的内容 最好不要光做王道 PPT上很细的点也要看题量很大25个选择 5个判断 十个填空 5个大题好像是 不熟练的话可能写不完所有的配图都是网上找的资源 比如19-22的学校题和408真题 非本人所有…

Low Cost and High Performance FPGA with ARM and SDRAM inside

AG10KSDE176 AGM AG10KSDE176 是由 AGM FPGA AG10K 与 SDRAM 叠封集成的芯片&#xff0c;具有 AG10K FPGA 的可编程功能&#xff0c;提供更多可编程 IO&#xff0c;同时内部连接大容量 SDRAM。  FPGA 外部管脚输出 EQFP176 封装底部 Pad 为 GND&#xff0c;管脚说明请见下表&…

使用cmake构建的工程的编译方法

1、克隆项目工程 2、进入到工程目录 3、执行 mkdir build && cd build 4、执行 cmake .. 5、执行 make 执行以上步骤即可完成对cmake编写的工程进行编译 &#xff0c;后面只需执行你的编译结果即可 $ git clone 你想要克隆的代码路径 $ cd 代码文件夹 $ mkdir bu…

鸿蒙前端开发-构建第一个ArkTS应用(Stage模型)

创建ArkTS工程 若首次打开DevEco Studio&#xff0c;请点击Create Project创建工程。如果已经打开了一个工程&#xff0c;请在菜单栏选择File > New > Create Project来创建一个新工程。 选择Application应用开发&#xff08;本文以应用开发为例&#xff0c;Atomic Serv…

四. 基于环视Camera的BEV感知算法-环视背景介绍

目录 前言0. 简述1. 环视背景介绍2. 环视思路3. 主流基于环视Camera的算法详解总结下载链接参考 前言 自动驾驶之心推出的《国内首个BVE感知全栈系列学习教程》&#xff0c;链接。记录下个人学习笔记&#xff0c;仅供自己参考 本次课程我们来学习下课程第四章——基于环视Camer…

Kafka Streams:深度探索实时流处理应用程序

Apache Kafka Streams 是一款强大的实时流处理库&#xff0c;为构建实时数据处理应用提供了灵活且高性能的解决方案。本文将深入探讨 Kafka Streams 的核心概念、详细原理&#xff0c;并提供更加丰富的示例代码&#xff0c;以帮助读者深入理解和应用这一流处理框架。 1. Kafka…

Redis,什么是缓存穿透?怎么解决?

Redis&#xff0c;什么是缓存穿透&#xff1f;怎么解决&#xff1f; 1、缓存穿透 一般的缓存系统&#xff0c;都是按照key去缓存查询&#xff0c;如果不存在对用的value&#xff0c;就应该去后端系统查找&#xff08;比如DB数据库&#xff09;。一些恶意的请求会故意查询不存在…

ELK简单介绍二

学习目标 能够部署kibana并连接elasticsearch集群能够通过kibana查看elasticsearch索引信息知道用filebeat收集日志相对于logstash的优点能够安装filebeat能够使用filebeat收集日志并传输给logstash kibana kibana介绍 Kibana是一个开源的可视化平台,可以为ElasticSearch集群…

【Spring教程25】Spring框架实战:从零开始学习SpringMVC 之 SpringMVC入门案例总结与SpringMVC工作流程分析

目录 1.入门案例总结2. 入门案例工作流程分析2.1 启动服务器初始化过程2.2 单次请求过程 欢迎大家回到《Java教程之Spring30天快速入门》&#xff0c;本教程所有示例均基于Maven实现&#xff0c;如果您对Maven还很陌生&#xff0c;请移步本人的博文《如何在windows11下安装Mave…

react.js源码二

三、调度Scheduler scheduling(调度)是fiber reconciliation的一个过程&#xff0c;主要决定应该在何时做什么?在stack reconciler中&#xff0c;reconciliation是“一气呵成”&#xff0c;对于函数来说&#xff0c;这没什么问题&#xff0c;因为我们只想要函数的运行结果&…

高云GW1NSR-4C开发板M3硬核应用

1.M3硬核IP下载&#xff1a;Embedded M3 Hard Core in GW1NS-4C - 科技 - 广东高云半导体科技股份有限公司 (gowinsemi.com.cn) 特别说明&#xff1a;IDE必须是1.9.9及以后版本&#xff0c;1.9.8会导致编译失败&#xff08;1.9.8下1.1.3版本IP核可用&#xff09; 以下根据官方…

【后端开发】Next.js 13.4:前端开发的游戏规则改变者!

自我介绍 做一个简单介绍&#xff0c;酒架年近48 &#xff0c;有20多年IT工作经历&#xff0c;目前在一家500强做企业架构&#xff0e;因为工作需要&#xff0c;另外也因为兴趣涉猎比较广&#xff0c;为了自己学习建立了三个博客&#xff0c;分别是【全球IT瞭望】&#xff0c;【…

云计算大屏,可视化云计算分析平台(云实时数据大屏PSD源文件)

大屏组件可以让UI设计师的工作更加便捷&#xff0c;使其更高效快速的完成设计任务。现分享可视化云分析系统、可视化云计算分析平台、云实时数据大屏的大屏Photoshop源文件&#xff0c;开箱即用&#xff01; 若需 更多行业 相关的大屏&#xff0c;请移步小7的另一篇文章&#…