首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >深入浅出 Spring 事务:从源码到实战,解密 @Transactional 的 "黑魔法"

深入浅出 Spring 事务:从源码到实战,解密 @Transactional 的 "黑魔法"

作者头像
果酱带你啃java
发布2026-04-14 10:37:55
发布2026-04-14 10:37:55
630
举报

在 Java 企业级开发中,事务管理是保证数据一致性的核心机制。想象一下:用户支付订单后,扣减库存和创建订单记录必须同时成功或同时失败,否则就会出现超卖或财务对账不平的严重问题。Spring 框架通过声明式事务(@Transactional注解)将开发者从复杂的事务管理代码中解放出来,但这个简单注解背后却隐藏着精妙的设计与实现。

本文将带你揭开 Spring 事务的神秘面纱,从数据库事务原理讲起,深入 Spring 事务的实现机制,剖析传播行为、隔离级别等核心概念,并通过可运行的实战案例展示事务在实际开发中的应用与陷阱。无论你是想夯实基础的初级开发者,还是希望深入理解底层原理的资深工程师,都能从本文获得有价值的知识。

一、事务基础:从数据库到 Spring

在探讨 Spring 事务之前,我们需要先理解数据库事务的本质。事务(Transaction)是数据库管理系统执行过程中的一个逻辑单位,它能够保证一组数据库操作要么全部成功,要么全部失败。

1.1 数据库事务的 ACID 特性

事务必须满足 ACID 特性,这是保证数据一致性的基础:

  • 原子性(Atomicity):事务中的所有操作要么全部完成,要么全部不完成,不会处于中间状态。如果事务执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态。
  • 一致性(Consistency):事务必须使数据库从一个一致性状态变换到另一个一致性状态。例如,转账操作后,两个账户的总金额应该与转账前相同。
  • 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行。数据库通过隔离级别控制并发事务之间的相互影响。
  • 持久性(Durability):一旦事务提交,其对数据库的修改就是永久性的,即使系统崩溃也不会丢失。

1.2 数据库事务的实现原理

数据库通过以下机制实现事务的 ACID 特性:

  1. 日志文件(Redo Log & Undo Log)
    • Redo Log(重做日志):记录事务对数据库的修改操作,用于系统崩溃后恢复数据,保证持久性。
    • Undo Log(回滚日志):记录事务修改前的数据状态,用于事务回滚,保证原子性。
  2. 锁机制
    • 保证隔离性,防止多个事务同时修改同一数据。
    • 包括行锁、表锁、共享锁(S 锁)、排他锁(X 锁)等。
  3. MVCC(多版本并发控制)
    • 数据库为每个数据行维护多个版本,允许读写操作不互相阻塞,提高并发性能。
    • 是实现隔离级别的重要基础。

1.3 Spring 事务的角色

Spring 并不直接管理事务,而是提供了事务管理的抽象层,通过封装不同的事务资源(如 JDBC、Hibernate、JPA 等),为开发者提供统一的事务操作接口。

Spring 事务的核心接口:

  • PlatformTransactionManager:事务管理器接口,定义了事务的基本操作(获取、提交、回滚事务)。
  • TransactionDefinition:事务定义接口,定义了事务的属性(传播行为、隔离级别、超时时间等)。
  • TransactionStatus:事务状态接口,提供了事务的当前状态信息(是否新事务、是否已标记回滚等)。

二、Spring 声明式事务的实现机制

Spring 提供了两种事务管理方式:编程式事务和声明式事务。其中,声明式事务通过@Transactional注解实现,是实际开发中最常用的方式,它将事务管理与业务逻辑分离,提高了代码的可读性和可维护性。

2.1 声明式事务的工作原理

@Transactional注解的实现基于 Spring 的 AOP(面向切面编程)机制,其核心原理是:

  1. Spring 容器启动时,会扫描所有带有@Transactional注解的类或方法。
  2. 对这些类或方法创建 AOP 代理(JDK 动态代理或 CGLIB 代理)。
  3. 当调用被代理的方法时,代理对象会先拦截方法调用,在方法执行前开启事务,在方法执行后根据执行结果(正常返回或抛出异常)决定提交事务或回滚事务。

2.2 Spring 事务的核心组件

Spring 事务管理涉及多个核心组件,它们协同工作完成事务的创建、提交和回滚:

  1. TransactionInterceptor:事务拦截器,是 AOP 的 Advice,负责在方法执行前后进行事务操作。
  2. TransactionAspectSupport:事务切面支持类,提供了事务管理的核心逻辑。
  3. AbstractPlatformTransactionManager:事务管理器的抽象实现,提供了事务操作的模板方法。
  4. DataSourceTransactionManager:基于 JDBC 的事务管理器,是最常用的事务管理器。

2.3 事务拦截器的工作流程

TransactionInterceptor 是事务管理的核心拦截器,其工作流程如下:

核心源码片段(简化版):

代码语言:javascript
复制
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        // 获取目标类和方法
        Class<?> targetClass = invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null;
        Method method = invocation.getMethod();

        // 执行目标方法并进行事务管理
        return invokeWithinTransaction(method, targetClass, invocation::proceed);
    }
}

