Consider Flyway and Liquibase as possible Batch DB initializers

This is a follow-on from 3fec4110 which only considered
BatchDataSourceInitializer as a possible initializer of Batch's
database schema. Flyway and Liquibase are now also considered.

Closes gh-27193
This commit is contained in:
Andy Wilkinson
2021-07-09 10:29:50 +01:00
parent 3fec41104e
commit 4b1b2d6603
3 changed files with 84 additions and 15 deletions

View File

@@ -18,6 +18,9 @@ package org.springframework.boot.autoconfigure.batch;
import javax.sql.DataSource;
import liquibase.integration.spring.SpringLiquibase;
import org.flywaydb.core.Flyway;
import org.springframework.batch.core.configuration.ListableJobLocator;
import org.springframework.batch.core.converter.JobParametersConverter;
import org.springframework.batch.core.explore.JobExplorer;
@@ -30,11 +33,11 @@ import org.springframework.boot.ExitCodeGenerator;
import org.springframework.boot.autoconfigure.AbstractDependsOnBeanFactoryPostProcessor;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration.DataSourceInitializerConfiguration.DataSourceInitializationJobRepositoryDependencyConfiguration;
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.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.flyway.FlywayMigrationInitializer;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
@@ -102,11 +105,9 @@ public class BatchAutoConfiguration {
return factory;
}
// Fully-qualified to work around javac bug
@org.springframework.context.annotation.Configuration(proxyBeanMethods = false)
@org.springframework.boot.autoconfigure.condition.ConditionalOnBean(DataSource.class)
@org.springframework.boot.autoconfigure.condition.ConditionalOnClass(DatabasePopulator.class)
@org.springframework.context.annotation.Import(DataSourceInitializationJobRepositoryDependencyConfiguration.class)
@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(DataSource.class)
@ConditionalOnClass(DatabasePopulator.class)
static class DataSourceInitializerConfiguration {
@Bean
@@ -118,17 +119,39 @@ public class BatchAutoConfiguration {
properties);
}
/**
* Post processor to ensure that {@link JobRepository} beans depend on any
* {@link BatchDataSourceInitializer} beans.
*/
static class DataSourceInitializationJobRepositoryDependencyConfiguration
extends AbstractDependsOnBeanFactoryPostProcessor {
}
DataSourceInitializationJobRepositoryDependencyConfiguration() {
super(JobRepository.class, BatchDataSourceInitializer.class);
}
@Configuration(proxyBeanMethods = false)
static class JobRepositoryDependencyConfiguration {
@Bean
JobRepositoryDependsOnBeanFactoryPostProcessor batchDataSourceInitializerJobRepositoryDependsOnBeanFactoryPostProcessor() {
return new JobRepositoryDependsOnBeanFactoryPostProcessor(BatchDataSourceInitializer.class);
}
@Bean
@ConditionalOnClass(name = "org.flywaydb.core.Flyway")
JobRepositoryDependsOnBeanFactoryPostProcessor flywayJobRepositoryDependsOnBeanFactoryPostProcessor() {
return new JobRepositoryDependsOnBeanFactoryPostProcessor(FlywayMigrationInitializer.class, Flyway.class);
}
@Bean
@ConditionalOnClass(name = "liquibase.integration.spring.SpringLiquibase")
JobRepositoryDependsOnBeanFactoryPostProcessor liquibaseJobRepositoryDependsOnBeanFactoryPostProcessor() {
return new JobRepositoryDependsOnBeanFactoryPostProcessor(FlywayMigrationInitializer.class,
SpringLiquibase.class);
}
}
/**
* {@link AbstractDependsOnBeanFactoryPostProcessor} for Spring Batch
* {@link JobRepository}.
*/
static class JobRepositoryDependsOnBeanFactoryPostProcessor extends AbstractDependsOnBeanFactoryPostProcessor {
JobRepositoryDependsOnBeanFactoryPostProcessor(Class<?>... dependencyTypes) {
super(JobRepository.class, dependencyTypes);
}
}

View File

@@ -22,6 +22,7 @@ import javax.sql.DataSource;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AbstractDependsOnBeanFactoryPostProcessor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -80,4 +81,17 @@ class JdbcSessionConfiguration {
}
/**
* Post processor to ensure that {@link JdbcIndexedSessionRepository} beans depend on
* any {@link JdbcSessionDataSourceInitializer} beans.
*/
static class DataSourceInitializationJdbcIndexedSessionRepositoryDependencyConfiguration
extends AbstractDependsOnBeanFactoryPostProcessor {
DataSourceInitializationJdbcIndexedSessionRepositoryDependencyConfiguration() {
super(JdbcIndexedSessionRepository.class, JdbcSessionDataSourceInitializer.class);
}
}
}

View File

@@ -44,7 +44,9 @@ 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.autoconfigure.flyway.FlywayAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.test.City;
import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration;
@@ -283,6 +285,36 @@ class BatchAutoConfigurationTests {
});
}
@Test
void jobRepositoryBeansDependOnFlyway() {
this.contextRunner.withUserConfiguration(TestConfiguration.class, EmbeddedDataSourceConfiguration.class)
.withUserConfiguration(FlywayAutoConfiguration.class)
.withPropertyValues("spring.batch.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
void jobRepositoryBeansDependOnLiquibase() {
this.contextRunner.withUserConfiguration(TestConfiguration.class, EmbeddedDataSourceConfiguration.class)
.withUserConfiguration(LiquibaseAutoConfiguration.class)
.withPropertyValues("spring.batch.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");
}
});
}
@Configuration(proxyBeanMethods = false)
protected static class BatchDataSourceConfiguration {