首页 > 解决方案 > 如果消息处理程序(消费者) 调用使用@Transactional 注释的服务

问题描述

该应用程序是一个 Spring Boot 应用程序,其中包含使用函数式编程模型定义的 Spring Data JPA 和 Spring Cloud Stream (RabbitMQ)。

功能消息处理程序调用服务:

@Configuration
class MessageHandlerConfiguration {
    @Bean
    public Consumer<Person> consume(Service service) {
        return person -> service.process(person);
    }
}

服务方法持久化实体并尝试获取延迟加载的关系:

@Service
class Service {
    //constructor injection
    private PersonRepo personRepo;
    
    @Transactional
    public void process(Person person) {
        // create personEntity
        // ...

        var personEntity = personRepo.save(personEntity);

        // throws org.hibernate.LazyInitializationException
        var addressEntity = personEntity.getAddress();
    }
}

personEntity.getAddress()通过throws访问延迟加载的实体org.hibernate.LazyInitializationException: could not initialize proxy - no Session。因此,Service没有代理,并且该process方法中没有可用的事务(和会话)。一些调试验证了这个假设。

但是,如果process从休息控制器调用该方法,则事务可用并且代码可以正常工作。

此外,process可以将消息处理程序中的方法调用包装到 中TransactionTemplate,这解决了丢失事务的问题:

@Bean
public Consumer<Person> consume(Service service, TransactionTemplate transactionTemplate) {
    return person -> transactionTemplate.execute(() -> service.process(person));
}

如果从消息处理程序调用,为什么不代理服务?Spring Cloud Stream 是否与声明式事务管理集成?

标签: spring-bootspring-data-jpaspring-cloud-streamspring-rabbitspring-cloud-function

解决方案


好的,我解决了。

我创建了一个简单的项目来测试用例,它按预期工作。代码可在GitHub上找到。

最初的问题发生在具有更复杂设置和依赖关系的更大项目中。


推荐阅读