SpringData JPA事务管理:@Transactional注解与事务传播

news2025/3/17 2:16:36

在这里插入图片描述

文章目录

    • 引言
    • 一、事务基础概念
    • 二、@Transactional注解详解
      • 2.1 基本用法
      • 2.2 属性配置
      • 2.3 类级别与方法级别
    • 三、事务传播行为详解
      • 3.1 REQUIRED(默认)
      • 3.2 REQUIRES_NEW
      • 3.3 其他传播行为
    • 四、事务隔离级别
    • 五、事务最佳实践
      • 5.1 正确设置事务边界
      • 5.2 合理使用只读事务
      • 5.3 选择合适的隔离级别
    • 总结

引言

在企业级Java应用开发中,事务管理是确保数据一致性和完整性的关键机制。Spring Data JPA提供了强大而灵活的事务管理功能,通过声明式注解简化了复杂事务的实现。本文将深入解析@Transactional注解的使用方法以及事务传播行为的各种模式,帮助开发者掌握如何在实际应用中正确使用事务,确保数据操作符合ACID特性(原子性、一致性、隔离性和持久性)。

一、事务基础概念

事务是数据库操作的基本单位,它确保一组相关的数据库操作要么全部成功,要么全部失败。Spring框架通过抽象层统一了不同数据访问技术的事务管理,为Spring Data JPA提供了一致的事务支持。

Spring事务管理的核心是事务管理器(PlatformTransactionManager),它负责事务的开始、提交和回滚。Spring Data JPA默认使用JpaTransactionManager作为事务管理器,与底层的JPA实现(如Hibernate)进行集成。

@Configuration
@EnableTransactionManagement
public class TransactionConfig {
    
    @Bean
    public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactory);
        return transactionManager;
    }
}

二、@Transactional注解详解

@Transactional是Spring框架提供的核心注解,用于声明事务边界。在Spring Data JPA中,它可以应用于类和方法级别,提供了丰富的配置选项。

2.1 基本用法

@Transactional注解的基本用法是将其应用于需要事务支持的方法或类:

@Service
public class OrderService {
    
    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private OrderItemRepository orderItemRepository;
    
    /**
     * 创建订单并保存订单项
     * 整个方法作为一个事务执行
     */
    @Transactional
    public Order createOrder(Order order, List<OrderItem> items) {
        // 保存订单
        Order savedOrder = orderRepository.save(order);
        
        // 保存订单项
        for (OrderItem item : items) {
            item.setOrder(savedOrder);
            orderItemRepository.save(item);
        }
        
        return savedOrder;
    }
}

2.2 属性配置

@Transactional注解提供了多个属性,用于配置事务的行为:

@Transactional(
    timeout = 30,                           // 事务超时时间(秒)
    readOnly = false,                       // 非只读事务
    isolation = Isolation.READ_COMMITTED,   // 隔离级别
    propagation = Propagation.REQUIRED,     // 传播行为
    rollbackFor = Exception.class           // 发生异常时回滚
)
public Payment processPayment(Payment payment, Long accountId, double amount) {
    // 事务操作
}

主要属性说明:

  • timeout:指定事务的超时时间,超过该时间事务将自动回滚
  • readOnly:标记事务是否为只读,只读事务可以优化性能
  • isolation:指定事务的隔离级别,控制并发事务的可见性
  • propagation:指定事务的传播行为,定义嵌套调用时的事务边界
  • rollbackFor/noRollbackFor:指定导致事务回滚或不回滚的异常类型

2.3 类级别与方法级别

@Transactional可以应用于类和方法级别,方法级别的配置会覆盖类级别的配置:

@Service
@Transactional(readOnly = true)  // 类级别默认为只读事务
public class ProductService {
    
    // 继承类级别的只读事务
    public List<Product> findAllProducts() {
        return productRepository.findAll();
    }
    
    // 覆盖类级别配置,设置为可写事务
    @Transactional(readOnly = false)
    public Product createProduct(Product product) {
        return productRepository.save(product);
    }
}

三、事务传播行为详解

事务传播行为定义了事务方法被另一个事务方法调用时如何处理事务边界。Spring支持七种传播行为,每种行为适用于不同的场景。

3.1 REQUIRED(默认)

REQUIRED是最常用的传播行为,如果当前存在事务,则加入该事务;如果不存在事务,则创建新事务。

@Service
public class OrderProcessingService {
    
    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private PaymentService paymentService;
    
    @Transactional
    public void processOrderPayment(Long orderId, Payment payment) {
        // 查询订单
        Order order = orderRepository.findById(orderId)
            .orElseThrow(() -> new RuntimeException("Order not found"));
        
        // 更新订单状态
        order.setStatus("PAYMENT_PROCESSING");
        orderRepository.save(order);
        
        // 调用支付服务处理支付(将加入当前事务)
        paymentService.processPayment(payment, order.getTotalAmount());
        
        // 更新订单状态为已支付
        order.setStatus("PAID");
        orderRepository.save(order);
    }
}

