Need to configure my JPA layer to use a Transactio

2020-03-08 06:53发布

问题:

I am using Spring Cloud Task + Batch in a project. I plan to use different datasources for business data and Spring audit data on the task. So I configured something like:

   @Bean
   public TaskConfigurer taskConfigurer() {
       return new DefaultTaskConfigurer(this.singletonNotExposedSpringDatasource());
   }

   @Bean
   public BatchConfigurer batchConfigurer() {
       return new DefaultBatchConfigurer(this.singletonNotExposedSpringDatasource());
   }

whereas main datasource is autoconfigured through JpaBaseConfiguration.

The problem comes when SimpleBatchConfiguration+DefaultBatchConfigurer expose a PlatformTransactionManager bean, since JpaBaseConfiguration has a @ConditionalOnMissingBean on PlatformTransactionManager. Therefore Batch's PlatformTransactionManager, binded to the spring.datasource takes place.

So far, this seems to be caused because this bug

So I tried to emulate what JpaBaseConfiguration does, defining my own PlatformTransactionManager over my biz datasource/entityManager.

    @Primary
    @Bean
    public PlatformTransactionManager appTransactionManager(final LocalContainerEntityManagerFactoryBean appEntityManager) {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(appEntityManager.getObject());
        this.appTransactionManager = transactionManager;
        return transactionManager;
    }

Note I have to define it with a name other than transactionManager, otherwise Spring finds 2 beans and complains (unregardless of @Primary!)

But now it comes the funny part. When running the tests, everything runs smooth, tests finish and DDLs are properly created for both business and Batch/Task's databases, database reads work flawlessly, but business data is not persisted in my testing database, so final assertThats fail when counting. If I @Autowire in my test PlatformTransactionManager or ÈntityManager, everything indicates they are the proper ones. But if I debug within entityRepository.save, and execute org.springframework.transaction.interceptor.TransactionAspectSupport.currentTransactionStatus(), it seems the DatasourceTransactionManager from Batch's configuration is overriding, so my custom exposed PlatformTransactionManager is not being used.

So I guess it is not a problem of my PlatformManager being the primary, but that something is configuring my JPA layer TransactionInterceptor to use the non primary but transactionManager named bean of Batch.

I also tried with making my @Configuration implement TransactionManagementConfigurer and override PlatformTransactionManager annotationDrivenTransactionManager() but still no luck

Thus, I guess what I am asking is whether there is a way to configure the primary TransactionManager for the JPA Layer.

回答1:

The problem comes when SimpleBatchConfiguration+DefaultBatchConfigurer expose a PlatformTransactionManager bean,

As you mentioned, this is indeed what was reported in BATCH-2788. The solution we are exploring is to expose the transaction manager bean only if Spring Batch creates it.

In the meantime you can set the property spring.main.allow-bean-definition-overriding=true to allow bean definition overriding and set the transaction manager you want Spring Batch to use with BatchConfigurer#getTransactionManager. In your case, it would be something like:

@Bean
public BatchConfigurer batchConfigurer() {
    return new DefaultBatchConfigurer(this.singletonNotExposedSpringDatasource()) {
        @Override
        public PlatformTransactionManager getTransactionManager() {
            return new MyTransactionManager();
        }
    };
}

Hope this helps.