我已经看到了许多Spring Batch项目的示例,这些示例要么(a)定义了一个数据源,要么(b)没有定义任何数据源。
但是,在我的项目中,我希望我的业务逻辑可以访问dataSource,但是我希望Spring Batch不使用dataSource。这可能吗?
这个人有类似的问题:Spring boot + spring batch without DataSource
最佳答案
通常,在没有数据库的情况下使用spring-batch并不是一个好主意,因为根据您定义的作业类型,可能会出现并发问题。因此,强烈建议至少使用一个内存数据库,特别是如果您打算在生产中使用该作业。
如果您未配置自己的数据源,则将SpringBatch与SpringBoot一起使用将初始化内存数据源。
考虑到这一点,让我重新定义您的问题,如下所示:我的业务逻辑可以使用springbatch用来更新其BATCH表的数据源吗?
是的,它可以。实际上,您可以在SpringBatch Jobs中使用任意数量的数据源。只需使用按名称自动装配即可。
这是我的方法:
我总是使用Configuration类,该类定义了我必须在Jobs中使用的所有数据源。
Configuration
public class DatasourceConfiguration {
@Bean
@ConditionalOnMissingBean(name = "dataSource")
public DataSource dataSource() {
// create datasource, that is used by springbatch
// for instance, create an inmemory datasource using the
// EmbeddedDatabaseFactory
return ...;
}
@Bean
@ConditionalOnMissingBean(name = "bl1datasource")
public DataSource bl1datasource() {
return ...; // your first datasource that is used in your businesslogic
}
@Bean
@ConditionalOnMissingBean(name = "bl2datasource")
public DataSource bl2datasource() {
return ...; // your second datasource that is used in your businesslogic
}
}
注意三点:
SpringBatch正在寻找一个名称为“ dataSource”的数据源,如果您不提供此名称(大写的“ S”)作为名称,Spring Batch将尝试按类型自动接线,并且如果发现多个DataSource实例,它将引发异常。
将您的数据源配置放在其自己的类中。不要将它们与jobdefinitions放在同一类中。 Spring需要在加载上下文时尽早实例化名为“ dataSource”的数据源-SpringBean。在开始实例化Job-Bean和Step-Bean之前。如果将数据源定义与作业/步骤定义放在同一类中,则Spring将无法正确执行此操作。
使用@ConditionalOnMissingBean不是强制性的,但是我发现它是一个很好的实践。它使更改单元/集成测试的数据源变得容易。只需在单元/ IT测试的ContextConfiguration中提供其他测试配置,例如,用inMemoryDataSource覆盖“ bl1Datasource”:
Configuration
public class TestBL1DatasourceConfiguration {
// overwritting bl1datasource with an inMemoryDatasource.
@Bean
public DataSource bl1datasource() {
return new EmbeddedDatabaseFactory.getDatabase();
}
}
为了使用businesslogic数据源,请按名称使用注入:
@Component
public class PrepareRe1Re2BezStepCreatorComponent {
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Autowired
private DataSource bl1datasource;
@Autowired
private DataSource bl2datasource;
public Step createStep() throws Exception {
SimpleStepBuilder<..., ...> builder =
stepBuilderFactory.get("astep") //
.<..., ...> chunk(100) //
.reader(createReader(bl1datasource)) //
.writer(createWriter(bl2datasource)); //
return builder.build();
}
}
此外,如果您想使用多个数据源,则可能要考虑使用XA-Datasources。
编辑:
既然您似乎真的不想使用数据源,则必须实现自己的BatchConfigurer(http://docs.spring.io/spring-batch/trunk/apidocs/org/springframework/batch/core/configuration/annotation/BatchConfigurer.html)(如上述的SpringBatch项目负责人Michael Minella所指出的)。
您可以使用org.springframework.batch.core.configuration.annotation.DefaultBatchConfigurer的代码作为自己实现的起点。只需删除所有数据源/事务管理器代码,并将
if (datasource === null)
部分的内容保留在initialize方法中。这将初始化MapBasedJobRepository和MapBasedJobExplorer。再次重申,这在生产环境中不是可用的解决方案,因为它不是线程安全的。编辑:
实施方法:
定义“ businessDataSource”的配置类:
@Configuration
public class DataSourceConfigurationSimple {
DataSource embeddedDataSource;
@Bean
public DataSource myBusinessDataSource() {
if (embeddedDataSource == null) {
EmbeddedDatabaseFactory factory = new EmbeddedDatabaseFactory();
embeddedDataSource = factory.getDatabase();
}
return embeddedDataSource;
}
}
具体的BatchConfigurer的实现:
(当然,必须实现这些方法...)
public class MyBatchConfigurer implements BatchConfigurer {
@Override
public JobRepository getJobRepository() throws Exception {
return null;
}
@Override
public PlatformTransactionManager getTransactionManager() throws Exception {
return null;
}
@Override
public JobLauncher getJobLauncher() throws Exception {
return null;
}
@Override
public JobExplorer getJobExplorer() throws Exception {
return null;
}
}
最后是主要的配置和启动类:
@SpringBootApplication
@Configuration
@EnableBatchProcessing
// Importing MyBatchConfigurer will install your BatchConfigurer instead of
// SpringBatch default configurer.
@Import({DataSourceConfigurationSimple.class, MyBatchConfigurer.class})
public class SimpleTestJob {
@Autowired
private JobBuilderFactory jobs;
@Autowired
private StepBuilderFactory steps;
@Bean
public Job job() throws Exception {
SimpleJobBuilder standardJob = this.jobs.get(JOB_NAME)
.start(step1());
return standardJob.build();
}
protected Step step1() throws Exception {
TaskletStepBuilder standardStep1 = this.steps.get("SimpleTest_step1_Step")
.tasklet(tasklet());
return standardStep1.build();
}
protected Tasklet tasklet() {
return (contribution, context) -> {
System.out.println("tasklet called");
return RepeatStatus.FINISHED;
};
}
public static void main(String[] args) throws Exception {
SpringApplication.run(SimpleTestJob.class, args);
}
}