From 6029b47592d7600892053f07e07e3e87c7930f4a Mon Sep 17 00:00:00 2001 From: Glenn Renfro Date: Wed, 15 Jun 2022 07:50:14 -0400 Subject: [PATCH] Users can specify different datasources for the jdbc-item reader and or jdbc-item writer Users can specify a unique data source for the JdbcCursorItemReader and or writer instead of using the default datasource. If a jdbc-item-reader or jdbc-item-writer data source is not specified the default will be used by the reader or writer. Updated docs Updated readme docs --- docs/src/main/asciidoc/batch-starter.adoc | 66 +++++++++++++++++ .../pom.xml | 4 ++ ...SingleStepDataSourceAutoConfiguration.java | 63 +++++++++++++++++ .../JdbcBatchItemWriterAutoConfiguration.java | 39 ++++++++++- ...JdbcCursorItemReaderAutoConfiguration.java | 41 ++++++++++- ...BatchItemWriterAutoConfigurationTests.java | 67 +++++++++++++++--- ...ursorItemReaderAutoConfigurationTests.java | 70 ++++++++++++++++--- .../single-step-batch-job/README.adoc | 18 ++++- 8 files changed, 343 insertions(+), 25 deletions(-) create mode 100644 spring-cloud-starter-single-step-batch-job/src/main/java/org/springframework/cloud/task/batch/autoconfigure/jdbc/JDBCSingleStepDataSourceAutoConfiguration.java diff --git a/docs/src/main/asciidoc/batch-starter.adoc b/docs/src/main/asciidoc/batch-starter.adoc index 570d9c53..b6524c31 100644 --- a/docs/src/main/asciidoc/batch-starter.adoc +++ b/docs/src/main/asciidoc/batch-starter.adoc @@ -287,6 +287,39 @@ can also use the following properties to configure a `JdbcCursorItemReader`: | SQL query from which to read. |=== +You can also specify JDBC DataSource specifically for the reader by using the following properties: +.`JdbcCursorItemReader` Properties +|=== +| Property | Type | Default Value | Description + +| `spring.batch.job.jdbccursoritemreader.datasource.enable` +| `boolean` +| `false` +| Determines whether `JdbcCursorItemReader` `DataSource` should be enabled. + +| `jdbccursoritemreader.datasource.url` +| `String` +| `null` +| JDBC URL of the database. + +| `jdbccursoritemreader.datasource.username` +| `String` +| `null` +| Login username of the database. + +| `jdbccursoritemreader.datasource.password` +| `String` +| `null` +| Login password of the database. + +| `jdbccursoritemreader.datasource.driver-class-name` +| `String` +| `null` +| Fully qualified name of the JDBC driver. +|=== + +NOTE: The default `DataSource` will be used by the `JDBCCursorItemReader` if the `jdbccursoritemreader_datasource` is not specified. + See the https://docs.spring.io/spring-batch/docs/4.3.x/api/org/springframework/batch/item/database/JdbcCursorItemReader.html[`JdbcCursorItemReader` documentation]. [[kafkaItemReader]] @@ -505,6 +538,39 @@ configuration options by setting the following properties: | Whether to verify that every insert results in the update of at least one record. |=== +You can also specify JDBC DataSource specifically for the writer by using the following properties: +.`JdbcBatchItemWriter` Properties +|=== +| Property | Type | Default Value | Description + +| `spring.batch.job.jdbcbatchitemwriter.datasource.enable` +| `boolean` +| `false` +| Determines whether `JdbcCursorItemReader` `DataSource` should be enabled. + +| `jdbcbatchitemwriter.datasource.url` +| `String` +| `null` +| JDBC URL of the database. + +| `jdbcbatchitemwriter.datasource.username` +| `String` +| `null` +| Login username of the database. + +| `jdbcbatchitemwriter.datasource.password` +| `String` +| `null` +| Login password of the database. + +| `jdbcbatchitemreader.datasource.driver-class-name` +| `String` +| `null` +| Fully qualified name of the JDBC driver. +|=== + +NOTE: The default `DataSource` will be used by the `JdbcBatchItemWriter` if the `jdbcbatchitemwriter_datasource` is not specified. + See the https://docs.spring.io/spring-batch/docs/4.3.x/api/org/springframework/batch/item/database/JdbcBatchItemWriter.html[`JdbcBatchItemWriter` documentation]. [[kafkaitemwriter]] diff --git a/spring-cloud-starter-single-step-batch-job/pom.xml b/spring-cloud-starter-single-step-batch-job/pom.xml index 535237c0..9d2a0205 100644 --- a/spring-cloud-starter-single-step-batch-job/pom.xml +++ b/spring-cloud-starter-single-step-batch-job/pom.xml @@ -17,6 +17,10 @@ + + org.springframework.cloud + spring-cloud-task-core + org.springframework.boot spring-boot-starter-batch diff --git a/spring-cloud-starter-single-step-batch-job/src/main/java/org/springframework/cloud/task/batch/autoconfigure/jdbc/JDBCSingleStepDataSourceAutoConfiguration.java b/spring-cloud-starter-single-step-batch-job/src/main/java/org/springframework/cloud/task/batch/autoconfigure/jdbc/JDBCSingleStepDataSourceAutoConfiguration.java new file mode 100644 index 00000000..fa259d2c --- /dev/null +++ b/spring-cloud-starter-single-step-batch-job/src/main/java/org/springframework/cloud/task/batch/autoconfigure/jdbc/JDBCSingleStepDataSourceAutoConfiguration.java @@ -0,0 +1,63 @@ +/* + * Copyright 2022-2022 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.cloud.task.batch.autoconfigure.jdbc; + +import javax.sql.DataSource; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cloud.task.configuration.DefaultTaskConfigurer; +import org.springframework.cloud.task.configuration.TaskConfigurer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; + +/** + * Establishes the default {@link DataSource} for the Task when creating a {@link DataSource} + * for {@link org.springframework.batch.item.database.JdbcCursorItemReader} + * or {@link org.springframework.batch.item.database.JdbcBatchItemWriter}. + * + * @author Glenn Renfro + * @since 3.0 + */ +class JDBCSingleStepDataSourceAutoConfiguration { + + @ConditionalOnMissingBean + @Bean + public TaskConfigurer myTaskConfigurer(DataSource dataSource) { + return new DefaultTaskConfigurer(dataSource); + } + + @ConditionalOnProperty(prefix = "spring.batch.job.jdbcsinglestep.datasource", name = "enable", havingValue = "true", matchIfMissing = true) + @ConditionalOnMissingBean(name = "springDataSourceProperties") + @Bean(name = "springDataSourceProperties") + @ConfigurationProperties("spring.datasource") + @Primary + public DataSourceProperties springDataSourceProperties() { + return new DataSourceProperties(); + } + + @ConditionalOnProperty(prefix = "spring.batch.job.jdbcsinglestep.datasource", name = "enable", havingValue = "true", matchIfMissing = true) + @Bean(name = "springDataSource") + @Primary + public DataSource dataSource(@Qualifier("springDataSourceProperties")DataSourceProperties springDataSourceProperties) { + DataSource dataSource = springDataSourceProperties.initializeDataSourceBuilder().build(); + return dataSource; + } +} diff --git a/spring-cloud-starter-single-step-batch-job/src/main/java/org/springframework/cloud/task/batch/autoconfigure/jdbc/JdbcBatchItemWriterAutoConfiguration.java b/spring-cloud-starter-single-step-batch-job/src/main/java/org/springframework/cloud/task/batch/autoconfigure/jdbc/JdbcBatchItemWriterAutoConfiguration.java index 110a4e33..cbb53318 100644 --- a/spring-cloud-starter-single-step-batch-job/src/main/java/org/springframework/cloud/task/batch/autoconfigure/jdbc/JdbcBatchItemWriterAutoConfiguration.java +++ b/spring-cloud-starter-single-step-batch-job/src/main/java/org/springframework/cloud/task/batch/autoconfigure/jdbc/JdbcBatchItemWriterAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2020 the original author or authors. + * Copyright 2020-2022 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. @@ -20,18 +20,26 @@ import java.util.Map; import javax.sql.DataSource; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + import org.springframework.batch.item.database.ItemPreparedStatementSetter; import org.springframework.batch.item.database.ItemSqlParameterSourceProvider; import org.springframework.batch.item.database.JdbcBatchItemWriter; import org.springframework.batch.item.database.builder.JdbcBatchItemWriterBuilder; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; +import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; /** * Autconfiguration for a {@code JdbcBatchItemWriter}. @@ -43,14 +51,21 @@ import org.springframework.context.annotation.Configuration; @Configuration(proxyBeanMethods = false) @EnableConfigurationProperties(JdbcBatchItemWriterProperties.class) @AutoConfigureAfter(BatchAutoConfiguration.class) +@Import(JDBCSingleStepDataSourceAutoConfiguration.class) public class JdbcBatchItemWriterAutoConfiguration { + private static final Log logger = LogFactory + .getLog(JdbcBatchItemWriterAutoConfiguration.class); + @Autowired(required = false) private ItemPreparedStatementSetter itemPreparedStatementSetter; @Autowired(required = false) private ItemSqlParameterSourceProvider itemSqlParameterSourceProvider; + @Autowired + ApplicationContext applicationContext; + private JdbcBatchItemWriterProperties properties; private DataSource dataSource; @@ -65,9 +80,16 @@ public class JdbcBatchItemWriterAutoConfiguration { @ConditionalOnMissingBean @ConditionalOnProperty(prefix = "spring.batch.job.jdbcbatchitemwriter", name = "name") public JdbcBatchItemWriter> itemWriter() { + DataSource writerDataSource = this.dataSource; + try { + writerDataSource = this.applicationContext.getBean("jdbcBatchItemWriterSpringDataSource", DataSource.class); + } + catch (Exception ex) { + logger.info("Using Default Data Source for the JdbcBatchItemWriter"); + } JdbcBatchItemWriterBuilder> jdbcBatchItemWriterBuilder = new JdbcBatchItemWriterBuilder>() - .dataSource(this.dataSource).sql(this.properties.getSql()); + .dataSource(writerDataSource).sql(this.properties.getSql()); if (this.itemPreparedStatementSetter != null) { jdbcBatchItemWriterBuilder .itemPreparedStatementSetter(this.itemPreparedStatementSetter); @@ -83,4 +105,17 @@ public class JdbcBatchItemWriterAutoConfiguration { return jdbcBatchItemWriterBuilder.build(); } + @ConditionalOnProperty(prefix = "spring.batch.job.jdbcbatchitemwriter.datasource", name = "enable", havingValue = "true") + @Bean(name = "jdbcBatchItemWriterDataSourceProperties") + @ConfigurationProperties("jdbcbatchitemwriter.datasource") + public DataSourceProperties jdbcBatchItemWriterDataSourceProperties() { + return new DataSourceProperties(); + } + + @ConditionalOnProperty(prefix = "spring.batch.job.jdbcbatchitemwriter.datasource", name = "enable", havingValue = "true") + @Bean(name = "jdbcBatchItemWriterSpringDataSource") + public DataSource writerDataSource(@Qualifier("jdbcBatchItemWriterDataSourceProperties") DataSourceProperties writerDataSourceProperties) { + DataSource result = writerDataSourceProperties.initializeDataSourceBuilder().build(); + return result; + } } diff --git a/spring-cloud-starter-single-step-batch-job/src/main/java/org/springframework/cloud/task/batch/autoconfigure/jdbc/JdbcCursorItemReaderAutoConfiguration.java b/spring-cloud-starter-single-step-batch-job/src/main/java/org/springframework/cloud/task/batch/autoconfigure/jdbc/JdbcCursorItemReaderAutoConfiguration.java index 27fdd0e8..fa5c3eb6 100644 --- a/spring-cloud-starter-single-step-batch-job/src/main/java/org/springframework/cloud/task/batch/autoconfigure/jdbc/JdbcCursorItemReaderAutoConfiguration.java +++ b/spring-cloud-starter-single-step-batch-job/src/main/java/org/springframework/cloud/task/batch/autoconfigure/jdbc/JdbcCursorItemReaderAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 the original author or authors. + * Copyright 2020-2022 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. @@ -23,16 +23,24 @@ import java.util.Map; import javax.sql.DataSource; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + import org.springframework.batch.item.database.JdbcCursorItemReader; import org.springframework.batch.item.database.builder.JdbcCursorItemReaderBuilder; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; +import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import org.springframework.jdbc.core.PreparedStatementSetter; import org.springframework.jdbc.core.RowMapper; @@ -45,8 +53,15 @@ import org.springframework.jdbc.core.RowMapper; @EnableConfigurationProperties(JdbcCursorItemReaderProperties.class) @AutoConfigureAfter(BatchAutoConfiguration.class) @ConditionalOnProperty(prefix = "spring.batch.job.jdbccursoritemreader", name = "name") +@Import(JDBCSingleStepDataSourceAutoConfiguration.class) public class JdbcCursorItemReaderAutoConfiguration { + private static final Log logger = LogFactory + .getLog(JdbcCursorItemReaderAutoConfiguration.class); + + @Autowired + ApplicationContext applicationContext; + private final JdbcCursorItemReaderProperties properties; private final DataSource dataSource; @@ -61,10 +76,18 @@ public class JdbcCursorItemReaderAutoConfiguration { @ConditionalOnMissingBean public JdbcCursorItemReader> itemReader(@Autowired(required = false) RowMapper> rowMapper, @Autowired(required = false) PreparedStatementSetter preparedStatementSetter) { + DataSource readerDataSource = this.dataSource; + try { + readerDataSource = this.applicationContext.getBean("jdbcCursorItemReaderSpringDataSource", DataSource.class); + } + catch (Exception e) { + logger.info("Using Default Data Source for the JdbcCursorItemReader"); + + } return new JdbcCursorItemReaderBuilder>() .name(this.properties.getName()) .currentItemCount(this.properties.getCurrentItemCount()) - .dataSource(this.dataSource) + .dataSource(readerDataSource) .driverSupportsAbsolute(this.properties.isDriverSupportsAbsolute()) .fetchSize(this.properties.getFetchSize()) .ignoreWarnings(this.properties.isIgnoreWarnings()) @@ -86,6 +109,20 @@ public class JdbcCursorItemReaderAutoConfiguration { return new MapRowMapper(); } + @ConditionalOnProperty(prefix = "spring.batch.job.jdbccursoritemreader.datasource", name = "enable", havingValue = "true") + @Bean(name = "jdbcCursorItemReaderDataSourceProperties") + @ConfigurationProperties("jdbccursoritemreader.datasource") + public DataSourceProperties jdbcCursorItemReaderDataSourceProperties() { + return new DataSourceProperties(); + } + + @ConditionalOnProperty(prefix = "spring.batch.job.jdbccursoritemreader.datasource", name = "enable", havingValue = "true") + @Bean(name = "jdbcCursorItemReaderSpringDataSource") + public DataSource readerDataSource(@Qualifier("jdbcCursorItemReaderDataSourceProperties")DataSourceProperties readerDataSourceProperties) { + DataSource result = readerDataSourceProperties.initializeDataSourceBuilder().build(); + return result; + } + public static class MapRowMapper implements RowMapper> { @Override diff --git a/spring-cloud-starter-single-step-batch-job/src/test/java/org/springframework/cloud/task/batch/autoconfigure/jdbc/JdbcBatchItemWriterAutoConfigurationTests.java b/spring-cloud-starter-single-step-batch-job/src/test/java/org/springframework/cloud/task/batch/autoconfigure/jdbc/JdbcBatchItemWriterAutoConfigurationTests.java index 33084709..3ca2ec5d 100644 --- a/spring-cloud-starter-single-step-batch-job/src/test/java/org/springframework/cloud/task/batch/autoconfigure/jdbc/JdbcBatchItemWriterAutoConfigurationTests.java +++ b/spring-cloud-starter-single-step-batch-job/src/test/java/org/springframework/cloud/task/batch/autoconfigure/jdbc/JdbcBatchItemWriterAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2020 the original author or authors. + * Copyright 2020-2022 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. @@ -40,6 +40,7 @@ import org.springframework.batch.item.database.ItemPreparedStatementSetter; import org.springframework.batch.item.database.ItemSqlParameterSourceProvider; import org.springframework.batch.item.database.JdbcBatchItemWriter; import org.springframework.batch.item.support.ListItemReader; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration; import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; @@ -59,6 +60,7 @@ import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; import org.springframework.test.util.ReflectionTestUtils; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; public class JdbcBatchItemWriterAutoConfigurationTests { @@ -106,10 +108,37 @@ public class JdbcBatchItemWriterAutoConfigurationTests { AutoConfigurations.of(PropertyPlaceholderAutoConfiguration.class, BatchAutoConfiguration.class, SingleStepJobAutoConfiguration.class, - JdbcBatchItemWriterAutoConfiguration.class)); + JdbcBatchItemWriterAutoConfiguration.class)) + .withPropertyValues("spring.batch.job.jdbcsinglestep.datasource.enable=false"); applicationContextRunner = updatePropertiesForTest(applicationContextRunner); - runTest(applicationContextRunner); + runTest(applicationContextRunner, false); + } + @Test + public void baseTestWithWriterDataSource() { + ApplicationContextRunner applicationContextRunner = new ApplicationContextRunner() + .withUserConfiguration( + TaskLauncherConfiguration.class, + JdbcBatchItemWriterAutoConfigurationTests.DelimitedJobConfiguration.class + ) + .withConfiguration( + AutoConfigurations.of(PropertyPlaceholderAutoConfiguration.class, + BatchAutoConfiguration.class, + SingleStepJobAutoConfiguration.class, + JdbcBatchItemWriterAutoConfiguration.class)) + .withPropertyValues("spring.batch.job.jdbcbatchitemwriter.datasource.enable=true", + "spring.batch.job.jdbcsinglestep.datasource.enable=false", + "spring.batch.job.jobName=job", + "spring.batch.job.stepName=step1", "spring.batch.job.chunkSize=5", + "spring.batch.job.jdbcbatchitemwriter.name=fooWriter", + "spring.batch.job.jdbcbatchitemwriter.sql=INSERT INTO item (item_name) VALUES (:item_name)", + "spring.batch.jdbc.initialize-schema=always", + "jdbcbatchitemwriter.datasource.url=" + DATASOURCE_URL, + "jdbcbatchitemwriter.datasource.username=" + DATASOURCE_USER_NAME, + "jdbcbatchitemwriter.datasource.password=" + DATASOURCE_USER_PASSWORD, + "jdbcbatchitemwriter.datasource.driverClassName=" + DATASOURCE_DRIVER_CLASS_NAME); + + runTest(applicationContextRunner, true); } @Test @@ -123,10 +152,11 @@ public class JdbcBatchItemWriterAutoConfigurationTests { AutoConfigurations.of(PropertyPlaceholderAutoConfiguration.class, BatchAutoConfiguration.class, SingleStepJobAutoConfiguration.class, - JdbcBatchItemWriterAutoConfiguration.class)); + JdbcBatchItemWriterAutoConfiguration.class)) + .withPropertyValues("spring.batch.job.jdbcsinglestep.datasource.enable=false"); applicationContextRunner = updatePropertiesForTest(applicationContextRunner); - runTest(applicationContextRunner); + runTest(applicationContextRunner, false); } @Test @@ -140,9 +170,10 @@ public class JdbcBatchItemWriterAutoConfigurationTests { AutoConfigurations.of(PropertyPlaceholderAutoConfiguration.class, BatchAutoConfiguration.class, SingleStepJobAutoConfiguration.class, - JdbcBatchItemWriterAutoConfiguration.class)); + JdbcBatchItemWriterAutoConfiguration.class)) + .withPropertyValues("spring.batch.job.jdbcsinglestep.datasource.enable=false"); applicationContextRunner = updatePropertiesForTest(applicationContextRunner); - runTest(applicationContextRunner); + runTest(applicationContextRunner, false); } private ApplicationContextRunner updatePropertiesForTest( @@ -154,8 +185,14 @@ public class JdbcBatchItemWriterAutoConfigurationTests { "spring.batch.jdbc.initialize-schema=always"); } - private void validateResultAndBean(ApplicationContext context) { - DataSource dataSource = context.getBean(DataSource.class); + private void validateResultAndBean(ApplicationContext context, boolean isWriterDataSourcePresent) { + DataSource dataSource; + try { + dataSource = context.getBean(DataSource.class); + } + catch (NoSuchBeanDefinitionException nde) { + dataSource = context.getBean("jdbcBatchItemWriterSpringDataSource", DataSource.class); + } JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); List> result = jdbcTemplate .queryForList("SELECT item_name FROM item ORDER BY item_name"); @@ -172,9 +209,17 @@ public class JdbcBatchItemWriterAutoConfigurationTests { .isEqualTo(1); assertThat((Boolean) ReflectionTestUtils.getField(writer, "usingNamedParameters")) .isTrue(); + if (!isWriterDataSourcePresent) { + assertThatThrownBy(() -> context.getBean("jdbcBatchItemWriterSpringDataSource")) + .isInstanceOf(NoSuchBeanDefinitionException.class) + .hasMessageContaining("No bean named 'jdbcBatchItemWriterSpringDataSource' available"); + } + else { + assertThat(context.getBean("jdbcBatchItemWriterSpringDataSource")).isNotNull(); + } } - private void runTest(ApplicationContextRunner applicationContextRunner) { + private void runTest(ApplicationContextRunner applicationContextRunner, boolean isWriterDataSourcePresent) { applicationContextRunner.run((context) -> { JobLauncher jobLauncher = context.getBean(JobLauncher.class); @@ -188,7 +233,7 @@ public class JdbcBatchItemWriterAutoConfigurationTests { Thread.sleep(1000); } - validateResultAndBean(context); + validateResultAndBean(context, isWriterDataSourcePresent); }); } diff --git a/spring-cloud-starter-single-step-batch-job/src/test/java/org/springframework/cloud/task/batch/autoconfigure/jdbc/JdbcCursorItemReaderAutoConfigurationTests.java b/spring-cloud-starter-single-step-batch-job/src/test/java/org/springframework/cloud/task/batch/autoconfigure/jdbc/JdbcCursorItemReaderAutoConfigurationTests.java index f50710bf..580e6bfd 100644 --- a/spring-cloud-starter-single-step-batch-job/src/test/java/org/springframework/cloud/task/batch/autoconfigure/jdbc/JdbcCursorItemReaderAutoConfigurationTests.java +++ b/spring-cloud-starter-single-step-batch-job/src/test/java/org/springframework/cloud/task/batch/autoconfigure/jdbc/JdbcCursorItemReaderAutoConfigurationTests.java @@ -36,7 +36,9 @@ import org.springframework.batch.core.launch.JobLauncher; import org.springframework.batch.item.database.JdbcCursorItemReader; import org.springframework.batch.item.support.ListItemWriter; import org.springframework.batch.item.util.ExecutionContextUserSupport; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration; import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.test.context.runner.ApplicationContextRunner; @@ -56,6 +58,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; /** * @author Michael Minella + * @author Glenn Renfro */ public class JdbcCursorItemReaderAutoConfigurationTests { @@ -90,18 +93,13 @@ public class JdbcCursorItemReaderAutoConfigurationTests { @Test public void testIntegration() { - ApplicationContextRunner applicationContextRunner = new ApplicationContextRunner() - .withUserConfiguration(TaskLauncherConfiguration.class, BaseConfiguration.class) - .withConfiguration( - AutoConfigurations.of(PropertyPlaceholderAutoConfiguration.class, - BatchAutoConfiguration.class, - SingleStepJobAutoConfiguration.class, - JdbcCursorItemReaderAutoConfiguration.class)) + ApplicationContextRunner applicationContextRunner = applicationContextRunner() .withPropertyValues("spring.batch.job.jobName=integrationJob", "spring.batch.job.stepName=step1", "spring.batch.job.chunkSize=5", "spring.batch.job.jdbccursoritemreader.name=fooReader", "spring.batch.job.jdbccursoritemreader.sql=select item_name from item", - "spring.batch.jdbc.initialize-schema=always"); + "spring.batch.jdbc.initialize-schema=always", + "spring.batch.job.jdbcsinglestep.datasource.enable=false"); applicationContextRunner.run((context) -> { JobLauncher jobLauncher = context.getBean(JobLauncher.class); @@ -123,6 +121,58 @@ public class JdbcCursorItemReaderAutoConfigurationTests { assertThat(items.get(0).get("ITEM_NAME")).isEqualTo("foo"); assertThat(items.get(1).get("ITEM_NAME")).isEqualTo("bar"); assertThat(items.get(2).get("ITEM_NAME")).isEqualTo("baz"); + assertThatThrownBy(() -> context.getBean("readerSpringDataSource")) + .isInstanceOf(NoSuchBeanDefinitionException.class) + .hasMessageContaining("No bean named 'readerSpringDataSource' available"); + }); + } + + private ApplicationContextRunner applicationContextRunner() { + return new ApplicationContextRunner() + .withUserConfiguration(TaskLauncherConfiguration.class, BaseConfiguration.class) + .withConfiguration( + AutoConfigurations.of(PropertyPlaceholderAutoConfiguration.class, + BatchAutoConfiguration.class, + SingleStepJobAutoConfiguration.class, + JdbcCursorItemReaderAutoConfiguration.class)); + } + + @Test + public void testIntegrationReaderDataSourceEnabled() { + ApplicationContextRunner applicationContextRunner = applicationContextRunner() + .withPropertyValues("spring.batch.job.jobName=integrationReaderJob", + "spring.batch.job.stepName=step1", "spring.batch.job.chunkSize=5", + "spring.batch.job.jdbccursoritemreader.name=fooReader", + "spring.batch.job.jdbccursoritemreader.sql=select item_name from item", + "spring.batch.jdbc.initialize-schema=always", + "spring.batch.job.jdbcsinglestep.datasource.enable=false", + "spring.batch.job.jdbccursoritemreader.datasource.enable=true", + "jdbccursoritemreader.datasource.url=" + DATASOURCE_URL, + "jdbccursoritemreader.datasource.username=" + DATASOURCE_USER_NAME, + "jdbccursoritemreader.datasource.password=" + DATASOURCE_USER_PASSWORD, + "jdbccursoritemreader.datasource.driverClassName=" + DATASOURCE_DRIVER_CLASS_NAME); + + applicationContextRunner.run((context) -> { + JobLauncher jobLauncher = context.getBean(JobLauncher.class); + + Job job = context.getBean(Job.class); + + JobExecution jobExecution = jobLauncher.run(job, new JobParameters()); + + JobExplorer jobExplorer = context.getBean(JobExplorer.class); + + while (jobExplorer.getJobExecution(jobExecution.getJobId()).isRunning()) { + Thread.sleep(1000); + } + + List> items = context.getBean(ListItemWriter.class) + .getWrittenItems(); + + assertThat(items.size()).isEqualTo(3); + assertThat(items.get(0).get("ITEM_NAME")).isEqualTo("foo"); + assertThat(items.get(1).get("ITEM_NAME")).isEqualTo("bar"); + assertThat(items.get(2).get("ITEM_NAME")).isEqualTo("baz"); + assertThat(context.getBean("jdbcCursorItemReaderSpringDataSource")).isNotNull(); }); } @@ -139,7 +189,8 @@ public class JdbcCursorItemReaderAutoConfigurationTests { "spring.batch.job.stepName=step1", "spring.batch.job.chunkSize=5", "spring.batch.job.jdbccursoritemreader.name=fooReader", "spring.batch.job.jdbccursoritemreader.sql=select * from item", - "spring.batch.jdbc.initialize-schema=always"); + "spring.batch.jdbc.initialize-schema=always", + "spring.batch.job.jdbcsinglestep.datasource.enable=false"); applicationContextRunner.run((context) -> { JobLauncher jobLauncher = context.getBean(JobLauncher.class); @@ -281,6 +332,7 @@ public class JdbcCursorItemReaderAutoConfigurationTests { }); } + @AutoConfigureBefore({JdbcCursorItemReaderAutoConfiguration.class, JDBCSingleStepDataSourceAutoConfiguration.class}) @Configuration public static class TaskLauncherConfiguration { diff --git a/spring-cloud-task-samples/single-step-batch-job/README.adoc b/spring-cloud-task-samples/single-step-batch-job/README.adoc index 6bf91cd9..5286024d 100644 --- a/spring-cloud-task-samples/single-step-batch-job/README.adoc +++ b/spring-cloud-task-samples/single-step-batch-job/README.adoc @@ -58,7 +58,7 @@ CREATE TABLE IF NOT EXISTS item ``` === JdbcCursorItemReader with a JdbcItemWriter batch job -In this example the batch job will read from the `item_sample` table in your data store and write the result to the `item` table in your data store. +In this example the batch job will read from the `item_sample` table in your data store (as specified in the default `DataSource` properties) and write the result to the `item` table in your data store (as specified in the default `DataSource` properties). ``` java -Dspring.profiles.active=jdbcreader,jdbcwriter -jar target/single-step-batch-job-2.3.0-SNAPSHOT.jar ``` @@ -82,6 +82,22 @@ INSERT INTO item_sample (item_name) VALUES ('qux'); INSERT INTO item_sample (item_name) VALUES ('Job'); ``` +You may also wish to read from and write to data sources different from the default `DataSource`. This can be done specifying datasources for the reader and writer as follows: +``` +# Jdbc Cursor Item Reader Data Source +export jdbccursoritemreader_datasource_url= +export jdbccursoritemreader_datasource_username= +export jdbccursoritemreader_datasource_password= +export jdbccursoritemreader_datasource_driverClassName= +export spring_batch_job_jdbccursoritemreader_datasource_enable=true +# Jdbc Batch Item Writer Data Source +export jdbcbatchitemwriter_datasource_url= +export jdbcbatchitemwriter_datasource_username= +export jdbcbatchitemwriter_datasource_password= +export jdbcbatchitemwriter_datasource_driverClassName= +export spring_batch_job_jdbcbatchitemwriter_datasource_enable=true +``` + === JdbcCursorItemReader with FlatfileItemWriter batch job In this example the batch job will read from the `item_sample` table in your data store and write the result to the `result.txt` file to the root directory of the project. ```