Я новичок в Spring Batch, и у меня возникает проблема при использовании нескольких источников данных в моем пакете.
Позвольте мне объяснить.
Я использую 2 базы данных на моем сервере с Spring Boot.
До сих пор все работало нормально с моей реализацией 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();
}
}
Для реализации требуется 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();
}
}
Когда Я получил запрос на свой сервер, у меня есть перехватчик basi c, который устанавливает текущую базу данных на основе некоторых входных данных, которые я имею в запросе. с помощью метода DatabaseContextHolder.setDatabase(db);
Все отлично работает с моими настоящими контроллерами.
Все становится сложнее, когда я пытаюсь запустить задание с одним тасклетом.
Один из моих контроллеров запускает асинхронный режим c задача вроде этой.
@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);
}
}
}
И мой тасклет:
@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.
}
Но RoutingDataSource не работает, даже если я установил свой контекст перед запуском работы. Например, если я установил для своей базы данных значение B, репозиторий будет работать с базой данных A. Всегда выбирается источник данных по умолчанию. (из-за этой строки setDefaultTargetDataSource(datasourceA);
)
Я попытался установить базу данных, передав значение в параметрах внутри тасклета, но все равно получил ту же проблему.
@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.
}
Мне кажется, проблема в том, что база данных была установлена в другом потоке, потому что моя работа асинхронная. Таким образом, он не может получить набор базы данных перед запуском задания. Но пока найти решение не удалось.
С уважением