Skip to content
清晨的一缕阳光
返回

Spring Boot 声明式事务管理

前言

事务管理是保证数据一致性的重要手段。Spring Boot 提供了声明式事务管理,通过@Transactional 注解可以方便地管理事务。本文将详细介绍事务管理的用法和注意事项。

快速开始

1. 添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- 或 -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>

2. 启用事务

@SpringBootApplication
@EnableTransactionManagement
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

3. 基本使用

@Service
@RequiredArgsConstructor
public class UserService {
    
    private final UserRepository userRepository;
    
    @Transactional
    public UserDTO create(UserCreateDTO dto) {
        User user = new User();
        user.setUsername(dto.getUsername());
        user.setEmail(dto.getEmail());
        
        return userRepository.save(user);
    }
}

@Transactional 详解

1. 注解属性

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

2. 传播行为

@Service
public class OrderService {
    
    @Autowired
    private UserService userService;
    
    /**
     * REQUIRED - 默认
     * 加入现有事务或创建新事务
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void createOrder() {
        // 创建订单
        userService.updateUser(); // 加入同一事务
    }
    
    /**
     * REQUIRES_NEW
     * 总是创建新事务,挂起现有事务
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void logOrder() {
        // 记录日志,独立事务
    }
    
    /**
     * NESTED
     * 嵌套事务,保存点
     */
    @Transactional(propagation = Propagation.NESTED)
    public void nestedMethod() {
        // 嵌套事务
    }
    
    /**
     * SUPPORTS
     * 支持事务,没有事务也以非事务运行
     */
    @Transactional(propagation = Propagation.SUPPORTS)
    public void queryMethod() {
        // 查询操作
    }
    
    /**
     * NOT_SUPPORTED
     * 以非事务方式运行,挂起现有事务
     */
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void batchOperation() {
        // 批量操作,不需要事务
    }
    
    /**
     * MANDATORY
     * 必须存在事务,否则抛出异常
     */
    @Transactional(propagation = Propagation.MANDATORY)
    public void mustInTransaction() {
        // 必须在事务中调用
    }
    
    /**
     * NEVER
     * 必须在非事务环境下运行
     */
    @Transactional(propagation = Propagation.NEVER)
    public void noTransaction() {
        // 不能在事务中调用
    }
}

3. 隔离级别

@Service
public class TransactionService {
    
    /**
     * DEFAULT - 使用数据库默认隔离级别
     */
    @Transactional(isolation = Isolation.DEFAULT)
    public void defaultIsolation() {
        // MySQL: REPEATABLE_READ
        // Oracle: READ_COMMITTED
    }
    
    /**
     * READ_UNCOMMITTED - 读未提交
     * 可能读到脏数据、不可重复读、幻读
     */
    @Transactional(isolation = Isolation.READ_UNCOMMITTED)
    public void readUncommitted() {
        // 性能最好,一致性最差
    }
    
    /**
     * READ_COMMITTED - 读已提交
     * 避免脏读,可能不可重复读、幻读
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void readCommitted() {
        // Oracle 默认,大多数场景适用
    }
    
    /**
     * REPEATABLE_READ - 可重复读
     * 避免脏读、不可重复读,可能幻读
     */
    @Transactional(isolation = Isolation.REPEATABLE_READ)
    public void repeatableRead() {
        // MySQL 默认
    }
    
    /**
     * SERIALIZABLE - 串行化
     * 避免所有并发问题
     */
    @Transactional(isolation = Isolation.SERIALIZABLE)
    public void serializable() {
        // 性能最差,一致性最好
    }
}

4. 回滚规则

@Service
public class TransactionService {
    
    /**
     * 默认只回滚 RuntimeException 和 Error
     */
    @Transactional
    public void method1() {
        // 抛出 RuntimeException 会回滚
        // 抛出 Checked Exception 不会回滚
    }
    
    /**
     * 指定回滚异常
     */
    @Transactional(rollbackFor = Exception.class)
    public void method2() {
        // 抛出任何 Exception 都会回滚
    }
    
    /**
     * 指定不回滚异常
     */
    @Transactional(noRollbackFor = ArithmeticException.class)
    public void method3() {
        // 抛出 ArithmeticException 不会回滚
    }
    
    /**
     * 多个回滚异常
     */
    @Transactional(rollbackFor = {IOException.class, SQLException.class})
    public void method4() throws IOException, SQLException {
        // 抛出指定异常会回滚
    }
}

事务失效场景

1. 非 public 方法

@Service
public class UserService {
    
    /**
     * ❌ 失效 - 非 public
     */
    @Transactional
    protected void method1() {
        // 事务失效
    }
    
    /**
     * ✅ 正确
     */
    @Transactional
    public void method2() {
        // 事务生效
    }
}

