Spring事务

一、什么是事务

事务是一组不可分割的数据库操作序列,这些操作要么全部成功执行 ,要么全部不执行,是一个完整的逻辑工作单元。

事务经典例子:银行转账

sql 复制代码
-- 从A账户转100元到B账户
UPDATE accounts SET balance = balance - 100 WHERE account_id = 'A';
UPDATE accounts SET balance = balance + 100 WHERE account_id = 'B';

如果没有事务管理,可能出现以下问题:

  1. A账户扣款成功,B账户收款失败 → A的钱消失了
  2. 系统在操作过程中崩溃 → 数据不一致

有了事务机制,就能确保:要么两个操作都成功,要么都失败。

二、事务的四大特性(ACID)

  1. 原子性(Atomicity):事务内的操作要么全部成功,要么全部失败
  2. 一致性(Consistency):事务执行前后数据保持一致状态
  3. 隔离性(Isolation):并发事务之间相互隔离
  4. 持久性(Durability):事务提交后数据永久保存

三、SQL中的事务控制

sql 复制代码
-- 1. 开始事务
START TRANSACTION;  -- 或 BEGIN

-- 2. 执行一系列操作
INSERT INTO orders (user_id, amount) VALUES (1, 100.00);
UPDATE accounts SET balance = balance - 100 WHERE user_id = 1;

-- 3. 根据情况选择
COMMIT;    -- 确认提交,永久保存
-- 或
ROLLBACK;  -- 撤销所有操作

四、Spring事务的使用

1.基础配置

Java 配置方式:

java 复制代码
@Configuration
@EnableTransactionManagement  // 启用事务管理
@ComponentScan("com.example")
public class AppConfig {
    
    @Bean
    public DataSource dataSource() {
        // 配置数据源
        return new DriverManagerDataSource(...);
    }
    
    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }
}

Spring Boot 自动配置:

Spring Boot 会自动配置事务管理器,只需要添加依赖:

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

2.使用 @Transactional 注解

基本使用

java 复制代码
@Service
public class UserService {
    
    @Autowired
    private UserRepository userRepository;
    
    @Transactional  // 添加事务注解
    public User createUser(User user) {
        // 业务操作
        userRepository.save(user);
        
        // 其他数据库操作
        userRepository.saveLog(user);
        
        // 如果抛出异常,事务会自动回滚
        return user;
    }
}

说明:可以在 application.yml 配置文件中开启事务管理日志,这样就能在控制台看到和事务相关的日志信息了

yml 复制代码
#spring事务管理日志
logging: 
  level: 
    org.springframework.jdbc.support.JdbcTransactionManager: debug

3.@Transactional 参数详解

java 复制代码
@Transactional(
    propagation = Propagation.REQUIRED,      // 传播行为
    isolation = Isolation.DEFAULT,           // 隔离级别
    readOnly = false,                        // 是否只读
    timeout = 30,                            // 超时时间(秒)
    rollbackFor = Exception.class,           // 触发回滚的异常
    noRollbackFor = RuntimeException.class   // 不触发回滚的异常
)
public void businessMethod() {
    // 业务逻辑
}

3.1 事务传播行为(Propagation)

事务的传播行为是指,当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行事务控制。

java 复制代码
@Service
public class OrderService {
    
    @Transactional(propagation = Propagation.REQUIRED)  // 默认值
    public void placeOrder(Order order) {
        // 如果当前没有事务,就新建一个事务
        // 如果已经存在事务,就加入该事务
    }
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateInventory() {
        // 总是新建一个事务,如果当前有事务则挂起
    }
    
    @Transactional(propagation = Propagation.SUPPORTS)
    public void getOrder() {
        // 如果当前有事务,就加入该事务
        // 如果没有事务,就以非事务方式执行
    }
    
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void sendNotification() {
        // 以非事务方式执行,挂起当前事务
    }
    
    @Transactional(propagation = Propagation.NEVER)
    public void validateData() {
        // 以非事务方式执行,如果当前存在事务则抛出异常
    }
    
    @Transactional(propagation = Propagation.NESTED)
    public void processSubTask() {
        // 如果当前存在事务,则在嵌套事务内执行
        // 嵌套事务可以独立提交或回滚
    }
}

说明:大部分情况下都是使用 REQUIRED 传播行为即可,当我们不希望事务之间互相影响时,可以使用 REQUIRES_NEW 传播行为。比如:下订单前需要记录日志,不论订单保存成功与否,都要保证日志记录能够记录成功。

3.2 隔离级别(Isolation)

java 复制代码
@Service
public class AccountService {
    
    @Transactional(isolation = Isolation.READ_COMMITTED)  // 常用
    public void transferMoney(Long fromId, Long toId, BigDecimal amount) {
        // 读取已提交的数据,避免脏读
    }
    
    @Transactional(isolation = Isolation.REPEATABLE_READ)
    public BigDecimal getBalance(Long accountId) {
        // 可重复读,MySQL默认级别
    }
    
    @Transactional(isolation = Isolation.SERIALIZABLE)
    public void batchUpdate() {
        // 串行化,性能最低但最安全
    }
}
隔离级别 说明
READ UNCOMMITTED 允许读取其他事务未提交的数据
READ COMMITTED 只能读取其他事务已提交的数据
REPEATABLE READ 确保在同一事务中多次读取同一数据,结果一致
SERIALIZABLE 强制事务串行执行,完全隔离

说明:Isolation.DEFAULT 是 Spring 事务中一个特殊的隔离级别常量,表示使用底层数据库的默认隔离级别。

java 复制代码
// Spring 事务隔离级别枚举
public enum Isolation {
    DEFAULT(-1),           // 使用数据库默认级别
    READ_UNCOMMITTED(1),   // 读未提交
    READ_COMMITTED(2),     // 读已提交
    REPEATABLE_READ(4),    // 可重复读
    SERIALIZABLE(8);       // 串行化
    
    private final int value;
    
    Isolation(int value) {
        this.value = value;
    }
}

3.3 触发回滚的异常(rollbackFor)

为什么需要 rollbackFor?

默认情况下,Spring 事务只在运行时异常 (RuntimeException)和错误 (Error)发生时回滚,而受检异常(Checked Exception)不会触发回滚。

java 复制代码
@Service
public class BankService {
    
    @Transactional
    public void transferMoney1() {
        // 运行时异常 → 自动回滚 ✓
        throw new RuntimeException("系统错误");
    }
    
    @Transactional
    public void transferMoney2() throws IOException {
        // 受检异常 → 不会回滚!❌
        throw new IOException("网络异常");
        // 事务会提交!这很危险
    }
}

基本用法

java 复制代码
// 指定单个异常类型
@Transactional(rollbackFor = Exception.class)
public void createOrder(Order order) throws BusinessException {
    try {
        // 业务逻辑
        validateOrder(order);
        saveOrder(order);
        reduceInventory(order);
        
    } catch (ValidationException e) {
        // 指定 BusinessException 触发回滚
        throw new BusinessException("订单创建失败", e);
    }
}

// 指定多个异常类型
@Transactional(rollbackFor = {
    BusinessException.class,
    PaymentException.class, 
    SQLException.class
})
public void processPayment() {
    // 当这些异常发生时,事务会回滚
}
相关推荐
JH30736 小时前
SpringBoot 优雅处理金额格式化:拦截器+自定义注解方案
java·spring boot·spring
Coder_Boy_7 小时前
技术让开发更轻松的底层矛盾
java·大数据·数据库·人工智能·深度学习
invicinble7 小时前
对tomcat的提供的功能与底层拓扑结构与实现机制的理解
java·tomcat
较真的菜鸟8 小时前
使用ASM和agent监控属性变化
java
黎雁·泠崖8 小时前
【魔法森林冒险】5/14 Allen类(三):任务进度与状态管理
java·开发语言
qq_12498707539 小时前
基于SSM的动物保护系统的设计与实现(源码+论文+部署+安装)
java·数据库·spring boot·毕业设计·ssm·计算机毕业设计
Coder_Boy_9 小时前
基于SpringAI的在线考试系统-考试系统开发流程案例
java·数据库·人工智能·spring boot·后端
Mr_sun.9 小时前
Day06——权限认证-项目集成
java
瑶山9 小时前
Spring Cloud微服务搭建四、集成RocketMQ消息队列
java·spring cloud·微服务·rocketmq·dashboard
abluckyboy9 小时前
Java 实现求 n 的 n^n 次方的最后一位数字
java·python·算法