欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > 文章正文

如何使用Springboot的@Transactional进行事务管理,

来源: javaer 分享于  点击 8630 次 点评:38

如何使用Springboot的@Transactional进行事务管理,


目录
  • 一、前置条件
  • 二、基本用法
    • 1. 在方法上添加注解
    • 2. 在类上添加注解
  • 三、核心配置参数
    • 1. 传播行为(Propagation)
    • 2. 隔离级别(Isolation)
    • 3. 超时时间(Timeout)
    • 4. 只读模式(readOnly)
    • 5. 回滚规则(rollbackFor/noRollbackFor)
  • 四、关键注意事项
    • 1. 方法可见性
    • 2. 自调用问题
    • 3. 异常处理
    • 4. 多数据源事务
  • 五、调试技巧
    • 六、最佳实践
      • 七、完整示例
        • 八、适用于关系型数据库
          • 总结

            在 Spring Boot 中,@Transactional 是用于声明式事务管理的关键注解。它基于 Spring 的 AOP(面向切面编程)实现,可以简化数据库事务的管理。

            一、前置条件

            • 依赖引入:确保项目中包含 spring-boot-starter-data-jpaspring-boot-starter-jdbc
            • 启用事务管理:Spring Boot 默认自动配置事务管理器(如 DataSourceTransactionManager),无需手动启用。

            二、基本用法

            1. 在方法上添加注解

            @Service
            public class UserService {
                @Autowired
                private UserRepository userRepository;
                @Transactional
                public void createUser(User user) {
                    userRepository.save(user);
                    // 其他数据库操作
                }
            }

            2. 在类上添加注解

            @Service
            @Transactional
            public class UserService {
                // 类中所有 public 方法都会应用事务
            }

            三、核心配置参数

            1. 传播行为(Propagation)

            控制事务的边界,默认为 Propagation.REQUIRED

            @Transactional(propagation = Propagation.REQUIRES_NEW)
            public void updateUser(User user) {
                // 始终开启新事务
            }

            常见选项:

            • REQUIRED(默认):如果当前存在事务,则加入;否则新建
            • REQUIRES_NEW:始终新建事务,挂起当前事务(如有)
            • SUPPORTS:如果当前存在事务,则加入;否则非事务运行
            • NOT_SUPPORTED:非事务运行,挂起当前事务(如有)
            • MANDATORY:必须在事务中调用,否则抛出异常
            • NEVER:必须在非事务中调用,否则抛出异常

            2. 隔离级别(Isolation)

            控制事务的隔离性,默认为 Isolation.DEFAULT(使用数据库默认)。

            @Transactional(isolation = Isolation.SERIALIZABLE)
            public void sensitiveOperation() {
                // 最高隔离级别
            }

            3. 超时时间(Timeout)

            事务超时时间(秒),默认-1(使用数据库默认)。

            @Transactional(timeout = 30)
            public void longRunningProcess() {
                // 超过30秒将回滚
            }

            4. 只读模式(readOnly)

            优化只读操作,默认为 false

            @Transactional(readOnly = true)
            public List<User> findAllUsers() {
                return userRepository.findAll();
            }

            5. 回滚规则(rollbackFor/noRollbackFor)

            指定触发回滚的异常类型:

            @Transactional(rollbackFor = CustomException.class)
            public void process() throws CustomException {
                // 抛出 CustomException 时回滚
            }

            四、关键注意事项

            1. 方法可见性

            必须为 public:由于 Spring AOP 的实现机制,非 public 方法上的 @Transactional 无效

            2. 自调用问题

            同类中的方法互相调用时,事务不会生效(绕过代理对象)

            // 错误示例
            public void methodA() {
                methodB(); // 事务不生效
            }
            @Transactional
            public void methodB() { ... }

            3. 异常处理

            • 默认回滚条件:抛出 RuntimeException 或 Error
            • 检查型异常(Checked Exception)默认不会触发回滚
            // 处理检查型异常
            @Transactional(rollbackFor = IOException.class)
            public void fileOperation() throws IOException {
                // ...
            }

            4. 多数据源事务

            需配置多个事务管理器,并通过 @Transactional(value = "specificTransactionManager") 指定

            五、调试技巧

            启用调试日志:

            logging.level.org.springframework.transaction.interceptor=TRACE
            logging.level.org.springframework.jdbc.datasource.DataSourceTransactionManager=DEBUG

            检查代理对象:

            System.out.println(userService.getClass().getName());
            // 输出应为代理类:com.sun.proxy.$ProxyXX 或 ...$$EnhancerBySpringCGLIB$$...

            六、最佳实践

            • 服务层使用:在 Service 层而非 Controller 层使用事务
            • 保持短事务:避免在事务中包含远程调用、文件IO等耗时操作
            • 精确回滚规则:明确指定 rollbackFor 而非依赖默认行为
            • 结合 JPA 使用
            @Transactional
            public void updateUserEmail(Long userId, String email) {
                User user = userRepository.findById(userId).orElseThrow();
                user.setEmail(email); // 自动脏检查,事务提交时更新
            }

            七、完整示例

            @Service
            public class OrderService {
                @Autowired
                private OrderRepository orderRepository;
                @Autowired
                private InventoryService inventoryService;
                @Transactional(
                    propagation = Propagation.REQUIRED,
                    isolation = Isolation.READ_COMMITTED,
                    timeout = 30,
                    rollbackFor = {InsufficientStockException.class, PaymentFailedException.class}
                )
                public void placeOrder(Order order) {
                    inventoryService.reduceStock(order.getItems()); // 可能抛出 InsufficientStockException
                    orderRepository.save(order);
                    processPayment(order); // 可能抛出 PaymentFailedException
                }
                private void processPayment(Order order) {
                    // 支付逻辑
                }
            }

            八、适用于关系型数据库

            @Transactional 的使用与数据库层有直接关系,但它的事务管理机制是基于数据库事务的。如果 Service 层方法使用了 Elasticsearch 客户端读写 Elasticsearch,那么 @Transactional 的行为会受到影响。

            1.@Transactional 的适用范围

            • @Transactional 是 Spring 提供的事务管理机制,主要用于管理数据库事务
            • 它依赖于底层的事务管理器(如 DataSourceTransactionManager),而这些事务管理器通常是与关系型数据库(如 MySQL、PostgreSQL)交互的。

            数据库事务@Transactional 可以确保数据库操作的原子性、一致性、隔离性和持久性(ACID)。非数据库操作:对于非数据库操作(如 Elasticsearch、Redis、文件系统等),@Transactional 无法直接管理其事务。

            2.Elasticsearch 与 @Transactional 的关系

            Elasticsearch 是一个分布式搜索引擎,它本身不支持传统意义上的事务(ACID)。因此,@Transactional 对 Elasticsearch 的操作没有直接的事务管理能力。

            (1)场景分析

            假设我们的 Service 方法同时操作了数据库和 Elasticsearch:

            @Service
            public class UserService {
                @Autowired
                private UserRepository userRepository; // 数据库操作
                @Autowired
                private ElasticsearchClient elasticsearchClient; // Elasticsearch 操作
                @Transactional
                public void createUser(User user) {
                    // 操作数据库
                    userRepository.save(user);
                    // 操作 Elasticsearch
                    IndexRequest request = new IndexRequest("users")
                        .id(user.getId().toString())
                        .source("name", user.getName(), "email", user.getEmail());
                    elasticsearchClient.index(request, RequestOptions.DEFAULT);
                }
            }

            (2)可能出现的问题

            • 数据库事务回滚,Elasticsearch 操作不回滚:
            • 如果 userRepository.save(user) 成功,但后续 Elasticsearch 操作失败,数据库事务会回滚(因为 @Transactional 生效),但 Elasticsearch 的数据已经写入,无法回滚。
            • 数据库事务提交,Elasticsearch 操作失败:
            • 如果数据库操作成功,但 Elasticsearch 操作失败,数据库事务已经提交,而 Elasticsearch 数据未更新,导致数据不一致。

            3.如何解决 Elasticsearch 与数据库的事务一致性问题

            由于 Elasticsearch 不支持事务,因此需要采用其他机制来保证数据一致性。

            (1)手动补偿机制 在 Elasticsearch 操作失败时,手动回滚数据库操作。

            示例:

            @Transactional
            public void createUser(User user) {
                try {
                    userRepository.save(user); // 数据库操作
                    IndexRequest request = new IndexRequest("users")
                        .id(user.getId().toString())
                        .source("name", user.getName(), "email", user.getEmail());
                    elasticsearchClient.index(request, RequestOptions.DEFAULT); // Elasticsearch 操作
                } catch (Exception e) {
                    // Elasticsearch 操作失败,手动回滚数据库
                    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                    throw e;
                }
            }

            (2)消息队列(最终一致性)

            • 将 Elasticsearch 操作异步化,通过消息队列(如 Kafka、RabbitMQ)实现最终一致性。
            • 示例:

              数据库操作完成后,发送消息到队列。

              消费者从队列中读取消息并更新 Elasticsearch。

            (3)两阶段提交(2PC)

            • 使用分布式事务管理器(如 Seata)实现数据库和 Elasticsearch 的分布式事务。
            • 这种方法复杂度较高,通常不推荐用于 Elasticsearch。

            (4)本地消息表

            • 在数据库中创建一张消息表,记录需要同步到 Elasticsearch 的数据。
            • 通过定时任务或事件监听器将数据同步到 Elasticsearch。

            总结

            • @Transactional 只对数据库事务有效:它无法管理 Elasticsearch 等非关系型数据存储的事务。
            • Elasticsearch 操作与数据库事务的一致性:需要通过补偿机制、消息队列等方式实现。
              • 设计建议: 尽量避免在同一个事务中混合数据库和 Elasticsearch 操作。
              • 采用异步化或最终一致性方案来保证数据一致性。

            如果你有更多具体的业务场景,可以进一步讨论如何设计解决方案!

            到此这篇关于使用Spring boot的@Transactional进行事务管理的文章就介绍到这了,更多相关Spring boot @Transactional事务管理内容请搜索3672js教程以前的文章或继续浏览下面的相关文章希望大家以后多多支持3672js教程!

            您可能感兴趣的文章:
            • springboot中事务管理@Transactional的注意事项与使用场景
            • 解决SpringBoot中使用@Transactional注解遇到的问题
            • springboot中使用@Transactional注解事物不生效的坑
            相关栏目:

            用户点评