之前说过,JobRepository 是基本的CRUD操作,用于持久化Spring Batch的领域对象(如JobExecution,StepExecution)。许多主要的框架组件(如JobLauncher,Job,Step)都需要使用JobRepository。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列的长度,在 sample schema scripts 中用于存储类似于exit code
这些描述的字符。如果你不修改schema并且也不会使用多字节编码,那么就不用修改它。
如果使用了namespace,repository会被自动加上事务控制,这是为了确保批处理操作元数据以及失败后重启的状态能够被准确的持久化,如果repository的方法不是事务控制的,那么框架的行为就不能够被准确的定义。create*
方法的隔离级别会被单独指定,为了确保任务启动时,如果两个操作尝试在同时启动相同的任务,那么只有一个任务能够被成功启动。这种方法默认的隔离级别是 SERIALIZABLE
,这是相当激进的做法: READ_COMMITED
能达到同样效果;如果两个操作不以这种方式冲突的话 READ_UNCOMMITED
也能很好工作。但是,由于调用 create*
方法是相当短暂的,只要数据库支持,就不会对性能产生太大影响。它也能被这样覆盖:
<job-repository id="jobRepository"
isolation-level-for-create="REPEATABLE_READ" />
如果factory的namespace没有被使用,那么可以使用AOP来配置repository的事务行为:
<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>
这个配置片段基本上可以不做修改直接使用。记住加上适当的namespace描述去确保spring-tx和spring-aop(或是整个spring)都在classpath中。
JobRepository 可以修改的另一个属性是元数据表的表前缀。默认是以BATCH_开头,BATCH_JOB_EXECUTION
和 BATCH_STEP_EXECUTION
就是两个例子。但是,有一些潜在的原因可能需要修改这个前缀。例如schema的名字需要被预置到表名中,或是不止一组的元数据表需要放在同一个schema中,那么表前缀就需要改变:
<job-repository id="jobRepository"
table-prefix="SYSTEM.TEST_" />
按照上面的修改配置,每一个元数据查询都会带上 SYSTEM.TEST_
的前缀,BATCH_JOB_EXECUTION
将会被更换为SYSTEM.TEST_JOB_EXECUTION
。
注意:表名前缀是可配置的,表名和列名是不可配置的。
有的时候不想把你的领域对象持久化到数据库中,可能是为了运行的更快速,因为每次提交都要开销额外的时间;也可能并不需要为特定任务保存状态。那么Spring Batch还提供了内存Map版本的job仓库:
<bean id="jobRepository"
class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean">
<property name="transactionManager" ref="transactionManager"/>
</bean>
需要注意的是 内存 Repository 是轻量的并且不能在两个JVM实例间重启任务,也不能允许同时启动带有相同参数的任务,不适合在多线程的任务或是一个本地分片任务的场景下使用。而使用数据库版本的Repository则能够拥有这些特性。
但是也需要定义一个事务管理器,因为仓库需要回滚语义,也因为商业逻辑要求事务性(例如RDBMS访问)。经过测试许多人觉得 ResourcelessTransactionManager 是很有用的。
如果使用的数据库平台不在支持的平台列表中,在SQL类型类似的情况下你可以使用近似的数据库类型。使用原生的 JobRepositoryFactoryBean 来取代命名空间缩写后设置一个相似的数据库类型:
<bean id="jobRepository" class="org...JobRepositoryFactoryBean">
<property name="databaseType" value="db2"/>
<property name="dataSource" ref="dataSource"/>
</bean>
(如果没有指定 databaseType
,JobRepositoryFactoryBean 会通过DataSource自动检测数据库的类型).平台之间的主要不同之处在于主键的计算策略,也可能需要覆盖 incrementerFactory
(使用Spring Framework提供的标准实现)。
如果它还不能工作,或是你不使用RDBMS,那么唯一的选择是让 SimpleJobRepository 使用Spring方式依赖并且绑定在手工实现的各种Dao接口上。