附录 A:元数据架构

A.1. Overview

Spring Batch 元数据表与 Java 中表示它们的 Domain 对象紧密匹配。例如,JobInstanceJobExecutionJobParametersStepExecution分别 Map 到BATCH_JOB_INSTANCEBATCH_JOB_EXECUTIONBATCH_JOB_EXECUTION_PARAMSBATCH_STEP_EXECUTIONExecutionContext同时 Map 到BATCH_JOB_EXECUTION_CONTEXTBATCH_STEP_EXECUTION_CONTEXTJobRepository负责将每个 Java 对象保存并存储到其正确的表中。本附录详细描述了元数据表,以及创建元数据表时做出的许多设计决策。查 Watch 下面的各种表创建语句时,重要的是要意识到所使用的数据类型应尽可能通用。 Spring Batch 提供了许多模式作为示例,由于各个数据库供应商处理数据类型的方式各不相同,因此所有模式都有不同的数据类型。下图显示了所有 6 个表的 ERD 模型及其相互之间的关系:

Spring Batch 元数据 ERD

图 1. Spring Batch 元数据 ERD

A.1.1.示例 DDL 脚本

Spring Batch Core JAR 文件包含用于为多个数据库平台创建关系表的示例脚本(依次由作业存储库工厂 bean 或等效名称空间自动检测到)。这些脚本可以按原样使用,也可以根据需要使用其他索引和约束进行修改。文件名的格式为schema-*.sql,其中“ *”是目标数据库平台的简称。脚本位于org.springframework.batch.core包中。

A.1.2.迁移 DDL 脚本

Spring Batch 提供了升级版本时需要执行的迁移 DDL 脚本。这些脚本可以在 Core Jar 文件的org/springframework/batch/core/migration下找到。迁移脚本被组织到与引入它们的版本号相对应的文件夹中:

  • 2.2:如果要从2.2之前的版本迁移到2.2版本,则包含所需的脚本

  • 2.2:如果要从2.2之前的版本迁移到2.2版本,则包含所需的脚本

A.1.3. Version

本附录中讨论的许多数据库表都包含一个 version 列。该列很重要,因为 Spring Batch 在处理数据库更新时采用了乐观锁定策略。这意味着每次“触摸”(更新)一条记录时,version 列中的值就会增加 1.当存储库返回以保存值时,如果版本号已更改,它将抛出OptimisticLockingFailureException,表示并发访问存在错误。该检查是必需的,因为即使不同的批处理作业可能在不同的计算机上运行,它们都使用相同的数据库表。

A.1.4. Identity

BATCH_JOB_INSTANCEBATCH_JOB_EXECUTIONBATCH_STEP_EXECUTION包含以_ID结尾的列。这些字段充当其各自表的主键。但是,它们不是数据库生成的密钥。相反,它们是由单独的序列生成的。这是必需的,因为在将一个域对象插入数据库后,需要在实际对象上设置给出的密钥,以便可以在 Java 中对其进行唯一标识。较新的数据库驱动程序(JDBC 3.0 及更高版本)通过数据库生成的密钥支持此功能。但是,不是要求该功能,而是使用序列。模式的每个变体都包含以下语句的某种形式:

CREATE SEQUENCE BATCH_STEP_EXECUTION_SEQ;
CREATE SEQUENCE BATCH_JOB_EXECUTION_SEQ;
CREATE SEQUENCE BATCH_JOB_SEQ;

许多数据库供应商不支持序列。在这些情况下,将使用变通方法,例如针对 MySQL 的以下语句:

CREATE TABLE BATCH_STEP_EXECUTION_SEQ (ID BIGINT NOT NULL) type=InnoDB;
INSERT INTO BATCH_STEP_EXECUTION_SEQ values(0);
CREATE TABLE BATCH_JOB_EXECUTION_SEQ (ID BIGINT NOT NULL) type=InnoDB;
INSERT INTO BATCH_JOB_EXECUTION_SEQ values(0);
CREATE TABLE BATCH_JOB_SEQ (ID BIGINT NOT NULL) type=InnoDB;
INSERT INTO BATCH_JOB_SEQ values(0);

在上述情况下,将使用表格代替每个序列。然后,Spring 核心类MySQLMaxValueIncrementer会按此 Sequences 递增一列,以提供类似的功能。

A.2. BATCH_JOB_INSTANCE

BATCH_JOB_INSTANCE表保存与JobInstance相关的所有信息,并用作整个层次结构的顶部。以下通用 DDL 语句用于创建它:

