java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Spring Boot 事务的用法

Spring Boot 中事务的用法示例详解

作者:大溪地C

本文详细介绍了Spring Boot中事务管理的使用方法,包括事务的基本概念、配置、传播行为、隔离级别以及回滚机制,通过使用@Transactional注解,可以方便地实现事务的控制,文章还讨论了事务方法的可见性、自我调用问题以及超时设置等注意事项,感兴趣的朋友一起看看吧

引言 

在 Spring Boot 中,事务管理是一个非常重要的功能,尤其是在涉及数据库操作的业务场景中。Spring 提供了强大的事务管理支持,能够帮助我们简化事务的管理和控制。本文将详细介绍 Spring Boot 中事务的用法,包括事务的基本概念、事务的配置、事务的传播行为、事务的隔离级别以及事务的回滚机制。

1. 事务的基本概念

事务(Transaction)是指一组数据库操作,这些操作要么全部成功,要么全部失败。事务的四大特性(ACID)包括:

在 Spring Boot 中,事务管理是通过 @Transactional 注解来实现的。

2. Spring Boot 中事务的配置

2.1 启用事务管理

Spring Boot 默认已经集成了事务管理功能,只需要在配置类或启动类上添加 @EnableTransactionManagement 注解即可启用事务管理。

@SpringBootApplication
@EnableTransactionManagement // 启用事务管理
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

2.2 配置数据源和事务管理器

Spring Boot 默认使用 DataSourceTransactionManager 作为事务管理器。如果你使用的是 Spring Data JPA,事务管理器会自动配置。

# application.yml
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver

3. 使用 @Transactional 注解

@Transactional 是 Spring 提供的事务管理注解,可以标注在类或方法上。标注在类上时,表示该类中的所有方法都启用事务管理;标注在方法上时,表示该方法启用事务管理。

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    @Transactional // 开启事务  表示该方法开启了事务
    public void createUser(User user) {
        userRepository.save(user);
    }
}

3.2 事务的传播行为

事务的传播行为(Propagation)定义了事务方法之间的调用关系。Spring 提供了以下几种传播行为:

示例:

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateUser(User user) {
    userRepository.save(user);
}

3.3 事务的隔离级别

事务的隔离级别(Isolation)定义了事务之间的可见性。Spring 支持以下几种隔离级别:

示例:

@Transactional(isolation = Isolation.READ_COMMITTED)
public User getUserById(Long id) {
    return userRepository.findById(id).orElse(null);
}

3.4 事务的回滚机制

默认情况下,Spring 会在方法抛出 RuntimeException 或 Error 时回滚事务。如果需要自定义回滚规则,可以通过 rollbackFor(哪些异常回滚) 和 noRollbackFor(哪些异常不会回滚) 属性来指定。

示例:

@Transactional(rollbackFor = Exception.class) // 所有异常都回滚
public void updateUser(User user) throws Exception {
    userRepository.save(user);
    if (user.getName() == null) {
        throw new Exception("用户名不能为空"); // 抛出受检异常
    }
}

4. 事务的嵌套与传播行为

在复杂的业务场景中,可能会存在事务方法调用事务方法的情况。此时,事务的传播行为决定了事务的嵌套方式。

4.1 嵌套事务示例

@Service
public class OrderService {
    @Autowired
    private UserService userService;
    @Transactional  //一级事务
    public void createOrder(Order order) {
        // 保存订单
        orderRepository.save(order);
        // 调用另一个事务方法
        userService.updateUser(order.getUser());
    }
}
@Service
public class UserService {
    @Transactional(propagation = Propagation.REQUIRES_NEW) //二级事务
    public void updateUser(User user) {
        userRepository.save(user);
    }
}

在上面的示例中,createOrder 方法调用 updateUser 方法时,updateUser 方法会开启一个新的事务。

5. 事务的注意事项(事务不生效的几种情况)

事务方法的可见性

@Transactional 只能应用于 public 方法。如果应用于 private 或 protected 方法,事务将不会生效。Spring 的事务管理是基于代理模式实现的,代理对象只能拦截 public 方法。对于 private 或 protected 方法,Spring 无法生成代理,因此事务不会生效。

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    // 正确:public 方法,事务生效
    @Transactional
    public void createUser(User user) {
        userRepository.save(user);
    }
    // 错误:private 方法,事务不会生效
    @Transactional
    private void updateUser(User user) {
        userRepository.save(user);
    }
    // 错误:protected 方法,事务不会生效
    @Transactional
    protected void deleteUser(Long userId) {
        userRepository.deleteById(userId);
    }
}

事务的自我调用问题

如果事务方法调用了同一个类中的另一个事务方法,事务的传播行为可能不会生效。这是因为Spring 的代理对象只能拦截从外部调用的方法。如果事务方法在同一个类中调用另一个事务方法,实际上是直接调用目标方法,而不是通过代理对象调用,因此事务的传播行为不会生效。

@Service
public class OrderService {
    @Autowired
    private OrderRepository orderRepository;
    @Transactional
    public void createOrder(Order order) {
        // 保存订单
        orderRepository.save(order);
        // 调用另一个事务方法(自我调用)
        updateInventory(order.getProductId(), order.getQuantity());
    }
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateInventory(Long productId, int quantity) {
        // 更新库存逻辑
    }
}

在上面的示例中,createOrder 方法调用了 updateInventory 方法,但由于是自我调用,updateInventory 方法的事务传播行为(REQUIRES_NEW)不会生效。

解决方法:

将事务方法拆分到不同的类中
将 updateInventory 方法移到另一个服务类中,通过依赖注入调用。

@Service
public class OrderService {
    @Autowired
    private OrderRepository orderRepository;
    @Autowired
    private InventoryService inventoryService;
    @Transactional
    public void createOrder(Order order) {
        // 保存订单
        orderRepository.save(order);
        // 调用另一个服务类的事务方法
        inventoryService.updateInventory(order.getProductId(), order.getQuantity());
    }
}
@Service
public class InventoryService {
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateInventory(Long productId, int quantity) {
        // 更新库存逻辑
    }
}

事务的超时设置

可以通过 @Transactional(timeout = 10) 设置事务的超时时间(单位为秒)。如果事务执行时间超过指定时间,事务将自动回滚。

@Service
public class ReportService {
    @Autowired
    private ReportRepository reportRepository;
    @Transactional(timeout = 10) // 设置事务超时时间为 10 秒
    public void generateReport() {
        // 模拟耗时操作
        for (int i = 0; i < 1000000; i++) {
            reportRepository.save(new Report("Report " + i));
        }
    }
}

在上面的示例中,如果 generateReport 方法的执行时间超过 10 秒,事务将自动回滚。

6. 总结

Spring Boot 提供了强大的事务管理功能,通过 @Transactional 注解可以轻松实现事务的控制。在实际开发中,需要根据业务需求选择合适的传播行为和隔离级别,同时注意事务方法的可见性和自我调用问题。

通过本文的介绍,相信你已经掌握了 Spring Boot 中事务的基本用法。如果你有更多问题,欢迎在评论区留言讨论!

到此这篇关于Spring Boot 中事务的用法详解的文章就介绍到这了,更多相关Spring Boot 事务的用法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文