3.2 REQUIRES_NEW

REQUIRES_NEW总是创建新事务,如果当前存在事务,则挂起当前事务。

@Service
public class UserService {
    
    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private AuditService auditService;
    
    @Transactional
    public User updateUser(Long userId, User userDetails) {
        User user = userRepository.findById(userId)
            .orElseThrow(() -> new RuntimeException("User not found"));
        
        // 更新用户信息
        user.setName(userDetails.getName());
        User savedUser = userRepository.save(user);
        
        try {
            // 记录审计日志(使用新事务,即使日志记录失败也不影响用户更新)
            auditService.logUserUpdate(userId, user.getName());
        } catch (Exception e) {
            // 捕获异常但不抛出,防止影响主事务
            System.err.println("Failed to log audit: " + e.getMessage());
        }
        
        return savedUser;
    }
}

@Service
class AuditService {
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void logUserUpdate(Long userId, String userName) {
        // 记录审计日志
    }
}

3.3 其他传播行为

Spring还支持以下传播行为:

  • SUPPORTS:如果当前存在事务,则加入该事务;如果不存在事务,则以非事务方式执行。
  • NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,则挂起当前事务。
  • MANDATORY:要求当前存在事务,否则抛出异常。
  • NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
  • NESTED:如果当前存在事务,则创建嵌套事务;如果不存在事务,则创建新事务。嵌套事务是当前事务的一个保存点,嵌套事务的回滚只影响该保存点之后的操作。

四、事务隔离级别

事务隔离级别定义了一个事务可能受其他并发事务影响的程度。Spring支持SQL标准定义的四种隔离级别:

  • READ_UNCOMMITTED:可以读取未提交的数据变更,可能出现脏读、不可重复读和幻读问题。
  • READ_COMMITTED:只能读取已提交的数据变更,可避免脏读,但可能出现不可重复读和幻读问题。
  • REPEATABLE_READ:多次读取同一数据集合的结果是一致的,可避免脏读和不可重复读,但可能出现幻读问题。
  • SERIALIZABLE:完全串行化事务执行,避免了脏读、不可重复读和幻读问题,但性能最差。
@Transactional(isolation = Isolation.READ_COMMITTED)
public Customer updateCustomer(Long customerId, Customer customerDetails) {
    Customer customer = customerRepository.findById(customerId)
        .orElseThrow(() -> new RuntimeException("Customer not found"));
    
    customer.setName(customerDetails.getName());
    customer.setEmail(customerDetails.getEmail());
    
    return customerRepository.save(customer);
}

五、事务最佳实践

在Spring Data JPA项目中,遵循一些最佳实践可以帮助避免常见的事务问题并提高应用性能。

5.1 正确设置事务边界

事务边界应当尽可能小,只包含必要的数据库操作,避免在事务中执行非数据库操作。

// 不推荐:在事务中执行外部服务调用
@Transactional
public void processOrderBad(Long orderId) {
    Order order = orderRepository.findById(orderId)
        .orElseThrow(() -> new RuntimeException("Order not found"));
    
    order.setStatus("PROCESSED");
    orderRepository.save(order);
    
    // 不应在事务中调用外部服务
    emailClient.sendOrderConfirmation(order);  // 可能导致事务长时间运行
}

// 推荐:分离事务和非事务操作
@Transactional
public Order updateOrderStatus(Long orderId, String status) {
    Order order = orderRepository.findById(orderId)
        .orElseThrow(() -> new RuntimeException("Order not found"));
    
    order.setStatus(status);
    return orderRepository.save(order);
}

// 非事务方法调用事务方法
public void processOrder(Long orderId) {
    // 执行事务操作
    Order updatedOrder = updateOrderStatus(orderId, "PROCESSED");
    
    // 事务外执行外部调用
    emailClient.sendOrderConfirmation(updatedOrder);
}

5.2 合理使用只读事务

对于只读操作,使用readOnly=true可以优化性能。

@Transactional(readOnly = true)
public List<Product> findProductsByCategory(String category) {
    return productRepository.findByCategory(category);
}

5.3 选择合适的隔离级别

根据业务需求选择合适的隔离级别,平衡数据一致性和性能。

// 普通业务查询可使用默认隔离级别
@Transactional(isolation = Isolation.READ_COMMITTED)
public Product findById(Long id) {
    return productRepository.findById(id).orElse(null);
}

// 财务操作需要更高隔离级别
@Transactional(isolation = Isolation.SERIALIZABLE)
public void transfer(Long fromAccountId, Long toAccountId, double amount) {
    // 转账操作
}

