前言

有个老项目的部分模块采用了jpa做持久化框架,由于以前测试不到位,最近发现许多定时任务都没正确执行,但是直接调用接口并不会报错,唯独定时任务执行的时候会抛错,如下:

org.springframework.dao.InvalidDataAccessApiUsageException: Executing an update/delete query; nested exception is javax.persistence.TransactionRequiredException: Executing an update/delete query

排查过程

看日志是事务方面的问题,由于jpa要求执行更新/删除时,必须支持事务,即需要加上 @Transactional 注解,当然这种我已经加上了,所以直接调用并不会报错,猜测可能是事务管理器的问题,首先在启动类上加上 @EnableTransactionManagement 注解,然后手动添加jpa的事务管理器:

    @Bean(name = "transactionManager")
    public PlatformTransactionManager configurationTm(EntityManagerFactory managerFactory){
        return new JpaTransactionManager(managerFactory);
    }

重启后发现并没什么用,而且经过测试事务确实是生效的,只是在执行sql时会报错,然后今天早上突然想到由于该项目比较复杂,既用了jpa,也用了mybatis,会不会是这jpa事务管理器没成功创建,然后打印了一下当前的事务管理器:

@Bean
public Object testBean(PlatformTransactionManager platformTransactionManager){
    System.out.println("看看当前事务管理器是谁:" + platformTransactionManager.getClass().getName());
    return new Object();
}

惊喜的发现生效的是默认的jdbc事务管理器,即

org.springframework.jdbc.datasource.DataSourceTransactionManager

说明jpa的事务管理器并没生效,现在就好办了,调整代码如下:

    @Primary
    @Bean(name = "jpaTransactionManager")
    public PlatformTransactionManager configurationTm(EntityManagerFactory managerFactory){
        return new JpaTransactionManager(managerFactory);
    }

由于该项目依赖的某个底层包中默认也初始化了 DataSourceTransactionManager ,因此需要加上 @Primary 注解,此时再次查看当前项目所用的事务管理器:

org.springframework.orm.jpa.JpaTransactionManager

最后在定时任务的方法上加上指定的事务管理器:

@Transactional(value = "jpaTransactionManager")

运行发现一切都恢复正常了~

最后扯一句,如果你的项目添加了 spring-boot-starter-data-jpa 依赖,那么框架会默认注入 JpaTransactionManager 实例,或者说你的项目是纯jpa,那么解决方式只会更简单~

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