public abstract class TransactionAspectSupport {
    protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
            final InvocationCallback invocation) throws Throwable {
        // 获取事务属性
        TransactionAttributeSource tas = getTransactionAttributeSource();
        final TransactionAttribute txAttr = tas != null ? tas.getTransactionAttribute(method, targetClass) : null;
        // 获取事务管理器
        final PlatformTransactionManager tm = determineTransactionManager(txAttr);
        final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

        // 创建事务信息
        TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
        Object retVal = null;
        try {
            // 执行目标方法
            retVal = invocation.proceedWithInvocation();
        } catch (Throwable ex) {
            // 发生异常,回滚事务
            completeTransactionAfterThrowing(txInfo, ex);
            throw ex;
        } finally {
            cleanupTransactionInfo(txInfo);
        }
        // 正常返回,提交事务
        commitTransactionAfterReturning(txInfo);
        return retVal;
    }
}
代码语言:javascript
复制

三、Spring 事务的核心属性解析

@Transactional注解提供了多个属性,用于配置事务的行为特征。理解这些属性是正确使用 Spring 事务的关键。

3.1 传播行为(Propagation)

传播行为定义了当一个事务方法被另一个事务方法调用时,事务如何传播。Spring 定义了 7 种传播行为:

传播行为

说明

REQUIRED

如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。

SUPPORTS

如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。

MANDATORY

如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。

REQUIRES_NEW

创建一个新的事务,如果当前存在事务,则把当前事务挂起。

NOT_SUPPORTED

以非事务的方式运行,如果当前存在事务,则把当前事务挂起。

NEVER

以非事务的方式运行,如果当前存在事务,则抛出异常。

NESTED

如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于 REQUIRED。

最常用的传播行为是REQUIREDREQUIRES_NEW,我们通过一个实例来理解它们的区别:

代码语言:javascript
复制
@Service
@Slf4j
public class OrderService {
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private PaymentService paymentService;

    /**
     * 创建订单,使用默认传播行为REQUIRED
     */
    @Transactional
    public void createOrder(Order order) {
        log.info("创建订单开始");
        orderMapper.insert(order);
        // 调用支付服务的支付方法
        paymentService.processPayment(order.getId(), order.getAmount());
        log.info("创建订单结束");
    }
}

@Service
@Slf4j
public class PaymentService {
    @Autowired
    private PaymentMapper paymentMapper;

    /**
     * 处理支付,使用REQUIRES_NEW传播行为
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void processPayment(Long orderId, BigDecimal amount) {
        log.info("处理支付开始");
        Payment payment = new Payment();
        payment.setOrderId(orderId);
        payment.setAmount(amount);
        paymentMapper.insert(payment);
        // 模拟异常
        // if (true) throw new RuntimeException("支付处理失败");
        log.info("处理支付结束");
    }
}
代码语言:javascript
复制

  • processPayment方法使用REQUIRED时,它会加入createOrder方法的事务中。如果processPayment抛出异常,整个事务会回滚,订单和支付记录都不会保存。
  • processPayment方法使用REQUIRES_NEW时,它会创建一个新的事务。如果processPayment抛出异常,只会回滚自己的事务(支付记录不保存),而createOrder方法的事务不受影响(订单记录会保存)。

3.2 隔离级别(Isolation)

隔离级别定义了多个并发事务之间的隔离程度,用于解决并发事务带来的问题(脏读、不可重复读、幻读)。

并发事务可能带来的问题:

  • 脏读:一个事务读取到另一个事务未提交的数据。
  • 不可重复读:一个事务两次读取同一数据,结果不一致(因为中间被另一个事务修改并提交了)。
  • 幻读:一个事务两次查询同一条件的数据,结果集的行数不一致(因为中间被另一个事务插入或删除了数据)。

Spring 支持的隔离级别:

隔离级别

说明

解决的问题

DEFAULT

使用数据库默认的隔离级别。这是默认值。

-

READ_UNCOMMITTED

允许读取未提交的数据。

READ_COMMITTED

只能读取已提交的数据。

脏读

REPEATABLE_READ

保证多次读取同一数据结果一致。

脏读、不可重复读

SERIALIZABLE

完全串行化执行,最高隔离级别。

脏读、不可重复读、幻读

不同数据库的默认隔离级别:

  • MySQL:REPEATABLE_READ
  • Oracle:READ_COMMITTED
  • SQL Server:READ_COMMITTED

隔离级别与并发性能成反比:隔离级别越高,并发性能越差。实际开发中需要根据业务需求平衡数据一致性和性能。

3.3 其他重要属性

  1. readOnly:指定事务是否为只读事务。设置为true时,数据库可以进行一些优化,提高查询性能。适用于只查询数据的方法。
  2. timeout:指定事务的超时时间(单位:秒)。如果事务执行时间超过该值,会自动回滚。默认值为 - 1,表示使用数据库的默认超时时间。
  3. rollbackFor:指定哪些异常会导致事务回滚。默认情况下,只有未检查异常(RuntimeException 和 Error)会导致事务回滚。
  4. noRollbackFor:指定哪些异常不会导致事务回滚。

示例:

代码语言:javascript
复制
/**
 * 查询订单列表,只读事务
 */
@Transactional(readOnly = true, timeout = 10)
public List<Order> queryOrders(Long userId) {
    return orderMapper.selectByUserId(userId);
}

/**
 * 更新订单状态,指定异常回滚
 */
@Transactional(rollbackFor = {BusinessException.class, SQLException.class})
public void updateOrderStatus(Long orderId, Integer status) {
    Order order = new Order();
    order.setId(orderId);
    order.setStatus(status);
    orderMapper.updateById(order);
}

/**
 * 处理退款,指定异常不回滚
 */
@Transactional(noRollbackFor = InsufficientFundsException.class)
public void processRefund(Long orderId) {
    // 处理退款逻辑
}
代码语言:javascript
复制

四、Spring 事务实战:完整案例

接下来,我们通过一个完整的实战案例,展示 Spring 事务在实际开发中的应用。案例实现一个简单的订单支付系统,包含创建订单、扣减库存、处理支付等功能,重点展示事务如何保证这些操作的一致性。

4.1 项目初始化

4.1.1 Maven 依赖
代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.5</version>
        <relativePath/>
    </parent>
    <groupId>com.example</groupId>
    <artifactId>spring-transaction-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-transaction-demo</name>
    <description>Demo project for Spring Transaction</description>

    <properties>
        <java.version>17</java.version>
        <lombok.version>1.18.30</lombok.version>
        <mybatis-plus.version>3.5.6</mybatis-plus.version>
        <mysql-connector.version>8.0.33</mysql-connector.version>
        <springdoc.version>2.4.0</springdoc.version>
        <fastjson2.version>2.0.47</fastjson2.version>
        <guava.version>33.0.0-jre</guava.version>
    </properties>

    <dependencies>
        <!-- Spring Boot核心 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

        <!-- 数据库相关 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis-plus.version}</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql-connector.version}</version>
            <scope>runtime</scope>
        </dependency>

        <!-- 工具类 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2</artifactId>
            <version>${fastjson2.version}</version>
        </dependency>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>${guava.version}</version>
        </dependency>

        <!-- API文档 -->
        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
            <version>${springdoc.version}</version>
        </dependency>

        <!-- 测试 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
代码语言:javascript
复制

4.1.2 配置文件
代码语言:javascript
复制
spring:
  application:
    name: spring-transaction-demo
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/transaction_demo?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: root
    hikari:
      # 自动提交从连接池返回的连接
      auto-commit: false
      # 连接池名称
      pool-name: TransactionDemoPool
      # 最大连接数
      maximum-pool-size: 10
      # 最小空闲连接数
      minimum-idle: 5
      # 连接超时时间(毫秒)
      connection-timeout: 30000
      # 连接最大存活时间(毫秒)
      max-lifetime: 600000

# MyBatis-Plus配置
mybatis-plus:
  mapper-locations: classpath*:mapper/**/*.xml
  type-aliases-package: com.example.springtransactiondemo.entity
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

# 日志配置
logging:
  level:
    com.example.springtransactiondemo: debug

# 服务器配置
server:
  port: 8080

# SpringDoc配置
springdoc:
  api-docs:
    path: /api-docs
  swagger-ui:
    path: /swagger-ui.html
    operationsSorter: method
代码语言:javascript
复制

4.1.3 数据库表结构
代码语言:javascript
复制
-- 创建数据库
CREATE DATABASE IF NOT EXISTS transaction_demo CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE transaction_demo;

-- 商品表
CREATE TABLE `product` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '商品ID',
  `name` varchar(100) NOT NULL COMMENT '商品名称',
  `stock` int NOT NULL COMMENT '库存数量',
  `price` decimal(10,2) NOT NULL COMMENT '商品价格',
  `created_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `updated_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品表';

-- 订单表
CREATE TABLE `order` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '订单ID',
  `user_id` bigint NOT NULL COMMENT '用户ID',
  `product_id` bigint NOT NULL COMMENT '商品ID',
  `quantity` int NOT NULL COMMENT '购买数量',
  `total_amount` decimal(10,2) NOT NULL COMMENT '订单总金额',
  `status` tinyint NOT NULL COMMENT '订单状态:0-待支付,1-已支付,2-已取消',
  `created_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `updated_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';

-- 支付记录表
CREATE TABLE `payment` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '支付记录ID',
  `order_id` bigint NOT NULL COMMENT '订单ID',
  `amount` decimal(10,2) NOT NULL COMMENT '支付金额',
  `payment_time` datetime NOT NULL COMMENT '支付时间',
  `payment_method` tinyint NOT NULL COMMENT '支付方式:1-微信,2-支付宝',
  `status` tinyint NOT NULL COMMENT '支付状态:0-处理中,1-成功,2-失败',
  `created_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `updated_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_order_id` (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='支付记录表';

-- 初始化数据
INSERT INTO `product` (`name`, `stock`, `price`) VALUES ('测试商品', 100, 99.99);
代码语言:javascript
复制

4.2 核心代码实现

4.2.1 实体类

Product.java

代码语言:javascript
复制
package com.example.springtransactiondemo.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
 * 商品实体类
 *
 * @author ken
 */
@Data
@TableName("product")
public class Product {

    /**
     * 商品ID
     */
    @TableId(type = IdType.AUTO)
    private Long id;

    /**
     * 商品名称
     */
    private String name;

    /**
     * 库存数量
     */
    private Integer stock;

    /**
     * 商品价格
     */
    private BigDecimal price;

    /**
     * 创建时间
     */
    private LocalDateTime createdTime;

    /**
     * 更新时间
     */
    private LocalDateTime updatedTime;
}
代码语言:javascript
复制

Order.java

代码语言:javascript
复制
package com.example.springtransactiondemo.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
 * 订单实体类
 *
 * @author ken
 */
@Data
@TableName("`order`")
public class Order {

    /**
     * 订单ID
     */
    @TableId(type = IdType.AUTO)
    private Long id;

    /**
     * 用户ID
     */
    private Long userId;

    /**
     * 商品ID
     */
    private Long productId;

    /**
     * 购买数量
     */
    private Integer quantity;

    /**
     * 订单总金额
     */
    private BigDecimal totalAmount;

    /**
     * 订单状态:0-待支付,1-已支付,2-已取消
     */
    private Integer status;

    /**
     * 创建时间
     */
    private LocalDateTime createdTime;

    /**
     * 更新时间
     */
    private LocalDateTime updatedTime;
}
代码语言:javascript
复制

Payment.java

代码语言:javascript
复制
package com.example.springtransactiondemo.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
 * 支付记录实体类
 *
 * @author ken
 */
@Data
@TableName("payment")
public class Payment {

    /**
     * 支付记录ID
     */
    @TableId(type = IdType.AUTO)
    private Long id;

    /**
     * 订单ID
     */
    private Long orderId;

    /**
     * 支付金额
     */
    private BigDecimal amount;

    /**
     * 支付时间
     */
    private LocalDateTime paymentTime;

    /**
     * 支付方式:1-微信,2-支付宝
     */
    private Integer paymentMethod;

    /**
     * 支付状态:0-处理中,1-成功,2-失败
     */
    private Integer status;

    /**
     * 创建时间
     */
    private LocalDateTime createdTime;

    /**
     * 更新时间
     */
    private LocalDateTime updatedTime;
}
代码语言:javascript
复制

4.2.2 DTO 类

CreateOrderDTO.java

代码语言:javascript
复制
package com.example.springtransactiondemo.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import lombok.Data;

/**
 * 创建订单请求DTO
 *
 * @author ken
 */
@Data
@Schema(description = "创建订单请求参数")
public class CreateOrderDTO {

    @NotNull(message = "用户ID不能为空")
    @Schema(description = "用户ID", example = "1001")
    private Long userId;

    @NotNull(message = "商品ID不能为空")
    @Schema(description = "商品ID", example = "1")
    private Long productId;

    @Min(value = 1, message = "购买数量不能小于1")
    @Schema(description = "购买数量", example = "2")
    private Integer quantity;

    @NotNull(message = "支付方式不能为空")
    @Schema(description = "支付方式:1-微信,2-支付宝", example = "1")
    private Integer paymentMethod;
}
代码语言:javascript
复制

ApiResponse.java

代码语言:javascript
复制
package com.example.springtransactiondemo.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

/**
 * 通用响应DTO
 *
 * @author ken
 */
@Data
@Schema(description = "通用响应结果")
public class ApiResponse<T> {

    @Schema(description = "状态码:200表示成功,其他表示失败", example = "200")
    private int code;

    @Schema(description = "响应消息", example = "操作成功")
    private String message;

    @Schema(description = "响应数据")
    private T data;

    /**
     * 成功响应
     *
     * @param data 响应数据
     * @return 成功响应对象
     */
    public static <T> ApiResponse<T> success(T data) {
        ApiResponse<T> response = new ApiResponse<>();
        response.setCode(200);
        response.setMessage("操作成功");
        response.setData(data);
        return response;
    }

    /**
     * 成功响应(无数据)
     *
     * @return 成功响应对象
     */
    public static <T> ApiResponse<T> success() {
        return success(null);
    }

    /**
     * 失败响应
     *
     * @param code 错误码
     * @param message 错误消息
     * @return 失败响应对象
     */
    public static <T> ApiResponse<T> fail(int code, String message) {
        ApiResponse<T> response = new ApiResponse<>();
        response.setCode(code);
        response.setMessage(message);
        response.setData(null);
        return response;
    }

    /**
     * 失败响应(默认错误码)
     *
     * @param message 错误消息
     * @return 失败响应对象
     */
    public static <T> ApiResponse<T> fail(String message) {
        return fail(500, message);
    }
}
代码语言:javascript
复制

4.2.3 异常处理

BusinessException.java

代码语言:javascript
复制
package com.example.springtransactiondemo.exception;

import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * 业务异常类
 *
 * @author ken
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class BusinessException extends RuntimeException {

    /**
     * 错误码
     */
    private int code;

    /**
     * 构造方法
     *
     * @param code 错误码
     * @param message 错误消息
     */
    public BusinessException(int code, String message) {
        super(message);
        this.code = code;
    }

    /**
     * 构造方法(默认错误码)
     *
     * @param message 错误消息
     */
    public BusinessException(String message) {
        this(500, message);
    }
}
代码语言:javascript
复制

GlobalExceptionHandler.java

代码语言:javascript
复制
package com.example.springtransactiondemo.exception;

import com.example.springtransactiondemo.dto.ApiResponse;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.stream.Collectors;

/**
 * 全局异常处理器
 *
 * @author ken
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 处理业务异常
     *
     * @param e 业务异常
     * @return 错误响应
     */
    @ExceptionHandler(BusinessException.class)
    public ApiResponse<Void> handleBusinessException(BusinessException e) {
        log.error("业务异常: {}", e.getMessage(), e);
        return ApiResponse.fail(e.getCode(), e.getMessage());
    }

    /**
     * 处理参数校验异常
     *
     * @param e 参数校验异常
     * @return 错误响应
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ApiResponse<Void> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        BindingResult bindingResult = e.getBindingResult();
        String errorMsg = bindingResult.getFieldErrors().stream()
                .map(FieldError::getDefaultMessage)
                .collect(Collectors.joining("; "));
        log.error("参数校验异常: {}", errorMsg);
        return ApiResponse.fail(400, errorMsg);
    }

    /**
     * 处理参数校验异常(非实体类参数)
     *
     * @param e 参数校验异常
     * @return 错误响应
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public ApiResponse<Void> handleConstraintViolationException(ConstraintViolationException e) {
        String errorMsg = e.getConstraintViolations().stream()
                .map(ConstraintViolation::getMessage)
                .collect(Collectors.joining("; "));
        log.error("参数校验异常: {}", errorMsg);
        return ApiResponse.fail(400, errorMsg);
    }

    /**
     * 处理其他异常
     *
     * @param e 异常
     * @return 错误响应
     */
    @ExceptionHandler(Exception.class)
    public ApiResponse<Void> handleException(Exception e) {
        log.error("系统异常: {}", e.getMessage(), e);
        return ApiResponse.fail("系统异常,请联系管理员");
    }
}
代码语言:javascript
复制

4.2.4 Mapper 接口及 XML

ProductMapper.java

代码语言:javascript
复制
package com.example.springtransactiondemo.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.springtransactiondemo.entity.Product;
import org.apache.ibatis.annotations.Param;

/**
 * 商品Mapper接口
 *
 * @author ken
 */
public interface ProductMapper extends BaseMapper<Product> {

    /**
     * 扣减商品库存
     *
     * @param productId 商品ID
     * @param quantity 扣减数量
     * @return 影响行数
     */
    int decreaseStock(@Param("productId") Long productId, @Param("quantity") Integer quantity);
}
代码语言:javascript
复制

ProductMapper.xml

代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.springtransactiondemo.mapper.ProductMapper">

    <!-- 扣减商品库存,带库存充足校验 -->
    <update id="decreaseStock">
        UPDATE product
        SET stock = stock - #{quantity},
            updated_time = NOW()
        WHERE id = #{productId} AND stock >= #{quantity}
    </update>
</mapper>
代码语言:javascript
复制

OrderMapper.java

代码语言:javascript
复制
package com.example.springtransactiondemo.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.springtransactiondemo.entity.Order;

/**
 * 订单Mapper接口
 *
 * @author ken
 */
public interface OrderMapper extends BaseMapper<Order> {
}
代码语言:javascript
复制

PaymentMapper.java

代码语言:javascript
复制
package com.example.springtransactiondemo.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.springtransactiondemo.entity.Payment;

/**
 * 支付记录Mapper接口
 *
 * @author ken
 */
public interface PaymentMapper extends BaseMapper<Payment> {
}
代码语言:javascript
复制

4.2.5 服务层实现

ProductService.java

代码语言:javascript
复制
package com.example.springtransactiondemo.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.example.springtransactiondemo.entity.Product;

/**
 * 商品服务接口
 *
 * @author ken
 */
public interface ProductService extends IService<Product> {

    /**
     * 扣减商品库存
     *
     * @param productId 商品ID
     * @param quantity 扣减数量
     * @return 是否扣减成功
     */
    boolean decreaseStock(Long productId, Integer quantity);
}
代码语言:javascript
复制

ProductServiceImpl.java

代码语言:javascript
复制
package com.example.springtransactiondemo.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.springtransactiondemo.entity.Product;
import com.example.springtransactiondemo.exception.BusinessException;
import com.example.springtransactiondemo.mapper.ProductMapper;
import com.example.springtransactiondemo.service.ProductService;
import com.example.springtransactiondemo.util.ObjectUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 * 商品服务实现类
 *
 * @author ken
 */
@Slf4j
@Service
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService {

    @Override
    public boolean decreaseStock(Long productId, Integer quantity) {
        log.info("开始扣减商品库存,商品ID:{},扣减数量:{}", productId, quantity);

        // 检查商品是否存在
        Product product = getOne(new LambdaQueryWrapper<Product>()
                .eq(Product::getId, productId));
        if (ObjectUtils.isEmpty(product)) {
            throw new BusinessException("商品不存在");
        }

        // 检查库存是否充足
        if (product.getStock() < quantity) {
            throw new BusinessException("商品库存不足");
        }

        // 扣减库存
        int rows = baseMapper.decreaseStock(productId, quantity);
        if (rows <= 0) {
            log.error("扣减商品库存失败,商品ID:{},扣减数量:{}", productId, quantity);
            return false;
        }

        log.info("商品库存扣减成功,商品ID:{},扣减数量:{}", productId, quantity);
        return true;
    }
}
代码语言:javascript
复制

OrderService.java

代码语言:javascript
复制
package com.example.springtransactiondemo.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.example.springtransactiondemo.dto.CreateOrderDTO;
import com.example.springtransactiondemo.entity.Order;

/**
 * 订单服务接口
 *
 * @author ken
 */
public interface OrderService extends IService<Order> {

    /**
     * 创建订单并完成支付
     *
     * @param createOrderDTO 订单信息
     * @return 创建的订单
     */
    Order createOrderAndPay(CreateOrderDTO createOrderDTO);
}
代码语言:javascript
复制

OrderServiceImpl.java

代码语言:javascript
复制
package com.example.springtransactiondemo.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.springtransactiondemo.dto.CreateOrderDTO;
import com.example.springtransactiondemo.entity.Order;
import com.example.springtransactiondemo.entity.Payment;
import com.example.springtransactiondemo.entity.Product;
import com.example.springtransactiondemo.exception.BusinessException;
import com.example.springtransactiondemo.mapper.OrderMapper;
import com.example.springtransactiondemo.service.OrderService;
import com.example.springtransactiondemo.service.PaymentService;
import com.example.springtransactiondemo.service.ProductService;
import com.example.springtransactiondemo.util.ObjectUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
 * 订单服务实现类
 *
 * @author ken
 */
@Slf4j
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {

    private final ProductService productService;
    private final PaymentService paymentService;

    public OrderServiceImpl(ProductService productService, PaymentService paymentService) {
        this.productService = productService;
        this.paymentService = paymentService;
    }

    /**
     * 创建订单并完成支付
     * 使用REQUIRED传播行为(默认),确保所有操作在同一个事务中
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Order createOrderAndPay(CreateOrderDTO createOrderDTO) {
        log.info("开始创建订单并支付,用户ID:{},商品ID:{},数量:{}",
                createOrderDTO.getUserId(), createOrderDTO.getProductId(), createOrderDTO.getQuantity());

        // 1. 查询商品信息
        Product product = productService.getById(createOrderDTO.getProductId());
        if (ObjectUtils.isEmpty(product)) {
            throw new BusinessException("商品不存在");
        }

        // 2. 计算订单总金额
        BigDecimal totalAmount = product.getPrice()
                .multiply(BigDecimal.valueOf(createOrderDTO.getQuantity()));

        // 3. 创建订单
        Order order = new Order();
        order.setUserId(createOrderDTO.getUserId());
        order.setProductId(createOrderDTO.getProductId());
        order.setQuantity(createOrderDTO.getQuantity());
        order.setTotalAmount(totalAmount);
        order.setStatus(0); // 待支付

        boolean saveOrderResult = save(order);
        if (!saveOrderResult) {
            log.error("创建订单失败");
            throw new BusinessException("创建订单失败");
        }

        try {
            // 4. 扣减库存
            boolean decreaseStockResult = productService.decreaseStock(
                    createOrderDTO.getProductId(), createOrderDTO.getQuantity());
            if (!decreaseStockResult) {
                throw new BusinessException("扣减库存失败");
            }

            // 5. 处理支付
            Payment payment = new Payment();
            payment.setOrderId(order.getId());
            payment.setAmount(totalAmount);
            payment.setPaymentTime(LocalDateTime.now());
            payment.setPaymentMethod(createOrderDTO.getPaymentMethod());
            payment.setStatus(1); // 支付成功

            boolean savePaymentResult = paymentService.savePayment(payment);
            if (!savePaymentResult) {
                throw new BusinessException("保存支付记录失败");
            }

            // 6. 更新订单状态为已支付
            order.setStatus(1); // 已支付
            boolean updateOrderResult = updateById(order);
            if (!updateOrderResult) {
                log.error("更新订单状态失败,订单ID:{}", order.getId());
                throw new BusinessException("更新订单状态失败");
            }

            log.info("订单创建并支付成功,订单ID:{}", order.getId());
            return order;
        } catch (Exception e) {
            log.error("订单处理过程中发生异常,订单ID:{}", order.getId(), e);
            // 抛出异常,触发事务回滚
            throw new BusinessException("订单处理失败:" + e.getMessage());
        }
    }
}
代码语言:javascript
复制

PaymentService.java

代码语言:javascript
复制
package com.example.springtransactiondemo.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.example.springtransactiondemo.entity.Payment;

/**
 * 支付服务接口
 *
 * @author ken
 */
public interface PaymentService extends IService<Payment> {

    /**
     * 保存支付记录
     *
     * @param payment 支付记录
     * @return 是否保存成功
     */
    boolean savePayment(Payment payment);
}
代码语言:javascript
复制

PaymentServiceImpl.java

代码语言:javascript
复制
package com.example.springtransactiondemo.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.springtransactiondemo.entity.Payment;
import com.example.springtransactiondemo.exception.BusinessException;
import com.example.springtransactiondemo.mapper.PaymentMapper;
import com.example.springtransactiondemo.service.PaymentService;
import com.example.springtransactiondemo.util.ObjectUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 * 支付服务实现类
 *
 * @author ken
 */
@Slf4j
@Service
public class PaymentServiceImpl extends ServiceImpl<PaymentMapper, Payment> implements PaymentService {

    @Override
    public boolean savePayment(Payment payment) {
        log.info("开始保存支付记录,订单ID:{},支付金额:{}",
                payment.getOrderId(), payment.getAmount());

        // 检查订单是否已支付
        Payment existingPayment = getOne(new LambdaQueryWrapper<Payment>()
                .eq(Payment::getOrderId, payment.getOrderId()));
        if (!ObjectUtils.isEmpty(existingPayment)) {
            throw new BusinessException("订单已支付,不能重复支付");
        }

        // 保存支付记录
        boolean saveResult = save(payment);
        if (!saveResult) {
            log.error("保存支付记录失败,订单ID:{}", payment.getOrderId());
            return false;
        }

        log.info("支付记录保存成功,订单ID:{}", payment.getOrderId());
        return true;
    }
}
代码语言:javascript
复制

4.2.6 控制器

OrderController.java

代码语言:javascript
复制
package com.example.springtransactiondemo.controller;

import com.example.springtransactiondemo.dto.ApiResponse;
import com.example.springtransactiondemo.dto.CreateOrderDTO;
import com.example.springtransactiondemo.entity.Order;
import com.example.springtransactiondemo.service.OrderService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 订单控制器
 *
 * @author ken
 */
@RestController
@RequestMapping("/api/order")
@Tag(name = "订单管理", description = "订单创建、支付等接口")
public class OrderController {

    private final OrderService orderService;

    public OrderController(OrderService orderService) {
        this.orderService = orderService;
    }

    /**
     * 创建订单并支付
     *
     * @param createOrderDTO 订单信息
     * @return 创建的订单
     */
    @PostMapping("/createAndPay")
    @Operation(summary = "创建订单并支付", description = "创建订单、扣减库存、处理支付的完整流程,保证事务一致性")
    public ApiResponse<Order> createOrderAndPay(@Valid @RequestBody CreateOrderDTO createOrderDTO) {
        Order order = orderService.createOrderAndPay(createOrderDTO);
        return ApiResponse.success(order);
    }
}
代码语言:javascript
复制

4.2.7 启动类
代码语言:javascript
复制
package com.example.springtransactiondemo;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.EnableTransactionManagement;

/**
 * Spring事务演示项目启动类
 *
 * @author ken
 */
@SpringBootApplication
@MapperScan("com.example.springtransactiondemo.mapper")
@EnableTransactionManagement // 启用事务管理,Spring Boot中可省略,会自动配置
public class SpringTransactionDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringTransactionDemoApplication.class, args);
    }
}
代码语言:javascript
复制

4.3 事务测试与验证

我们通过以下测试场景验证事务的正确性:

4.3.1 正常流程测试

调用/api/order/createAndPay接口,传入正确的参数:

代码语言:javascript
复制
{
  "userId": 1001,
  "productId": 1,
  "quantity": 2,
  "paymentMethod": 1
}
代码语言:javascript
复制

预期结果:

  • 订单表中新增一条状态为 1(已支付)的订单记录
  • 商品表中对应商品的库存减少 2
  • 支付记录表中新增一条对应订单的支付记录
4.3.2 异常流程测试

修改OrderServiceImpl类,在扣减库存后手动抛出异常:

代码语言:javascript
复制
// 4. 扣减库存
boolean decreaseStockResult = productService.decreaseStock(
        createOrderDTO.getProductId(), createOrderDTO.getQuantity());
if (!decreaseStockResult) {
    throw new BusinessException("扣减库存失败");
}

// 手动抛出异常,测试事务回滚
throw new RuntimeException("测试事务回滚");
代码语言:javascript
复制

再次调用相同接口,预期结果:

  • 订单表中没有新增记录(或新增记录被回滚)
  • 商品表中库存没有变化(扣减操作被回滚)
  • 支付记录表中没有新增记录

这说明事务正常工作,当发生异常时,所有操作都被回滚。

4.3.3 传播行为测试

修改PaymentServicesavePayment方法,添加REQUIRES_NEW传播行为:

代码语言:javascript
复制
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public boolean savePayment(Payment payment) {
    // 方法实现不变
}
代码语言:javascript
复制

OrderServiceImpl的支付操作后抛出异常:

代码语言:javascript
复制
// 5. 处理支付
Payment payment = new Payment();
// ... 设置支付信息 ...
boolean savePaymentResult = paymentService.savePayment(payment);
if (!savePaymentResult) {
    throw new BusinessException("保存支付记录失败");
}

// 手动抛出异常
throw new RuntimeException("测试传播行为");
代码语言:javascript
复制

调用接口,预期结果:

  • 支付记录表中新增了支付记录(因为savePayment使用了REQUIRES_NEW,事务独立提交)
  • 订单表和商品表的操作被回滚(订单未创建,库存未减少)

这验证了REQUIRES_NEW传播行为的效果:创建新的独立事务,与外部事务互不影响。

五、Spring 事务常见问题与解决方案

在使用 Spring 事务的过程中,开发者常常会遇到各种问题。本节将介绍一些常见问题及其解决方案。

5.1 事务不生效的常见原因

方法不是 public 的:Spring 事务只能应用在 public 方法上,非 public 方法上的@Transactional注解不会生效。

解决方案:将事务方法改为 public 修饰。

自调用问题:在同一个类中,一个非事务方法调用事务方法,事务不会生效,因为自调用不会经过 AOP 代理。

代码语言:javascript
复制
@Service
public class OrderService {
    // 非事务方法
    public void processOrder(Order order) {
        // 自调用事务方法,事务不生效
        createOrder(order);
    }

    // 事务方法
    @Transactional
    public void createOrder(Order order) {
        // 业务逻辑
    }
}
代码语言:javascript
复制

解决方案:

  • 将方法拆分到不同的类中
  • 自己注入自己(通过 Spring 上下文获取代理对象)
  • 使用 AopContext.currentProxy () 获取代理对象

异常被捕获:如果事务方法内部捕获了异常,没有重新抛出,事务不会回滚。

代码语言:javascript
复制
@Transactional
public void createOrder(Order order) {
    try {
        // 业务逻辑
        throw new RuntimeException("发生异常");
    } catch (Exception e) {
        // 捕获异常但未重新抛出,事务不会回滚
        log.error("发生异常", e);
    }
}
代码语言:javascript
复制

解决方案:捕获异常后重新抛出,或使用TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()手动标记回滚。

错误的异常类型:默认情况下,只有 RuntimeException 和 Error 会导致事务回滚,检查异常(如 SQLException)不会。

解决方案:通过rollbackFor属性指定需要回滚的异常类型。

代码语言:javascript
复制
@Transactional(rollbackFor = Exception.class)
public void createOrder(Order order) throws SQLException {
    // 业务逻辑
}
代码语言:javascript
复制


数据源未配置事务管理器:如果没有为数据源配置对应的事务管理器,事务注解不会生效。

解决方案:确保配置了合适的事务管理器,如DataSourceTransactionManager

代码语言:javascript
复制
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);
}
代码语言:javascript
复制


5.2 事务隔离级别与并发问题

即使正确使用了事务,仍然可能遇到并发问题,需要根据业务场景选择合适的隔离级别。

  1. 脏读问题:问题:一个事务读取到另一个事务未提交的数据。解决方案:将隔离级别设置为 READ_COMMITTED 或更高。
  2. 不可重复读问题:问题:一个事务两次读取同一数据,结果不一致。解决方案:将隔离级别设置为 REPEATABLE_READ 或 SERIALIZABLE。
  3. 幻读问题:问题:一个事务两次查询同一条件的数据,结果集行数不一致。解决方案:
    • 将隔离级别设置为 SERIALIZABLE(性能较差)
    • 使用数据库锁(如 SELECT ... FOR UPDATE)
  4. 死锁问题:问题:两个或多个事务相互等待对方释放资源而陷入无限等待。解决方案:
    • 统一操作顺序,避免交叉操作
    • 设置合理的事务超时时间
    • 减少事务范围,缩短事务持有锁的时间

5.3 分布式事务问题

在分布式系统中,单个 Spring 事务无法跨多个数据源保证一致性,需要使用分布式事务解决方案:

  1. 2PC(两阶段提交)
    • 引入协调者角色,分准备阶段和提交阶段
    • 实现复杂,性能较差
    • 示例:Atomikos、Bitronix
  2. TCC(Try-Confirm-Cancel)
    • 每个事务参与者实现 Try、Confirm、Cancel 三个接口
    • 侵入性强,但性能较好
    • 示例:Seata
  3. SAGA 模式
    • 将分布式事务拆分为一系列本地事务
    • 每个本地事务都有对应的补偿操作
    • 适合长事务场景
  4. 本地消息表
    • 通过本地事务保证消息的可靠发送
    • 异步执行跨服务操作
    • 实现简单,适合非实时场景

六、总结与最佳实践

Spring 事务是保证数据一致性的重要机制,通过@Transactional注解,开发者可以轻松实现声明式事务管理。本文从数据库事务原理出发,深入剖析了 Spring 事务的实现机制,讲解了传播行为、隔离级别等核心概念,并通过完整的实战案例展示了事务的应用。

6.1 核心要点总结

  1. 事务的 ACID 特性:原子性、一致性、隔离性、持久性是事务的四大特性,是保证数据一致性的基础。
  2. Spring 事务的实现:基于 AOP 机制,通过代理对象在方法执行前后进行事务的开启、提交或回滚操作。
  3. 传播行为:定义了事务方法之间调用时的事务传播规则,常用的有 REQUIRED 和 REQUIRES_NEW。
  4. 隔离级别:控制并发事务之间的相互影响,需要根据业务需求平衡一致性和性能。
  5. 常见问题:事务不生效的原因包括自调用、异常被捕获、错误的异常类型等,需要特别注意。

6.2 最佳实践建议

  1. 合理设置事务属性
    • 根据业务需求选择合适的传播行为和隔离级别
    • 对只读操作设置readOnly = true
    • 合理设置超时时间,避免长事务
  2. 缩小事务范围
    • 只在必要的方法上添加事务注解
    • 事务方法中只包含核心业务逻辑,避免耗时操作(如 IO、网络请求)
  3. 正确处理异常
    • 不要在事务方法内部捕获异常而不重新抛出
    • 明确指定rollbackFor属性,避免因异常类型问题导致事务不回滚
  4. 避免事务滥用
    • 简单查询操作不需要事务
    • 可以通过乐观锁等机制替代部分事务场景,提高性能
  5. 测试事务行为
    • 编写单元测试验证事务的提交和回滚行为
    • 进行并发测试,验证隔离级别是否符合预期

通过合理使用 Spring 事务,我们可以在保证数据一致性的同时,兼顾系统性能和开发效率。深入理解事务的原理和机制,不仅能帮助我们解决实际开发中的问题,还能提升对整个系统设计的理解。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-10-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 果酱带你啃java 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、事务基础:从数据库到 Spring
    • 1.1 数据库事务的 ACID 特性
    • 1.2 数据库事务的实现原理
    • 1.3 Spring 事务的角色
  • 二、Spring 声明式事务的实现机制
    • 2.1 声明式事务的工作原理
    • 2.2 Spring 事务的核心组件
    • 2.3 事务拦截器的工作流程
  • 三、Spring 事务的核心属性解析
    • 3.1 传播行为(Propagation)
    • 3.2 隔离级别(Isolation)
    • 3.3 其他重要属性
  • 四、Spring 事务实战:完整案例
    • 4.1 项目初始化
      • 4.1.1 Maven 依赖
      • 4.1.2 配置文件
      • 4.1.3 数据库表结构
    • 4.2 核心代码实现
      • 4.2.1 实体类
      • 4.2.2 DTO 类
      • 4.2.3 异常处理
      • 4.2.4 Mapper 接口及 XML
      • 4.2.5 服务层实现
      • 4.2.6 控制器
      • 4.2.7 启动类
    • 4.3 事务测试与验证
      • 4.3.1 正常流程测试
      • 4.3.2 异常流程测试
      • 4.3.3 传播行为测试
  • 五、Spring 事务常见问题与解决方案
    • 5.1 事务不生效的常见原因
    • 5.2 事务隔离级别与并发问题
    • 5.3 分布式事务问题
  • 六、总结与最佳实践
    • 6.1 核心要点总结
    • 6.2 最佳实践建议
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档