当前版本仍在开发中,尚不被视为稳定版本。最新稳定版请使用 Spring Batch 文档 6.0.2!

配置 JobRepository

如前文所述,JobRepository 用于对 Spring Batch 中各类需要持久化的领域对象执行基础 CRUD 操作, 例如 JobExecutionStepExecution。它是许多核心框架特性的基础依赖, 例如 JobOperatorJobStep 都需要它。

配置无资源 JobRepository

JobRepository 接口最简单的实现是 ResourcelessJobRepository。 这个实现不会使用或存储任何批处理元数据。它适用于不需要重启能力、也完全不依赖执行上下文的场景 (例如不会通过执行上下文在步骤之间共享数据,或者不会在分区步骤中通过执行上下文在 manager 与 worker 之间共享分区元数据等)。 该实现仅保留运行单个作业所需的最小状态 (即 1 个 job instance + 1 个 job execution + N 个 step execution)。 它适合在独立 JVM 中执行的一次性作业。这个作业仓库既可以与事务性 step 配合使用, 也可以与非事务性 step 配合使用(后者通常结合 ResourcelessTransactionManager)。

该实现不是线程安全的,不应用于任何并发环境。

默认情况下,使用 @EnableBatchProcessingDefaultBatchConfiguration 时, Spring Batch 会为你提供一个 ResourcelessJobRepository

配置 JDBC JobRepository

  • Java

  • XML

使用 @EnableBatchProcessing 时,默认提供的是一个 ResourcelessJobRepository。 本节说明如何对 JobRepository 进行定制。Spring Batch 提供了两种由数据库支撑的 JobRepository 实现:一种是 JDBC 实现 (可用于任何兼容 JDBC 的数据库),另一种是 MongoDB 实现。 这两种实现分别通过 @EnableJdbcJobRepository@EnableMongoJobRepository 注解启用。

下面的示例展示了如何通过 @EnableJdbcJobRepository 注解的属性, 来定制基于 JDBC 的作业仓库:

Java Configuration
@Configuration
@EnableBatchProcessing
@EnableJdbcJobRepository(
		dataSourceRef = "batchDataSource",
		transactionManagerRef = "batchTransactionManager",
		tablePrefix = "BATCH_",
		maxVarCharLength = 1000,
		isolationLevelForCreate = "SERIALIZABLE")
public class MyJobConfiguration {

   // job definition

}

这里列出的配置项都不是必需的。 如果没有显式设置,就会使用前面提到的默认值。 其中 varchar 的最大长度默认是 2500, 这与示例 schema 脚本中长 VARCHAR 列的长度一致。

Batch 命名空间已经屏蔽了 JobRepository 实现及其协作者的大量实现细节。 不过,仍然保留了一些可配置选项,如下例所示:

XML Configuration
<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, 这与示例 schema 脚本中长 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 配置中实现这一点:

Java Configuration
@Bean
public MongoTemplate mongoTemplate(Mongo数据库Factory mongo数据库Factory) {
    MongoTemplate template = new MongoTemplate(mongo数据库Factory);
    MappingMongoConverter converter = (MappingMongoConverter) template.getConverter();
    converter.setMapKeyDotReplacement("_");
    return template;
}

JobRepository 的事务配置

如果使用了命名空间或框架提供的 FactoryBean,系统会自动在仓库外围创建事务增强。 这样做是为了确保批处理元数据能够被正确持久化,包括失败后重启所需的那些状态信息。 如果仓库方法不具备事务性,框架行为将难以得到可靠定义。 `create*` 方法上的隔离级别之所以被单独指定,是为了确保在启动作业时,如果两个进程同时尝试启动同一个作业, 最终只有一个能够成功。该方法默认的隔离级别是 SERIALIZABLE,这是一个比较激进的设置。 通常 READ_COMMITTED 也同样可行。如果两个进程几乎不可能发生这种冲突, 那么 READ_UNCOMMITTED 也可以接受。不过,由于对 create* 方法的调用通常很短, 只要数据库平台支持,SERIALIZABLE 一般也不太会造成实际问题。当然,你也可以覆盖这个设置。

  • Java

  • XML

