Add support for in-memory Batch infrastructure
This commit moves the existing JDBC-based Spring Batch infrastructure to a new 'spring-boot-batch-jdbc' module, while the existing module only offers in-memory (aka resourceless) support. The commit also updates the reference guide to provide some more information about what's available and how to use it. Closes gh-46307
This commit is contained in:
parent
54ffc42309
commit
4f6bbac13e
|
@ -387,8 +387,6 @@
|
|||
* xref:how-to:batch.adoc#howto.batch.restarting-a-failed-job[#howto.batch.restarting-a-failed-job]
|
||||
* xref:how-to:batch.adoc#howto.batch.running-from-the-command-line[#howto-spring-batch-running-command-line]
|
||||
* xref:how-to:batch.adoc#howto.batch.running-from-the-command-line[#howto.batch.running-from-the-command-line]
|
||||
* xref:how-to:batch.adoc#howto.batch.running-jobs-on-startup[#howto-spring-batch-running-jobs-on-startup]
|
||||
* xref:how-to:batch.adoc#howto.batch.running-jobs-on-startup[#howto.batch.running-jobs-on-startup]
|
||||
* xref:how-to:batch.adoc#howto.batch.specifying-a-data-source[#howto-spring-batch-specifying-a-data-source]
|
||||
* xref:how-to:batch.adoc#howto.batch.specifying-a-data-source[#howto.batch.specifying-a-data-source]
|
||||
* xref:how-to:batch.adoc#howto.batch.specifying-a-transaction-manager[#howto.batch.specifying-a-transaction-manager]
|
||||
|
@ -1633,6 +1631,8 @@
|
|||
* xref:reference:features/ssl.adoc#features.ssl[#features.ssl]
|
||||
* xref:reference:features/task-execution-and-scheduling.adoc#features.task-execution-and-scheduling[#boot-features-task-execution-scheduling]
|
||||
* xref:reference:features/task-execution-and-scheduling.adoc#features.task-execution-and-scheduling[#features.task-execution-and-scheduling]
|
||||
* xref:reference:io/spring-batch.adoc#io.spring-batch.running-jobs-on-startup[#howto-spring-batch-running-jobs-on-startup]
|
||||
* xref:reference:io/spring-batch.adoc#io.spring-batch.running-jobs-on-startup[#howto.batch.running-jobs-on-startup]
|
||||
* xref:reference:io/caching.adoc#io.caching.provider.cache2k[#io.caching.provider.cache2k]
|
||||
* xref:reference:io/caching.adoc#io.caching.provider.caffeine[#boot-features-caching-provider-caffeine]
|
||||
* xref:reference:io/caching.adoc#io.caching.provider.caffeine[#features.caching.provider.caffeine]
|
||||
|
|
|
@ -37,20 +37,6 @@ If you do so and want two task executors (for example by retaining the auto-conf
|
|||
|
||||
|
||||
|
||||
[[howto.batch.running-jobs-on-startup]]
|
||||
== Running Spring Batch Jobs on Startup
|
||||
|
||||
Spring Batch auto-configuration is enabled by adding `spring-boot-starter-batch` to your application's classpath.
|
||||
|
||||
If a single javadoc:org.springframework.batch.core.Job[] bean is found in the application context, it is executed on startup (see javadoc:org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner[] for details).
|
||||
If multiple javadoc:org.springframework.batch.core.Job[] beans are found, the job that should be executed must be specified using configprop:spring.batch.job.name[].
|
||||
|
||||
To disable running a javadoc:org.springframework.batch.core.Job[] found in the application context, set the configprop:spring.batch.job.enabled[] to `false`.
|
||||
|
||||
See {code-spring-boot-autoconfigure-src}/batch/BatchAutoConfiguration.java[`BatchAutoConfiguration`] for more details.
|
||||
|
||||
|
||||
|
||||
[[howto.batch.running-from-the-command-line]]
|
||||
== Running From the Command Line
|
||||
|
||||
|
|
|
@ -3,6 +3,6 @@
|
|||
|
||||
Most applications will need to deal with input and output concerns at some point.
|
||||
Spring Boot provides utilities and integrations with a range of technologies to help when you need IO capabilities.
|
||||
This section covers standard IO features such as caching and validation as well as more advanced topics such as scheduling and distributed transactions.
|
||||
This section covers standard IO features such as caching and validation as well as more advanced topics such as batch, scheduling, and distributed transactions.
|
||||
We will also cover calling remote REST or SOAP services and sending email.
|
||||
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
[[io.spring-batch]]
|
||||
= Spring Batch
|
||||
|
||||
Spring Boot offers several conveniences for working with {url-spring-batch-site}[Spring Batch], including running a Job on startup.
|
||||
|
||||
If Spring Batch is available on your classpath, it is initialized through the javadoc:org.springframework.batch.core.configuration.annotation.EnableBatchProcessing[format=annotation] annotation.
|
||||
|
||||
When building a batch application, the following stores can be auto-configured:
|
||||
|
||||
* In-memory
|
||||
* JDBC
|
||||
|
||||
Each store has specific additional settings.
|
||||
For instance, it is possible to customize the tables prefix for the JDBC store, as shown in the following example:
|
||||
|
||||
[configprops,yaml]
|
||||
----
|
||||
spring:
|
||||
batch:
|
||||
jdbc:
|
||||
table-prefix: "CUSTOM_"
|
||||
----
|
||||
|
||||
You can take control over Spring Batch's configuration using javadoc:org.springframework.batch.core.configuration.annotation.EnableBatchProcessing[format=annotation].
|
||||
This will cause the auto-configuration to back off.
|
||||
Spring Batch can then be configured using the `@Enable*JobRepository` annotation's attributes rather than the previously described configuration properties.
|
||||
|
||||
|
||||
|
||||
[[io.spring-batch.running-jobs-on-startup]]
|
||||
== Running Spring Batch Jobs on Startup
|
||||
|
||||
When Spring Boot auto-configures Spring Batch, and if a single javadoc:org.springframework.batch.core.Job[] bean is found in the application context, it is executed on startup (see javadoc:org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner[] for details).
|
||||
If multiple javadoc:org.springframework.batch.core.Job[] beans are found, the job that should be executed must be specified using configprop:spring.batch.job.name[].
|
||||
|
||||
You can disable running a javadoc:org.springframework.batch.core.Job[] found in the application context, as shown in the following example:
|
||||
|
||||
[configprops,yaml]
|
||||
----
|
||||
spring:
|
||||
batch:
|
||||
job:
|
||||
enabled: false
|
||||
----
|
||||
|
||||
|
||||
See javadoc:org.springframework.boot.batch.autoconfigure.BatchAutoConfiguration[] and javadoc:org.springframework.boot.batch.jdbc.autoconfigure.BatchJdbcAutoConfiguration[] for more details.
|
|
@ -39,6 +39,7 @@
|
|||
|
||||
** xref:reference:io/index.adoc[]
|
||||
*** xref:reference:io/caching.adoc[]
|
||||
*** xref:reference:io/spring-batch.adoc[]
|
||||
*** xref:reference:io/hazelcast.adoc[]
|
||||
*** xref:reference:io/quartz.adoc[]
|
||||
*** xref:reference:io/email.adoc[]
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright 2012-present 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.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id "java-library"
|
||||
id "org.springframework.boot.auto-configuration"
|
||||
id "org.springframework.boot.configuration-properties"
|
||||
id "org.springframework.boot.deployed"
|
||||
id "org.springframework.boot.optional-dependencies"
|
||||
}
|
||||
|
||||
description = "Spring Boot Batch JDBC"
|
||||
|
||||
dependencies {
|
||||
api(project(":module:spring-boot-batch"))
|
||||
api(project(":module:spring-boot-jdbc"))
|
||||
|
||||
implementation(project(":module:spring-boot-tx"))
|
||||
|
||||
optional(project(":core:spring-boot-autoconfigure"))
|
||||
optional(project(":module:spring-boot-hibernate"))
|
||||
optional(project(":module:spring-boot-micrometer-observation"))
|
||||
|
||||
testImplementation(project(":core:spring-boot-test"))
|
||||
testImplementation(project(":module:spring-boot-flyway"))
|
||||
testImplementation(project(":module:spring-boot-liquibase"))
|
||||
testImplementation(project(":test-support:spring-boot-test-support"))
|
||||
testImplementation(testFixtures(project(":core:spring-boot-autoconfigure")))
|
||||
testImplementation("io.micrometer:micrometer-observation-test")
|
||||
|
||||
testRuntimeOnly("ch.qos.logback:logback-classic")
|
||||
testRuntimeOnly("com.fasterxml.jackson.core:jackson-databind")
|
||||
testRuntimeOnly("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")
|
||||
testRuntimeOnly("com.h2database:h2")
|
||||
testRuntimeOnly("com.zaxxer:HikariCP")
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.batch.autoconfigure;
|
||||
package org.springframework.boot.batch.jdbc.autoconfigure;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.batch.autoconfigure;
|
||||
package org.springframework.boot.batch.jdbc.autoconfigure;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@ -43,7 +43,7 @@ public class BatchDataSourceScriptDatabaseInitializer extends DataSourceScriptDa
|
|||
* @param properties the Spring Batch JDBC properties
|
||||
* @see #getSettings
|
||||
*/
|
||||
public BatchDataSourceScriptDatabaseInitializer(DataSource dataSource, BatchProperties.Jdbc properties) {
|
||||
public BatchDataSourceScriptDatabaseInitializer(DataSource dataSource, BatchJdbcProperties properties) {
|
||||
this(dataSource, getSettings(dataSource, properties));
|
||||
}
|
||||
|
||||
|
@ -58,16 +58,15 @@ public class BatchDataSourceScriptDatabaseInitializer extends DataSourceScriptDa
|
|||
}
|
||||
|
||||
/**
|
||||
* Adapts {@link BatchProperties.Jdbc Spring Batch JDBC properties} to
|
||||
* {@link DatabaseInitializationSettings} replacing any {@literal @@platform@@}
|
||||
* placeholders.
|
||||
* Adapts {@link BatchJdbcProperties} to {@link DatabaseInitializationSettings}
|
||||
* replacing any {@literal @@platform@@} placeholders.
|
||||
* @param dataSource the Spring Batch data source
|
||||
* @param properties batch JDBC properties
|
||||
* @return a new {@link DatabaseInitializationSettings} instance
|
||||
* @see #BatchDataSourceScriptDatabaseInitializer(DataSource,
|
||||
* DatabaseInitializationSettings)
|
||||
*/
|
||||
public static DatabaseInitializationSettings getSettings(DataSource dataSource, BatchProperties.Jdbc properties) {
|
||||
public static DatabaseInitializationSettings getSettings(DataSource dataSource, BatchJdbcProperties properties) {
|
||||
DatabaseInitializationSettings settings = new DatabaseInitializationSettings();
|
||||
settings.setSchemaLocations(resolveSchemaLocations(dataSource, properties));
|
||||
settings.setMode(properties.getInitializeSchema());
|
||||
|
@ -75,7 +74,7 @@ public class BatchDataSourceScriptDatabaseInitializer extends DataSourceScriptDa
|
|||
return settings;
|
||||
}
|
||||
|
||||
private static List<String> resolveSchemaLocations(DataSource dataSource, BatchProperties.Jdbc properties) {
|
||||
private static List<String> resolveSchemaLocations(DataSource dataSource, BatchJdbcProperties properties) {
|
||||
PlatformPlaceholderDatabaseDriverResolver platformResolver = new PlatformPlaceholderDatabaseDriverResolver();
|
||||
if (StringUtils.hasText(properties.getPlatform())) {
|
||||
return platformResolver.resolveAll(properties.getPlatform(), properties.getSchema());
|
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
* Copyright 2012-present 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.batch.jdbc.autoconfigure;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
|
||||
import org.springframework.batch.core.configuration.support.DefaultBatchConfiguration;
|
||||
import org.springframework.batch.core.configuration.support.JdbcDefaultBatchConfiguration;
|
||||
import org.springframework.batch.core.converter.JobParametersConverter;
|
||||
import org.springframework.batch.core.launch.JobOperator;
|
||||
import org.springframework.batch.core.repository.ExecutionContextSerializer;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.batch.autoconfigure.BatchAutoConfiguration;
|
||||
import org.springframework.boot.batch.autoconfigure.BatchConversionServiceCustomizer;
|
||||
import org.springframework.boot.batch.autoconfigure.BatchJobLauncherAutoConfiguration;
|
||||
import org.springframework.boot.batch.autoconfigure.BatchTaskExecutor;
|
||||
import org.springframework.boot.batch.autoconfigure.BatchTransactionManager;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.sql.autoconfigure.init.OnDatabaseInitializationCondition;
|
||||
import org.springframework.boot.sql.init.dependency.DatabaseInitializationDependencyConfigurer;
|
||||
import org.springframework.boot.transaction.autoconfigure.TransactionAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.convert.support.ConfigurableConversionService;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.jdbc.datasource.init.DatabasePopulator;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for Spring Batch using a JDBC store.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Eddú Meléndez
|
||||
* @author Kazuki Shimizu
|
||||
* @author Mahmoud Ben Hassine
|
||||
* @author Lars Uffmann
|
||||
* @author Lasse Wulff
|
||||
* @author Yanming Zhou
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@AutoConfiguration(before = { BatchAutoConfiguration.class, BatchJobLauncherAutoConfiguration.class },
|
||||
after = { DataSourceAutoConfiguration.class, TransactionAutoConfiguration.class },
|
||||
afterName = "org.springframework.boot.hibernate.autoconfigure.HibernateJpaAutoConfiguration")
|
||||
@ConditionalOnClass({ JobOperator.class, DataSource.class, DatabasePopulator.class })
|
||||
@ConditionalOnBean({ DataSource.class, PlatformTransactionManager.class })
|
||||
@ConditionalOnMissingBean(value = DefaultBatchConfiguration.class, annotation = EnableBatchProcessing.class)
|
||||
@EnableConfigurationProperties(BatchJdbcProperties.class)
|
||||
@Import(DatabaseInitializationDependencyConfigurer.class)
|
||||
public final class BatchJdbcAutoConfiguration {
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class SpringBootBatchJdbcConfiguration extends JdbcDefaultBatchConfiguration {
|
||||
|
||||
private final DataSource dataSource;
|
||||
|
||||
private final PlatformTransactionManager transactionManager;
|
||||
|
||||
private final @Nullable TaskExecutor taskExecutor;
|
||||
|
||||
private final BatchJdbcProperties properties;
|
||||
|
||||
private final List<BatchConversionServiceCustomizer> batchConversionServiceCustomizers;
|
||||
|
||||
private final @Nullable ExecutionContextSerializer executionContextSerializer;
|
||||
|
||||
private final @Nullable JobParametersConverter jobParametersConverter;
|
||||
|
||||
SpringBootBatchJdbcConfiguration(DataSource dataSource,
|
||||
@BatchDataSource ObjectProvider<DataSource> batchDataSource,
|
||||
PlatformTransactionManager transactionManager,
|
||||
@BatchTransactionManager ObjectProvider<PlatformTransactionManager> batchTransactionManager,
|
||||
@BatchTaskExecutor ObjectProvider<TaskExecutor> batchTaskExecutor, BatchJdbcProperties properties,
|
||||
ObjectProvider<BatchConversionServiceCustomizer> batchConversionServiceCustomizers,
|
||||
ObjectProvider<ExecutionContextSerializer> executionContextSerializer,
|
||||
ObjectProvider<JobParametersConverter> jobParametersConverter) {
|
||||
this.dataSource = batchDataSource.getIfAvailable(() -> dataSource);
|
||||
this.transactionManager = batchTransactionManager.getIfAvailable(() -> transactionManager);
|
||||
this.taskExecutor = batchTaskExecutor.getIfAvailable();
|
||||
this.properties = properties;
|
||||
this.batchConversionServiceCustomizers = batchConversionServiceCustomizers.orderedStream().toList();
|
||||
this.executionContextSerializer = executionContextSerializer.getIfAvailable();
|
||||
this.jobParametersConverter = jobParametersConverter.getIfAvailable();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataSource getDataSource() {
|
||||
return this.dataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PlatformTransactionManager getTransactionManager() {
|
||||
return this.transactionManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTablePrefix() {
|
||||
String tablePrefix = this.properties.getTablePrefix();
|
||||
return (tablePrefix != null) ? tablePrefix : super.getTablePrefix();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean getValidateTransactionState() {
|
||||
return this.properties.isValidateTransactionState();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Isolation getIsolationLevelForCreate() {
|
||||
Isolation isolation = this.properties.getIsolationLevelForCreate();
|
||||
return (isolation != null) ? isolation : super.getIsolationLevelForCreate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConfigurableConversionService getConversionService() {
|
||||
ConfigurableConversionService conversionService = super.getConversionService();
|
||||
for (BatchConversionServiceCustomizer customizer : this.batchConversionServiceCustomizers) {
|
||||
customizer.customize(conversionService);
|
||||
}
|
||||
return conversionService;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ExecutionContextSerializer getExecutionContextSerializer() {
|
||||
return (this.executionContextSerializer != null) ? this.executionContextSerializer
|
||||
: super.getExecutionContextSerializer();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated(since = "4.0.0", forRemoval = true)
|
||||
@SuppressWarnings("removal")
|
||||
protected JobParametersConverter getJobParametersConverter() {
|
||||
return (this.jobParametersConverter != null) ? this.jobParametersConverter
|
||||
: super.getJobParametersConverter();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TaskExecutor getTaskExecutor() {
|
||||
return (this.taskExecutor != null) ? this.taskExecutor : super.getTaskExecutor();
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Conditional(OnBatchDatasourceInitializationCondition.class)
|
||||
static class DataSourceInitializerConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
BatchDataSourceScriptDatabaseInitializer batchDataSourceInitializer(DataSource dataSource,
|
||||
@BatchDataSource ObjectProvider<DataSource> batchDataSource, BatchJdbcProperties properties) {
|
||||
return new BatchDataSourceScriptDatabaseInitializer(batchDataSource.getIfAvailable(() -> dataSource),
|
||||
properties);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class OnBatchDatasourceInitializationCondition extends OnDatabaseInitializationCondition {
|
||||
|
||||
OnBatchDatasourceInitializationCondition() {
|
||||
super("Batch", "spring.batch.jdbc.initialize-schema");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* Copyright 2012-present 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.batch.jdbc.autoconfigure;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.sql.init.DatabaseInitializationMode;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
|
||||
/**
|
||||
* Configuration properties for Spring Batch using a JDBC store.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@ConfigurationProperties("spring.batch.jdbc")
|
||||
public class BatchJdbcProperties {
|
||||
|
||||
private static final String DEFAULT_SCHEMA_LOCATION = "classpath:org/springframework/"
|
||||
+ "batch/core/schema-@@platform@@.sql";
|
||||
|
||||
/**
|
||||
* Whether to validate the transaction state.
|
||||
*/
|
||||
private boolean validateTransactionState = true;
|
||||
|
||||
/**
|
||||
* Transaction isolation level to use when creating job meta-data for new jobs.
|
||||
*/
|
||||
private @Nullable Isolation isolationLevelForCreate;
|
||||
|
||||
/**
|
||||
* Path to the SQL file to use to initialize the database schema.
|
||||
*/
|
||||
private String schema = DEFAULT_SCHEMA_LOCATION;
|
||||
|
||||
/**
|
||||
* Platform to use in initialization scripts if the @@platform@@ placeholder is used.
|
||||
* Auto-detected by default.
|
||||
*/
|
||||
private @Nullable String platform;
|
||||
|
||||
/**
|
||||
* Table prefix for all the batch meta-data tables.
|
||||
*/
|
||||
private @Nullable String tablePrefix;
|
||||
|
||||
/**
|
||||
* Database schema initialization mode.
|
||||
*/
|
||||
private DatabaseInitializationMode initializeSchema = DatabaseInitializationMode.EMBEDDED;
|
||||
|
||||
public boolean isValidateTransactionState() {
|
||||
return this.validateTransactionState;
|
||||
}
|
||||
|
||||
public void setValidateTransactionState(boolean validateTransactionState) {
|
||||
this.validateTransactionState = validateTransactionState;
|
||||
}
|
||||
|
||||
public @Nullable Isolation getIsolationLevelForCreate() {
|
||||
return this.isolationLevelForCreate;
|
||||
}
|
||||
|
||||
public void setIsolationLevelForCreate(@Nullable Isolation isolationLevelForCreate) {
|
||||
this.isolationLevelForCreate = isolationLevelForCreate;
|
||||
}
|
||||
|
||||
public String getSchema() {
|
||||
return this.schema;
|
||||
}
|
||||
|
||||
public void setSchema(String schema) {
|
||||
this.schema = schema;
|
||||
}
|
||||
|
||||
public @Nullable String getPlatform() {
|
||||
return this.platform;
|
||||
}
|
||||
|
||||
public void setPlatform(@Nullable String platform) {
|
||||
this.platform = platform;
|
||||
}
|
||||
|
||||
public @Nullable String getTablePrefix() {
|
||||
return this.tablePrefix;
|
||||
}
|
||||
|
||||
public void setTablePrefix(@Nullable String tablePrefix) {
|
||||
this.tablePrefix = tablePrefix;
|
||||
}
|
||||
|
||||
public DatabaseInitializationMode getInitializeSchema() {
|
||||
return this.initializeSchema;
|
||||
}
|
||||
|
||||
public void setInitializeSchema(DatabaseInitializationMode initializeSchema) {
|
||||
this.initializeSchema = initializeSchema;
|
||||
}
|
||||
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.batch.autoconfigure;
|
||||
package org.springframework.boot.batch.jdbc.autoconfigure;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright 2012-present 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Auto-configuration for Spring Batch JDBC.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.boot.batch.jdbc.autoconfigure;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"properties": [
|
||||
{
|
||||
"name": "spring.batch.initialize-schema",
|
||||
"type": "org.springframework.boot.sql.init.DatabaseInitializationMode",
|
||||
"deprecation": {
|
||||
"replacement": "spring.batch.jdbc.initialize-schema",
|
||||
"level": "error"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "spring.batch.initializer.enabled",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "Create the required batch tables on startup if necessary. Enabled automatically\n if no custom table prefix is set or if a custom schema is configured.",
|
||||
"deprecation": {
|
||||
"replacement": "spring.batch.jdbc.initialize-schema",
|
||||
"level": "error"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "spring.batch.schema",
|
||||
"type": "java.lang.String",
|
||||
"deprecation": {
|
||||
"replacement": "spring.batch.jdbc.schema",
|
||||
"level": "error"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "spring.batch.table-prefix",
|
||||
"type": "java.lang.String",
|
||||
"deprecation": {
|
||||
"replacement": "spring.batch.jdbc.table-prefix",
|
||||
"level": "error"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,3 +1,3 @@
|
|||
# Depends on Database Initialization Detectors
|
||||
org.springframework.boot.sql.init.dependency.DependsOnDatabaseInitializationDetector=\
|
||||
org.springframework.boot.batch.autoconfigure.JobRepositoryDependsOnDatabaseInitializationDetector
|
||||
org.springframework.boot.batch.jdbc.autoconfigure.JobRepositoryDependsOnDatabaseInitializationDetector
|
|
@ -0,0 +1 @@
|
|||
org.springframework.boot.batch.jdbc.autoconfigure.BatchJdbcAutoConfiguration
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.batch.autoconfigure;
|
||||
package org.springframework.boot.batch.jdbc.autoconfigure;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.Connection;
|
||||
|
@ -52,10 +52,10 @@ class BatchDataSourceScriptDatabaseInitializerTests {
|
|||
@Test
|
||||
void getSettingsWithPlatformDoesNotTouchDataSource() {
|
||||
DataSource dataSource = mock(DataSource.class);
|
||||
BatchProperties properties = new BatchProperties();
|
||||
properties.getJdbc().setPlatform("test");
|
||||
BatchJdbcProperties properties = new BatchJdbcProperties();
|
||||
properties.setPlatform("test");
|
||||
DatabaseInitializationSettings settings = BatchDataSourceScriptDatabaseInitializer.getSettings(dataSource,
|
||||
properties.getJdbc());
|
||||
properties);
|
||||
assertThat(settings.getSchemaLocations())
|
||||
.containsOnly("classpath:org/springframework/batch/core/schema-test.sql");
|
||||
then(dataSource).shouldHaveNoInteractions();
|
||||
|
@ -66,7 +66,7 @@ class BatchDataSourceScriptDatabaseInitializerTests {
|
|||
"INFORMIX", "JTDS", "PHOENIX", "REDSHIFT", "TERADATA", "TESTCONTAINERS", "UNKNOWN" })
|
||||
void batchSchemaCanBeLocated(DatabaseDriver driver) throws SQLException {
|
||||
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||
BatchProperties properties = new BatchProperties();
|
||||
BatchJdbcProperties properties = new BatchJdbcProperties();
|
||||
DataSource dataSource = mock(DataSource.class);
|
||||
Connection connection = mock(Connection.class);
|
||||
given(dataSource.getConnection()).willReturn(connection);
|
||||
|
@ -75,7 +75,7 @@ class BatchDataSourceScriptDatabaseInitializerTests {
|
|||
String productName = (String) ReflectionTestUtils.getField(driver, "productName");
|
||||
given(metadata.getDatabaseProductName()).willReturn(productName);
|
||||
DatabaseInitializationSettings settings = BatchDataSourceScriptDatabaseInitializer.getSettings(dataSource,
|
||||
properties.getJdbc());
|
||||
properties);
|
||||
List<String> schemaLocations = settings.getSchemaLocations();
|
||||
assertThat(schemaLocations).isNotEmpty()
|
||||
.allSatisfy((location) -> assertThat(resourceLoader.getResource(location).exists()).isTrue());
|
|
@ -0,0 +1,822 @@
|
|||
/*
|
||||
* Copyright 2012-present 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.batch.jdbc.autoconfigure;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import jakarta.persistence.EntityManagerFactory;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.batch.core.BatchStatus;
|
||||
import org.springframework.batch.core.configuration.JobRegistry;
|
||||
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
|
||||
import org.springframework.batch.core.configuration.support.DefaultBatchConfiguration;
|
||||
import org.springframework.batch.core.converter.DefaultJobParametersConverter;
|
||||
import org.springframework.batch.core.converter.JobParametersConverter;
|
||||
import org.springframework.batch.core.converter.JsonJobParametersConverter;
|
||||
import org.springframework.batch.core.job.AbstractJob;
|
||||
import org.springframework.batch.core.job.Job;
|
||||
import org.springframework.batch.core.job.JobExecution;
|
||||
import org.springframework.batch.core.job.parameters.JobParameters;
|
||||
import org.springframework.batch.core.job.parameters.JobParametersBuilder;
|
||||
import org.springframework.batch.core.launch.JobOperator;
|
||||
import org.springframework.batch.core.repository.ExecutionContextSerializer;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.core.repository.dao.DefaultExecutionContextSerializer;
|
||||
import org.springframework.batch.core.repository.dao.Jackson2ExecutionContextStringSerializer;
|
||||
import org.springframework.batch.core.step.Step;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.DefaultApplicationArguments;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage;
|
||||
import org.springframework.boot.batch.autoconfigure.BatchConversionServiceCustomizer;
|
||||
import org.springframework.boot.batch.autoconfigure.BatchJobLauncherAutoConfiguration;
|
||||
import org.springframework.boot.batch.autoconfigure.BatchTaskExecutor;
|
||||
import org.springframework.boot.batch.autoconfigure.BatchTransactionManager;
|
||||
import org.springframework.boot.batch.autoconfigure.JobLauncherApplicationRunner;
|
||||
import org.springframework.boot.batch.jdbc.autoconfigure.BatchJdbcAutoConfiguration.SpringBootBatchJdbcConfiguration;
|
||||
import org.springframework.boot.batch.jdbc.autoconfigure.domain.City;
|
||||
import org.springframework.boot.flyway.autoconfigure.FlywayAutoConfiguration;
|
||||
import org.springframework.boot.hibernate.autoconfigure.HibernateJpaAutoConfiguration;
|
||||
import org.springframework.boot.jdbc.DataSourceBuilder;
|
||||
import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.jdbc.autoconfigure.DataSourceTransactionManagerAutoConfiguration;
|
||||
import org.springframework.boot.jdbc.autoconfigure.EmbeddedDataSourceConfiguration;
|
||||
import org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer;
|
||||
import org.springframework.boot.liquibase.autoconfigure.LiquibaseAutoConfiguration;
|
||||
import org.springframework.boot.sql.init.DatabaseInitializationMode;
|
||||
import org.springframework.boot.sql.init.DatabaseInitializationSettings;
|
||||
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.boot.testsupport.classpath.resources.WithPackageResources;
|
||||
import org.springframework.boot.testsupport.classpath.resources.WithResource;
|
||||
import org.springframework.boot.transaction.autoconfigure.TransactionAutoConfiguration;
|
||||
import org.springframework.boot.transaction.autoconfigure.TransactionManagerCustomizationAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.convert.support.ConfigurableConversionService;
|
||||
import org.springframework.core.task.AsyncTaskExecutor;
|
||||
import org.springframework.core.task.SimpleAsyncTaskExecutor;
|
||||
import org.springframework.core.task.SyncTaskExecutor;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.jdbc.BadSqlGrammarException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
||||
import org.springframework.jdbc.datasource.init.DatabasePopulator;
|
||||
import org.springframework.orm.jpa.JpaTransactionManager;
|
||||
import org.springframework.test.util.AopTestUtils;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link BatchJdbcAutoConfiguration}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Stephane Nicoll
|
||||
* @author Vedran Pavic
|
||||
* @author Kazuki Shimizu
|
||||
* @author Mahmoud Ben Hassine
|
||||
* @author Lars Uffmann
|
||||
* @author Lasse Wulff
|
||||
* @author Yanming Zhou
|
||||
*/
|
||||
class BatchJdbcAutoConfigurationTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(BatchJobLauncherAutoConfiguration.class,
|
||||
BatchJdbcAutoConfiguration.class, TransactionManagerCustomizationAutoConfiguration.class,
|
||||
TransactionAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void testDefaultContext() {
|
||||
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class).run((context) -> {
|
||||
assertThat(context).hasSingleBean(JobRepository.class);
|
||||
assertThat(context).hasSingleBean(JobOperator.class);
|
||||
assertThat(context.getBean(BatchJdbcProperties.class).getInitializeSchema())
|
||||
.isEqualTo(DatabaseInitializationMode.EMBEDDED);
|
||||
assertThat(new JdbcTemplate(context.getBean(DataSource.class))
|
||||
.queryForList("select * from BATCH_JOB_EXECUTION")).isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoconfigurationBacksOffEntirelyIfSpringJdbcAbsent() {
|
||||
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
|
||||
.withClassLoader(new FilteredClassLoader(DatabasePopulator.class))
|
||||
.run((context) -> {
|
||||
assertThat(context).doesNotHaveBean(JobLauncherApplicationRunner.class);
|
||||
assertThat(context).doesNotHaveBean(BatchDataSourceScriptDatabaseInitializer.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoConfigurationBacksOffWhenUserEnablesBatchProcessing() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(EnableBatchProcessingConfiguration.class, EmbeddedDataSourceConfiguration.class)
|
||||
.withClassLoader(new FilteredClassLoader(DatabasePopulator.class))
|
||||
.run((context) -> assertThat(context).doesNotHaveBean(BatchJdbcAutoConfiguration.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoConfigurationBacksOffWhenUserProvidesBatchConfiguration() {
|
||||
this.contextRunner.withUserConfiguration(CustomBatchConfiguration.class, EmbeddedDataSourceConfiguration.class)
|
||||
.withClassLoader(new FilteredClassLoader(DatabasePopulator.class))
|
||||
.run((context) -> assertThat(context).doesNotHaveBean(BatchJdbcAutoConfiguration.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDefinesAndLaunchesJob() {
|
||||
this.contextRunner.withUserConfiguration(JobConfiguration.class, EmbeddedDataSourceConfiguration.class)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(JobOperator.class);
|
||||
context.getBean(JobLauncherApplicationRunner.class)
|
||||
.run(new DefaultApplicationArguments("jobParam=test"));
|
||||
JobParameters jobParameters = new JobParametersBuilder().addString("jobParam", "test")
|
||||
.toJobParameters();
|
||||
assertThat(context.getBean(JobRepository.class).getLastJobExecution("job", jobParameters)).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDefinesAndLaunchesJobIgnoreOptionArguments() {
|
||||
this.contextRunner.withUserConfiguration(JobConfiguration.class, EmbeddedDataSourceConfiguration.class)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(JobOperator.class);
|
||||
context.getBean(JobLauncherApplicationRunner.class)
|
||||
.run(new DefaultApplicationArguments("--spring.property=value", "jobParam=test"));
|
||||
JobParameters jobParameters = new JobParametersBuilder().addString("jobParam", "test")
|
||||
.toJobParameters();
|
||||
assertThat(context.getBean(JobRepository.class).getLastJobExecution("job", jobParameters)).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRegisteredAndLocalJob() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(NamedJobConfigurationWithRegisteredAndLocalJob.class,
|
||||
EmbeddedDataSourceConfiguration.class)
|
||||
.withPropertyValues("spring.batch.job.name:discreteRegisteredJob")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(JobOperator.class);
|
||||
context.getBean(JobLauncherApplicationRunner.class).run();
|
||||
assertThat(context.getBean(JobRepository.class)
|
||||
.getLastJobExecution("discreteRegisteredJob", new JobParameters())
|
||||
.getStatus()).isEqualTo(BatchStatus.COMPLETED);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDefinesAndLaunchesLocalJob() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(NamedJobConfigurationWithLocalJob.class, EmbeddedDataSourceConfiguration.class)
|
||||
.withPropertyValues("spring.batch.job.name:discreteLocalJob")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(JobOperator.class);
|
||||
context.getBean(JobLauncherApplicationRunner.class).run();
|
||||
assertThat(context.getBean(JobRepository.class)
|
||||
.getLastJobExecution("discreteLocalJob", new JobParameters())).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultipleJobsAndNoJobName() {
|
||||
this.contextRunner.withUserConfiguration(MultipleJobConfiguration.class, EmbeddedDataSourceConfiguration.class)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasFailed();
|
||||
assertThat(context.getStartupFailure().getCause().getMessage())
|
||||
.contains("Job name must be specified in case of multiple jobs");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultipleJobsAndJobName() {
|
||||
this.contextRunner.withUserConfiguration(MultipleJobConfiguration.class, EmbeddedDataSourceConfiguration.class)
|
||||
.withPropertyValues("spring.batch.job.name:discreteLocalJob")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(JobOperator.class);
|
||||
context.getBean(JobLauncherApplicationRunner.class).run();
|
||||
assertThat(context.getBean(JobRepository.class)
|
||||
.getLastJobExecution("discreteLocalJob", new JobParameters())).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDisableLaunchesJob() {
|
||||
this.contextRunner.withUserConfiguration(JobConfiguration.class, EmbeddedDataSourceConfiguration.class)
|
||||
.withPropertyValues("spring.batch.job.enabled:false")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(JobOperator.class);
|
||||
assertThat(context).doesNotHaveBean(CommandLineRunner.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDisableSchemaLoader() {
|
||||
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
|
||||
.withPropertyValues("spring.datasource.generate-unique-name=true",
|
||||
"spring.batch.jdbc.initialize-schema:never")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(JobOperator.class);
|
||||
assertThat(context.getBean(BatchJdbcProperties.class).getInitializeSchema())
|
||||
.isEqualTo(DatabaseInitializationMode.NEVER);
|
||||
assertThat(context).doesNotHaveBean(BatchDataSourceScriptDatabaseInitializer.class);
|
||||
assertThatExceptionOfType(BadSqlGrammarException.class)
|
||||
.isThrownBy(() -> new JdbcTemplate(context.getBean(DataSource.class))
|
||||
.queryForList("select * from BATCH_JOB_EXECUTION"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUsingJpa() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(TestJpaConfiguration.class, EmbeddedDataSourceConfiguration.class,
|
||||
HibernateJpaAutoConfiguration.class)
|
||||
.run((context) -> {
|
||||
PlatformTransactionManager transactionManager = context.getBean(PlatformTransactionManager.class);
|
||||
// It's a lazy proxy, but it does render its target if you ask for
|
||||
// toString():
|
||||
assertThat(transactionManager.toString()).contains("JpaTransactionManager");
|
||||
assertThat(context).hasSingleBean(EntityManagerFactory.class);
|
||||
// Ensure the JobRepository can be used (no problem with isolation
|
||||
// level)
|
||||
assertThat(context.getBean(JobRepository.class).getLastJobExecution("job", new JobParameters()))
|
||||
.isNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithPackageResources("custom-schema.sql")
|
||||
void testRenamePrefix() {
|
||||
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
|
||||
.withPropertyValues("spring.datasource.generate-unique-name=true",
|
||||
"spring.batch.jdbc.schema:classpath:custom-schema.sql", "spring.batch.jdbc.table-prefix:PREFIX_")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(JobOperator.class);
|
||||
assertThat(context.getBean(BatchJdbcProperties.class).getInitializeSchema())
|
||||
.isEqualTo(DatabaseInitializationMode.EMBEDDED);
|
||||
assertThat(new JdbcTemplate(context.getBean(DataSource.class))
|
||||
.queryForList("select * from PREFIX_JOB_EXECUTION")).isEmpty();
|
||||
JobRepository jobRepository = context.getBean(JobRepository.class);
|
||||
assertThat(jobRepository.findRunningJobExecutions("test")).isEmpty();
|
||||
assertThat(jobRepository.getLastJobExecution("test", new JobParameters())).isNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeJpaTransactionManagerUsingProperties() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(TestJpaConfiguration.class, EmbeddedDataSourceConfiguration.class,
|
||||
HibernateJpaAutoConfiguration.class)
|
||||
.withPropertyValues("spring.transaction.default-timeout:30",
|
||||
"spring.transaction.rollback-on-commit-failure:true")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(BatchJobLauncherAutoConfiguration.class);
|
||||
JpaTransactionManager transactionManager = JpaTransactionManager.class
|
||||
.cast(context.getBean(SpringBootBatchJdbcConfiguration.class).getTransactionManager());
|
||||
assertThat(transactionManager.getDefaultTimeout()).isEqualTo(30);
|
||||
assertThat(transactionManager.isRollbackOnCommitFailure()).isTrue();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeDataSourceTransactionManagerUsingProperties() {
|
||||
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
|
||||
.withPropertyValues("spring.transaction.default-timeout:30",
|
||||
"spring.transaction.rollback-on-commit-failure:true")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(BatchJdbcAutoConfiguration.class);
|
||||
DataSourceTransactionManager transactionManager = DataSourceTransactionManager.class
|
||||
.cast(context.getBean(SpringBootBatchJdbcConfiguration.class).getTransactionManager());
|
||||
assertThat(transactionManager.getDefaultTimeout()).isEqualTo(30);
|
||||
assertThat(transactionManager.isRollbackOnCommitFailure()).isTrue();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBatchDataSource() {
|
||||
this.contextRunner.withUserConfiguration(BatchDataSourceConfiguration.class).run((context) -> {
|
||||
assertThat(context).hasSingleBean(BatchJdbcAutoConfiguration.class)
|
||||
.hasSingleBean(BatchDataSourceScriptDatabaseInitializer.class)
|
||||
.hasBean("batchDataSource");
|
||||
DataSource batchDataSource = context.getBean("batchDataSource", DataSource.class);
|
||||
assertThat(context.getBean(SpringBootBatchJdbcConfiguration.class).getDataSource())
|
||||
.isEqualTo(batchDataSource);
|
||||
assertThat(context.getBean(BatchDataSourceScriptDatabaseInitializer.class))
|
||||
.hasFieldOrPropertyWithValue("dataSource", batchDataSource);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBatchTransactionManager() {
|
||||
this.contextRunner.withUserConfiguration(BatchTransactionManagerConfiguration.class).run((context) -> {
|
||||
assertThat(context).hasSingleBean(BatchJdbcAutoConfiguration.class);
|
||||
PlatformTransactionManager batchTransactionManager = context.getBean("batchTransactionManager",
|
||||
PlatformTransactionManager.class);
|
||||
assertThat(context.getBean(SpringBootBatchJdbcConfiguration.class).getTransactionManager())
|
||||
.isEqualTo(batchTransactionManager);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBatchTaskExecutor() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(BatchTaskExecutorConfiguration.class, EmbeddedDataSourceConfiguration.class)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(BatchJdbcAutoConfiguration.class).hasBean("batchTaskExecutor");
|
||||
TaskExecutor batchTaskExecutor = context.getBean("batchTaskExecutor", TaskExecutor.class);
|
||||
assertThat(batchTaskExecutor).isInstanceOf(AsyncTaskExecutor.class);
|
||||
assertThat(context.getBean(SpringBootBatchJdbcConfiguration.class).getTaskExecutor())
|
||||
.isEqualTo(batchTaskExecutor);
|
||||
JobOperator jobOperator = AopTestUtils.getTargetObject(context.getBean(JobOperator.class));
|
||||
assertThat(jobOperator).hasFieldOrPropertyWithValue("taskExecutor", batchTaskExecutor);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void jobRepositoryBeansDependOnBatchDataSourceInitializer() {
|
||||
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class).run((context) -> {
|
||||
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
|
||||
String[] jobRepositoryNames = beanFactory.getBeanNamesForType(JobRepository.class);
|
||||
assertThat(jobRepositoryNames).isNotEmpty();
|
||||
for (String jobRepositoryName : jobRepositoryNames) {
|
||||
assertThat(beanFactory.getBeanDefinition(jobRepositoryName).getDependsOn())
|
||||
.contains("batchDataSourceInitializer");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void jobRepositoryBeansDependOnFlyway() {
|
||||
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, FlywayAutoConfiguration.class)
|
||||
.withPropertyValues("spring.batch.jdbc.initialize-schema=never")
|
||||
.run((context) -> {
|
||||
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
|
||||
String[] jobRepositoryNames = beanFactory.getBeanNamesForType(JobRepository.class);
|
||||
assertThat(jobRepositoryNames).isNotEmpty();
|
||||
for (String jobRepositoryName : jobRepositoryNames) {
|
||||
assertThat(beanFactory.getBeanDefinition(jobRepositoryName).getDependsOn()).contains("flyway",
|
||||
"flywayInitializer");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithResource(name = "db/changelog/db.changelog-master.yaml", content = "databaseChangeLog:")
|
||||
void jobRepositoryBeansDependOnLiquibase() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(EmbeddedDataSourceConfiguration.class, LiquibaseAutoConfiguration.class)
|
||||
.withPropertyValues("spring.batch.jdbc.initialize-schema=never")
|
||||
.run((context) -> {
|
||||
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
|
||||
String[] jobRepositoryNames = beanFactory.getBeanNamesForType(JobRepository.class);
|
||||
assertThat(jobRepositoryNames).isNotEmpty();
|
||||
for (String jobRepositoryName : jobRepositoryNames) {
|
||||
assertThat(beanFactory.getBeanDefinition(jobRepositoryName).getDependsOn()).contains("liquibase");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenTheUserDefinesTheirOwnBatchDatabaseInitializerThenTheAutoConfiguredInitializerBacksOff() {
|
||||
this.contextRunner.withUserConfiguration(CustomBatchDatabaseInitializerConfiguration.class)
|
||||
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
|
||||
DataSourceTransactionManagerAutoConfiguration.class))
|
||||
.run((context) -> assertThat(context).hasSingleBean(BatchDataSourceScriptDatabaseInitializer.class)
|
||||
.doesNotHaveBean("batchDataSourceScriptDatabaseInitializer")
|
||||
.hasBean("customInitializer"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenTheUserDefinesTheirOwnDatabaseInitializerThenTheAutoConfiguredBatchInitializerRemains() {
|
||||
this.contextRunner.withUserConfiguration(CustomDatabaseInitializerConfiguration.class)
|
||||
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
|
||||
DataSourceTransactionManagerAutoConfiguration.class))
|
||||
.run((context) -> assertThat(context).hasSingleBean(BatchDataSourceScriptDatabaseInitializer.class)
|
||||
.hasBean("customInitializer"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void conversionServiceCustomizersAreCalled() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(EmbeddedDataSourceConfiguration.class,
|
||||
ConversionServiceCustomizersConfiguration.class)
|
||||
.run((context) -> {
|
||||
BatchConversionServiceCustomizer customizer = context.getBean("batchConversionServiceCustomizer",
|
||||
BatchConversionServiceCustomizer.class);
|
||||
BatchConversionServiceCustomizer anotherCustomizer = context
|
||||
.getBean("anotherBatchConversionServiceCustomizer", BatchConversionServiceCustomizer.class);
|
||||
InOrder inOrder = Mockito.inOrder(customizer, anotherCustomizer);
|
||||
ConfigurableConversionService configurableConversionService = context
|
||||
.getBean(SpringBootBatchJdbcConfiguration.class)
|
||||
.getConversionService();
|
||||
inOrder.verify(customizer).customize(configurableConversionService);
|
||||
inOrder.verify(anotherCustomizer).customize(configurableConversionService);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenTheUserDefinesAJobNameAsJobInstanceValidates() {
|
||||
JobLauncherApplicationRunner runner = createInstance("another");
|
||||
runner.setJobs(Collections.singletonList(mockJob("test")));
|
||||
runner.setJobName("test");
|
||||
runner.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenTheUserDefinesAJobNameAsRegisteredJobValidates() {
|
||||
JobLauncherApplicationRunner runner = createInstance("test");
|
||||
runner.setJobName("test");
|
||||
runner.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenTheUserDefinesAJobNameThatDoesNotExistWithJobInstancesFailsFast() {
|
||||
JobLauncherApplicationRunner runner = createInstance();
|
||||
runner.setJobs(Arrays.asList(mockJob("one"), mockJob("two")));
|
||||
runner.setJobName("three");
|
||||
assertThatIllegalStateException().isThrownBy(runner::afterPropertiesSet)
|
||||
.withMessage("No job found with name 'three'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenTheUserDefinesAJobNameThatDoesNotExistWithRegisteredJobFailsFast() {
|
||||
JobLauncherApplicationRunner runner = createInstance("one", "two");
|
||||
runner.setJobName("three");
|
||||
assertThatIllegalStateException().isThrownBy(runner::afterPropertiesSet)
|
||||
.withMessage("No job found with name 'three'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void customExecutionContextSerializerIsUsed() {
|
||||
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
|
||||
.withBean(ExecutionContextSerializer.class, Jackson2ExecutionContextStringSerializer::new)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(Jackson2ExecutionContextStringSerializer.class);
|
||||
assertThat(context.getBean(SpringBootBatchJdbcConfiguration.class).getExecutionContextSerializer())
|
||||
.isInstanceOf(Jackson2ExecutionContextStringSerializer.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void defaultExecutionContextSerializerIsUsed() {
|
||||
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class).run((context) -> {
|
||||
assertThat(context).doesNotHaveBean(ExecutionContextSerializer.class);
|
||||
assertThat(context.getBean(SpringBootBatchJdbcConfiguration.class).getExecutionContextSerializer())
|
||||
.isInstanceOf(DefaultExecutionContextSerializer.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void customJdbcPropertiesIsUsed() {
|
||||
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
|
||||
.withPropertyValues("spring.batch.jdbc.validate-transaction-state:false",
|
||||
"spring.batch.jdbc.isolation-level-for-create:READ_COMMITTED")
|
||||
.run((context) -> {
|
||||
SpringBootBatchJdbcConfiguration configuration = context
|
||||
.getBean(SpringBootBatchJdbcConfiguration.class);
|
||||
assertThat(configuration.getValidateTransactionState()).isEqualTo(false);
|
||||
assertThat(configuration.getIsolationLevelForCreate()).isEqualTo(Isolation.READ_COMMITTED);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Deprecated(since = "4.0.0", forRemoval = true)
|
||||
@SuppressWarnings("removal")
|
||||
void customJobParametersConverterIsUsed() {
|
||||
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
|
||||
.withBean(JobParametersConverter.class, JsonJobParametersConverter::new)
|
||||
.withPropertyValues("spring.datasource.generate-unique-name=true")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(JsonJobParametersConverter.class);
|
||||
assertThat(context.getBean(SpringBootBatchJdbcConfiguration.class).getJobParametersConverter())
|
||||
.isInstanceOf(JsonJobParametersConverter.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@Deprecated(since = "4.0.0", forRemoval = true)
|
||||
@SuppressWarnings("removal")
|
||||
void defaultJobParametersConverterIsUsed() {
|
||||
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class).run((context) -> {
|
||||
assertThat(context).doesNotHaveBean(JobParametersConverter.class);
|
||||
assertThat(context.getBean(SpringBootBatchJdbcConfiguration.class).getJobParametersConverter())
|
||||
.isInstanceOf(DefaultJobParametersConverter.class);
|
||||
});
|
||||
}
|
||||
|
||||
private JobLauncherApplicationRunner createInstance(String... registeredJobNames) {
|
||||
JobLauncherApplicationRunner runner = new JobLauncherApplicationRunner(mock(JobOperator.class));
|
||||
JobRegistry jobRegistry = mock(JobRegistry.class);
|
||||
given(jobRegistry.getJobNames()).willReturn(Arrays.asList(registeredJobNames));
|
||||
runner.setJobRegistry(jobRegistry);
|
||||
return runner;
|
||||
}
|
||||
|
||||
private Job mockJob(String name) {
|
||||
Job job = mock(Job.class);
|
||||
given(job.getName()).willReturn(name);
|
||||
return job;
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class BatchDataSourceConfiguration {
|
||||
|
||||
@Bean
|
||||
DataSource normalDataSource() {
|
||||
return DataSourceBuilder.create().url("jdbc:h2:mem:normal").username("sa").build();
|
||||
}
|
||||
|
||||
@BatchDataSource
|
||||
@Bean(defaultCandidate = false)
|
||||
DataSource batchDataSource() {
|
||||
return DataSourceBuilder.create().url("jdbc:h2:mem:batchdatasource").username("sa").build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class BatchTransactionManagerConfiguration {
|
||||
|
||||
@Bean
|
||||
DataSource dataSource() {
|
||||
return DataSourceBuilder.create().url("jdbc:h2:mem:database").username("sa").build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
PlatformTransactionManager normalTransactionManager() {
|
||||
return mock(PlatformTransactionManager.class);
|
||||
}
|
||||
|
||||
@BatchTransactionManager
|
||||
@Bean(defaultCandidate = false)
|
||||
PlatformTransactionManager batchTransactionManager() {
|
||||
return mock(PlatformTransactionManager.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class BatchTaskExecutorConfiguration {
|
||||
|
||||
@Bean
|
||||
TaskExecutor taskExecutor() {
|
||||
return new SyncTaskExecutor();
|
||||
}
|
||||
|
||||
@BatchTaskExecutor
|
||||
@Bean(defaultCandidate = false)
|
||||
TaskExecutor batchTaskExecutor() {
|
||||
return new SimpleAsyncTaskExecutor();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class EmptyConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@TestAutoConfigurationPackage(City.class)
|
||||
static class TestJpaConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class EntityManagerFactoryConfiguration {
|
||||
|
||||
@Bean
|
||||
EntityManagerFactory entityManagerFactory() {
|
||||
return mock(EntityManagerFactory.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class NamedJobConfigurationWithRegisteredAndLocalJob {
|
||||
|
||||
@Autowired
|
||||
private JobRepository jobRepository;
|
||||
|
||||
@Bean
|
||||
Job discreteJob() {
|
||||
AbstractJob job = new AbstractJob("discreteRegisteredJob") {
|
||||
|
||||
private static int count = 0;
|
||||
|
||||
@Override
|
||||
public Collection<String> getStepNames() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Step getStep(String stepName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doExecute(JobExecution execution) {
|
||||
if (count == 0) {
|
||||
execution.setStatus(BatchStatus.COMPLETED);
|
||||
}
|
||||
else {
|
||||
execution.setStatus(BatchStatus.FAILED);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
};
|
||||
job.setJobRepository(this.jobRepository);
|
||||
return job;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class NamedJobConfigurationWithLocalJob {
|
||||
|
||||
@Autowired
|
||||
private JobRepository jobRepository;
|
||||
|
||||
@Bean
|
||||
Job discreteJob() {
|
||||
AbstractJob job = new AbstractJob("discreteLocalJob") {
|
||||
|
||||
@Override
|
||||
public Collection<String> getStepNames() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Step getStep(String stepName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doExecute(JobExecution execution) {
|
||||
execution.setStatus(BatchStatus.COMPLETED);
|
||||
}
|
||||
};
|
||||
job.setJobRepository(this.jobRepository);
|
||||
return job;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class MultipleJobConfiguration {
|
||||
|
||||
@Autowired
|
||||
private JobRepository jobRepository;
|
||||
|
||||
@Bean
|
||||
Job discreteJob() {
|
||||
AbstractJob job = new AbstractJob("discreteLocalJob") {
|
||||
|
||||
@Override
|
||||
public Collection<String> getStepNames() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Step getStep(String stepName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doExecute(JobExecution execution) {
|
||||
execution.setStatus(BatchStatus.COMPLETED);
|
||||
}
|
||||
};
|
||||
job.setJobRepository(this.jobRepository);
|
||||
return job;
|
||||
}
|
||||
|
||||
@Bean
|
||||
Job job2() {
|
||||
return new Job() {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "discreteLocalJob2";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(JobExecution execution) {
|
||||
execution.setStatus(BatchStatus.COMPLETED);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class JobConfiguration {
|
||||
|
||||
@Autowired
|
||||
private JobRepository jobRepository;
|
||||
|
||||
@Bean
|
||||
Job job() {
|
||||
AbstractJob job = new AbstractJob() {
|
||||
|
||||
@Override
|
||||
public Collection<String> getStepNames() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Step getStep(String stepName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doExecute(JobExecution execution) {
|
||||
execution.setStatus(BatchStatus.COMPLETED);
|
||||
}
|
||||
};
|
||||
job.setJobRepository(this.jobRepository);
|
||||
return job;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class CustomBatchDatabaseInitializerConfiguration {
|
||||
|
||||
@Bean
|
||||
BatchDataSourceScriptDatabaseInitializer customInitializer(DataSource dataSource,
|
||||
BatchJdbcProperties properties) {
|
||||
return new BatchDataSourceScriptDatabaseInitializer(dataSource, properties);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class CustomDatabaseInitializerConfiguration {
|
||||
|
||||
@Bean
|
||||
DataSourceScriptDatabaseInitializer customInitializer(DataSource dataSource) {
|
||||
return new DataSourceScriptDatabaseInitializer(dataSource, new DatabaseInitializationSettings());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class CustomBatchConfiguration extends DefaultBatchConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@EnableBatchProcessing
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class EnableBatchProcessingConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class ConversionServiceCustomizersConfiguration {
|
||||
|
||||
@Bean
|
||||
@Order(1)
|
||||
BatchConversionServiceCustomizer batchConversionServiceCustomizer() {
|
||||
return mock(BatchConversionServiceCustomizer.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(2)
|
||||
BatchConversionServiceCustomizer anotherBatchConversionServiceCustomizer() {
|
||||
return mock(BatchConversionServiceCustomizer.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.batch.autoconfigure;
|
||||
package org.springframework.boot.batch.jdbc.autoconfigure;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
|
@ -25,8 +25,8 @@ import org.springframework.batch.core.launch.JobOperator;
|
|||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage;
|
||||
import org.springframework.boot.batch.autoconfigure.BatchAutoConfiguration.SpringBootBatchConfiguration;
|
||||
import org.springframework.boot.batch.autoconfigure.domain.City;
|
||||
import org.springframework.boot.batch.jdbc.autoconfigure.BatchJdbcAutoConfiguration.SpringBootBatchJdbcConfiguration;
|
||||
import org.springframework.boot.batch.jdbc.autoconfigure.domain.City;
|
||||
import org.springframework.boot.jdbc.autoconfigure.DataSourceTransactionManagerAutoConfiguration;
|
||||
import org.springframework.boot.jdbc.autoconfigure.EmbeddedDataSourceConfiguration;
|
||||
import org.springframework.boot.sql.init.DatabaseInitializationMode;
|
||||
|
@ -40,15 +40,15 @@ import org.springframework.transaction.annotation.Isolation;
|
|||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link BatchAutoConfiguration} when JPA is not on the classpath.
|
||||
* Tests for {@link BatchJdbcAutoConfiguration} when JPA is not on the classpath.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@ClassPathExclusions("hibernate-jpa-*.jar")
|
||||
class BatchAutoConfigurationWithoutJpaTests {
|
||||
class BatchJdbcAutoConfigurationWithoutJpaTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(BatchAutoConfiguration.class, TransactionAutoConfiguration.class,
|
||||
.withConfiguration(AutoConfigurations.of(BatchJdbcAutoConfiguration.class, TransactionAutoConfiguration.class,
|
||||
DataSourceTransactionManagerAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
|
@ -58,7 +58,7 @@ class BatchAutoConfigurationWithoutJpaTests {
|
|||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(JobOperator.class);
|
||||
assertThat(context).hasSingleBean(JobRepository.class);
|
||||
assertThat(context.getBean(BatchProperties.class).getJdbc().getInitializeSchema())
|
||||
assertThat(context.getBean(BatchJdbcProperties.class).getInitializeSchema())
|
||||
.isEqualTo(DatabaseInitializationMode.EMBEDDED);
|
||||
assertThat(new JdbcTemplate(context.getBean(DataSource.class))
|
||||
.queryForList("select * from BATCH_JOB_EXECUTION")).isEmpty();
|
||||
|
@ -89,7 +89,7 @@ class BatchAutoConfigurationWithoutJpaTests {
|
|||
.withPropertyValues("spring.datasource.generate-unique-name=true",
|
||||
"spring.batch.jdbc.isolation-level-for-create=read_committed")
|
||||
.run((context) -> assertThat(
|
||||
context.getBean(SpringBootBatchConfiguration.class).getIsolationLevelForCreate())
|
||||
context.getBean(SpringBootBatchJdbcConfiguration.class).getIsolationLevelForCreate())
|
||||
.isEqualTo(Isolation.READ_COMMITTED));
|
||||
}
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.batch.autoconfigure;
|
||||
package org.springframework.boot.batch.jdbc.autoconfigure;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
@ -23,15 +23,15 @@ import org.springframework.batch.core.configuration.support.JdbcDefaultBatchConf
|
|||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link BatchProperties}.
|
||||
* Tests for {@link BatchJdbcProperties}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class BatchPropertiesTests {
|
||||
class BatchJdbcPropertiesTests {
|
||||
|
||||
@Test
|
||||
void validateTransactionStateDefaultMatchesSpringBatchDefault() {
|
||||
assertThat(new BatchProperties().getJdbc().isValidateTransactionState())
|
||||
assertThat(new BatchJdbcProperties().isValidateTransactionState())
|
||||
.isEqualTo(new TestBatchConfiguration().getValidateTransactionState());
|
||||
}
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.batch.autoconfigure.domain;
|
||||
package org.springframework.boot.batch.jdbc.autoconfigure.domain;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
|
@ -26,18 +26,12 @@ description = "Spring Boot Batch"
|
|||
|
||||
dependencies {
|
||||
api(project(":core:spring-boot"))
|
||||
api(project(":module:spring-boot-jdbc"))
|
||||
api("org.springframework.batch:spring-batch-core")
|
||||
|
||||
implementation(project(":module:spring-boot-tx"))
|
||||
|
||||
optional(project(":core:spring-boot-autoconfigure"))
|
||||
optional(project(":module:spring-boot-hibernate"))
|
||||
optional(project(":module:spring-boot-micrometer-observation"))
|
||||
|
||||
testImplementation(project(":core:spring-boot-test"))
|
||||
testImplementation(project(":module:spring-boot-flyway"))
|
||||
testImplementation(project(":module:spring-boot-liquibase"))
|
||||
testImplementation(project(":test-support:spring-boot-test-support"))
|
||||
testImplementation(testFixtures(project(":core:spring-boot-autoconfigure")))
|
||||
testImplementation("io.micrometer:micrometer-observation-test")
|
||||
|
@ -45,6 +39,4 @@ dependencies {
|
|||
testRuntimeOnly("ch.qos.logback:logback-classic")
|
||||
testRuntimeOnly("com.fasterxml.jackson.core:jackson-databind")
|
||||
testRuntimeOnly("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")
|
||||
testRuntimeOnly("com.h2database:h2")
|
||||
testRuntimeOnly("com.zaxxer:HikariCP")
|
||||
}
|
||||
|
|
|
@ -16,163 +16,47 @@
|
|||
|
||||
package org.springframework.boot.batch.autoconfigure;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
|
||||
import org.springframework.batch.core.configuration.support.DefaultBatchConfiguration;
|
||||
import org.springframework.batch.core.configuration.support.JdbcDefaultBatchConfiguration;
|
||||
import org.springframework.batch.core.converter.JobParametersConverter;
|
||||
import org.springframework.batch.core.launch.JobOperator;
|
||||
import org.springframework.batch.core.repository.ExecutionContextSerializer;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.ExitCodeGenerator;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.sql.autoconfigure.init.OnDatabaseInitializationCondition;
|
||||
import org.springframework.boot.sql.init.dependency.DatabaseInitializationDependencyConfigurer;
|
||||
import org.springframework.boot.transaction.autoconfigure.TransactionAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.convert.support.ConfigurableConversionService;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.jdbc.datasource.init.DatabasePopulator;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for Spring Batch. If a single job is
|
||||
* found in the context, it will be executed on startup.
|
||||
* <p>
|
||||
* Disable this behavior with {@literal spring.batch.job.enabled=false}).
|
||||
* <p>
|
||||
* If multiple jobs are found, a job name to execute on startup can be supplied by the
|
||||
* User with : {@literal spring.batch.job.name=job1}. In this case the Runner will first
|
||||
* find jobs registered as Beans, then those in the existing JobRegistry.
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for Spring Batch using an in-memory
|
||||
* store.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Eddú Meléndez
|
||||
* @author Kazuki Shimizu
|
||||
* @author Mahmoud Ben Hassine
|
||||
* @author Lars Uffmann
|
||||
* @author Lasse Wulff
|
||||
* @author Yanming Zhou
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@AutoConfiguration(after = { DataSourceAutoConfiguration.class, TransactionAutoConfiguration.class },
|
||||
afterName = "org.springframework.boot.hibernate.autoconfigure.HibernateJpaAutoConfiguration")
|
||||
@ConditionalOnClass({ JobOperator.class, DataSource.class, DatabasePopulator.class })
|
||||
@ConditionalOnBean({ DataSource.class, PlatformTransactionManager.class })
|
||||
@AutoConfiguration
|
||||
@ConditionalOnClass(JobOperator.class)
|
||||
@ConditionalOnMissingBean(value = DefaultBatchConfiguration.class, annotation = EnableBatchProcessing.class)
|
||||
@EnableConfigurationProperties(BatchProperties.class)
|
||||
@Import(DatabaseInitializationDependencyConfigurer.class)
|
||||
public final class BatchAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnBooleanProperty(name = "spring.batch.job.enabled", matchIfMissing = true)
|
||||
JobLauncherApplicationRunner jobLauncherApplicationRunner(JobOperator jobOperator, BatchProperties properties) {
|
||||
JobLauncherApplicationRunner runner = new JobLauncherApplicationRunner(jobOperator);
|
||||
String jobName = properties.getJob().getName();
|
||||
if (StringUtils.hasText(jobName)) {
|
||||
runner.setJobName(jobName);
|
||||
}
|
||||
return runner;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ExitCodeGenerator.class)
|
||||
JobExecutionExitCodeGenerator jobExecutionExitCodeGenerator() {
|
||||
return new JobExecutionExitCodeGenerator();
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class SpringBootBatchConfiguration extends JdbcDefaultBatchConfiguration {
|
||||
|
||||
private final DataSource dataSource;
|
||||
|
||||
private final PlatformTransactionManager transactionManager;
|
||||
static class SpringBootBatchDefaultConfiguration extends DefaultBatchConfiguration {
|
||||
|
||||
private final @Nullable TaskExecutor taskExecutor;
|
||||
|
||||
private final BatchProperties properties;
|
||||
|
||||
private final List<BatchConversionServiceCustomizer> batchConversionServiceCustomizers;
|
||||
|
||||
private final @Nullable ExecutionContextSerializer executionContextSerializer;
|
||||
|
||||
private final @Nullable JobParametersConverter jobParametersConverter;
|
||||
|
||||
SpringBootBatchConfiguration(DataSource dataSource, @BatchDataSource ObjectProvider<DataSource> batchDataSource,
|
||||
PlatformTransactionManager transactionManager,
|
||||
@BatchTransactionManager ObjectProvider<PlatformTransactionManager> batchTransactionManager,
|
||||
@BatchTaskExecutor ObjectProvider<TaskExecutor> batchTaskExecutor, BatchProperties properties,
|
||||
ObjectProvider<BatchConversionServiceCustomizer> batchConversionServiceCustomizers,
|
||||
ObjectProvider<ExecutionContextSerializer> executionContextSerializer,
|
||||
SpringBootBatchDefaultConfiguration(@BatchTaskExecutor ObjectProvider<TaskExecutor> batchTaskExecutor,
|
||||
ObjectProvider<JobParametersConverter> jobParametersConverter) {
|
||||
this.dataSource = batchDataSource.getIfAvailable(() -> dataSource);
|
||||
this.transactionManager = batchTransactionManager.getIfAvailable(() -> transactionManager);
|
||||
this.taskExecutor = batchTaskExecutor.getIfAvailable();
|
||||
this.properties = properties;
|
||||
this.batchConversionServiceCustomizers = batchConversionServiceCustomizers.orderedStream().toList();
|
||||
this.executionContextSerializer = executionContextSerializer.getIfAvailable();
|
||||
this.jobParametersConverter = jobParametersConverter.getIfAvailable();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataSource getDataSource() {
|
||||
return this.dataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PlatformTransactionManager getTransactionManager() {
|
||||
return this.transactionManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTablePrefix() {
|
||||
String tablePrefix = this.properties.getJdbc().getTablePrefix();
|
||||
return (tablePrefix != null) ? tablePrefix : super.getTablePrefix();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean getValidateTransactionState() {
|
||||
return this.properties.getJdbc().isValidateTransactionState();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Isolation getIsolationLevelForCreate() {
|
||||
Isolation isolation = this.properties.getJdbc().getIsolationLevelForCreate();
|
||||
return (isolation != null) ? isolation : super.getIsolationLevelForCreate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConfigurableConversionService getConversionService() {
|
||||
ConfigurableConversionService conversionService = super.getConversionService();
|
||||
for (BatchConversionServiceCustomizer customizer : this.batchConversionServiceCustomizers) {
|
||||
customizer.customize(conversionService);
|
||||
}
|
||||
return conversionService;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ExecutionContextSerializer getExecutionContextSerializer() {
|
||||
return (this.executionContextSerializer != null) ? this.executionContextSerializer
|
||||
: super.getExecutionContextSerializer();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated(since = "4.0.0", forRemoval = true)
|
||||
@SuppressWarnings("removal")
|
||||
|
@ -188,26 +72,4 @@ public final class BatchAutoConfiguration {
|
|||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Conditional(OnBatchDatasourceInitializationCondition.class)
|
||||
static class DataSourceInitializerConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
BatchDataSourceScriptDatabaseInitializer batchDataSourceInitializer(DataSource dataSource,
|
||||
@BatchDataSource ObjectProvider<DataSource> batchDataSource, BatchProperties properties) {
|
||||
return new BatchDataSourceScriptDatabaseInitializer(batchDataSource.getIfAvailable(() -> dataSource),
|
||||
properties.getJdbc());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class OnBatchDatasourceInitializationCondition extends OnDatabaseInitializationCondition {
|
||||
|
||||
OnBatchDatasourceInitializationCondition() {
|
||||
super("Batch", "spring.batch.jdbc.initialize-schema");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright 2012-present 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.batch.autoconfigure;
|
||||
|
||||
import org.springframework.batch.core.launch.JobOperator;
|
||||
import org.springframework.boot.ExitCodeGenerator;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for Spring Batch. If a single job is
|
||||
* found in the context, it will be executed on startup.
|
||||
* <p>
|
||||
* Disable this behavior with {@literal spring.batch.job.enabled=false}).
|
||||
* <p>
|
||||
* If multiple jobs are found, a job name to execute on startup can be supplied by the
|
||||
* User with : {@literal spring.batch.job.name=job1}. In this case the Runner will first
|
||||
* find jobs registered as Beans, then those in the existing JobRegistry.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Eddú Meléndez
|
||||
* @author Kazuki Shimizu
|
||||
* @author Mahmoud Ben Hassine
|
||||
* @author Lars Uffmann
|
||||
* @author Lasse Wulff
|
||||
* @author Yanming Zhou
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@AutoConfiguration(after = BatchAutoConfiguration.class)
|
||||
@ConditionalOnClass(JobOperator.class)
|
||||
@ConditionalOnBean(JobOperator.class)
|
||||
@EnableConfigurationProperties(BatchProperties.class)
|
||||
public final class BatchJobLauncherAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnBooleanProperty(name = "spring.batch.job.enabled", matchIfMissing = true)
|
||||
JobLauncherApplicationRunner jobLauncherApplicationRunner(JobOperator jobOperator, BatchProperties properties) {
|
||||
JobLauncherApplicationRunner runner = new JobLauncherApplicationRunner(jobOperator);
|
||||
String jobName = properties.getJob().getName();
|
||||
if (StringUtils.hasText(jobName)) {
|
||||
runner.setJobName(jobName);
|
||||
}
|
||||
return runner;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ExitCodeGenerator.class)
|
||||
JobExecutionExitCodeGenerator jobExecutionExitCodeGenerator() {
|
||||
return new JobExecutionExitCodeGenerator();
|
||||
}
|
||||
|
||||
}
|
|
@ -16,11 +16,7 @@
|
|||
|
||||
package org.springframework.boot.batch.autoconfigure;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.sql.init.DatabaseInitializationMode;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
|
||||
/**
|
||||
* Configuration properties for Spring Batch.
|
||||
|
@ -37,16 +33,10 @@ public class BatchProperties {
|
|||
|
||||
private final Job job = new Job();
|
||||
|
||||
private final Jdbc jdbc = new Jdbc();
|
||||
|
||||
public Job getJob() {
|
||||
return this.job;
|
||||
}
|
||||
|
||||
public Jdbc getJdbc() {
|
||||
return this.jdbc;
|
||||
}
|
||||
|
||||
public static class Job {
|
||||
|
||||
/**
|
||||
|
@ -65,90 +55,4 @@ public class BatchProperties {
|
|||
|
||||
}
|
||||
|
||||
public static class Jdbc {
|
||||
|
||||
private static final String DEFAULT_SCHEMA_LOCATION = "classpath:org/springframework/"
|
||||
+ "batch/core/schema-@@platform@@.sql";
|
||||
|
||||
/**
|
||||
* Whether to validate the transaction state.
|
||||
*/
|
||||
private boolean validateTransactionState = true;
|
||||
|
||||
/**
|
||||
* Transaction isolation level to use when creating job meta-data for new jobs.
|
||||
*/
|
||||
private @Nullable Isolation isolationLevelForCreate;
|
||||
|
||||
/**
|
||||
* Path to the SQL file to use to initialize the database schema.
|
||||
*/
|
||||
private String schema = DEFAULT_SCHEMA_LOCATION;
|
||||
|
||||
/**
|
||||
* Platform to use in initialization scripts if the @@platform@@ placeholder is
|
||||
* used. Auto-detected by default.
|
||||
*/
|
||||
private @Nullable String platform;
|
||||
|
||||
/**
|
||||
* Table prefix for all the batch meta-data tables.
|
||||
*/
|
||||
private @Nullable String tablePrefix;
|
||||
|
||||
/**
|
||||
* Database schema initialization mode.
|
||||
*/
|
||||
private DatabaseInitializationMode initializeSchema = DatabaseInitializationMode.EMBEDDED;
|
||||
|
||||
public boolean isValidateTransactionState() {
|
||||
return this.validateTransactionState;
|
||||
}
|
||||
|
||||
public void setValidateTransactionState(boolean validateTransactionState) {
|
||||
this.validateTransactionState = validateTransactionState;
|
||||
}
|
||||
|
||||
public @Nullable Isolation getIsolationLevelForCreate() {
|
||||
return this.isolationLevelForCreate;
|
||||
}
|
||||
|
||||
public void setIsolationLevelForCreate(@Nullable Isolation isolationLevelForCreate) {
|
||||
this.isolationLevelForCreate = isolationLevelForCreate;
|
||||
}
|
||||
|
||||
public String getSchema() {
|
||||
return this.schema;
|
||||
}
|
||||
|
||||
public void setSchema(String schema) {
|
||||
this.schema = schema;
|
||||
}
|
||||
|
||||
public @Nullable String getPlatform() {
|
||||
return this.platform;
|
||||
}
|
||||
|
||||
public void setPlatform(@Nullable String platform) {
|
||||
this.platform = platform;
|
||||
}
|
||||
|
||||
public @Nullable String getTablePrefix() {
|
||||
return this.tablePrefix;
|
||||
}
|
||||
|
||||
public void setTablePrefix(@Nullable String tablePrefix) {
|
||||
this.tablePrefix = tablePrefix;
|
||||
}
|
||||
|
||||
public DatabaseInitializationMode getInitializeSchema() {
|
||||
return this.initializeSchema;
|
||||
}
|
||||
|
||||
public void setInitializeSchema(DatabaseInitializationMode initializeSchema) {
|
||||
this.initializeSchema = initializeSchema;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,43 +1,10 @@
|
|||
{
|
||||
"properties": [
|
||||
{
|
||||
"name": "spring.batch.initialize-schema",
|
||||
"type": "org.springframework.boot.sql.init.DatabaseInitializationMode",
|
||||
"deprecation": {
|
||||
"replacement": "spring.batch.jdbc.initialize-schema",
|
||||
"level": "error"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "spring.batch.initializer.enabled",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "Create the required batch tables on startup if necessary. Enabled automatically\n if no custom table prefix is set or if a custom schema is configured.",
|
||||
"deprecation": {
|
||||
"replacement": "spring.batch.jdbc.initialize-schema",
|
||||
"level": "error"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "spring.batch.job.enabled",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "Whether to execute a Spring Batch job on startup. When multiple jobs are present in the context, set spring.batch.job.name to identify the job to execute.",
|
||||
"defaultValue": true
|
||||
},
|
||||
{
|
||||
"name": "spring.batch.schema",
|
||||
"type": "java.lang.String",
|
||||
"deprecation": {
|
||||
"replacement": "spring.batch.jdbc.schema",
|
||||
"level": "error"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "spring.batch.table-prefix",
|
||||
"type": "java.lang.String",
|
||||
"deprecation": {
|
||||
"replacement": "spring.batch.jdbc.table-prefix",
|
||||
"level": "error"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,2 +1,3 @@
|
|||
org.springframework.boot.batch.autoconfigure.BatchAutoConfiguration
|
||||
org.springframework.boot.batch.autoconfigure.BatchJobLauncherAutoConfiguration
|
||||
org.springframework.boot.batch.autoconfigure.observation.BatchObservationAutoConfiguration
|
||||
|
|
|
@ -17,86 +17,42 @@
|
|||
package org.springframework.boot.batch.autoconfigure;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import jakarta.persistence.EntityManagerFactory;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.batch.core.BatchStatus;
|
||||
import org.springframework.batch.core.configuration.JobRegistry;
|
||||
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
|
||||
import org.springframework.batch.core.configuration.support.DefaultBatchConfiguration;
|
||||
import org.springframework.batch.core.converter.DefaultJobParametersConverter;
|
||||
import org.springframework.batch.core.converter.JobParametersConverter;
|
||||
import org.springframework.batch.core.converter.JsonJobParametersConverter;
|
||||
import org.springframework.batch.core.job.AbstractJob;
|
||||
import org.springframework.batch.core.job.Job;
|
||||
import org.springframework.batch.core.job.JobExecution;
|
||||
import org.springframework.batch.core.job.parameters.JobParameters;
|
||||
import org.springframework.batch.core.job.parameters.JobParametersBuilder;
|
||||
import org.springframework.batch.core.launch.JobOperator;
|
||||
import org.springframework.batch.core.repository.ExecutionContextSerializer;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.core.repository.dao.DefaultExecutionContextSerializer;
|
||||
import org.springframework.batch.core.repository.dao.Jackson2ExecutionContextStringSerializer;
|
||||
import org.springframework.batch.core.step.Step;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.DefaultApplicationArguments;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage;
|
||||
import org.springframework.boot.batch.autoconfigure.BatchAutoConfiguration.SpringBootBatchConfiguration;
|
||||
import org.springframework.boot.batch.autoconfigure.domain.City;
|
||||
import org.springframework.boot.flyway.autoconfigure.FlywayAutoConfiguration;
|
||||
import org.springframework.boot.hibernate.autoconfigure.HibernateJpaAutoConfiguration;
|
||||
import org.springframework.boot.jdbc.DataSourceBuilder;
|
||||
import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.jdbc.autoconfigure.DataSourceTransactionManagerAutoConfiguration;
|
||||
import org.springframework.boot.jdbc.autoconfigure.EmbeddedDataSourceConfiguration;
|
||||
import org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer;
|
||||
import org.springframework.boot.liquibase.autoconfigure.LiquibaseAutoConfiguration;
|
||||
import org.springframework.boot.sql.init.DatabaseInitializationMode;
|
||||
import org.springframework.boot.sql.init.DatabaseInitializationSettings;
|
||||
import org.springframework.boot.batch.autoconfigure.BatchAutoConfiguration.SpringBootBatchDefaultConfiguration;
|
||||
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.boot.test.system.OutputCaptureExtension;
|
||||
import org.springframework.boot.testsupport.classpath.resources.WithPackageResources;
|
||||
import org.springframework.boot.testsupport.classpath.resources.WithResource;
|
||||
import org.springframework.boot.transaction.autoconfigure.TransactionAutoConfiguration;
|
||||
import org.springframework.boot.transaction.autoconfigure.TransactionManagerCustomizationAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.convert.support.ConfigurableConversionService;
|
||||
import org.springframework.core.task.AsyncTaskExecutor;
|
||||
import org.springframework.core.task.SimpleAsyncTaskExecutor;
|
||||
import org.springframework.core.task.SyncTaskExecutor;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.jdbc.BadSqlGrammarException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
||||
import org.springframework.jdbc.datasource.init.DatabasePopulator;
|
||||
import org.springframework.orm.jpa.JpaTransactionManager;
|
||||
import org.springframework.test.util.AopTestUtils;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link BatchAutoConfiguration}.
|
||||
* Tests for {@link BatchJobLauncherAutoConfiguration}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Stephane Nicoll
|
||||
|
@ -110,336 +66,43 @@ import static org.mockito.Mockito.mock;
|
|||
@ExtendWith(OutputCaptureExtension.class)
|
||||
class BatchAutoConfigurationTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withConfiguration(
|
||||
AutoConfigurations.of(BatchAutoConfiguration.class, TransactionManagerCustomizationAutoConfiguration.class,
|
||||
TransactionAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class));
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(BatchAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void testDefaultContext() {
|
||||
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class).run((context) -> {
|
||||
this.contextRunner.run((context) -> {
|
||||
assertThat(context).hasSingleBean(JobRepository.class);
|
||||
assertThat(context).hasSingleBean(JobOperator.class);
|
||||
assertThat(context.getBean(BatchProperties.class).getJdbc().getInitializeSchema())
|
||||
.isEqualTo(DatabaseInitializationMode.EMBEDDED);
|
||||
assertThat(new JdbcTemplate(context.getBean(DataSource.class))
|
||||
.queryForList("select * from BATCH_JOB_EXECUTION")).isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoconfigurationBacksOffEntirelyIfSpringJdbcAbsent() {
|
||||
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
|
||||
.withClassLoader(new FilteredClassLoader(DatabasePopulator.class))
|
||||
.run((context) -> {
|
||||
assertThat(context).doesNotHaveBean(JobLauncherApplicationRunner.class);
|
||||
assertThat(context).doesNotHaveBean(BatchDataSourceScriptDatabaseInitializer.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoConfigurationBacksOffWhenUserEnablesBatchProcessing() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(EnableBatchProcessingConfiguration.class, EmbeddedDataSourceConfiguration.class)
|
||||
.withClassLoader(new FilteredClassLoader(DatabasePopulator.class))
|
||||
.run((context) -> assertThat(context).doesNotHaveBean(SpringBootBatchConfiguration.class));
|
||||
this.contextRunner.withUserConfiguration(EnableBatchProcessingConfiguration.class)
|
||||
.run((context) -> assertThat(context).doesNotHaveBean(SpringBootBatchDefaultConfiguration.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoConfigurationBacksOffWhenUserProvidesBatchConfiguration() {
|
||||
this.contextRunner.withUserConfiguration(CustomBatchConfiguration.class, EmbeddedDataSourceConfiguration.class)
|
||||
this.contextRunner.withUserConfiguration(CustomBatchConfiguration.class)
|
||||
.withClassLoader(new FilteredClassLoader(DatabasePopulator.class))
|
||||
.run((context) -> assertThat(context).doesNotHaveBean(SpringBootBatchConfiguration.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDefinesAndLaunchesJob() {
|
||||
this.contextRunner.withUserConfiguration(JobConfiguration.class, EmbeddedDataSourceConfiguration.class)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(JobOperator.class);
|
||||
context.getBean(JobLauncherApplicationRunner.class)
|
||||
.run(new DefaultApplicationArguments("jobParam=test"));
|
||||
JobParameters jobParameters = new JobParametersBuilder().addString("jobParam", "test")
|
||||
.toJobParameters();
|
||||
assertThat(context.getBean(JobRepository.class).getLastJobExecution("job", jobParameters)).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDefinesAndLaunchesJobIgnoreOptionArguments() {
|
||||
this.contextRunner.withUserConfiguration(JobConfiguration.class, EmbeddedDataSourceConfiguration.class)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(JobOperator.class);
|
||||
context.getBean(JobLauncherApplicationRunner.class)
|
||||
.run(new DefaultApplicationArguments("--spring.property=value", "jobParam=test"));
|
||||
JobParameters jobParameters = new JobParametersBuilder().addString("jobParam", "test")
|
||||
.toJobParameters();
|
||||
assertThat(context.getBean(JobRepository.class).getLastJobExecution("job", jobParameters)).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRegisteredAndLocalJob() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(NamedJobConfigurationWithRegisteredAndLocalJob.class,
|
||||
EmbeddedDataSourceConfiguration.class)
|
||||
.withPropertyValues("spring.batch.job.name:discreteRegisteredJob")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(JobOperator.class);
|
||||
context.getBean(JobLauncherApplicationRunner.class).run();
|
||||
assertThat(context.getBean(JobRepository.class)
|
||||
.getLastJobExecution("discreteRegisteredJob", new JobParameters())
|
||||
.getStatus()).isEqualTo(BatchStatus.COMPLETED);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDefinesAndLaunchesLocalJob() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(NamedJobConfigurationWithLocalJob.class, EmbeddedDataSourceConfiguration.class)
|
||||
.withPropertyValues("spring.batch.job.name:discreteLocalJob")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(JobOperator.class);
|
||||
context.getBean(JobLauncherApplicationRunner.class).run();
|
||||
assertThat(context.getBean(JobRepository.class)
|
||||
.getLastJobExecution("discreteLocalJob", new JobParameters())).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultipleJobsAndNoJobName() {
|
||||
this.contextRunner.withUserConfiguration(MultipleJobConfiguration.class, EmbeddedDataSourceConfiguration.class)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasFailed();
|
||||
assertThat(context.getStartupFailure().getCause().getMessage())
|
||||
.contains("Job name must be specified in case of multiple jobs");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultipleJobsAndJobName() {
|
||||
this.contextRunner.withUserConfiguration(MultipleJobConfiguration.class, EmbeddedDataSourceConfiguration.class)
|
||||
.withPropertyValues("spring.batch.job.name:discreteLocalJob")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(JobOperator.class);
|
||||
context.getBean(JobLauncherApplicationRunner.class).run();
|
||||
assertThat(context.getBean(JobRepository.class)
|
||||
.getLastJobExecution("discreteLocalJob", new JobParameters())).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDisableLaunchesJob() {
|
||||
this.contextRunner.withUserConfiguration(JobConfiguration.class, EmbeddedDataSourceConfiguration.class)
|
||||
.withPropertyValues("spring.batch.job.enabled:false")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(JobOperator.class);
|
||||
assertThat(context).doesNotHaveBean(CommandLineRunner.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDisableSchemaLoader() {
|
||||
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
|
||||
.withPropertyValues("spring.datasource.generate-unique-name=true",
|
||||
"spring.batch.jdbc.initialize-schema:never")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(JobOperator.class);
|
||||
assertThat(context.getBean(BatchProperties.class).getJdbc().getInitializeSchema())
|
||||
.isEqualTo(DatabaseInitializationMode.NEVER);
|
||||
assertThat(context).doesNotHaveBean(BatchDataSourceScriptDatabaseInitializer.class);
|
||||
assertThatExceptionOfType(BadSqlGrammarException.class)
|
||||
.isThrownBy(() -> new JdbcTemplate(context.getBean(DataSource.class))
|
||||
.queryForList("select * from BATCH_JOB_EXECUTION"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUsingJpa() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(TestJpaConfiguration.class, EmbeddedDataSourceConfiguration.class,
|
||||
HibernateJpaAutoConfiguration.class)
|
||||
.run((context) -> {
|
||||
PlatformTransactionManager transactionManager = context.getBean(PlatformTransactionManager.class);
|
||||
// It's a lazy proxy, but it does render its target if you ask for
|
||||
// toString():
|
||||
assertThat(transactionManager.toString()).contains("JpaTransactionManager");
|
||||
assertThat(context).hasSingleBean(EntityManagerFactory.class);
|
||||
// Ensure the JobRepository can be used (no problem with isolation
|
||||
// level)
|
||||
assertThat(context.getBean(JobRepository.class).getLastJobExecution("job", new JobParameters()))
|
||||
.isNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithPackageResources("custom-schema.sql")
|
||||
void testRenamePrefix() {
|
||||
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
|
||||
.withPropertyValues("spring.datasource.generate-unique-name=true",
|
||||
"spring.batch.jdbc.schema:classpath:custom-schema.sql", "spring.batch.jdbc.table-prefix:PREFIX_")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(JobOperator.class);
|
||||
assertThat(context.getBean(BatchProperties.class).getJdbc().getInitializeSchema())
|
||||
.isEqualTo(DatabaseInitializationMode.EMBEDDED);
|
||||
assertThat(new JdbcTemplate(context.getBean(DataSource.class))
|
||||
.queryForList("select * from PREFIX_JOB_EXECUTION")).isEmpty();
|
||||
JobRepository jobRepository = context.getBean(JobRepository.class);
|
||||
assertThat(jobRepository.findRunningJobExecutions("test")).isEmpty();
|
||||
assertThat(jobRepository.getLastJobExecution("test", new JobParameters())).isNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeJpaTransactionManagerUsingProperties() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(TestJpaConfiguration.class, EmbeddedDataSourceConfiguration.class,
|
||||
HibernateJpaAutoConfiguration.class)
|
||||
.withPropertyValues("spring.transaction.default-timeout:30",
|
||||
"spring.transaction.rollback-on-commit-failure:true")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(BatchAutoConfiguration.class);
|
||||
JpaTransactionManager transactionManager = JpaTransactionManager.class
|
||||
.cast(context.getBean(SpringBootBatchConfiguration.class).getTransactionManager());
|
||||
assertThat(transactionManager.getDefaultTimeout()).isEqualTo(30);
|
||||
assertThat(transactionManager.isRollbackOnCommitFailure()).isTrue();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeDataSourceTransactionManagerUsingProperties() {
|
||||
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
|
||||
.withPropertyValues("spring.transaction.default-timeout:30",
|
||||
"spring.transaction.rollback-on-commit-failure:true")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(SpringBootBatchConfiguration.class);
|
||||
DataSourceTransactionManager transactionManager = DataSourceTransactionManager.class
|
||||
.cast(context.getBean(SpringBootBatchConfiguration.class).getTransactionManager());
|
||||
assertThat(transactionManager.getDefaultTimeout()).isEqualTo(30);
|
||||
assertThat(transactionManager.isRollbackOnCommitFailure()).isTrue();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBatchDataSource() {
|
||||
this.contextRunner.withUserConfiguration(BatchDataSourceConfiguration.class).run((context) -> {
|
||||
assertThat(context).hasSingleBean(SpringBootBatchConfiguration.class)
|
||||
.hasSingleBean(BatchDataSourceScriptDatabaseInitializer.class)
|
||||
.hasBean("batchDataSource");
|
||||
DataSource batchDataSource = context.getBean("batchDataSource", DataSource.class);
|
||||
assertThat(context.getBean(SpringBootBatchConfiguration.class).getDataSource()).isEqualTo(batchDataSource);
|
||||
assertThat(context.getBean(BatchDataSourceScriptDatabaseInitializer.class))
|
||||
.hasFieldOrPropertyWithValue("dataSource", batchDataSource);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBatchTransactionManager() {
|
||||
this.contextRunner.withUserConfiguration(BatchTransactionManagerConfiguration.class).run((context) -> {
|
||||
assertThat(context).hasSingleBean(SpringBootBatchConfiguration.class);
|
||||
PlatformTransactionManager batchTransactionManager = context.getBean("batchTransactionManager",
|
||||
PlatformTransactionManager.class);
|
||||
assertThat(context.getBean(SpringBootBatchConfiguration.class).getTransactionManager())
|
||||
.isEqualTo(batchTransactionManager);
|
||||
});
|
||||
.run((context) -> assertThat(context).doesNotHaveBean(SpringBootBatchDefaultConfiguration.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBatchTaskExecutor() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(BatchTaskExecutorConfiguration.class, EmbeddedDataSourceConfiguration.class)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(SpringBootBatchConfiguration.class).hasBean("batchTaskExecutor");
|
||||
this.contextRunner.withUserConfiguration(BatchTaskExecutorConfiguration.class).run((context) -> {
|
||||
assertThat(context).hasSingleBean(SpringBootBatchDefaultConfiguration.class).hasBean("batchTaskExecutor");
|
||||
TaskExecutor batchTaskExecutor = context.getBean("batchTaskExecutor", TaskExecutor.class);
|
||||
assertThat(batchTaskExecutor).isInstanceOf(AsyncTaskExecutor.class);
|
||||
assertThat(context.getBean(SpringBootBatchConfiguration.class).getTaskExecutor())
|
||||
assertThat(context.getBean(SpringBootBatchDefaultConfiguration.class).getTaskExecutor())
|
||||
.isEqualTo(batchTaskExecutor);
|
||||
JobOperator jobOperator = AopTestUtils.getTargetObject(context.getBean(JobOperator.class));
|
||||
assertThat(jobOperator).hasFieldOrPropertyWithValue("taskExecutor", batchTaskExecutor);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void jobRepositoryBeansDependOnBatchDataSourceInitializer() {
|
||||
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class).run((context) -> {
|
||||
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
|
||||
String[] jobRepositoryNames = beanFactory.getBeanNamesForType(JobRepository.class);
|
||||
assertThat(jobRepositoryNames).isNotEmpty();
|
||||
for (String jobRepositoryName : jobRepositoryNames) {
|
||||
assertThat(beanFactory.getBeanDefinition(jobRepositoryName).getDependsOn())
|
||||
.contains("batchDataSourceInitializer");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void jobRepositoryBeansDependOnFlyway() {
|
||||
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, FlywayAutoConfiguration.class)
|
||||
.withPropertyValues("spring.batch.jdbc.initialize-schema=never")
|
||||
.run((context) -> {
|
||||
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
|
||||
String[] jobRepositoryNames = beanFactory.getBeanNamesForType(JobRepository.class);
|
||||
assertThat(jobRepositoryNames).isNotEmpty();
|
||||
for (String jobRepositoryName : jobRepositoryNames) {
|
||||
assertThat(beanFactory.getBeanDefinition(jobRepositoryName).getDependsOn()).contains("flyway",
|
||||
"flywayInitializer");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithResource(name = "db/changelog/db.changelog-master.yaml", content = "databaseChangeLog:")
|
||||
void jobRepositoryBeansDependOnLiquibase() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(EmbeddedDataSourceConfiguration.class, LiquibaseAutoConfiguration.class)
|
||||
.withPropertyValues("spring.batch.jdbc.initialize-schema=never")
|
||||
.run((context) -> {
|
||||
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
|
||||
String[] jobRepositoryNames = beanFactory.getBeanNamesForType(JobRepository.class);
|
||||
assertThat(jobRepositoryNames).isNotEmpty();
|
||||
for (String jobRepositoryName : jobRepositoryNames) {
|
||||
assertThat(beanFactory.getBeanDefinition(jobRepositoryName).getDependsOn()).contains("liquibase");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenTheUserDefinesTheirOwnBatchDatabaseInitializerThenTheAutoConfiguredInitializerBacksOff() {
|
||||
this.contextRunner.withUserConfiguration(CustomBatchDatabaseInitializerConfiguration.class)
|
||||
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
|
||||
DataSourceTransactionManagerAutoConfiguration.class))
|
||||
.run((context) -> assertThat(context).hasSingleBean(BatchDataSourceScriptDatabaseInitializer.class)
|
||||
.doesNotHaveBean("batchDataSourceScriptDatabaseInitializer")
|
||||
.hasBean("customInitializer"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenTheUserDefinesTheirOwnDatabaseInitializerThenTheAutoConfiguredBatchInitializerRemains() {
|
||||
this.contextRunner.withUserConfiguration(CustomDatabaseInitializerConfiguration.class)
|
||||
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
|
||||
DataSourceTransactionManagerAutoConfiguration.class))
|
||||
.run((context) -> assertThat(context).hasSingleBean(BatchDataSourceScriptDatabaseInitializer.class)
|
||||
.hasBean("customInitializer"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void conversionServiceCustomizersAreCalled() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(EmbeddedDataSourceConfiguration.class,
|
||||
ConversionServiceCustomizersConfiguration.class)
|
||||
.run((context) -> {
|
||||
BatchConversionServiceCustomizer customizer = context.getBean("batchConversionServiceCustomizer",
|
||||
BatchConversionServiceCustomizer.class);
|
||||
BatchConversionServiceCustomizer anotherCustomizer = context
|
||||
.getBean("anotherBatchConversionServiceCustomizer", BatchConversionServiceCustomizer.class);
|
||||
InOrder inOrder = Mockito.inOrder(customizer, anotherCustomizer);
|
||||
ConfigurableConversionService configurableConversionService = context
|
||||
.getBean(SpringBootBatchConfiguration.class)
|
||||
.getConversionService();
|
||||
inOrder.verify(customizer).customize(configurableConversionService);
|
||||
inOrder.verify(anotherCustomizer).customize(configurableConversionService);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenTheUserDefinesAJobNameAsJobInstanceValidates() {
|
||||
JobLauncherApplicationRunner runner = createInstance("another");
|
||||
|
@ -472,49 +135,13 @@ class BatchAutoConfigurationTests {
|
|||
.withMessage("No job found with name 'three'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void customExecutionContextSerializerIsUsed() {
|
||||
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
|
||||
.withBean(ExecutionContextSerializer.class, Jackson2ExecutionContextStringSerializer::new)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(Jackson2ExecutionContextStringSerializer.class);
|
||||
assertThat(context.getBean(SpringBootBatchConfiguration.class).getExecutionContextSerializer())
|
||||
.isInstanceOf(Jackson2ExecutionContextStringSerializer.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void defaultExecutionContextSerializerIsUsed() {
|
||||
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class).run((context) -> {
|
||||
assertThat(context).doesNotHaveBean(ExecutionContextSerializer.class);
|
||||
assertThat(context.getBean(SpringBootBatchConfiguration.class).getExecutionContextSerializer())
|
||||
.isInstanceOf(DefaultExecutionContextSerializer.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void customJdbcPropertiesIsUsed() {
|
||||
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
|
||||
.withPropertyValues("spring.batch.jdbc.validate-transaction-state:false",
|
||||
"spring.batch.jdbc.isolation-level-for-create:READ_COMMITTED")
|
||||
.run((context) -> {
|
||||
SpringBootBatchConfiguration configuration = context.getBean(SpringBootBatchConfiguration.class);
|
||||
assertThat(configuration.getValidateTransactionState()).isEqualTo(false);
|
||||
assertThat(configuration.getIsolationLevelForCreate()).isEqualTo(Isolation.READ_COMMITTED);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Deprecated(since = "4.0.0", forRemoval = true)
|
||||
@SuppressWarnings("removal")
|
||||
void customJobParametersConverterIsUsed() {
|
||||
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
|
||||
.withBean(JobParametersConverter.class, JsonJobParametersConverter::new)
|
||||
.withPropertyValues("spring.datasource.generate-unique-name=true")
|
||||
.run((context) -> {
|
||||
this.contextRunner.withBean(JobParametersConverter.class, JsonJobParametersConverter::new).run((context) -> {
|
||||
assertThat(context).hasSingleBean(JsonJobParametersConverter.class);
|
||||
assertThat(context.getBean(SpringBootBatchConfiguration.class).getJobParametersConverter())
|
||||
assertThat(context.getBean(SpringBootBatchDefaultConfiguration.class).getJobParametersConverter())
|
||||
.isInstanceOf(JsonJobParametersConverter.class);
|
||||
});
|
||||
}
|
||||
|
@ -523,9 +150,9 @@ class BatchAutoConfigurationTests {
|
|||
@Deprecated(since = "4.0.0", forRemoval = true)
|
||||
@SuppressWarnings("removal")
|
||||
void defaultJobParametersConverterIsUsed() {
|
||||
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class).run((context) -> {
|
||||
this.contextRunner.run((context) -> {
|
||||
assertThat(context).doesNotHaveBean(JobParametersConverter.class);
|
||||
assertThat(context.getBean(SpringBootBatchConfiguration.class).getJobParametersConverter())
|
||||
assertThat(context.getBean(SpringBootBatchDefaultConfiguration.class).getJobParametersConverter())
|
||||
.isInstanceOf(DefaultJobParametersConverter.class);
|
||||
});
|
||||
}
|
||||
|
@ -544,44 +171,6 @@ class BatchAutoConfigurationTests {
|
|||
return job;
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class BatchDataSourceConfiguration {
|
||||
|
||||
@Bean
|
||||
DataSource normalDataSource() {
|
||||
return DataSourceBuilder.create().url("jdbc:h2:mem:normal").username("sa").build();
|
||||
}
|
||||
|
||||
@BatchDataSource
|
||||
@Bean(defaultCandidate = false)
|
||||
DataSource batchDataSource() {
|
||||
return DataSourceBuilder.create().url("jdbc:h2:mem:batchdatasource").username("sa").build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class BatchTransactionManagerConfiguration {
|
||||
|
||||
@Bean
|
||||
DataSource dataSource() {
|
||||
return DataSourceBuilder.create().url("jdbc:h2:mem:database").username("sa").build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
PlatformTransactionManager normalTransactionManager() {
|
||||
return mock(PlatformTransactionManager.class);
|
||||
}
|
||||
|
||||
@BatchTransactionManager
|
||||
@Bean(defaultCandidate = false)
|
||||
PlatformTransactionManager batchTransactionManager() {
|
||||
return mock(PlatformTransactionManager.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class BatchTaskExecutorConfiguration {
|
||||
|
||||
|
@ -603,188 +192,6 @@ class BatchAutoConfigurationTests {
|
|||
|
||||
}
|
||||
|
||||
@TestAutoConfigurationPackage(City.class)
|
||||
static class TestJpaConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class EntityManagerFactoryConfiguration {
|
||||
|
||||
@Bean
|
||||
EntityManagerFactory entityManagerFactory() {
|
||||
return mock(EntityManagerFactory.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class NamedJobConfigurationWithRegisteredAndLocalJob {
|
||||
|
||||
@Autowired
|
||||
private JobRepository jobRepository;
|
||||
|
||||
@Bean
|
||||
Job discreteJob() {
|
||||
AbstractJob job = new AbstractJob("discreteRegisteredJob") {
|
||||
|
||||
private static int count = 0;
|
||||
|
||||
@Override
|
||||
public Collection<String> getStepNames() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Step getStep(String stepName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doExecute(JobExecution execution) {
|
||||
if (count == 0) {
|
||||
execution.setStatus(BatchStatus.COMPLETED);
|
||||
}
|
||||
else {
|
||||
execution.setStatus(BatchStatus.FAILED);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
};
|
||||
job.setJobRepository(this.jobRepository);
|
||||
return job;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class NamedJobConfigurationWithLocalJob {
|
||||
|
||||
@Autowired
|
||||
private JobRepository jobRepository;
|
||||
|
||||
@Bean
|
||||
Job discreteJob() {
|
||||
AbstractJob job = new AbstractJob("discreteLocalJob") {
|
||||
|
||||
@Override
|
||||
public Collection<String> getStepNames() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Step getStep(String stepName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doExecute(JobExecution execution) {
|
||||
execution.setStatus(BatchStatus.COMPLETED);
|
||||
}
|
||||
};
|
||||
job.setJobRepository(this.jobRepository);
|
||||
return job;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class MultipleJobConfiguration {
|
||||
|
||||
@Autowired
|
||||
private JobRepository jobRepository;
|
||||
|
||||
@Bean
|
||||
Job discreteJob() {
|
||||
AbstractJob job = new AbstractJob("discreteLocalJob") {
|
||||
|
||||
@Override
|
||||
public Collection<String> getStepNames() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Step getStep(String stepName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doExecute(JobExecution execution) {
|
||||
execution.setStatus(BatchStatus.COMPLETED);
|
||||
}
|
||||
};
|
||||
job.setJobRepository(this.jobRepository);
|
||||
return job;
|
||||
}
|
||||
|
||||
@Bean
|
||||
Job job2() {
|
||||
return new Job() {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "discreteLocalJob2";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(JobExecution execution) {
|
||||
execution.setStatus(BatchStatus.COMPLETED);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class JobConfiguration {
|
||||
|
||||
@Autowired
|
||||
private JobRepository jobRepository;
|
||||
|
||||
@Bean
|
||||
Job job() {
|
||||
AbstractJob job = new AbstractJob() {
|
||||
|
||||
@Override
|
||||
public Collection<String> getStepNames() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Step getStep(String stepName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doExecute(JobExecution execution) {
|
||||
execution.setStatus(BatchStatus.COMPLETED);
|
||||
}
|
||||
};
|
||||
job.setJobRepository(this.jobRepository);
|
||||
return job;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class CustomBatchDatabaseInitializerConfiguration {
|
||||
|
||||
@Bean
|
||||
BatchDataSourceScriptDatabaseInitializer customInitializer(DataSource dataSource, BatchProperties properties) {
|
||||
return new BatchDataSourceScriptDatabaseInitializer(dataSource, properties.getJdbc());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class CustomDatabaseInitializerConfiguration {
|
||||
|
||||
@Bean
|
||||
DataSourceScriptDatabaseInitializer customInitializer(DataSource dataSource) {
|
||||
return new DataSourceScriptDatabaseInitializer(dataSource, new DatabaseInitializationSettings());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class CustomBatchConfiguration extends DefaultBatchConfiguration {
|
||||
|
||||
|
|
|
@ -0,0 +1,277 @@
|
|||
/*
|
||||
* Copyright 2012-present 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.batch.autoconfigure;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.batch.core.BatchStatus;
|
||||
import org.springframework.batch.core.job.AbstractJob;
|
||||
import org.springframework.batch.core.job.Job;
|
||||
import org.springframework.batch.core.job.JobExecution;
|
||||
import org.springframework.batch.core.job.parameters.JobParameters;
|
||||
import org.springframework.batch.core.job.parameters.JobParametersBuilder;
|
||||
import org.springframework.batch.core.launch.JobOperator;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.core.step.Step;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.DefaultApplicationArguments;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link BatchJobLauncherAutoConfiguration}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class BatchJobLauncherAutoConfigurationTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withConfiguration(
|
||||
AutoConfigurations.of(BatchAutoConfiguration.class, BatchJobLauncherAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void testDefinesAndLaunchesJob() {
|
||||
this.contextRunner.withUserConfiguration(JobConfiguration.class).run((context) -> {
|
||||
assertThat(context).hasSingleBean(JobOperator.class);
|
||||
context.getBean(JobLauncherApplicationRunner.class).run(new DefaultApplicationArguments("jobParam=test"));
|
||||
JobParameters jobParameters = new JobParametersBuilder().addString("jobParam", "test").toJobParameters();
|
||||
assertThat(context.getBean(JobRepository.class).getLastJobExecution("job", jobParameters)).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDefinesAndLaunchesJobIgnoreOptionArguments() {
|
||||
this.contextRunner.withUserConfiguration(JobConfiguration.class).run((context) -> {
|
||||
assertThat(context).hasSingleBean(JobOperator.class);
|
||||
context.getBean(JobLauncherApplicationRunner.class)
|
||||
.run(new DefaultApplicationArguments("--spring.property=value", "jobParam=test"));
|
||||
JobParameters jobParameters = new JobParametersBuilder().addString("jobParam", "test").toJobParameters();
|
||||
assertThat(context.getBean(JobRepository.class).getLastJobExecution("job", jobParameters)).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRegisteredAndLocalJob() {
|
||||
this.contextRunner.withUserConfiguration(NamedJobConfigurationWithRegisteredAndLocalJob.class)
|
||||
.withPropertyValues("spring.batch.job.name:discreteRegisteredJob")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(JobOperator.class);
|
||||
context.getBean(JobLauncherApplicationRunner.class).run();
|
||||
assertThat(context.getBean(JobRepository.class)
|
||||
.getLastJobExecution("discreteRegisteredJob", new JobParameters())
|
||||
.getStatus()).isEqualTo(BatchStatus.COMPLETED);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDefinesAndLaunchesLocalJob() {
|
||||
this.contextRunner.withUserConfiguration(NamedJobConfigurationWithLocalJob.class)
|
||||
.withPropertyValues("spring.batch.job.name:discreteLocalJob")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(JobOperator.class);
|
||||
context.getBean(JobLauncherApplicationRunner.class).run();
|
||||
assertThat(context.getBean(JobRepository.class)
|
||||
.getLastJobExecution("discreteLocalJob", new JobParameters())).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultipleJobsAndNoJobName() {
|
||||
this.contextRunner.withUserConfiguration(MultipleJobConfiguration.class).run((context) -> {
|
||||
assertThat(context).hasFailed();
|
||||
assertThat(context.getStartupFailure().getCause().getMessage())
|
||||
.contains("Job name must be specified in case of multiple jobs");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultipleJobsAndJobName() {
|
||||
this.contextRunner.withUserConfiguration(MultipleJobConfiguration.class)
|
||||
.withPropertyValues("spring.batch.job.name:discreteLocalJob")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(JobOperator.class);
|
||||
context.getBean(JobLauncherApplicationRunner.class).run();
|
||||
assertThat(context.getBean(JobRepository.class)
|
||||
.getLastJobExecution("discreteLocalJob", new JobParameters())).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDisableLaunchesJob() {
|
||||
this.contextRunner.withUserConfiguration(JobConfiguration.class)
|
||||
.withPropertyValues("spring.batch.job.enabled:false")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(JobOperator.class);
|
||||
assertThat(context).doesNotHaveBean(CommandLineRunner.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class NamedJobConfigurationWithRegisteredAndLocalJob {
|
||||
|
||||
@Autowired
|
||||
private JobRepository jobRepository;
|
||||
|
||||
@Bean
|
||||
Job discreteJob() {
|
||||
AbstractJob job = new AbstractJob("discreteRegisteredJob") {
|
||||
|
||||
private static int count = 0;
|
||||
|
||||
@Override
|
||||
public Collection<String> getStepNames() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Step getStep(String stepName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doExecute(JobExecution execution) {
|
||||
if (count == 0) {
|
||||
execution.setStatus(BatchStatus.COMPLETED);
|
||||
}
|
||||
else {
|
||||
execution.setStatus(BatchStatus.FAILED);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
};
|
||||
job.setJobRepository(this.jobRepository);
|
||||
return job;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class NamedJobConfigurationWithLocalJob {
|
||||
|
||||
@Autowired
|
||||
private JobRepository jobRepository;
|
||||
|
||||
@Bean
|
||||
Job discreteJob() {
|
||||
AbstractJob job = new AbstractJob("discreteLocalJob") {
|
||||
|
||||
@Override
|
||||
public Collection<String> getStepNames() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Step getStep(String stepName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doExecute(JobExecution execution) {
|
||||
execution.setStatus(BatchStatus.COMPLETED);
|
||||
}
|
||||
};
|
||||
job.setJobRepository(this.jobRepository);
|
||||
return job;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class MultipleJobConfiguration {
|
||||
|
||||
@Autowired
|
||||
private JobRepository jobRepository;
|
||||
|
||||
@Bean
|
||||
Job discreteJob() {
|
||||
AbstractJob job = new AbstractJob("discreteLocalJob") {
|
||||
|
||||
@Override
|
||||
public Collection<String> getStepNames() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Step getStep(String stepName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doExecute(JobExecution execution) {
|
||||
execution.setStatus(BatchStatus.COMPLETED);
|
||||
}
|
||||
};
|
||||
job.setJobRepository(this.jobRepository);
|
||||
return job;
|
||||
}
|
||||
|
||||
@Bean
|
||||
Job job2() {
|
||||
return new Job() {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "discreteLocalJob2";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(JobExecution execution) {
|
||||
execution.setStatus(BatchStatus.COMPLETED);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class JobConfiguration {
|
||||
|
||||
@Autowired
|
||||
private JobRepository jobRepository;
|
||||
|
||||
@Bean
|
||||
Job job() {
|
||||
AbstractJob job = new AbstractJob() {
|
||||
|
||||
@Override
|
||||
public Collection<String> getStepNames() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Step getStep(String stepName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doExecute(JobExecution execution) {
|
||||
execution.setStatus(BatchStatus.COMPLETED);
|
||||
}
|
||||
};
|
||||
job.setJobRepository(this.jobRepository);
|
||||
return job;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -16,44 +16,30 @@
|
|||
|
||||
package org.springframework.boot.batch.autoconfigure;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
|
||||
import org.springframework.batch.core.configuration.annotation.EnableJdbcJobRepository;
|
||||
import org.springframework.batch.core.job.Job;
|
||||
import org.springframework.batch.core.job.JobExecution;
|
||||
import org.springframework.batch.core.job.JobExecutionException;
|
||||
import org.springframework.batch.core.job.JobInstance;
|
||||
import org.springframework.batch.core.job.builder.JobBuilder;
|
||||
import org.springframework.batch.core.job.builder.SimpleJobBuilder;
|
||||
import org.springframework.batch.core.job.parameters.JobParameters;
|
||||
import org.springframework.batch.core.job.parameters.JobParametersBuilder;
|
||||
import org.springframework.batch.core.launch.JobOperator;
|
||||
import org.springframework.batch.core.launch.support.RunIdIncrementer;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.core.repository.JobRestartException;
|
||||
import org.springframework.batch.core.step.Step;
|
||||
import org.springframework.batch.core.step.builder.StepBuilder;
|
||||
import org.springframework.batch.core.step.tasklet.Tasklet;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.jdbc.autoconfigure.DataSourceTransactionManagerAutoConfiguration;
|
||||
import org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer;
|
||||
import org.springframework.boot.sql.init.DatabaseInitializationSettings;
|
||||
import org.springframework.batch.support.transaction.ResourcelessTransactionManager;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.boot.transaction.autoconfigure.TransactionAutoConfiguration;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.fail;
|
||||
|
||||
/**
|
||||
* Tests for {@link JobLauncherApplicationRunner}.
|
||||
|
@ -66,120 +52,37 @@ import static org.assertj.core.api.Assertions.fail;
|
|||
class JobLauncherApplicationRunnerTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class, TransactionAutoConfiguration.class,
|
||||
DataSourceTransactionManagerAutoConfiguration.class))
|
||||
.withBean(PlatformTransactionManager.class, ResourcelessTransactionManager::new)
|
||||
.withUserConfiguration(BatchConfiguration.class);
|
||||
|
||||
@Test
|
||||
void basicExecution() {
|
||||
void basicExecutionSuccess() {
|
||||
this.contextRunner.run((context) -> {
|
||||
JobLauncherApplicationRunnerContext jobLauncherContext = new JobLauncherApplicationRunnerContext(context);
|
||||
jobLauncherContext.executeJob(new JobParameters());
|
||||
assertThat(jobLauncherContext.jobInstances()).hasSize(1);
|
||||
jobLauncherContext.executeJob(new JobParametersBuilder().addLong("id", 1L).toJobParameters());
|
||||
assertThat(jobLauncherContext.jobInstances()).hasSize(2);
|
||||
List<JobInstance> jobInstances = jobLauncherContext.jobInstances();
|
||||
assertThat(jobInstances).hasSize(1);
|
||||
List<JobExecution> jobExecutions = jobLauncherContext.jobExecutions(jobInstances.get(0));
|
||||
assertThat(jobExecutions).hasSize(1);
|
||||
assertThat(jobExecutions.get(0).getExitStatus().getExitCode())
|
||||
.isEqualTo(ExitStatus.COMPLETED.getExitCode());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void incrementExistingExecution() {
|
||||
void basicExecutionFailure() {
|
||||
this.contextRunner.run((context) -> {
|
||||
JobLauncherApplicationRunnerContext jobLauncherContext = new JobLauncherApplicationRunnerContext(context);
|
||||
Job job = jobLauncherContext.configureJob().incrementer(new RunIdIncrementer()).build();
|
||||
JobParameters jobParameters = new JobParameters();
|
||||
jobLauncherContext.runner.execute(job, jobParameters);
|
||||
jobLauncherContext.runner.execute(job, jobParameters);
|
||||
assertThat(jobLauncherContext.jobInstances()).hasSize(2);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void retryFailedExecutionWithIncrementer() {
|
||||
this.contextRunner.run((context) -> {
|
||||
PlatformTransactionManager transactionManager = context.getBean(PlatformTransactionManager.class);
|
||||
JobLauncherApplicationRunnerContext jobLauncherContext = new JobLauncherApplicationRunnerContext(context);
|
||||
Job job = jobLauncherContext.jobBuilder()
|
||||
.start(jobLauncherContext.stepBuilder().tasklet(throwingTasklet(), transactionManager).build())
|
||||
.incrementer(new RunIdIncrementer())
|
||||
.build();
|
||||
jobLauncherContext.runner.execute(job, new JobParameters());
|
||||
jobLauncherContext.runner.execute(job, new JobParameters());
|
||||
// with an incrementer, we always create a new job instance
|
||||
assertThat(jobLauncherContext.jobInstances()).hasSize(2);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void retryFailedExecutionWithoutIncrementer() {
|
||||
this.contextRunner.run((context) -> {
|
||||
PlatformTransactionManager transactionManager = context.getBean(PlatformTransactionManager.class);
|
||||
JobLauncherApplicationRunnerContext jobLauncherContext = new JobLauncherApplicationRunnerContext(context);
|
||||
Job job = jobLauncherContext.jobBuilder()
|
||||
.start(jobLauncherContext.stepBuilder().tasklet(throwingTasklet(), transactionManager).build())
|
||||
.build();
|
||||
JobParameters jobParameters = new JobParametersBuilder().addLong("run.id", 1L).toJobParameters();
|
||||
jobLauncherContext.runner.execute(job, jobParameters);
|
||||
jobLauncherContext.runner.execute(job, jobParameters);
|
||||
assertThat(jobLauncherContext.jobInstances()).hasSize(1);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void runDifferentInstances() {
|
||||
this.contextRunner.run((context) -> {
|
||||
PlatformTransactionManager transactionManager = context.getBean(PlatformTransactionManager.class);
|
||||
JobLauncherApplicationRunnerContext jobLauncherContext = new JobLauncherApplicationRunnerContext(context);
|
||||
Job job = jobLauncherContext.jobBuilder()
|
||||
.start(jobLauncherContext.stepBuilder().tasklet(throwingTasklet(), transactionManager).build())
|
||||
.build();
|
||||
// start a job instance
|
||||
JobParameters jobParameters = new JobParametersBuilder().addString("name", "foo").toJobParameters();
|
||||
jobLauncherContext.runner.execute(job, jobParameters);
|
||||
assertThat(jobLauncherContext.jobInstances()).hasSize(1);
|
||||
// start a different job instance
|
||||
JobParameters otherJobParameters = new JobParametersBuilder().addString("name", "bar").toJobParameters();
|
||||
jobLauncherContext.runner.execute(job, otherJobParameters);
|
||||
assertThat(jobLauncherContext.jobInstances()).hasSize(2);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void retryFailedExecutionOnNonRestartableJob() {
|
||||
this.contextRunner.run((context) -> {
|
||||
PlatformTransactionManager transactionManager = context.getBean(PlatformTransactionManager.class);
|
||||
JobLauncherApplicationRunnerContext jobLauncherContext = new JobLauncherApplicationRunnerContext(context);
|
||||
Job job = jobLauncherContext.jobBuilder()
|
||||
.preventRestart()
|
||||
.start(jobLauncherContext.stepBuilder().tasklet(throwingTasklet(), transactionManager).build())
|
||||
.build();
|
||||
JobParameters jobParameters = new JobParametersBuilder().addString("name", "foo").toJobParameters();
|
||||
jobLauncherContext.runner.execute(job, jobParameters);
|
||||
assertThat(jobLauncherContext.jobInstances()).hasSize(1);
|
||||
assertThatExceptionOfType(JobRestartException.class).isThrownBy(() -> {
|
||||
// try to re-run a failed execution
|
||||
jobLauncherContext.runner.execute(job, jobParameters);
|
||||
fail("expected JobRestartException");
|
||||
}).withMessageContaining("JobInstance already exists and is not restartable");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void retryFailedExecutionWithNonIdentifyingParameters() {
|
||||
this.contextRunner.run((context) -> {
|
||||
PlatformTransactionManager transactionManager = context.getBean(PlatformTransactionManager.class);
|
||||
JobLauncherApplicationRunnerContext jobLauncherContext = new JobLauncherApplicationRunnerContext(context);
|
||||
Job job = jobLauncherContext.jobBuilder()
|
||||
.start(jobLauncherContext.stepBuilder().tasklet(throwingTasklet(), transactionManager).build())
|
||||
.build();
|
||||
JobParameters jobParameters = new JobParametersBuilder().addLong("run.id", 1L, true)
|
||||
.addLong("foo", 2L, false)
|
||||
.toJobParameters();
|
||||
jobLauncherContext.runner.execute(job, jobParameters);
|
||||
assertThat(jobLauncherContext.jobInstances()).hasSize(1);
|
||||
// try to re-run a failed execution with non identifying parameters
|
||||
jobLauncherContext.runner.execute(job,
|
||||
new JobParametersBuilder(jobParameters).addLong("run.id", 1L).toJobParameters());
|
||||
assertThat(jobLauncherContext.jobInstances()).hasSize(1);
|
||||
List<JobInstance> jobInstances = jobLauncherContext.jobInstances();
|
||||
assertThat(jobInstances).hasSize(1);
|
||||
List<JobExecution> jobExecutions = jobLauncherContext.jobExecutions(jobInstances.get(0));
|
||||
assertThat(jobExecutions).hasSize(1);
|
||||
assertThat(jobExecutions.get(0).getExitStatus().getExitCode()).isEqualTo(ExitStatus.FAILED.getExitCode());
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -201,16 +104,14 @@ class JobLauncherApplicationRunnerTests {
|
|||
|
||||
private final StepBuilder stepBuilder;
|
||||
|
||||
private final Step step;
|
||||
|
||||
JobLauncherApplicationRunnerContext(ApplicationContext context) {
|
||||
JobOperator jobOperator = context.getBean(JobOperator.class);
|
||||
JobRepository jobRepository = context.getBean(JobRepository.class);
|
||||
PlatformTransactionManager transactionManager = context.getBean(PlatformTransactionManager.class);
|
||||
this.stepBuilder = new StepBuilder("step", jobRepository);
|
||||
this.step = this.stepBuilder.tasklet((contribution, chunkContext) -> null, transactionManager).build();
|
||||
Step step = this.stepBuilder.tasklet((contribution, chunkContext) -> null, transactionManager).build();
|
||||
this.jobBuilder = new JobBuilder("job", jobRepository);
|
||||
this.job = this.jobBuilder.start(this.step).build();
|
||||
this.job = this.jobBuilder.start(step).build();
|
||||
this.jobRepository = context.getBean(JobRepository.class);
|
||||
this.runner = new JobLauncherApplicationRunner(jobOperator);
|
||||
}
|
||||
|
@ -219,6 +120,10 @@ class JobLauncherApplicationRunnerTests {
|
|||
return this.jobRepository.getJobInstances("job", 0, 100);
|
||||
}
|
||||
|
||||
List<JobExecution> jobExecutions(JobInstance jobInstance) {
|
||||
return this.jobRepository.getJobExecutions(jobInstance);
|
||||
}
|
||||
|
||||
void executeJob(JobParameters jobParameters) throws JobExecutionException {
|
||||
this.runner.execute(this.job, jobParameters);
|
||||
}
|
||||
|
@ -231,30 +136,12 @@ class JobLauncherApplicationRunnerTests {
|
|||
return this.stepBuilder;
|
||||
}
|
||||
|
||||
SimpleJobBuilder configureJob() {
|
||||
return this.jobBuilder.start(this.step);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableBatchProcessing
|
||||
@EnableJdbcJobRepository
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableBatchProcessing
|
||||
static class BatchConfiguration {
|
||||
|
||||
private final DataSource dataSource;
|
||||
|
||||
protected BatchConfiguration(DataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
@Bean
|
||||
DataSourceScriptDatabaseInitializer batchDataSourceInitializer() {
|
||||
DatabaseInitializationSettings settings = new DatabaseInitializationSettings();
|
||||
settings.setSchemaLocations(Arrays.asList("classpath:org/springframework/batch/core/schema-h2.sql"));
|
||||
return new DataSourceScriptDatabaseInitializer(this.dataSource, settings);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -85,6 +85,7 @@ include "module:spring-boot-artemis"
|
|||
include "module:spring-boot-autoconfigure-classic"
|
||||
include "module:spring-boot-autoconfigure-classic-modules"
|
||||
include "module:spring-boot-batch"
|
||||
include "module:spring-boot-batch-jdbc"
|
||||
include "module:spring-boot-cache"
|
||||
include "module:spring-boot-cache-test"
|
||||
include "module:spring-boot-cassandra"
|
||||
|
@ -204,6 +205,7 @@ include "starter:spring-boot-starter-amqp"
|
|||
include "starter:spring-boot-starter-artemis"
|
||||
include "starter:spring-boot-starter-aspectj"
|
||||
include "starter:spring-boot-starter-batch"
|
||||
include "starter:spring-boot-starter-batch-jdbc"
|
||||
include "starter:spring-boot-starter-cache"
|
||||
include "starter:spring-boot-starter-cassandra"
|
||||
include "starter:spring-boot-starter-classic"
|
||||
|
@ -317,6 +319,7 @@ include ":smoke-test:spring-boot-smoke-test-artemis"
|
|||
include ":smoke-test:spring-boot-smoke-test-aspectj"
|
||||
include ":smoke-test:spring-boot-smoke-test-autoconfigure-classic"
|
||||
include ":smoke-test:spring-boot-smoke-test-batch"
|
||||
include ":smoke-test:spring-boot-smoke-test-batch-jdbc"
|
||||
include ":smoke-test:spring-boot-smoke-test-bootstrap-registry"
|
||||
include ":smoke-test:spring-boot-smoke-test-cache"
|
||||
include ":smoke-test:spring-boot-smoke-test-config"
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright 2012-present 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.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id "java"
|
||||
}
|
||||
|
||||
description = "Spring Boot Batch with JDBC smoke test"
|
||||
|
||||
dependencies {
|
||||
implementation(project(":starter:spring-boot-starter-batch-jdbc"))
|
||||
|
||||
runtimeOnly("org.hsqldb:hsqldb")
|
||||
|
||||
testImplementation(project(":starter:spring-boot-starter-test"))
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright 2012-present 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 smoketest.batch;
|
||||
|
||||
import org.springframework.batch.core.job.Job;
|
||||
import org.springframework.batch.core.job.builder.JobBuilder;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.core.step.Step;
|
||||
import org.springframework.batch.core.step.builder.StepBuilder;
|
||||
import org.springframework.batch.core.step.tasklet.Tasklet;
|
||||
import org.springframework.batch.repeat.RepeatStatus;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
@SpringBootApplication
|
||||
public class SampleBatchApplication {
|
||||
|
||||
@Bean
|
||||
Tasklet tasklet() {
|
||||
return (contribution, context) -> RepeatStatus.FINISHED;
|
||||
}
|
||||
|
||||
@Bean
|
||||
Job job(JobRepository jobRepository, Step step) {
|
||||
return new JobBuilder("job", jobRepository).start(step).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Step step1(JobRepository jobRepository, Tasklet tasklet, PlatformTransactionManager transactionManager) {
|
||||
return new StepBuilder("step1", jobRepository).tasklet(tasklet, transactionManager).build();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// System.exit is common for Batch applications since the exit code can be used to
|
||||
// drive a workflow
|
||||
System.exit(SpringApplication.exit(SpringApplication.run(SampleBatchApplication.class, args)));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright 2012-present 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.
|
||||
*/
|
||||
|
||||
@NullMarked
|
||||
package smoketest.batch;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright 2012-present 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 smoketest.batch;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.test.system.CapturedOutput;
|
||||
import org.springframework.boot.test.system.OutputCaptureExtension;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@ExtendWith(OutputCaptureExtension.class)
|
||||
class SampleBatchApplicationTests {
|
||||
|
||||
@Test
|
||||
void testDefaultSettings(CapturedOutput output) {
|
||||
assertThat(SpringApplication.exit(SpringApplication.run(SampleBatchApplication.class))).isZero();
|
||||
assertThat(output).contains("completed with the following parameters");
|
||||
}
|
||||
|
||||
}
|
|
@ -23,7 +23,5 @@ description = "Spring Boot Batch smoke test"
|
|||
dependencies {
|
||||
implementation(project(":starter:spring-boot-starter-batch"))
|
||||
|
||||
runtimeOnly("org.hsqldb:hsqldb")
|
||||
|
||||
testImplementation(project(":starter:spring-boot-starter-test"))
|
||||
}
|
|
@ -26,7 +26,6 @@ import org.springframework.batch.repeat.RepeatStatus;
|
|||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
@SpringBootApplication
|
||||
public class SampleBatchApplication {
|
||||
|
@ -42,8 +41,8 @@ public class SampleBatchApplication {
|
|||
}
|
||||
|
||||
@Bean
|
||||
Step step1(JobRepository jobRepository, Tasklet tasklet, PlatformTransactionManager transactionManager) {
|
||||
return new StepBuilder("step1", jobRepository).tasklet(tasklet, transactionManager).build();
|
||||
Step step1(JobRepository jobRepository, Tasklet tasklet) {
|
||||
return new StepBuilder("step1", jobRepository).tasklet(tasklet).build();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright 2012-present 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.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id "org.springframework.boot.starter"
|
||||
}
|
||||
|
||||
description = "Starter for using Spring Batch with JDBC"
|
||||
|
||||
dependencies {
|
||||
api(project(":starter:spring-boot-starter"))
|
||||
api(project(":starter:spring-boot-starter-jdbc"))
|
||||
|
||||
api(project(":module:spring-boot-batch-jdbc"))
|
||||
}
|
|
@ -22,8 +22,6 @@ description = "Starter for using Spring Batch"
|
|||
|
||||
dependencies {
|
||||
api(project(":starter:spring-boot-starter"))
|
||||
api(project(":starter:spring-boot-starter-jdbc"))
|
||||
|
||||
api(project(":module:spring-boot-batch"))
|
||||
api(project(":module:spring-boot-jdbc"))
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue