配置 JobRepository
如先前所述,JobRepository 用于对 Spring Batch 中各种持久化领域对象(如 JobExecution 和 StepExecution)执行基本的 CRUD 操作。许多核心框架功能(如 JobOperator、Job 和 Step)都需要它。
配置无资源 JobRepository
JobRepository 接口最简单的实现是 ResourcelessJobRepository。此实现不使用或存储批处理元数据。它适用于不需要重启能力且执行上下文完全不参与的场景(例如通过执行上下文在步骤间共享数据,或通过执行上下文在管理器与工作器之间共享分区元数据的分区步骤等)。该实现仅维护运行单个作业所需的最小状态(即 1 个作业实例 + 1 个作业执行 + N 个步骤执行)。这适用于在独立 JVM 中执行的一次性作业。此作业仓库既支持事务性步骤,也支持非事务性步骤(需与 ResourcelessTransactionManager 配合使用)。
此实现并非线程安全,不应在任何并发环境中使用。
默认情况下,当使用 @EnableBatchProcessing 或 DefaultBatchConfiguration 时,系统会为你提供一个 ResourcelessJobRepository。
配置 JDBC JobRepository
- Java
- XML
当使用 @EnableBatchProcessing 时,系统会为你提供一个 ResourcelessJobRepository。本节介绍如何自定义它。Spring Batch 提供了两种由数据库支持的 JobRepository 接口实现:一种是 JDBC 实现(可用于任何符合 JDBC 标准的数据库),另一种是 MongoDB 实现。这两种实现分别由 @EnableJdbcJobRepository 和 @EnableMongoJobRepository 注解提供。
以下示例展示了如何通过 @EnableJdbcJobRepository 注解的属性自定义基于 JDBC 的作业仓库:
@Configuration
@EnableBatchProcessing
@EnableJdbcJobRepository(
dataSourceRef = "batchDataSource",
transactionManagerRef = "batchTransactionManager",
tablePrefix = "BATCH_",
maxVarCharLength = 1000,
isolationLevelForCreate = "SERIALIZABLE")
public class MyJobConfiguration {
// job definition
}
此处列出的配置选项都不是必需的。如果未设置,则使用前面显示的默认值。最大 varchar 长度默认为 2500,这是示例模式脚本中长 VARCHAR 列的长度。
batch 命名空间抽象了 JobRepository 实现及其协作者的许多实现细节。但是,仍然有一些可用的配置选项,如下例所示:
<job-repository id="jobRepository"
data-source="dataSource"
transaction-manager="transactionManager"
isolation-level-for-create="SERIALIZABLE"
table-prefix="BATCH_"
max-varchar-length="1000"/>
除了 id 之外,前面列出的配置选项都不是必需的。如果未设置,则使用前面显示的默认值。max-varchar-length 默认为 2500,这是示例模式脚本中长 VARCHAR 列的长度。
配置 MongoDB JobRepository
与基于 JDBC 的 JobRepository 类似,基于 MongoDB 的 JobRepository 也需要一些集合来存储批处理元数据。这些集合定义在 spring-batch-core jar 包的 org/springframework/batch/core/schema-mongodb.jsonl 文件中。与基于 JDBC 的 JobRepository 一样,您需要在运行任何作业之前,在您的 MongoDB 数据库中创建这些集合。
此外,由于在MongoDB文档的字段名中使用.是不被推荐的,你需要自定义MongoJobRepositoryFactoryBean所使用的MongoTemplate,将字段名中的.替换为其他字符(例如_)。这可以通过自定义MongoTemplate所使用的MappingMongoConverter来实现。以下示例展示了如何在Java配置中完成此操作:
@Bean
public MongoTemplate mongoTemplate(MongoDatabaseFactory mongoDatabaseFactory) {
MongoTemplate template = new MongoTemplate(mongoDatabaseFactory);
MappingMongoConverter converter = (MappingMongoConverter) template.getConverter();
converter.setMapKeyDotReplacement("_");
return template;
}
JobRepository 的事务配置
如果使用了命名空间或提供的 FactoryBean,事务性建议会自动在存储库周围创建。这是为了确保批处理元数据(包括故障后重启所需的状态)能够被正确持久化。如果存储库方法不是事务性的,框架的行为将无法明确定义。create* 方法属性中的隔离级别被单独指定,以确保在启动作业时,如果两个进程试图同时启动同一个作业,只有一个会成功。该方法的默认隔离级别是 SERIALIZABLE,这是相当严格的。通常 READ_COMMITTED 也能很好地工作。如果两个进程不太可能以这种方式冲突,READ_UNCOMMITTED 也是可以的。然而,由于对 create* 方法的调用非常短暂,只要数据库平台支持,SERIALIZED 不太可能导致问题。不过,你可以覆盖此设置。
- Java
- XML
以下示例展示了如何在 Java 中覆盖隔离级别:
@Configuration
@EnableBatchProcessing
@EnableJdbcJobRepository(isolationLevelForCreate = "ISOLATION_REPEATABLE_READ")
public class MyJobConfiguration {
// 作业定义
}
以下示例展示了如何在 XML 中覆盖隔离级别:
<job-repository id="jobRepository"
isolation-level-for-create="REPEATABLE_READ" />
如果未使用命名空间,则必须通过使用 AOP 来配置存储库的事务行为。
- Java
- XML
以下示例展示了如何在 Java 中配置存储库的事务行为:
@Bean
public TransactionProxyFactoryBean baseProxy() {
TransactionProxyFactoryBean transactionProxyFactoryBean = new TransactionProxyFactoryBean();
Properties transactionAttributes = new Properties();
transactionAttributes.setProperty("*", "PROPAGATION_REQUIRED");
transactionProxyFactoryBean.setTransactionAttributes(transactionAttributes);
transactionProxyFactoryBean.setTarget(jobRepository());
transactionProxyFactoryBean.setTransactionManager(transactionManager());
return transactionProxyFactoryBean;
}
以下示例展示了如何在 XML 中配置存储库的事务行为:
<aop:config>
<aop:advisor
pointcut="execution(* org.springframework.batch.core..*Repository+.*(..))"/>
<advice-ref="txAdvice" />
</aop:config>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" />
</tx:attributes>
</tx:advice>
你可以几乎原封不动地使用前面的代码片段,几乎无需修改。同时请记住包含适当的命名空间声明,并确保 spring-tx 和 spring-aop(或整个 Spring)在类路径中。
更改表前缀
JobRepository 的另一个可修改属性是元数据表的表前缀。默认情况下,它们都以 BATCH_ 开头。BATCH_JOB_EXECUTION 和 BATCH_STEP_EXECUTION 就是两个例子。然而,修改此前缀可能存在一些原因。如果需要在表名前添加模式名称,或者在同一模式中需要多组元数据表,就需要更改表前缀。
- Java
- XML
以下示例展示了如何在 Java 中更改表前缀:
@Configuration
@EnableBatchProcessing
@EnableJdbcJobRepository(tablePrefix = "SYSTEM.TEST_")
public class MyJobConfiguration {
// job definition
}
以下示例展示了如何在 XML 中更改表前缀:
<job-repository id="jobRepository"
table-prefix="SYSTEM.TEST_" />
根据之前的更改,对元数据表的每次查询都会以 SYSTEM.TEST_ 作为前缀。BATCH_JOB_EXECUTION 现在被引用为 SYSTEM.TEST_JOB_EXECUTION。
仅表前缀可配置,表和列名不可配置。
仓库中的非标准数据库类型
如果您使用的数据库平台不在支持平台列表中,但SQL变体足够接近,您仍有可能使用其中一种受支持的类型。为此,您可以使用原始的 JdbcJobRepositoryFactoryBean 而非命名空间快捷方式,并通过它设置数据库类型为最接近的匹配项。
- Java
- XML
以下示例展示了如何在 Java 中使用 JdbcJobRepositoryFactoryBean 将数据库类型设置为最接近的匹配项:
@Bean
public JobRepository jobRepository() throws Exception {
JdbcJobRepositoryFactoryBean factory = new JdbcJobRepositoryFactoryBean();
factory.setDataSource(dataSource);
factory.setDatabaseType("db2");
factory.setTransactionManager(transactionManager);
return factory.getObject();
}
以下示例展示了如何在 XML 中使用 JdbcJobRepositoryFactoryBean 将数据库类型设置为最接近的匹配项:
<bean id="jobRepository" class="org...JdbcJobRepositoryFactoryBean">
<property name="databaseType" value="db2"/>
<property name="dataSource" ref="dataSource"/>
</bean>
如果未指定数据库类型,JdbcJobRepositoryFactoryBean 会尝试从 DataSource 自动检测数据库类型。不同平台之间的主要差异主要体现在主键递增策略上,因此通常也需要覆盖 incrementerFactory(通过使用 Spring Framework 中的某个标准实现)。
如果上述方法仍然无效,或者您没有使用关系型数据库管理系统(RDBMS),那么唯一的选择可能就是实现 SimpleJobRepository 所依赖的各种 Dao 接口,并按照常规的 Spring 方式手动装配一个实例。