下面的示例展示了如何在 Java 中覆盖隔离级别:

Java Configuration
@Configuration
@EnableBatchProcessing
@EnableJdbcJobRepository(isolationLevelForCreate = "ISOLATION_REPEATABLE_READ")
public class MyJobConfiguration {

   // job definition

}

下面的示例展示了如何在 XML 中覆盖隔离级别:

XML Configuration
<job-repository id="jobRepository"
                isolation-level-for-create="REPEATABLE_READ" />

如果没有使用命名空间,那么你还必须借助 AOP 来配置仓库的事务行为。

  • Java

  • XML

下面的示例展示了如何在 Java 中配置仓库的事务行为:

Java Configuration
@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 中配置仓库的事务行为:

XML Configuration
<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-txspring-aop (或者整个 Spring)位于类路径中。

修改表前缀

JobRepository 另一个可修改属性是元数据表的表前缀。 默认情况下,这些表都以前缀 BATCH_ 开头,例如 BATCH_JOB_EXECUTIONBATCH_STEP_EXECUTION。不过,在某些场景下你可能需要修改此前缀。 例如需要把 schema 名称拼接到表名前,或者同一个 schema 中需要维护多套元数据表时, 就需要变更这个表前缀。

  • Java

  • XML

下面的示例展示了如何在 Java 中修改表前缀:

Java Configuration
@Configuration
@EnableBatchProcessing
@EnableJdbcJobRepository(tablePrefix = "SYSTEM.TEST_")
public class MyJobConfiguration {

   // job definition

}

下面的示例展示了如何在 XML 中修改表前缀:

XML Configuration
<job-repository id="jobRepository"
                table-prefix="SYSTEM.TEST_" />

完成上述修改后,所有针对元数据表的查询都会带上 SYSTEM.TEST_ 前缀。 例如,BATCH_JOB_EXECUTION 将被引用为 SYSTEM.TEST_JOB_EXECUTION

只有表前缀可以配置,表名和列名本身不能修改。

仓库中的非标准数据库类型

如果你使用的数据库平台不在官方支持列表中,但其 SQL 方言与某个已支持平台足够接近, 那么你仍然有机会复用其中一种已支持的类型。为此,你可以不使用命名空间快捷配置, 而是直接使用原始的 JdbcJobRepositoryFactoryBean,并把数据库类型设置为最接近的匹配项。

  • Java

  • XML

下面的示例展示了如何在 Java 中使用 JdbcJobRepositoryFactoryBean, 把数据库类型设置为最接近的匹配项:

Java Configuration
@Bean
public JobRepository jobRepository() throws Exception {
    JdbcJobRepositoryFactoryBean factory = new JdbcJobRepositoryFactoryBean();
    factory.setDataSource(dataSource);
    factory.set数据库Type("db2");
    factory.setTransactionManager(transactionManager);
    return factory.getObject();
}

下面的示例展示了如何在 XML 中使用 JdbcJobRepositoryFactoryBean, 把数据库类型设置为最接近的匹配项:

XML Configuration
<bean id="jobRepository" class="org...JdbcJobRepositoryFactoryBean">
    <property name="databaseType" value="db2"/>
    <property name="dataSource" ref="dataSource"/>
</bean>

如果没有显式指定数据库类型,JdbcJobRepositoryFactoryBean 会尝试根据 DataSource 自动探测数据库类型。不同平台之间的主要差异通常集中在主键递增策略上, 因此很多时候还需要一并覆盖 incrementerFactory (通常可使用 Spring Framework 提供的标准实现之一)。

如果即便如此仍然不可行,或者你根本没有使用关系型数据库,那么唯一的选择可能就是自行实现 SimpleJobRepository 所依赖的各个 Dao 接口, 然后按照常规的 Spring 方式手动装配它们。