总结

Spring Data JPA的事务管理机制为企业级应用提供了强大的数据一致性保障。通过@Transactional注解,开发者可以以声明式方式定义事务边界,并通过灵活配置属性来控制事务的行为特性。事务传播行为定义了事务方法相互调用时的事务边界处理规则,而隔离级别则解决了并发事务可能导致的数据不一致问题。

在实际应用中,开发者应当根据业务需求选择合适的事务配置,正确设置事务边界,避免事务嵌套导致的复杂问题,并选择适当的隔离级别平衡数据一致性和性能。遵循事务管理的最佳实践,可以构建出高效、可靠的数据访问层,确保应用系统在面对并发访问和异常情况时依然能够维护数据的完整性和一致性。

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

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

相关文章

第2章、WPF窗体及其属性

1、窗体的宽与高。 2、启动窗体设置 3、窗体的启动位置设置 4、窗体图标更换 5、应用程序的图标更改 6、 7、窗体属性汇总&#xff1a; AllowsTransparency 类型: bool 描述: 该属性决定窗口是否可以有透明效果。如果设置为true&#xff0c;窗口的背景必须设置为Transpar…

关于ModbusTCP/RTU协议对接Ethernet/IP(CIP)协议的方案

IGT-DSER智能网关模块支持西门子、倍福(BECKHOFF)、罗克韦尔AB&#xff0c;以及三菱、欧姆龙等各种品牌的PLC之间通讯&#xff0c;支持Ethernet/IP(CIP)、Profinet(S7)&#xff0c;以及FINS、MC等工业自动化常用协议&#xff0c;同时也支持PLC与Modbus协议的工业机器人、智能仪…

WPF 与 GMap.NET 结合实现雷达目标动态显示与地图绘制

概述 雷达上位机是雷达系统中用于数据可视化、分析和控制的核心软件。本文将介绍如何使用 C# 和 WPF 框架开发一个雷达上位机程序&#xff0c;主要功能包括&#xff1a; 显示目标轨迹&#xff1a;在界面上实时绘制雷达探测到的目标轨迹。点击显示详细信息&#xff1a;用户点击…

A SURVEY ON POST-TRAINING OF LARGE LANGUAGE MODELS——大型语言模型的训练后优化综述——第2部分

3、微调&#xff08;上一部分内容&#xff09; 4、LLMs的对齐 大型语言模型&#xff08;LLMs&#xff09;中的对齐涉及引导模型输出以符合人类预期和偏好&#xff0c;特别是在安全关键或用户面对的应用程序中。本章讨论了实现对齐的三个主要范式&#xff1a; 带有反馈的人工…

某大厂自动化工程师面试题

一些大厂的自动化工程师面试题汇总: 基础知识类 请解释什么是PLC(可编程逻辑控制器)?什么是PID控制?它在自动化系统中的作用是什么?请描述一下工业4.0的基本概念。编程与控制系统类 你熟悉哪些PLC编程语言?请举例说明。如何在SCADA系统中实现数据采集和监控?请解释一下…

zend server试用分析

文件&#xff1a;ZendServer-2021.4.1-multi-php-Windows_x86.exe 安装后可以试用30天&#xff0c;想分析下限制原理, 根据安装日志&#xff0c;发现了2个关键的文件&#xff1a; ZendServer\gui\module\Configuration\src\Configuration\License\Wrapper.php ZendServer\gu…

C# NX二次开发:在多个体的模型中如何实现拉伸操作布尔减

大家好&#xff0c;今天接着上一篇拉伸文章去讲。 UF_MODL_create_extruded1 (view source) uf_list_p_tobjectsInputList of objects to be extruded.char *taper_angleInputTaper angle (in degrees).char *limit [ 2 ]InputLimit of extrusion. This is declared as: char …

15 | 定义简洁架构 Store 层的数据类型

提示&#xff1a; 所有体系课见专栏&#xff1a;Go 项目开发极速入门实战课&#xff1b;欢迎加入 云原生 AI 实战 星球&#xff0c;12 高质量体系课、20 高质量实战项目助你在 AI 时代建立技术竞争力&#xff08;聚焦于 Go、云原生、AI Infra&#xff09;&#xff1b;本节课最终…

2.3 滑动窗口专题:最大连续1的个数 III(LeetCode 1004)

1. ​题目链接 1004. 最大连续1的个数 III - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/max-consecutive-ones-iii/ 2. ​题目描述 给定一个二进制数组 nums 和一个整数 k&#xff0c;允许将最多 k 个 0 翻转为 1&#xff0c;求翻转后最长的连续 1 …

【微服务】Nacos 配置动态刷新(简易版)(附配置)