2. 自调用

@Service
public class UserService {
    
    /**
     * ❌ 失效 - 自调用
     */
    public void method1() {
        method2(); // 事务失效
    }
    
    @Transactional
    public void method2() {
        // 事务失效
    }
    
    /**
     * ✅ 正确 - 通过代理调用
     */
    @Autowired
    @Lazy
    private UserService self;
    
    public void method3() {
        self.method4(); // 事务生效
    }
    
    @Transactional
    public void method4() {
        // 事务生效
    }
}

3. 异常被捕获

@Service
public class UserService {
    
    /**
     * ❌ 失效 - 异常被捕获
     */
    @Transactional
    public void method1() {
        try {
            // 业务逻辑
        } catch (Exception e) {
            log.error("错误", e);
            // 异常被捕获,不会回滚
        }
    }
    
    /**
     * ✅ 正确 - 重新抛出异常
     */
    @Transactional
    public void method2() {
        try {
            // 业务逻辑
        } catch (Exception e) {
            log.error("错误", e);
            throw new RuntimeException(e); // 重新抛出
        }
    }
}

4. 错误配置

@Service
public class UserService {
    
    /**
     * ❌ 失效 - 异常类型不匹配
     */
    @Transactional(rollbackFor = IOException.class)
    public void method1() throws SQLException {
        // 抛出 SQLException 不会回滚
    }
    
    /**
     * ✅ 正确
     */
    @Transactional(rollbackFor = Exception.class)
    public void method2() throws SQLException {
        // 抛出 SQLException 会回滚
    }
}

编程式事务

1. 使用 TransactionTemplate

@Service
@RequiredArgsConstructor
public class TransactionService {
    
    private final TransactionTemplate transactionTemplate;
    
    public void createOrder(Order order) {
        transactionTemplate.execute(status -> {
            try {
                // 业务逻辑
                orderRepository.save(order);
                inventoryService.decrease(order.getProductId(), order.getQuantity());
                return order;
            } catch (Exception e) {
                status.setRollbackOnly();
                throw e;
            }
        });
    }
}

2. 使用 PlatformTransactionManager

@Service
@RequiredArgsConstructor
public class TransactionService {
    
    private final PlatformTransactionManager transactionManager;
    
    public void createOrder(Order order) {
        TransactionStatus status = transactionManager.getTransaction(
            new DefaultTransactionDefinition()
        );
        
        try {
            // 业务逻辑
            orderRepository.save(order);
            inventoryService.decrease(order.getProductId(), order.getQuantity());
            
            transactionManager.commit(status);
        } catch (Exception e) {
            transactionManager.rollback(status);
            throw e;
        }
    }
}

最佳实践

1. 事务粒度

// ✅ 推荐 - 合理的事务粒度
@Service
public class OrderService {
    
    @Transactional
    public void createOrder(OrderCreateDTO dto) {
        // 创建订单
        Order order = createOrder(dto);
        // 扣减库存
        inventoryService.decrease(dto.getProductId(), dto.getQuantity());
        // 更新用户积分
        userService.addPoints(dto.getUserId(), dto.getPoints());
    }
}

// ❌ 不推荐 - 事务粒度过大
@Service
public class OrderService {
    
    @Transactional
    public void processEverything() {
        // 创建订单
        // 扣减库存
        // 更新积分
        // 发送短信
        // 发送邮件
        // ... 太多操作在一个事务中
    }
}

2. 只读事务

@Service
public class QueryService {
    
    /**
     * 只读事务,性能更好
     */
    @Transactional(readOnly = true)
    public List<Order> findAll() {
        return orderRepository.findAll();
    }
    
    /**
     * 只读事务
     */
    @Transactional(readOnly = true)
    public Order findById(Long id) {
        return orderRepository.findById(id).orElse(null);
    }
}

3. 超时设置

@Service
public class BatchService {
    
    /**
     * 设置超时时间
     */
    @Transactional(timeout = 60)
    public void batchInsert(List<Order> orders) {
        for (Order order : orders) {
            orderRepository.save(order);
        }
    }
}

4. 嵌套事务

@Service
public class ParentService {
    
    @Autowired
    private ChildService childService;
    
    @Transactional
    public void parentMethod() {
        // 父事务
        
        try {
            childService.childMethod(); // 子事务
        } catch (Exception e) {
            // 子事务回滚,父事务可以选择不回滚
        }
    }
}

@Service
public class ChildService {
    
    @Transactional(propagation = Propagation.NESTED)
    public void childMethod() {
        // 子事务,有独立的保存点
    }
}

总结

事务管理要点:

良好的事务管理,能保证数据的一致性和完整性。


分享这篇文章到:

上一篇文章
Sentinel 流量控制
下一篇文章
Sentinel 控制台实战