Spring事务管理
大约 6 分钟
Spring事务管理
事务基础概念
事务是数据库操作的最小工作单元,是一系列数据库操作的集合,这些操作要么全部执行成功,要么全部执行失败。事务具有ACID特性:
- 原子性(Atomicity):事务是最小的执行单位,不允许分割,事务中的操作要么全部完成,要么全部不执行
- 一致性(Consistency):事务必须使数据库从一个一致性状态变换到另一个一致性状态
- 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行
- 持久性(Durability):事务一旦提交,对数据库的改变是永久性的
Spring事务管理模型
Spring提供了两种事务管理方式:
编程式事务管理
通过TransactionTemplate或PlatformTransactionManager手动管理事务:
@Service
public class UserService {
    
    @Autowired
    private PlatformTransactionManager transactionManager;
    
    public void saveUser(User user) {
        TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
        
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                try {
                    // 数据库操作
                    userRepository.save(user);
                    userProfileRepository.save(user.getProfile());
                } catch (Exception e) {
                    // 回滚事务
                    status.setRollbackOnly();
                    throw e;
                }
            }
        });
    }
}声明式事务管理
通过注解或XML配置管理事务,是Spring推荐的方式:
@Service
@Transactional
public class UserService {
    
    public void saveUser(User user) {
        // 数据库操作,自动进行事务管理
        userRepository.save(user);
        userProfileRepository.save(user.getProfile());
    }
}事务属性详解
传播行为(Propagation)
传播行为定义了事务方法被另一个事务方法调用时,事务如何传播:
@Service
public class UserService {
    
    // REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务
    @Transactional(propagation = Propagation.REQUIRED)
    public void saveUser(User user) {
        // 业务逻辑
    }
    
    // REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateUser(User user) {
        // 业务逻辑
    }
    
    // SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行
    @Transactional(propagation = Propagation.SUPPORTS)
    public void queryUser(Long userId) {
        // 查询操作
    }
}常用传播行为:
- REQUIRED(默认):支持当前事务,如果当前没有事务则新建事务
- SUPPORTS:支持当前事务,如果当前没有事务则以非事务方式执行
- MANDATORY:支持当前事务,如果当前没有事务则抛出异常
- REQUIRES_NEW:新建事务,如果当前有事务则挂起当前事务
- NOT_SUPPORTED:以非事务方式执行,如果当前有事务则挂起当前事务
- NEVER:以非事务方式执行,如果当前有事务则抛出异常
- NESTED:如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则创建新事务
隔离级别(Isolation)
隔离级别定义了一个事务可能受其他并发事务影响的程度:
@Service
public class UserService {
    
    // DEFAULT:使用数据库默认的隔离级别
    @Transactional(isolation = Isolation.DEFAULT)
    public User findUser(Long userId) {
        return userRepository.findById(userId);
    }
    
    // READ_COMMITTED:读已提交,避免脏读
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void updateUser(User user) {
        userRepository.save(user);
    }
    
    // REPEATABLE_READ:可重复读,避免脏读和不可重复读
    @Transactional(isolation = Isolation.REPEATABLE_READ)
    public List<User> findAllUsers() {
        return userRepository.findAll();
    }
    
    // SERIALIZABLE:串行化,避免所有并发问题
    @Transactional(isolation = Isolation.SERIALIZABLE)
    public void batchUpdate(List<User> users) {
        userRepository.saveAll(users);
    }
}隔离级别说明:
- DEFAULT:使用底层数据库的默认隔离级别
- READ_UNCOMMITTED:读未提交,可能出现脏读、不可重复读、幻读
- READ_COMMITTED:读已提交,避免脏读,可能出现不可重复读、幻读
- REPEATABLE_READ:可重复读,避免脏读、不可重复读,可能出现幻读
- SERIALIZABLE:串行化,避免所有并发问题,性能最差
回滚规则(Rollback Rules)
定义哪些异常会导致事务回滚:
@Service
public class UserService {
    
    // 默认情况下,RuntimeException和Error会导致回滚
    @Transactional
    public void saveUser(User user) {
        userRepository.save(user);
        if (user.getAge() < 0) {
            throw new IllegalArgumentException("年龄不能为负数");
        }
    }
    
    // 指定特定异常回滚
    @Transactional(rollbackFor = {IOException.class, SQLException.class})
    public void importUsers(File file) throws IOException, SQLException {
        // 文件导入逻辑
    }
    
    // 指定特定异常不回滚
    @Transactional(noRollbackFor = BusinessException.class)
    public void processUser(User user) {
        // 业务处理逻辑
    }
}超时设置(Timeout)
事务超时时间,超过指定时间自动回滚:
@Service
public class UserService {
    
    // 设置事务超时时间为30秒
    @Transactional(timeout = 30)
    public void batchProcessUsers(List<User> users) {
        // 批量处理用户,可能耗时较长
        for (User user : users) {
            processUser(user);
        }
    }
}只读事务(Read Only)
优化只读操作的性能:
@Service
public class UserService {
    
    // 只读事务,优化查询性能
    @Transactional(readOnly = true)
    public List<User> findAllUsers() {
        return userRepository.findAll();
    }
    
    @Transactional(readOnly = true)
    public User findUserById(Long userId) {
        return userRepository.findById(userId).orElse(null);
    }
}事务配置方式
基于注解的配置
@Configuration
@EnableTransactionManagement
public class TransactionConfig {
    
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}基于XML的配置
<!-- 启用注解事务 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 配置事务管理器 -->
<bean id="transactionManager" 
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>全局事务配置
@Configuration
@EnableTransactionManagement
public class GlobalTransactionConfig {
    
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
    
    // 配置事务模板
    @Bean
    public TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager) {
        TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
        transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);
        transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        return transactionTemplate;
    }
}事务最佳实践
1. 合理使用事务传播行为
@Service
public class OrderService {
    
    @Autowired
    private UserService userService;
    
    @Transactional
    public void createOrder(Order order) {
        // 在同一事务中执行
        userService.saveUser(order.getUser());
        
        // 保存订单
        orderRepository.save(order);
    }
}2. 避免事务范围过大
@Service
public class UserService {
    
    // 不推荐:事务范围过大
    @Transactional
    public void processAllUsers() {
        List<User> users = findAllUsers(); // 查询操作不需要事务
        for (User user : users) {
            updateUser(user); // 只有这个操作需要事务
        }
    }
    
    // 推荐:缩小事务范围
    public void processAllUsers() {
        List<User> users = findAllUsers(); // 非事务查询
        for (User user : users) {
            updateUserInTransaction(user); // 事务操作
        }
    }
    
    @Transactional
    private void updateUserInTransaction(User user) {
        updateUser(user);
    }
}3. 正确处理异常
@Service
public class UserService {
    
    @Transactional
    public void saveUser(User user) {
        try {
            userRepository.save(user);
            // 发送通知
            notificationService.sendNotification(user);
        } catch (NotificationException e) {
            // 通知发送失败不应该影响用户保存
            log.warn("通知发送失败", e);
            // 不重新抛出异常
        }
    }
}4. 使用只读事务优化查询
@Service
@Transactional(readOnly = true)
public class UserService {
    
    public List<User> findAllUsers() {
        return userRepository.findAll();
    }
    
    public User findUserById(Long userId) {
        return userRepository.findById(userId).orElse(null);
    }
    
    @Transactional // 覆盖类级别的只读设置
    public void saveUser(User user) {
        userRepository.save(user);
    }
}5. 事务与缓存结合使用
@Service
public class UserService {
    
    @Cacheable("users")
    @Transactional(readOnly = true)
    public User findUserById(Long userId) {
        return userRepository.findById(userId).orElse(null);
    }
    
    @CacheEvict(value = "users", key = "#user.id")
    @Transactional
    public void updateUser(User user) {
        userRepository.save(user);
    }
}事务陷阱与注意事项
1. 同一类中方法调用事务失效
@Service
public class UserService {
    
    // 事务失效:同类方法调用不会经过代理
    public void processUser(User user) {
        saveUser(user); // 这个调用不会触发事务
    }
    
    @Transactional
    public void saveUser(User user) {
        userRepository.save(user);
    }
    
    // 解决方案1:注入自身
    @Autowired
    private UserService self;
    
    public void processUserWithSelf(User user) {
        self.saveUser(user); // 通过代理调用,事务生效
    }
    
    // 解决方案2:分离到不同类
    @Autowired
    private UserSaveService userSaveService;
    
    public void processUserWithService(User user) {
        userSaveService.saveUser(user); // 事务生效
    }
}2. 异常被捕获导致事务不回滚
@Service
public class UserService {
    
    @Transactional
    public void saveUser(User user) {
        try {
            userRepository.save(user);
            throw new RuntimeException("模拟异常");
        } catch (RuntimeException e) {
            // 异常被捕获,事务不会回滚
            log.error("保存用户失败", e);
        }
    }
    
    // 正确做法:重新抛出异常或标记回滚
    @Transactional
    public void saveUserCorrect(User user) {
        try {
            userRepository.save(user);
            throw new RuntimeException("模拟异常");
        } catch (RuntimeException e) {
            log.error("保存用户失败", e);
            // 方法1:重新抛出异常
            throw e;
            // 方法2:标记回滚
            // TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }
}通过以上内容,我们可以全面了解Spring事务管理的各个方面,包括基础概念、配置方式、最佳实践以及常见陷阱。正确使用Spring事务管理能够保证数据的一致性和完整性。