CREATE TABLE BATCH_JOB_INSTANCE  (
  JOB_INSTANCE_ID BIGINT  PRIMARY KEY ,
  VERSION BIGINT,
  JOB_NAME VARCHAR(100) NOT NULL ,
  JOB_KEY VARCHAR(2500)
);

下表描述了表中的每一列:

  • JOB_INSTANCE_ID:标识实例的唯一 ID。这也是主键。该列的值应该可以通过在JobInstance上调用getId方法来获得。

  • VERSION:请参见Version

  • JOB_NAME:从Job对象获得的作业的名称。因为需要标识实例,所以它不能为 null。

  • JOB_KEYJobParameters的序列化,可唯一地标识同一作业的单独实例。 (具有相同作业名称的JobInstances必须具有不同的JobParameters,因此具有不同的JOB_KEY值)。

A.3. BATCH_JOB_EXECUTION_PARAMS

BATCH_JOB_EXECUTION_PARAMS表保存与JobParameters对象有关的所有信息。它包含传递给Job的 0 个或多个键/值对,并用作运行作业的参数的记录。对于有助于生成作业身份的每个参数,IDENTIFYING标志设置为 true。请注意该表已被规范化。没有为每种类型创建单独的表,而是有一个表,其中的一列指示该类型,如以下 Lists 所示:

CREATE TABLE BATCH_JOB_EXECUTION_PARAMS  (
        JOB_EXECUTION_ID BIGINT NOT NULL ,
        TYPE_CD VARCHAR(6) NOT NULL ,
        KEY_NAME VARCHAR(100) NOT NULL ,
        STRING_VAL VARCHAR(250) ,
        DATE_VAL DATETIME DEFAULT NULL ,
        LONG_VAL BIGINT ,
        DOUBLE_VAL DOUBLE PRECISION ,
        IDENTIFYING CHAR(1) NOT NULL ,
        constraint JOB_EXEC_PARAMS_FK foreign key (JOB_EXECUTION_ID)
        references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
);

以下列表描述了每一列:

  • JOB_EXECUTION_IDBATCH_JOB_EXECUTION表中的外键,指示参数条目所属的作业执行。注意,每次执行可能存在多个行(即键/值对)。

  • TYPE_CD:存储的值类型的字符串表示形式,可以是字符串,日期,长整型或双精度型。因为必须知道类型,所以不能为 null。

  • KEY_NAME:参数键。

  • STRING_VAL:参数值(如果类型为字符串)。

  • DATE_VAL:参数值(如果类型为日期)。

  • LONG_VAL:参数值(如果类型很长)。

  • DOUBLE_VAL:参数值(如果类型为 double)。

  • IDENTIFYING:标记,指示参数是否有助于相关JobInstance的身份。

请注意,此表没有主键。这是因为该框架没有用处,因此不需要它。如果需要,可以添加主键,也可以添加数据库生成的键而不会对框架本身造成任何问题。

A.4. BATCH_JOB_EXECUTION

BATCH_JOB_EXECUTION表保存与JobExecution对象有关的所有信息。每次运行Job时,此表中总会有一个新的JobExecution和一个新行。以下 Lists 显示了BATCH_JOB_EXECUTION表的定义:

CREATE TABLE BATCH_JOB_EXECUTION  (
  JOB_EXECUTION_ID BIGINT  PRIMARY KEY ,
  VERSION BIGINT,
  JOB_INSTANCE_ID BIGINT NOT NULL,
  CREATE_TIME TIMESTAMP NOT NULL,
  START_TIME TIMESTAMP DEFAULT NULL,
  END_TIME TIMESTAMP DEFAULT NULL,
  STATUS VARCHAR(10),
  EXIT_CODE VARCHAR(20),
  EXIT_MESSAGE VARCHAR(2500),
  LAST_UPDATED TIMESTAMP,
  JOB_CONFIGURATION_LOCATION VARCHAR(2500) NULL,
  constraint JOB_INSTANCE_EXECUTION_FK foreign key (JOB_INSTANCE_ID)
  references BATCH_JOB_INSTANCE(JOB_INSTANCE_ID)
) ;

以下列表描述了每一列:

  • JOB_EXECUTION_ID:唯一标识此执行的主键。可通过调用JobExecution对象的getId方法获得此列的值。

  • VERSION:请参见Version

  • JOB_INSTANCE_IDBATCH_JOB_INSTANCE表中的外键。它指示此执行所属的实例。每个实例可能有多个执行。

  • CREATE_TIME:时间戳,表示创建执行的时间。

  • START_TIME:时间戳,表示开始执行的时间。

  • END_TIME:时间戳,表示执行完成的时间,无论成功或失败。当前未运行作业时,此列中的值为空,表明存在某种类型的错误,并且框架无法执行最后的保存,然后再失败。

  • STATUS:表示执行状态的字符串。这可能是COMPLETEDSTARTED等。此列的对象表示形式是BatchStatus枚举。

  • EXIT_CODE:代表执行的退出代码的字符串。对于命令行作业,可以将其转换为数字。

  • EXIT_MESSAGE:字符串,表示有关作业如何退出的更详细描述。在发生故障的情况下,这可能包括尽可能多的堆栈跟踪。

  • LAST_UPDATED:时间戳记,表示上次执行被持久化的时间。

A.5. BATCH_STEP_EXECUTION

BATCH_STEP_EXECUTION 表包含与StepExecution对象有关的所有信息。该表在很多方面与BATCH_JOB_EXECUTION表相似,并且对于每个JobExecution创建的每个Step总是至少存在一个条目。以下 Lists 显示了BATCH_STEP_EXECUTION表的定义:

CREATE TABLE BATCH_STEP_EXECUTION  (
  STEP_EXECUTION_ID BIGINT  PRIMARY KEY ,
  VERSION BIGINT NOT NULL,
  STEP_NAME VARCHAR(100) NOT NULL,
  JOB_EXECUTION_ID BIGINT NOT NULL,
  START_TIME TIMESTAMP NOT NULL ,
  END_TIME TIMESTAMP DEFAULT NULL,
  STATUS VARCHAR(10),
  COMMIT_COUNT BIGINT ,
  READ_COUNT BIGINT ,
  FILTER_COUNT BIGINT ,
  WRITE_COUNT BIGINT ,
  READ_SKIP_COUNT BIGINT ,
  WRITE_SKIP_COUNT BIGINT ,
  PROCESS_SKIP_COUNT BIGINT ,
  ROLLBACK_COUNT BIGINT ,
  EXIT_CODE VARCHAR(20) ,
  EXIT_MESSAGE VARCHAR(2500) ,
  LAST_UPDATED TIMESTAMP,
  constraint JOB_EXECUTION_STEP_FK foreign key (JOB_EXECUTION_ID)
  references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
) ;

以下列表描述了每一列:

  • STEP_EXECUTION_ID:唯一标识此执行的主键。该列的值应可通过调用StepExecution对象的getId方法获得。

  • VERSION:请参见Version

  • STEP_NAME:此执行所属的步骤的名称。

  • JOB_EXECUTION_IDBATCH_JOB_EXECUTION表中的外键。它指示此StepExecution所属的JobExecution。对于给定的Step名称,给定的JobExecution可能只有一个StepExecution

  • START_TIME:时间戳,表示开始执行的时间。

  • END_TIME:时间戳,表示执行完成的时间,无论成功或失败。即使该作业当前未在运行,此列中的空值也表明存在某种类型的错误,并且框架无法在失败前执行上一次保存。

  • STATUS:表示执行状态的字符串。这可能是COMPLETEDSTARTED等。此列的对象表示形式是BatchStatus枚举。

  • COMMIT_COUNT:步骤在此执行过程中提交事务的次数。

  • READ_COUNT:在此执行期间读取的 Item 数。

  • FILTER_COUNT:从该执行中筛选出的 Item 数。

  • WRITE_COUNT:在此执行期间写入和提交的 Item 数。

  • READ_SKIP_COUNT:在此执行期间读取时跳过的 Item 数。

  • WRITE_SKIP_COUNT:此执行期间写入时跳过的 Item 数。

  • PROCESS_SKIP_COUNT:在此执行期间的处理过程中跳过的 Item 数。

  • ROLLBACK_COUNT:执行期间的回滚次数。请注意,此计数包括每次回滚发生的时间,包括用于重试的回滚和跳过恢复过程中的回滚。

  • EXIT_CODE:代表执行的退出代码的字符串。对于命令行作业,可以将其转换为数字。

  • EXIT_MESSAGE:字符串,表示有关作业如何退出的更详细描述。在发生故障的情况下,这可能包括尽可能多的堆栈跟踪。

  • LAST_UPDATED:时间戳记,表示上次执行被持久化的时间。

A.6. BATCH_JOB_EXECUTION_CONTEXT

BATCH_JOB_EXECUTION_CONTEXT表保存与JobExecutionContext相关的所有信息。每个JobExecution恰好有一个Job ExecutionContext,并且它包含特定作业执行所需的所有作业级别数据。此数据通常表示故障后必须检索的状态,以便JobInstance可以“从中断处开始”。以下 Lists 显示了BATCH_JOB_EXECUTION_CONTEXT表的定义:

CREATE TABLE BATCH_JOB_EXECUTION_CONTEXT  (
  JOB_EXECUTION_ID BIGINT PRIMARY KEY,
  SHORT_CONTEXT VARCHAR(2500) NOT NULL,
  SERIALIZED_CONTEXT CLOB,
  constraint JOB_EXEC_CTX_FK foreign key (JOB_EXECUTION_ID)
  references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
) ;

以下列表描述了每一列:

  • JOB_EXECUTION_ID:表示上下文所属的JobExecution的外键。与给定执行相关联的行可能不止一个。

  • SHORT_CONTEXTSERIALIZED_CONTEXT的字符串版本。

  • SERIALIZED_CONTEXT:整个上下文,已序列化。

A.7. BATCH_STEP_EXECUTION_CONTEXT

BATCH_STEP_EXECUTION_CONTEXT表保存与StepExecutionContext相关的所有信息。每个StepExecution恰好有一个ExecutionContext,并且它包含特定步骤执行需要保留的所有数据。该数据通常表示故障后必须恢复的状态,以便JobInstance可以从中断处开始。以下 Lists 显示了BATCH_STEP_EXECUTION_CONTEXT表的定义:

CREATE TABLE BATCH_STEP_EXECUTION_CONTEXT  (
  STEP_EXECUTION_ID BIGINT PRIMARY KEY,
  SHORT_CONTEXT VARCHAR(2500) NOT NULL,
  SERIALIZED_CONTEXT CLOB,
  constraint STEP_EXEC_CTX_FK foreign key (STEP_EXECUTION_ID)
  references BATCH_STEP_EXECUTION(STEP_EXECUTION_ID)
) ;

以下列表描述了每一列:

  • STEP_EXECUTION_ID:表示上下文所属的StepExecution的外键。与给定执行相关联的行可能不止一个。

  • SHORT_CONTEXTSERIALIZED_CONTEXT的字符串版本。

  • SERIALIZED_CONTEXT:整个上下文,已序列化。

A.8. Archiving

因为每次运行批处理作业时多个表中都有条目,所以通常为元数据表创建一个归档策略。这些表本身旨在显示过去发生的情况的记录,通常不影响任何作业的运行,但有一些与重启有关的显着 exception:

  • 框架使用元数据表来确定以前是否已运行过特定的JobInstance。如果已运行并且作业无法重新启动,则将引发异常。

  • 如果删除JobInstance的条目而未成功完成,则框架认为该作业是新的,而不是重新启动。

  • 如果作业重新启动,则框架将使用已保留到ExecutionContext的任何数据来恢复Job's状态。因此,从该表中删除所有未成功完成的作业的条目将阻止它们从正确的位置开始(如果再次运行)。

A.9.国际和多字节字符

如果在业务处理中使用多字节字符集(例如中文或西里尔字母),则可能需要将这些字符保留在 Spring Batch 模式中。许多用户发现,只需将架构更改为VARCHAR列的长度的两倍就足够了。其他人更喜欢用VARCHAR列长度值的一半max-varchar-length配置JobRepository。一些用户还报告说,他们在架构定义中使用NVARCHAR代替VARCHAR。最佳结果取决于数据库平台和本地配置数据库服务器的方式。

A.10.对元数据表构建索引的建议

Spring Batch 为几个常见数据库平台的核心 jar 文件中的元数据表提供了 DDL 示例。索引声明未包含在该 DDL 中,因为用户可能希望索引的方式有太多变化,具体取决于用户的精确平台,本地约定以及工作方式的业务要求。下面的内容提供了一些指示,这些指示是 Spring Batch 提供的 DAO 实现将在WHERE子句中使用哪些列以及它们的使用频率,以便各个 Item 可以对索引制定自己的决定:

表 1. SQL 语句中的 where 子句(不包括主键)及其大致使用频率。

默认表名Where ClauseFrequency
BATCH_JOB_INSTANCEJOB_NAME =吗?和 JOB_KEY =?每次启动工作
BATCH_JOB_EXECUTIONJOB_INSTANCE_ID =?每次重新启动作业
BATCH_STEP_EXECUTION版本=?在提交间隔上,又称为块(以及步骤的开始和结束)
BATCH_STEP_EXECUTIONSTEP_NAME =吗?和 JOB_EXECUTION_ID =?在执行每个步骤之前