spring-kafka导致spring事务失效的解决办法

spring-kafka导致spring事务失效的解决办法

spring-boot版本:2.1.16.RELEASE

spring-kafka版本:2.2.14.RELEASE

数据库:mybatis-plus + oracle

发现问题

一个spring-boot工程,已经通过配置spring.kafka.producer.transactionIdPrefix开启KafkaTemplate的executeInTransaction事务功能。因为业务上需要发送kafka消息后同时更新数据库记录状态,结果发现单元测试跑完后数据库并不会回滚。

调试过程

  1. 测试类已经使用
@SpringBootTest
@RunWith(SpringRunner.class)
@Transactional

进行标注,@Rollback标注在类上或者测试方法上均无效。

  1. 数据库操作的service方法已经标注@Transactional,在此方法内下断点,通过Evaluate Expression TransactionSynchronizationManager#isSynchronizationActive()发现结果是false
  2. 定位到TransactionAspectSupport#determineTransactionManager方法,发现此方法返回了一个类型为KafkaTransactionManager的对象,至此问题初步定位为spring没有拿到正确的transcationManager

源码分析

查看KafkaAutoConfiguration发现注册了KafkaTransactionManager对象,这个类还继承了AbstractPlatformTransactionManager

因为项目使用的mybaits-plus,并配置了spring.datasource,没有使用hibernate。打开DataSourceTransactionManagerAutoConfiguration 发现默认的transactionManager注册条件是没有PlatformTransactionManager,而KafkaTransactionManager又正好是这个类型,所以这里根本不会注册。在注册transactionManager的方法里面下断点,果然不会进去。

Google搜索

搜索一下类似的问题,找到这个帖子https://stackoverflow.com/questions/47354521/transaction-synchronization-in-spring-kafka

解决问题

需要增加一个Configuration,

  1. 是要解决DataSourceTransactionManagerAutoConfiguration默认不注册transactionManager的问题
  2. 是让transactionManager变成默认的事务管理器,免得在所有@Transactional指明事务管理器名称
  3. 是要增加一个chainedTransactionManager使得kafka和数据库操作同时在事务中
@Configuration
public class TransactionConfig {
    private final DataSource dataSource;

    private final TransactionManagerCustomizers transactionManagerCustomizers;

    TransactionConfig(DataSource dataSource,
                                              ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
        this.dataSource = dataSource;
        this.transactionManagerCustomizers = transactionManagerCustomizers.getIfAvailable();
    }

    @Bean //去掉了ConditionalOnMissingBean解决问题1
    @Primary  //解决问题2
    public DataSourceTransactionManager transactionManager(DataSourceProperties properties) {
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(this.dataSource);
        if (this.transactionManagerCustomizers != null) {
            this.transactionManagerCustomizers.customize(transactionManager);
        }
        return transactionManager;
    }

    @Bean  //解决问题3
    public ChainedKafkaTransactionManager chainedKafkaTransactionManager(DataSourceTransactionManager transactionManager,
                                                               KafkaTransactionManager<?, ?> kafkaTransactionManager){
        return new ChainedKafkaTransactionManager<>(transactionManager, kafkaTransactionManager);
    }
}

最后修改单元测试类(junit4)上的注解为:

@SpringBootTest
@RunWith(SpringRunner.class)
@Transactional(transactionManager = "chainedKafkaTransactionManager")

问题解决

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值