【Bug】定时任务中 Jpa Save 方法失效

【Bug】定时任务中 Jpa Save 方法失效

首先说一下问题,在定时任务中调用 jpa 的 save 方法没有效果,但是通过外界调用,比如 controller 中注入 service 来调用是可以的,真是巨巨巨离谱,我被折磨了好几天。

我这个问题的根源在于多数据源的配置上,所以如果你的项目中没有使用多数据源,那接下来的内容应该对你没什么帮助了~

一般来讲,在配置多数据源的时候,除了要继承 AbstractRoutingDataSource 类

java 复制代码
package com.xsdl.content;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class MultiDataSource extends AbstractRoutingDataSource {

    private static final ThreadLocal<String> keys = new ThreadLocal<String>();

    @Override
    protected Object determineCurrentLookupKey() {
        return keys.get();
    }

    public static void setDataSource(String dataSource) {
        keys.set(dataSource);
    }

    public static String getDatasource() {
        return keys.get();
    }

    public static void clearDataSource() {
        keys.remove();
    }

}

还会配置对应数据源的 datasource、jdbcTemplate 等

java 复制代码
package com.xsdl.config;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Description;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class MultiDataSourceConfig {

    @Bean(name = "dataSourceAAA")
    @Primary
    @ConfigurationProperties("spring.aaa.datasource")
    public DataSource dataSourceAAA() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "dataSourceBBB")
    @ConfigurationProperties("spring.bbb.datasource")
    public DataSource dataSourceBBB() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "multiDataSource")
    public MultiDataSource multipleDataSource(@Qualifier("dataSourceAAA") DataSource dataSourceA,
                                              @Qualifier("dataSourceBBB") DataSource dataSourceB) {
        Map<Object, Object> map = new HashMap<>();
        map.put("dataSourceA", dataSourceA);
        map.put("dataSourceB", dataSourceB);
        MultiDataSource multipleDataSource = new MultiDataSource();
        multipleDataSource.setDefaultTargetDataSource(dataSourceA);
        multipleDataSource.setTargetDataSources(map);
        return multipleDataSource;
    }

    @Bean
    public JdbcTemplate jdbcTemplate(MultiDataSource multipleDataSource) {
        return new JdbcTemplate(multipleDataSource);
    }

    @Bean
    @Primary
    @Description("事务管理器")
    public PlatformTransactionManager transactionManager(@Qualifier("multiDataSource") MultiDataSource multiDataSource) {
        return new DataSourceTransactionManager(multiDataSource);
    }

}

事情到现在是很正常的,启动项目也可以正常启动,正常在 service 的业务逻辑里调用对应的 dao 或者 repository 去保存数据也可以

突然有一天,我接到一个需求,有一个接收记录的表,我需要定时从中查询执行失败的,重试进行补偿,那我补偿完成后,当然要更新回去对吧,于是我写了一个类似下面这样的代码:

java 复制代码
package com.xsdl.job;

import com.xsdl.dao.UserDao;
import com.xsdl.pojo.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.UUID;
import java.util.stream.IntStream;

@Component("tsJob")
@Slf4j
public class TsJob {

    @Autowired
    private UserDao userDao;

    @Scheduled(cron = "0 0/2 * * * ?")
    public void execute() {
        log.warn("定时任务开始执行");
        IntStream.rangeClosed(1, 10).forEach(i -> {
            User user = new User();
            user.setUuid(UUID.randomUUID().toString());
            user.setName("name" + i);
            userDao.save(user);
        });
        log.warn("定时任务执行结束");
    }

}

具体逻辑不用关注,就是查数据库,然后修改了几个字段,重新 save 回去,这时候问题出现了,save 方法执行无效,数据库里数据没更新,我一路 debug,期间各种磕磕绊绊,就是找不到问题,反而找到了几个同病相怜的人:

[在spring的定时任务中使用 jpa 的save 方法失效](https://forum.springdoc.cn/t/topic/776)\](https://forum.springdoc.cn/t/topic/776) [no transaction is in progress](https://developer.aliyun.com/article/1573966) 我一开始以为是事务的开启问题,包括但这样尝试: ```java @Scheduled(cron = "0 0/2 * * * ?") public void execute() { log.warn("定时任务开始执行"); IntStream.rangeClosed(1, 10).forEach(i -> { User user = new User(); user.setUuid(UUID.randomUUID().toString()); user.setName("name" + i); getThis.doSave(user); }); log.warn("定时任务执行结束"); } @Transactional(propagation = Propagation.New) public void doSave(User user) { userDao.save(user); } private TsJob getThis() { return ApplicationContextUtil.getBean("tsJob",TsJob.class); } ``` 然而什么用都没有 最关键的是,这个方法如果在一个 controller 里手动调用是可以执行的,我觉得太离谱了,就想开个新项目复现一下,结果怎么都复现不出来,最后突然觉得区别也就是在多数据源上了,中间各种尝试,直到我把这个代码注释掉,终于可以了\~ ```java @Bean @Primary @Description("事务管理器") public PlatformTransactionManager transactionManager(@Qualifier("multiDataSource") MultiDataSource multiDataSource) { return new DataSourceTransactionManager(multiDataSource); } ``` 如果你的项目里也这样配置了 transactionManager ,可能就是这个问题\~

相关推荐
凤年徐8 分钟前
【数据结构与算法】203.移除链表元素(LeetCode)图文详解
c语言·开发语言·数据结构·算法·leetcode·链表·刷题
不像程序员的程序媛11 分钟前
redis的一些疑问
java·redis·mybatis
知其然亦知其所以然22 分钟前
Java 面试高频题:GC 到底回收了什么、怎么回收、啥时候回收?
java·后端·面试
Z_W_H_23 分钟前
【SpringBoot】 整合MyBatis+Postgresql
java·spring boot·后端
nbsaas-boot30 分钟前
多租户架构下的多线程处理实践指南
java·开发语言·spring
无小道40 分钟前
c++--typedef和#define的用法及区别
c语言·开发语言·汇编·c++
青云交1 小时前
Java 大视界 -- Java 大数据在智能医疗远程手术机器人操作数据记录与分析中的应用(342)
java·大数据·数据记录·远程手术机器人·基层医疗·跨院协作·弱网络适配
知北游天1 小时前
Linux:多线程---同步&&生产者消费者模型
java·linux·网络
SoniaChen331 小时前
Rust基础-part2-变量和可变类型
开发语言·后端·rust
钢铁男儿1 小时前
C#接口实现详解:从理论到实践,掌握面向对象编程的核心技巧
java·前端·c#