文章目录 1、实现方法2、配置依赖 yaml3、验证效果 1、实现方法 环境&#xff1a;Nacos、Java、SpringBoot等 主要是在boostrap.yaml中的data-id属性下配置refresh:true来实现动态更新 2、配置依赖 yaml 具体的版本参考官方的说明&#xff1a;官方版本说明 <!--读取boo…

六十天前端强化训练之第二十天React Router 基础详解

欢迎来到编程星辰海的博客讲解 看完可以给一个免费的三连吗&#xff0c;谢谢大佬&#xff01; 目录 一、核心概念 1.1 核心组件 1.2 路由模式对比 二、核心代码示例 2.1 基础路由配置 2.2 动态路由示例 2.3 嵌套路由实现 2.4 完整示例代码 三、关键功能实现效果 四、…

用 DeepSeek 构建 Vue.js 底层架构:高效协作与问题解决实践

文章目录 1. **DeepSeek 与 Vue.js 的完美协作**2. **问题背景**3. **问题分析与解决**3.1 **动态路由未正确生成**3.2 **路由路径配置错误**3.3 **路由嵌套问题**3.4 **通配符路由未配置** 4. **DeepSeek 的核心价值** 在现代前端开发中&#xff0c;Vue.js 以其简洁的语法和灵…

深入探讨RAID 5的性能与容错能力:实验与分析(磁盘阵列)

前言—— 本实验旨在探讨 RAID 5 的性能和容错能力。通过创建 RAID 5 阵列并进行一系列读写性能测试及故障模拟&#xff0c;我们将观察 RAID 5 在数据冗余和故障恢复方面的表现&#xff0c;以验证其在实际应用中的可靠性和效率。 首先说明&#xff1a;最少三块硬盘, 使用 4 块…

蓝桥杯备赛-二分-技能升级

问题描述 小蓝最近正在玩一款 RPG 游戏。他的角色一共有 NN 个可以加攻击力的技能。 其中第 ii 个技能首次升级可以提升 AiAi​ 点攻击力, 以后每次升级增加的点数 都会减少 Bi。「AiBi⌉Bi​。「Bi​Ai​​⌉ (上取整) 次之后, 再升级该技能将不会改变攻击力。 现在小蓝可以…

电子招采软件系统,如何实现10年可追溯审计

一、在当前经济环境下&#xff0c;中小企业面临着巨大的生存压力&#xff0c;传统产业的数字化转型迫在眉睫。AI技术为企业的低成本高效发展提供了新机会&#xff0c;混合办公成为新常态&#xff0c;数据安全法的深入落实则进一步推动企业重视数据安全。区块链存证技术凭借独特…

Ubuntu从源代码编译安装QT

1. 下载源码 wget https://download.qt.io/official_releases/qt/5.15/5.15.2/single/qt-everywhere-src-5.15.2.tar.xz tar xf qt-everywhere-src-5.15.2.tar.xz cd qt-everywhere-src-5.15.22. 安装依赖库 sudo apt update sudo apt install build-essential libgl1-mesa-d…

X86 RouterOS 7.18 设置笔记七:不使用Upnp的映射方法

X86 j4125 4网口小主机折腾笔记五&#xff1a;PVE安装ROS RouterOS X86 RouterOS 7.18 设置笔记一&#xff1a;基础设置 X86 RouterOS 7.18 设置笔记二&#xff1a;网络基础设置(IPV4) X86 RouterOS 7.18 设置笔记三&#xff1a;防火墙设置(IPV4) X86 RouterOS 7.18 设置笔记四…

数字隔离器,如何提升储能系统的安全与效能?

随着全球对光伏、风电等可再生能源需求的持续增长&#xff0c;在全球能源转型的浪潮中&#xff0c;储能技术凭借着可平衡能源供需、提高能源利用效率等优势&#xff0c;已成为实现 “双碳” 目标的核心支撑。据国家能源局公布数据显示&#xff0c;截至2024年底&#xff0c;我国…

基于UniApp + Vue3开发的智能汉字转拼音工具

基于UniApp Vue3开发的智能汉字转拼音工具 项目简介 这是一个基于 UniApp Vue3 开发的智能汉字转拼音工具&#xff0c;前端使用 Vue3 构建界面&#xff0c;后端采用 Classic ASP 提供接口支持&#xff0c;通过 pinyin-pro 库实现精准的中文转拼音功能。本工具支持以下特性&…

PyTorch 深度学习实战(14):Deep Deterministic Policy Gradient (DDPG) 算法

在上一篇文章中&#xff0c;我们介绍了 Proximal Policy Optimization (PPO) 算法&#xff0c;并使用它解决了 CartPole 问题。本文将深入探讨 Deep Deterministic Policy Gradient (DDPG) 算法&#xff0c;这是一种用于连续动作空间的强化学习算法。我们将使用 PyTorch 实现 D…