From f449665e2a7f20f34145f0796d36e1f4e5b44bef Mon Sep 17 00:00:00 2001 From: Dmytro Nosan Date: Mon, 1 Jul 2019 15:12:09 +0300 Subject: [PATCH 1/2] Add support for configuring a Batch-specific DataSource See gh-17375 --- .../batch/BatchAutoConfiguration.java | 6 ++- .../batch/BatchConfigurerConfiguration.java | 14 ++++--- .../autoconfigure/batch/BatchDataSource.java | 39 +++++++++++++++++++ .../batch/BatchAutoConfigurationTests.java | 33 ++++++++++++++++ .../src/main/asciidoc/howto.adoc | 8 +++- 5 files changed, 91 insertions(+), 9 deletions(-) create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchDataSource.java diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfiguration.java index 996715629c0..f6617ed9ef9 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfiguration.java @@ -76,8 +76,10 @@ public class BatchAutoConfiguration { @Bean @ConditionalOnMissingBean @ConditionalOnBean(DataSource.class) - public BatchDataSourceInitializer batchDataSourceInitializer(DataSource dataSource, ResourceLoader resourceLoader) { - return new BatchDataSourceInitializer(dataSource, resourceLoader, this.properties); + public BatchDataSourceInitializer batchDataSourceInitializer(ObjectProvider dataSource, + @BatchDataSource ObjectProvider batchDataSource, ResourceLoader resourceLoader) { + return new BatchDataSourceInitializer(batchDataSource.getIfAvailable(dataSource::getIfAvailable), + resourceLoader, this.properties); } @Bean diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchConfigurerConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchConfigurerConfiguration.java index 36264a8bf24..0cce388339c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchConfigurerConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchConfigurerConfiguration.java @@ -36,6 +36,7 @@ import org.springframework.transaction.PlatformTransactionManager; */ @ConditionalOnClass(PlatformTransactionManager.class) @ConditionalOnMissingBean(BatchConfigurer.class) +@ConditionalOnBean(DataSource.class) @Configuration(proxyBeanMethods = false) class BatchConfigurerConfiguration { @@ -44,9 +45,11 @@ class BatchConfigurerConfiguration { static class JdbcBatchConfiguration { @Bean - BasicBatchConfigurer batchConfigurer(BatchProperties properties, DataSource dataSource, + BasicBatchConfigurer batchConfigurer(BatchProperties properties, ObjectProvider dataSource, + @BatchDataSource ObjectProvider batchDataSource, ObjectProvider transactionManagerCustomizers) { - return new BasicBatchConfigurer(properties, dataSource, transactionManagerCustomizers.getIfAvailable()); + return new BasicBatchConfigurer(properties, batchDataSource.getIfAvailable(dataSource::getIfAvailable), + transactionManagerCustomizers.getIfAvailable()); } } @@ -57,11 +60,12 @@ class BatchConfigurerConfiguration { static class JpaBatchConfiguration { @Bean - JpaBatchConfigurer batchConfigurer(BatchProperties properties, DataSource dataSource, + JpaBatchConfigurer batchConfigurer(BatchProperties properties, ObjectProvider dataSource, + @BatchDataSource ObjectProvider batchDataSource, ObjectProvider transactionManagerCustomizers, EntityManagerFactory entityManagerFactory) { - return new JpaBatchConfigurer(properties, dataSource, transactionManagerCustomizers.getIfAvailable(), - entityManagerFactory); + return new JpaBatchConfigurer(properties, batchDataSource.getIfAvailable(dataSource::getIfAvailable), + transactionManagerCustomizers.getIfAvailable(), entityManagerFactory); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchDataSource.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchDataSource.java new file mode 100644 index 00000000000..cf9d381accc --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchDataSource.java @@ -0,0 +1,39 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.batch; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.beans.factory.annotation.Qualifier; + +/** + * Qualifier annotation to allow a Batch-specific DataSource. + * + * @author Dmytro Nosan + * @since 2.2.0 + */ +@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Qualifier +public @interface BatchDataSource { + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfigurationTests.java index d9ed265e860..aac755cdf4f 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfigurationTests.java @@ -49,10 +49,12 @@ import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfigurati import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; import org.springframework.boot.autoconfigure.orm.jpa.test.City; import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration; +import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.boot.jdbc.DataSourceInitializationMode; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; import org.springframework.jdbc.BadSqlGrammarException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; @@ -233,6 +235,37 @@ class BatchAutoConfigurationTests { }); } + @Test + void testBatchDataSource() { + this.contextRunner.withUserConfiguration(TestConfiguration.class, BatchDataSourceConfiguration.class) + .run((context) -> { + assertThat(context).hasSingleBean(BatchConfigurer.class) + .hasSingleBean(BatchDataSourceInitializer.class).hasBean("batchDataSource"); + DataSource batchDataSource = context.getBean("batchDataSource", DataSource.class); + assertThat(context.getBean(BatchConfigurer.class)).hasFieldOrPropertyWithValue("dataSource", + batchDataSource); + assertThat(context.getBean(BatchDataSourceInitializer.class)) + .hasFieldOrPropertyWithValue("dataSource", batchDataSource); + }); + } + + @Configuration(proxyBeanMethods = false) + protected static class BatchDataSourceConfiguration { + + @Bean + @Primary + public DataSource normalDataSource() { + return DataSourceBuilder.create().url("jdbc:hsqldb:mem:normal").username("sa").build(); + } + + @BatchDataSource + @Bean + public DataSource batchDataSource() { + return DataSourceBuilder.create().url("jdbc:hsqldb:mem:batchdatasource").username("sa").build(); + } + + } + @Configuration(proxyBeanMethods = false) static class EmptyConfiguration { diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/howto.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/howto.adoc index d8dc41015d0..9edf05db8b1 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/howto.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/howto.adoc @@ -2429,8 +2429,12 @@ other factory that your application defines, if any. This section answers questions that arise from using Spring Batch with Spring Boot. -NOTE: By default, batch applications require a `DataSource` to store job details. If you -want to deviate from that, you need to implement `BatchConfigurer`. See +NOTE: By default, batch applications require a `DataSource` to store job details. +Batch autowires a single `DataSource` in your context and +uses that for processing. If you like to use a different `DataSource`, you can create +one and mark it is `@Bean` as `@BatchDataSource`. If you do so and want two data sources, +remember to create another one and mark it as `@Primary`. If you want to deviate from that, + you need to implement `BatchConfigurer`. See {spring-batch-javadoc}/core/configuration/annotation/EnableBatchProcessing.html[The Javadoc of `@EnableBatchProcessing`] for more details. From 072453bf582d48f7f8b350aa71db774f95869120 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 8 Jul 2019 13:14:27 +0100 Subject: [PATCH 2/2] Polish "Add support for configuring a Batch-specific DataSource" See gh-17375 --- .../autoconfigure/batch/BatchAutoConfiguration.java | 6 +++--- .../batch/BatchConfigurerConfiguration.java | 9 ++++----- .../boot/autoconfigure/batch/BatchDataSource.java | 5 ++++- .../spring-boot-docs/src/main/asciidoc/howto.adoc | 12 ++++++------ 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfiguration.java index f6617ed9ef9..7411f3ce197 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfiguration.java @@ -76,10 +76,10 @@ public class BatchAutoConfiguration { @Bean @ConditionalOnMissingBean @ConditionalOnBean(DataSource.class) - public BatchDataSourceInitializer batchDataSourceInitializer(ObjectProvider dataSource, + public BatchDataSourceInitializer batchDataSourceInitializer(DataSource dataSource, @BatchDataSource ObjectProvider batchDataSource, ResourceLoader resourceLoader) { - return new BatchDataSourceInitializer(batchDataSource.getIfAvailable(dataSource::getIfAvailable), - resourceLoader, this.properties); + return new BatchDataSourceInitializer(batchDataSource.getIfAvailable(() -> dataSource), resourceLoader, + this.properties); } @Bean diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchConfigurerConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchConfigurerConfiguration.java index 0cce388339c..415f479a391 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchConfigurerConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchConfigurerConfiguration.java @@ -36,7 +36,6 @@ import org.springframework.transaction.PlatformTransactionManager; */ @ConditionalOnClass(PlatformTransactionManager.class) @ConditionalOnMissingBean(BatchConfigurer.class) -@ConditionalOnBean(DataSource.class) @Configuration(proxyBeanMethods = false) class BatchConfigurerConfiguration { @@ -45,10 +44,10 @@ class BatchConfigurerConfiguration { static class JdbcBatchConfiguration { @Bean - BasicBatchConfigurer batchConfigurer(BatchProperties properties, ObjectProvider dataSource, + BasicBatchConfigurer batchConfigurer(BatchProperties properties, DataSource dataSource, @BatchDataSource ObjectProvider batchDataSource, ObjectProvider transactionManagerCustomizers) { - return new BasicBatchConfigurer(properties, batchDataSource.getIfAvailable(dataSource::getIfAvailable), + return new BasicBatchConfigurer(properties, batchDataSource.getIfAvailable(() -> dataSource), transactionManagerCustomizers.getIfAvailable()); } @@ -60,11 +59,11 @@ class BatchConfigurerConfiguration { static class JpaBatchConfiguration { @Bean - JpaBatchConfigurer batchConfigurer(BatchProperties properties, ObjectProvider dataSource, + JpaBatchConfigurer batchConfigurer(BatchProperties properties, DataSource dataSource, @BatchDataSource ObjectProvider batchDataSource, ObjectProvider transactionManagerCustomizers, EntityManagerFactory entityManagerFactory) { - return new JpaBatchConfigurer(properties, batchDataSource.getIfAvailable(dataSource::getIfAvailable), + return new JpaBatchConfigurer(properties, batchDataSource.getIfAvailable(() -> dataSource), transactionManagerCustomizers.getIfAvailable(), entityManagerFactory); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchDataSource.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchDataSource.java index cf9d381accc..fe12c8fd2d7 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchDataSource.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchDataSource.java @@ -23,9 +23,12 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Primary; /** - * Qualifier annotation to allow a Batch-specific DataSource. + * Qualifier annotation for a DataSource to be injected into Batch auto-configuration. Can + * be used on a secondary data source, if there is another one marked as + * {@link Primary @Primary}. * * @author Dmytro Nosan * @since 2.2.0 diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/howto.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/howto.adoc index 9edf05db8b1..2a39ef62f7d 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/howto.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/howto.adoc @@ -2430,12 +2430,12 @@ other factory that your application defines, if any. This section answers questions that arise from using Spring Batch with Spring Boot. NOTE: By default, batch applications require a `DataSource` to store job details. -Batch autowires a single `DataSource` in your context and -uses that for processing. If you like to use a different `DataSource`, you can create -one and mark it is `@Bean` as `@BatchDataSource`. If you do so and want two data sources, -remember to create another one and mark it as `@Primary`. If you want to deviate from that, - you need to implement `BatchConfigurer`. See -{spring-batch-javadoc}/core/configuration/annotation/EnableBatchProcessing.html[The +Batch autowires a single `DataSource` in your context and uses that for processing. To +have Batch use a `DataSource` other than the application’s main `DataSource`, declare a +`DataSource` bean, annotating its `@Bean` method with `@BatchDataSource`. If you do so and +want two data sources, remember to create another one and mark it as `@Primary`. To take +greater control, implement `BatchConfigurer`. See +{spring-batch-javadoc}/core/configuration/annotation/EnableBatchProcessing.html[the Javadoc of `@EnableBatchProcessing`] for more details. For more about Spring Batch, see the https://projects.spring.io/spring-batch/[Spring Batch