Java 如何将@Transactional与@KafkaListener一起使用?

Java 如何将@Transactional与@KafkaListener一起使用?,java,spring-boot,spring-kafka,spring-transactions,Java,Spring Boot,Spring Kafka,Spring Transactions,是否有可能将声明性tx管理(通过@Transactional)与@KafkaListener注释方法一起使用? 例如,我想使用它来定义每个侦听器的单独tx超时。 我的设置如下: TransactionManager: @Bean @ConditionalOnBean(value = {HibernateTransactionManager.class}) public ChainedKafkaTransactionManager<Object, Object

是否有可能将声明性tx管理(通过@Transactional)与@KafkaListener注释方法一起使用? 例如,我想使用它来定义每个侦听器的单独tx超时。 我的设置如下:

TransactionManager:

@Bean
        @ConditionalOnBean(value = {HibernateTransactionManager.class})
        public ChainedKafkaTransactionManager<Object, Object> chainedHibernateTm(KafkaTransactionManager<String, String> kafkaTransactionManager,
                                                                                 org.springframework.orm.hibernate5.HibernateTransactionManager hibernateTransactionManager) {
            return new ChainedKafkaTransactionManager<>(kafkaTransactionManager, hibernateTransactionManager);
        }
问题是-KafkCamessageListenerContainer在调用该方法之前创建自己的事务-它使用自己的事务模板:

@Nullable
        private TransactionTemplate determineTransactionTemplate() {
            return this.transactionManager != null
                    ? new TransactionTemplate(this.transactionManager)
                    : null;
        }


未使用TransactionInterceptor。那么,如何为具体的@KafkaListener方法设置特定的发送超时呢?

通过普通的
@Transactional
进行事务管理的各个方面不会像您所希望的那样与Kafka侦听器一起工作-没有内置的交互

有一章专门介绍如何在事务中使用应用程序事件:使用注释有助于确保事务已成功完成

您可以使用@EventListener注释注册常规事件侦听器。如果需要将其绑定到事务,请使用@TransactionalEventListener。当您这样做时,默认情况下侦听器被绑定到事务的提交阶段


可以这样做,但有点复杂,因为您必须将消耗的偏移量发送到Kafka事务

您可以对容器使用
kafkartransactionmanager
,对
hibernatransactionmanager
使用
@Transactional
,而不是使用
chainedkafkaftransactionmanager

这将给出类似的结果,因为hibernate TX将在Kafka事务之前提交(如果hibernate提交失败,Kafka TX将回滚)

编辑

要在每个侦听器容器中配置不同的chained TM,可以执行以下操作

@组成部分 类ContainerFactoryCustomizer{

ContainerFactoryCustomizer(AbstractKafkaListenerContainerFactory<?, ?, ?> factory,
        ChainedKafkaTransactionManager<?, ?> chainedOne,
        ChainedKafkaTransactionManager<?, ?> chainedTwo) {
    factory.setContainerCustomizer(
            container -> {
                String groupId = container.getContainerProperties().getGroupId();
                if (groupId.equals("foo")) {
                    container.getContainerProperties().setTransactionManager(chainedOne);
                }
                else {
                    container.getContainerProperties().setTransactionManager(chainedTwo);
                }
            });
}

Garry Russell。谢谢。但是,您能给我一些更详细的建议,如何实现每个KafkaListener的自定义发送超时吗?我仍然希望与hibernate发送管理器同步。您说过这是可以做到的,但我不知道如何做到。虽然hibernate有事务超时的概念,但Kafka事务中没有超时的概念最简单的解决方案是让容器只管理Kafka事务,并对Hibernate TM使用
@Transactional
;结果将类似于事务同步。要在容器中使用链式TM,可以将每个容器配置为使用不同的链式TM,其中Hibernate TM配置为different默认超时。请参阅对我的答案的编辑。感谢详细解释。如果我选择第一个选项(不带chained tm),我是否必须手动将消耗的偏移量发送到kafka事务?此选项看起来很有趣,因为我可以使用@Transactional当前(带chained tm)配置hib.tm此注释被完全忽略。我也考虑过定制KafkaListenerContainerManager,但我必须完全重写它,因为用于启动事务的TransactionTemplate是最终的,无法修改:)
>如果我选择第一个选项(不带chained tm)我是否必须手动将消耗的偏移量发送到kafka事务?
。否;容器不知道DB tx,并将发送偏移量(如果DB tx提交)。如果DB提交失败,kafka tx将回滚(回滚后处理器将重新查找分区)。注释不会“完全忽略”,默认传播是
REQUIRED
,拦截器看到存在一个现有事务,因此无需启动另一个事务。只有在容器不是事务性的情况下才需要自己发送偏移量。正确;但对于仅生产者事务,只使用事务同步更简单。
ContainerFactoryCustomizer(AbstractKafkaListenerContainerFactory<?, ?, ?> factory,
        ChainedKafkaTransactionManager<?, ?> chainedOne,
        ChainedKafkaTransactionManager<?, ?> chainedTwo) {
    factory.setContainerCustomizer(
            container -> {
                String groupId = container.getContainerProperties().getGroupId();
                if (groupId.equals("foo")) {
                    container.getContainerProperties().setTransactionManager(chainedOne);
                }
                else {
                    container.getContainerProperties().setTransactionManager(chainedTwo);
                }
            });
}

Where each chained TM has a Hibernate TM with a different default timeout.

The `groupid` is populated from the `@KafkaListener` `id` or `groupId` property.