Alberto Favaro
Alberto Favaro

Reputation: 1842

Set-up the same TransactionManager on Batch repository and Task repository with Spring Boot auto-config

I've been working for a few days on a Spring Batch (4.3.10) on Spring Cloud Task (2.4.6) all via Spring Boot (2.7.18). I'm going crazy trying to figure out how to configure my application and specifically how auto-configurations works... What I don't understand is how to properly configure the same TransactionManager in JOB+TASK repositories. I currently have this configurations (in summary):

DataSourceConfiguration

@Configuration
public class DataSourceConfiguration {

  @BatchDataSource
  @Bean
  @ConfigurationProperties("batchandtask.datasource")
  public DataSource batchDataSource() {
    return batchDataSourceProperties().initializeDataSourceBuilder().build();
  }

  @Bean
  public DataSourceProperties batchDataSourceProperties() {
    return new DataSourceProperties();
  }

  @Bean
  public TaskConfigurer taskConfigurer() {
    return new DefaultTaskConfigurer(batchDataSource());
  }
  ...others DataSource configs

Some notes on this configuration:

JobConfiguration

@Configuration
@EnableBatchProcessing
@EnableTask
public class JobConfiguration {

private final JobBuilderFactory jobBuilderFactory;

private final StepBuilderFactory stepBuilderFactory;

public JobConfiguration(JobBuilderFactory jobBuilderFactory,
    StepBuilderFactory stepBuilderFactory) {
  this.jobBuilderFactory = jobBuilderFactory;
  this.stepBuilderFactory = stepBuilderFactory;
}
... Job and Steps beans

Auto-configurations

From what I saw in the code, the auto-configurations of the task occur before those of the batch and specifically this bean is created during this auto-config:

public class SimpleTaskAutoConfiguration {
...
@Bean
public PlatformTransactionManager springCloudTaskTransactionManager() {
    return this.platformTransactionManager;
}

So now I have a TransactionManager type bean named "springCloudTaskTransactionManager" in the context which is responsible for managing the transactions of the TASK repository. Now I want to configure the same transaction manager for my JOB repository but here I didn't understand how the auto-configurations work. Specifically, I identified that for Spring Batch (unlike Spring Task ) there are two configurators (BatchConfigurer) implementation:

  1. BasicBatchConfigurer, that came from org.springframework.boot:spring-boot-autoconfigure:2.7.18 (Spring Boot). Is the one that is actually picked-up in my application and is instantiated as a bean via the BatchAutoConfiguration auto-config condition @ConditionalOnMissingBean(BatchConfigurer.class). This configurator creates his own TransactionManager that is different from the "springCloudTaskTransactionManager"
  2. DefaultBatchConfigurer, that came from org.springframework.batch:spring-batch-core:4.3.10 (Spring Batch). This is annotated as @Component but is ignored during the 1. auto-config (i don't understand why) and it is directly instantiated in the absence of a bean of type BatchConfigurer.class by the @EnableBatchProcessing annotation. Also in this case he creates his own TransactionManager.

In both cases it is through @EnableBatchProcessing -> SimpleBatchConfiguration which the TransactionManager is exposed in the context through this set-up:

@Bean
public PlatformTransactionManager transactionManager() throws Exception {
    return createLazyProxy(transactionManager, PlatformTransactionManager.class);
}

where transactionManager it is respectively the one created by one of the two configurators and is qualified as a generic "transactionManager" (problems in sight with other datasource/transactions Spring Boot auto-configurations?)

So after all this magic we have two transactionManagers the one qualified as "springCloudTaskTransactionManager" and the one as "transactionManager".

Questions

I would like to ask for help in understanding some things:

  1. Why do we have two "basic" configurators and why is only the first one (the one from Spring Boot) seen by @EnableBatchProcessing? That there is nothing wrong but why?
  2. Could @EnableBatchProcessing have some unwanted behavior with Spring Boot, in particular regarding the transactionManager?
  3. How do I configure the same TransactionManager for the TASK repository and the JOB repository?

Upvotes: 1

Views: 703

Answers (1)

Henning
Henning

Reputation: 3889

Introduction

Using Spring Cloud Task together with Spring Boot and Spring Batch is probably the most common use case for Spring Cloud Task. But it is not the only supported one.

For example, you can also use just Spring Batch (with or without Spring Boot) or you can use Spring Cloud Task with Spring Boot but without Spring Batch. Each of these possibilities comes with its own bootstrapping facilities, and there are multiple ways how to define the transaction management.

This can indeed be confusing and that's basically why a lot of this has been revamped for Spring Boot 3. But it's all doable with Spring Boot 2.

BatchConfigurer

The annotation @Component on DefaultBatchConfigurer doesn't do anything in your case. You should ignore it. @Component marks classes for auto-scanning but Spring Boot will only scan the packages below your main application class (unless you specify something else). By default, Spring Boot will not auto-scan any dependencies like Spring Batch for bean definitions.

The class SimpleBatchConfiguration is imported by using @EnableBatchProcessing. It's method initialize determines the BatchConfigurer that will be used: If the application context contains a BatchConfigurer, then it will be used. Otherwise, a DefaultBatchConfigurer will be instantiated.

The BasicBatchConfigurer that you see being used is added to the application context by Spring Boot's BatchConfigurerConfiguration. This configuration class will expose one of two BatchConfigurers depending on whether JPA is on the classpath or not. But BatchConfigurerConfiguration is annotated with @ConditionalOnMissingBean(BatchConfigurer.class), which means that it will back off (and not do anything) if you expose your own BatchConfigurer.

Solution

Short answer

You need to implement and expose your own BatchConfigurer and TaskConfigurer, which set the DataSource and TransactionManager as you need.

Long answer

You'll need your own implementation of BatchConfigurer and expose it as a bean. It will then be used to set up Spring Batch because Spring Boot's BatchConfigurerConfiguration will back off.

You can extend either BasicBatchConfigurer or DefaultBatchConfigurer, set the DataSource that you want, and override the method getTransactionManager to return the transaction manager that you want to use.

With Spring Cloud Task 2.x, you can really only use a transaction manager that is called springCloudTaskTransactionManager as it is referenced by name. You have two options:

You can use DefaultTaskConfigurer to set the DataSource that you want Spring Cloud to use, let it build springCloudTaskTransactionManager, and then inject this transaction manager into your custom BatchConfigurer.

Or you can implement your own TaskConfigurer (e.g. by extending DefaultTaskConfigurer) to return the transaction manager that you want in getTransactionManager.

Upvotes: 2

Related Questions