简体   繁体   English

在 Spring 批处理小任务中使用多个数据源

[英]Using Multiple DataSource in Spring Batch Tasklet

I am new to spring batch, and I'm encountering an issue when using multiple data source in my batch.我是 spring 批处理的新手,在批处理中使用多个数据源时遇到问题。

Let me explain.让我解释。

I am using 2 databases in my server with Spring Boot.我正在使用 Spring 引导的服务器中的 2 个数据库。

So far everything worked fine with my implementation of RoutingDataSource.到目前为止,我的 RoutingDataSource 实现一切正常。

@Component("dataSource")
public class RoutingDataSource extends AbstractRoutingDataSource {

  @Autowired
  @Qualifier("datasourceA")
  DataSource datasourceA;

  @Autowired
  @Qualifier("datasourceB")
  DataSource datasourceB;

  @PostConstruct
  public void init() {
    setDefaultTargetDataSource(datasourceA);
    final Map<Object, Object> map = new HashMap<>();
    map.put(Database.A, datasourceA);
    map.put(Database.B, datasourceB);
    setTargetDataSources(map);
  }

  
  @Override
  protected Object determineCurrentLookupKey() {
    return DatabaseContextHolder.getDatabase();
  }
}

The implementation require a DatabaseContextHolder, here it is:该实现需要一个DatabaseContextHolder,这里是:

public class DatabaseContextHolder {
    private static final ThreadLocal<Database> contextHolder = new ThreadLocal<>();

    public static void setDatabase(final Database dbConnection) {
        contextHolder.set(dbConnection);
    }

    public static Database getDatabase() {
        return contextHolder.get();
    }
}

When I received a request on my server, I have a basic interceptor that sets the current database based on some input I have in the request.当我在我的服务器上收到一个请求时,我有一个基本的拦截器,它根据我在请求中的一些输入设置当前数据库。 with the method DatabaseContextHolder.setDatabase(db);使用方法DatabaseContextHolder.setDatabase(db); Everything works fine with my actual controllers.我的实际控制器一切正常。

It gets more complicated when I try to run a job with one tasklet.当我尝试使用一个 tasklet 运行作业时,情况会变得更加复杂。

One of my controller start an async task like this.我的 controller 之一启动了这样的异步任务。

@GetMapping("/batch")
public void startBatch() {
  return jobLauncher.run("myJob", new JobParameters());
}

@EnableBatchProcessing
@Configuration
public class MyBatch extends DefaultBatchConfigurer {


  @Autowired private JobBuilderFactory jobs;

  @Autowired private StepBuilderFactory steps;

  @Autowired private MyTasklet tasklet;

  @Bean
  public Job job(Step step) {
    return jobs.get("myJob").start(step).build();
  }

  @Bean
  protected Step registeredDeliveryTask() {
    return steps.get("myTask").tasklet(tasklet).build();
  }

  /** Overring the joblauncher get method to make it asynchornous */
  @Override
  public JobLauncher getJobLauncher() {
    try {
      SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
      jobLauncher.setJobRepository(super.getJobRepository());
      jobLauncher.setTaskExecutor(new SimpleAsyncTaskExecutor());
      jobLauncher.afterPropertiesSet();
      return jobLauncher;
    } catch (Exception e) {
      throw new BatchConfigurationException(e);
    }
  }
}

And my Tasklet:还有我的小任务:

@Component
public class MyTasklet implements Tasklet {

  @Autowired
  private UserRepository repository;

  @Override
  public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext)throws Exception {

  //Do stuff with the repository.

  }

But the RoutingDataSource doesn't work, even if I set my Context before starting the job.但是 RoutingDataSource 不起作用,即使我在开始工作之前设置了我的上下文。 For example if I set my database to B, the repo will work on database A. It is always the default datasource that is selected.例如,如果我将数据库设置为 B,则 repo 将在数据库 A 上运行。它始终是选择的默认数据源。 (because of this line setDefaultTargetDataSource(datasourceA); ) (因为这一行setDefaultTargetDataSource(datasourceA);

I tried to set the database, by passing the value in the parameters, inside the tasklet, but still got the same issue.我尝试通过在 tasklet 内传递参数中的值来设置数据库,但仍然遇到同样的问题。

@GetMapping("/batch")
public void startBatch() {
  Map<String, JobParameter> parameters = new HashMap<>();
  parameters.put("database", new JobParameter(DatabaseContextHolder.getCircaDatabase().toString()));
  return jobLauncher.run("myJob", new JobParameters(parameters));
}
  @Override
  public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext)throws Exception {

    String database =
        chunkContext.getStepContext().getStepExecution().getJobParameters().getString("database");
    DatabaseContextHolder.setDatabase(Database.valueOf(database));
  //Do stuff with the repository.

  }

I feel like the problem is because the database was set in a different thread, because my job is asynchronous.我觉得问题是因为数据库设置在不同的线程中,因为我的工作是异步的。 So it cannot fetch the database set before launching the job.因此它无法在启动作业之前获取数据库集。 But I couldn't find any solution so far.但到目前为止我找不到任何解决方案。

Regards问候

Your routing datasource is being used for Spring Batch's meta-data, which means the job repository will interact with a different database depending on the thread processing the request.您的路由数据源正用于 Spring Batch 的元数据,这意味着作业存储库将根据处理请求的线程与不同的数据库进行交互。 This is not needed for batch jobs.这对于批处理作业是不需要的。 You need to configure Spring Batch to work with a fixed data source.您需要配置 Spring 批处理以使用固定数据源。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM