当前位置: 首页 > news >正文

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

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 {@Autowiredprivate UserRepository userRepository;@Transactionalpublic 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 {@Autowiredprivate 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 {@Autowiredprivate TransactionTemplate transactionTemplate;public void executeComplexLogic() {transactionTemplate.execute(new TransactionCallback<Void>() {@Overridepublic 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() {@Overridepublic void afterCommit() {// 记录事务提交后的日志}@Overridepublic 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.lryc.cn/news/257286.html

相关文章:

  • 应用安全:JAVA反序列化漏洞之殇
  • 基于以太坊的智能合约开发Solidity(函数继承篇)
  • 【论文极速读】LVM,视觉大模型的GPT时刻?
  • TS基础语法
  • 【基于NLP的微博情感分析:从数据爬取到情感洞察】
  • Ubuntu 18.04使用Qemu和GDB搭建运行内核的环境
  • GEE——利用Landsat系列数据集进行1984-2023EVI指数趋势分析
  • JAVA安全之Spring参数绑定漏洞CVE-2022-22965
  • 辨析旅行商问题(TSP)与车辆路径问题(VRP)
  • 2024年JAVA招聘行情如何?
  • 【合集】SpringBoot——Spring,SpringBoot,SpringCloud相关的博客文章合集
  • yolov5 获取漏检图片脚本
  • Unity之OpenXR+XR Interaction Toolkit接入微软VR设备Windows Mixed Reality
  • 【小聆送书第二期】人工智能时代AIGC重塑教育
  • 中国移动公网IP申请过程
  • 动态获取绝对路径
  • pytorch中的归一化:BatchNorm、LayerNorm 和 GroupNorm
  • RocketMq源码分析(九)--顺序消息
  • Windows下nginx的启动,重启,关闭等功能bat脚本
  • Python 字典:dic = {} 和 dic = defaultdict(list)之间的区别
  • 绘图 Seaborn 10个示例
  • airserver mac 7.27官方破解版2024最新安装激活图文教程
  • 文章解读与仿真程序复现思路——电力系统自动化EI\CSCD\北大核心《考虑移动式储能调度的配电网灾后多源协同孤岛运行策略》
  • Spring Boot 优雅地处理重复请求
  • TailwindCSS 多主题色配置
  • Vue3:表格单元格内容由:图标+具体内容 构成
  • 【项目日记(一)】高并发内存池项目介绍
  • 4-Docker命令之docker commit
  • RabbitMQ学习笔记10 综合实战 实现新商家规定时间内上架商品检查
  • Project Euler 865 Triplicate Numbers(线性dp)