Spring 重试模板不适用于 @org.springframework.transaction.annotation.Transactional

标签 spring spring-boot spring-data-jpa spring-retry

在数据库不可用的情况下,我需要重试执行我的服务方法3次。为此,我使用 Spring Retry Template。但是使用@Transactional注释,当数据库不可用时,无法捕获特定的异常。

@Configuration
public class RetryTemplateConfig {

    @Bean
    public  RetryTemplate createRetryTemplate(){
        RetryTemplate retryTemplate = new RetryTemplate();

        FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
        fixedBackOffPolicy.setBackOffPeriod(5000l);
        retryTemplate.setBackOffPolicy(fixedBackOffPolicy);

        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
        retryPolicy.setMaxAttempts(5);
        retryTemplate.setRetryPolicy(retryPolicy);

        return retryTemplate;
    }

}


@Service
public class MessageService {

    @Autowired
    private RetryTemplate retryTemplate;

    @Autowired
    MessageRepo messageRepo;

    @Transactional
    public void sendMessage(Message message) throws RuntimeException{

        retryTemplate.execute(retryContext -> {
            log.info("Executing for {}  " ,  retryContext.getRetryCount());
            Message model = new Message();
            messageRepo.save(model);
            return "";
        });
    }
}

但是,如果我在数据库不可用时尝试使用 @Retryable,则 RuntimeExcption 会成功重试,并在重试结束时调用 @Recover 方法。

@Service
@EnableRetry
public class MessageService {

    @Autowired
    private RetryTemplate retryTemplate;

    @Autowired
    MessageRepo messageRepo;

    @Retryable(
            value = {RuntimeException.class},
            backoff = @Backoff(delay = 2000),
            maxAttempts = 5
    )
    @Transactional
    public void sendMessage(Message message) throws RuntimeException{
        Message model = new Message();
        messageRepo.save(model);
    }
}


    @Recover
    public void recover(){
        log.info("Recover is called ");
    }
}

有人可以解释这两种不同行为的原因并建议我重试的最佳方法吗? (我正在使用 JPA 数据)

最佳答案

在第一种情况下,在使用 retryTemplate 时,即使在执行到达 retryTemplate.execute(...) 调用之前也会发生异常,因为 spring 无法打开您需要的事务( 通过@Transactional注释,因为打开事务需要数据库连接),

因此异常发生在 spring 框架级别,而不是在您的方法中,因此您的重试模板在这种情况下毫无用处。

在第二种情况下,打开事务发生在重试范围内,因此重试捕获异常并且一切都很好。

关于Spring 重试模板不适用于 @org.springframework.transaction.annotation.Transactional,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57809431/

相关文章:

spring - 在没有 Web 流的情况下在 Spring 中管理路径面包屑的最佳实践

java - model.addAttribute() 对于每个循环

spring - 带有可选参数的 jpa 查询

java - 有什么方法可以在从 PostgreSQL 数据库表获取的给定日期时间从 Spring Boot 自动调用函数吗?

spring - 从 xml 配置中读取 spring yml 属性

java - 使用@PATCH 方法进行 Spring REST 部分更新

java - Paypal MassPayout 出现 403 错误

java - 使用 Spring Data JPA Stream 获取数据

spring-boot - 如何在Spring Boot Elastic Search与MongoDB集成中实现对非结构化数据的搜索

java - JPA继承两个或多个父类(super class)