对于最新稳定版本,请使用Spring Batch Documentation 6.0.0spring-doc.cadn.net.cn

FlatFileItemReader

平面文件是指任何最多包含二维(表格)数据的文件类型。 在 Spring Batch 框架中读取平面文件由名为FlatFileItemReader该功能提供了读取和解析平板的基本功能 文件。两个最重要的依赖关系FlatFileItemReader资源线图仪.这线图仪界面将在下一个区域进一步探讨 部分。资源属性代表一个Spring核心资源.文档 关于如何制作这种Beans的介绍可以在《春季》一集中找到 框架,第五章。资源。因此,本指南不涉及 创建资源对象之外展示了以下简单示例:spring-doc.cadn.net.cn

Resource resource = new FileSystemResource("resources/trades.csv");

在复杂的批处理环境中,目录结构通常由企业应用集成(EAI)管理 基础设施,即为外部接口设置投放区以传输文件 从FTP地点到批处理地点,反之亦然。文件移动工具 超出了春批架构的范围,但批处理中并不罕见 作业流中包含文件移动工具作为作业流中的步骤。批次 架构只需要知道如何定位待处理的文件。Spring Batch 开始将数据从该起点输入管道的过程。然而,Spring 集成提供了许多 这些类型的服务。spring-doc.cadn.net.cn

其他性质FlatFileItemReader让你进一步说明你的数据是怎样的 如下表所述的解释:spring-doc.cadn.net.cn

表1。FlatFileItemReader性能
属性 类型 描述

评论spring-doc.cadn.net.cn

弦[]spring-doc.cadn.net.cn

指定表示注释行的前缀。spring-doc.cadn.net.cn

编码spring-doc.cadn.net.cn

字符串spring-doc.cadn.net.cn

指定使用哪种文本编码。默认值为UTF-8.spring-doc.cadn.net.cn

线图仪spring-doc.cadn.net.cn

线图仪spring-doc.cadn.net.cn

转换为字符串到一个对象代表该物品。spring-doc.cadn.net.cn

linesToSkipspring-doc.cadn.net.cn

智力spring-doc.cadn.net.cn

文件顶部可以忽略的行数。spring-doc.cadn.net.cn

记录分离器策略spring-doc.cadn.net.cn

记录分隔器策略spring-doc.cadn.net.cn

用于确定线尾位置 并且如果在引号字符串内,可以继续在结尾的行上继续。spring-doc.cadn.net.cn

资源spring-doc.cadn.net.cn

资源spring-doc.cadn.net.cn

阅读资源。spring-doc.cadn.net.cn

跳过了回拨spring-doc.cadn.net.cn

线路回调处理程序spring-doc.cadn.net.cn

传递原始行内容的接口 文件中要跳过的行。如果linesToSkip设置为2,则该接口为 打了两遍电话。spring-doc.cadn.net.cn

严格spring-doc.cadn.net.cn

布尔spring-doc.cadn.net.cn

在严格模式下,读卡器会抛出一个异常执行上下文如果 输入资源不存在。否则,它会记录问题并继续。spring-doc.cadn.net.cn

线图仪

如同行图仪,该结构采用低层构造,如结果集以及返回 一对象,平面文件处理需要相同的构造来转换字符串线 变成了对象如以下接口定义所示:spring-doc.cadn.net.cn

public interface LineMapper<T> {

    T mapLine(String line, int lineNumber) throws Exception;

}

基本契约是,给定当前行及其所对应的行号 关联后,映射器应返回一个结果域对象。这类似于行图仪即每行都与其行号相关联,就像 a 中的每一行一样结果集与其行号绑定。这使得行号可以与 结果域对象用于身份比较或更有用的日志记录。然而 与行图仪线图仪给出一条原始直线,如上所述,仅 能帮你完成一半。该行必须被标记为野外集,此时可以是 映射到一个对象,如本文档后面所述。spring-doc.cadn.net.cn

LineTokenizer

将输入线转换为野外集是必要的,因为 可以有多种格式的平面文件数据需要转换为野外集.在 Spring Batch,这个接口是LineTokenizer:spring-doc.cadn.net.cn

public interface LineTokenizer {

    FieldSet tokenize(String line);

}

LineTokenizer满足于,给定一行输入(理论上字符串可以包含多行),a野外集表示该直线为 返回。这野外集然后可以传递给FieldSetMapper.Spring Batch包含 以下内容LineTokenizer实现:spring-doc.cadn.net.cn

  • DelimitedLineTokenizer: 用于记录中字段之间被 定界符。最常见的分隔符是逗号,但也常用管道或分号 也。spring-doc.cadn.net.cn

  • 固定长度标记器: 用于记录中字段各自为“固定”的文件 宽度“。每个字段的宽度必须为每种记录类型定义。spring-doc.cadn.net.cn

  • 模式匹配复合线分词器: 确定LineTokenizer在以下列表中 分词器应通过与某个模式进行对照来对应特定行。spring-doc.cadn.net.cn

FieldSetMapper

FieldSetMapper接口定义了单一方法,mapFieldSet,该过程具有野外集对象并将其内容映射到一个对象。该对象可以是自定义DTO,也是一个 域对象,或数组,具体取决于作业需求。这FieldSetMapper是 与LineTokenizer将数据行从资源中转换出来 进入目标类型的对象,如以下接口定义所示:spring-doc.cadn.net.cn

public interface FieldSetMapper<T> {

    T mapFieldSet(FieldSet fieldSet) throws BindException;

}

所用图案与行图仪使用Jdbc模板.spring-doc.cadn.net.cn

默认线图器

现在定义了平面文件读取的基本接口,它变为 明确需要三个基本步骤:spring-doc.cadn.net.cn

  1. 读文件里的一句话。spring-doc.cadn.net.cn

  2. 传给字符串进入LineTokenizer#tokenize()获取野外集.spring-doc.cadn.net.cn

  3. 传给野外集从分词化到AFieldSetMapper,返回 结果由ItemReader#read()方法。spring-doc.cadn.net.cn

上述两种接口代表两个独立任务:将一条线转换为野外集以及映射一个野外集到域对象。因为LineTokenizer线图仪(一行),以及FieldSetMapper与 的输出相匹配线图仪,一个默认实现 同时使用LineTokenizer以及一个FieldSetMapper提供。这默认线图器, 以下类定义所示,代表大多数用户所需的行为:spring-doc.cadn.net.cn

public class DefaultLineMapper<T> implements LineMapper<>, InitializingBean {

    private LineTokenizer tokenizer;

    private FieldSetMapper<T> fieldSetMapper;

    public T mapLine(String line, int lineNumber) throws Exception {
        return fieldSetMapper.mapFieldSet(tokenizer.tokenize(line));
    }

    public void setLineTokenizer(LineTokenizer tokenizer) {
        this.tokenizer = tokenizer;
    }

    public void setFieldSetMapper(FieldSetMapper<T> fieldSetMapper) {
        this.fieldSetMapper = fieldSetMapper;
    }
}

上述功能是在默认实现中提供,而非被开发 进入阅读器本身(如同之前版本框架所做的那样),以便用户能够使用 在控制解析过程时,尤其是在访问原始文件时,更灵活 需要这条线。spring-doc.cadn.net.cn

简单分隔文件读取示例

以下示例展示了如何用实际的域场景读取一个平面文件。 这个批次作业会从以下文件中读取球员:spring-doc.cadn.net.cn

ID,lastName,firstName,position,birthYear,debutYear
"AbduKa00,Abdul-Jabbar,Karim,rb,1974,1996",
"AbduRa00,Abdullah,Rabih,rb,1975,1999",
"AberWa00,Abercrombie,Walter,rb,1959,1982",
"AbraDa00,Abramowicz,Danny,wr,1945,1967",
"AdamBo00,Adams,Bob,te,1946,1969",
"AdamCh00,Adams,Charlie,wr,1979,2003"

该文件的内容映射到以下内容选手领域对象:spring-doc.cadn.net.cn

public class Player implements Serializable {

    private String ID;
    private String lastName;
    private String firstName;
    private String position;
    private int birthYear;
    private int debutYear;

    public String toString() {
        return "PLAYER:ID=" + ID + ",Last Name=" + lastName +
            ",First Name=" + firstName + ",Position=" + position +
            ",Birth Year=" + birthYear + ",DebutYear=" +
            debutYear;
    }

    // setters and getters...
}

映射一个野外集变成了选手对象,aFieldSetMapper这回馈了玩家的需求 待定义,如下例所示:spring-doc.cadn.net.cn

protected static class PlayerFieldSetMapper implements FieldSetMapper<Player> {
    public Player mapFieldSet(FieldSet fieldSet) {
        Player player = new Player();

        player.setID(fieldSet.readString(0));
        player.setLastName(fieldSet.readString(1));
        player.setFirstName(fieldSet.readString(2));
        player.setPosition(fieldSet.readString(3));
        player.setBirthYear(fieldSet.readInt(4));
        player.setDebutYear(fieldSet.readInt(5));

        return player;
    }
}

然后可以通过正确构造FlatFileItemReader以及呼唤如下例所示:spring-doc.cadn.net.cn

FlatFileItemReader<Player> itemReader = new FlatFileItemReader<>();
itemReader.setResource(new FileSystemResource("resources/players.csv"));
DefaultLineMapper<Player> lineMapper = new DefaultLineMapper<>();
//DelimitedLineTokenizer defaults to comma as its delimiter
lineMapper.setLineTokenizer(new DelimitedLineTokenizer());
lineMapper.setFieldSetMapper(new PlayerFieldSetMapper());
itemReader.setLineMapper(lineMapper);
itemReader.open(new ExecutionContext());
Player player = itemReader.read();

每次呼叫退还一个新的选手文件中每行的对象。当文件结尾为 达到被归还。spring-doc.cadn.net.cn

按名称映射字段

还有一项额外的功能是两者都允许的DelimitedLineTokenizer固定长度标记器且 在功能上类似于 JDBC结果集.场的名称可以注入到任一中LineTokenizer提升映射函数可读性的实现。 首先,将平面文件中所有字段的列名注入到分词器中, 如下例所示:spring-doc.cadn.net.cn

tokenizer.setNames(new String[] {"ID", "lastName", "firstName", "position", "birthYear", "debutYear"});

一个FieldSetMapper你可以根据以下方式使用这些信息:spring-doc.cadn.net.cn

public class PlayerMapper implements FieldSetMapper<Player> {
    public Player mapFieldSet(FieldSet fs) {

       if (fs == null) {
           return null;
       }

       Player player = new Player();
       player.setID(fs.readString("ID"));
       player.setLastName(fs.readString("lastName"));
       player.setFirstName(fs.readString("firstName"));
       player.setPosition(fs.readString("position"));
       player.setDebutYear(fs.readInt("debutYear"));
       player.setBirthYear(fs.readInt("birthYear"));

       return player;
   }
}

将字段集自动映射到域对象

对许多人来说,必须写出特定的内容FieldSetMapper和写作一样繁琐 一个特定的行图仪对于Jdbc模板.Spring Batch通过提供 一个FieldSetMapper它通过匹配字段名与设定器,自动映射字段 在对象上使用JavaBean规范。spring-doc.cadn.net.cn

再次以足球为例,BeanWrapperFieldSetMapper配置看起来像 以下是爪哇语的摘录:spring-doc.cadn.net.cn

Java 配置
@Bean
public FieldSetMapper fieldSetMapper() {
	BeanWrapperFieldSetMapper fieldSetMapper = new BeanWrapperFieldSetMapper();

	fieldSetMapper.setPrototypeBeanName("player");

	return fieldSetMapper;
}

@Bean
@Scope("prototype")
public Player player() {
	return new Player();
}

再次以足球为例,BeanWrapperFieldSetMapper配置看起来像 以下为XML格式的片段:spring-doc.cadn.net.cn

XML 配置
<bean id="fieldSetMapper"
      class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
    <property name="prototypeBeanName" value="player" />
</bean>

<bean id="player"
      class="org.springframework.batch.samples.domain.Player"
      scope="prototype" />

对于每个野外集,映射器在新的 上寻找对应的定位器 实例选手对象(因此,需要原型作用波)在 就像 Spring 容器寻找与属性名称匹配的设置器一样。每种情况都可用 在野外集映射,结果选手返回对象,且 需要代码。spring-doc.cadn.net.cn

固定长度文件格式

到目前为止,只有分隔文件被详细讨论过。然而,它们代表 只有一半的文件显示图片。许多使用平面文件的组织使用固定文件 长度格式。以下是一个固定长度文件示例:spring-doc.cadn.net.cn

UK21341EAH4121131.11customer1
UK21341EAH4221232.11customer2
UK21341EAH4321333.11customer3
UK21341EAH4421434.11customer4
UK21341EAH4521535.11customer5

虽然看起来像是一个大场,但实际上代表了四个不同的场:spring-doc.cadn.net.cn

  1. ISIN:订单物品的唯一标识符——12个字符。spring-doc.cadn.net.cn

  2. 数量:订购物品的数量——3个字符。spring-doc.cadn.net.cn

  3. 价格:物品价格——5个字符长度。spring-doc.cadn.net.cn

  4. 顾客:订购该商品的顾客身份证——9个字符。spring-doc.cadn.net.cn

在配置固定长度线标记器必须提供每个长度 以范围的形式出现。spring-doc.cadn.net.cn

以下示例展示了如何定义固定长度线标记器在 Java:spring-doc.cadn.net.cn

Java 配置
@Bean
public FixedLengthTokenizer fixedLengthTokenizer() {
	FixedLengthTokenizer tokenizer = new FixedLengthTokenizer();

	tokenizer.setNames("ISIN", "Quantity", "Price", "Customer");
	tokenizer.setColumns(new Range(1, 12),
						new Range(13, 15),
						new Range(16, 20),
						new Range(21, 29));

	return tokenizer;
}

以下示例展示了如何定义固定长度线标记器在 XML:spring-doc.cadn.net.cn

XML 配置
<bean id="fixedLengthLineTokenizer"
      class="org.springframework.batch.item.file.transform.FixedLengthTokenizer">
    <property name="names" value="ISIN,Quantity,Price,Customer" />
    <property name="columns" value="1-12, 13-15, 16-20, 21-29" />
</bean>

因为固定长度线标记器使用方式相同LineTokenizer作为接口 如前所述,返回结果相同野外集仿佛用了分隔符。这 允许在处理其输出时采用相同的方法,例如使用BeanWrapperFieldSetMapper.spring-doc.cadn.net.cn

支持前述范围语法需要专业的属性编辑器,RangeArrayPropertyEditor,配置为应用上下文.不过,这颗豆子 自动声明为应用上下文其中使用批处理命名空间。spring-doc.cadn.net.cn

因为固定长度线标记器使用方式相同LineTokenizer作为接口 如上所述,返回结果相同野外集仿佛用了分隔符。这 允许在处理其输出时使用相同的方法,例如使用BeanWrapperFieldSetMapper.spring-doc.cadn.net.cn

单一文件中的多种记录类型

到目前为止所有文件读取示例都假设了 为了简化:文件中的所有记录格式相同。不过,这可能 但情况并非总是如此。一个文件中可能有不同的记录是非常常见的 需要以不同方式标记并映射到不同对象的格式。这 以下摘录为一份文件,说明了这一点:spring-doc.cadn.net.cn

USER;Smith;Peter;;T;20014539;F
LINEA;1044391041ABC037.49G201XX1383.12H
LINEB;2134776319DEF422.99M005LI

在这个文件中,我们有三种类型的记录:“USER”、“LINEA”和“LINEB”。一个“用户”行 对应于用户对象。“LINEA”和“LINEB”都对应于线对象 不过“LINEA”比“LINEB”提供更多信息。spring-doc.cadn.net.cn

ItemReader分别读取每行,但必须指定不同的行LineTokenizerFieldSetMapper使得ItemWriter接收 正确的物品。这模式匹配复合线映射器通过允许映射,这变得简单 模式 到线分词器模式为FieldSetMappers需要配置。spring-doc.cadn.net.cn

Java 配置
@Bean
public PatternMatchingCompositeLineMapper orderFileLineMapper() {
	PatternMatchingCompositeLineMapper lineMapper =
		new PatternMatchingCompositeLineMapper();

	Map<String, LineTokenizer> tokenizers = new HashMap<>(3);
	tokenizers.put("USER*", userTokenizer());
	tokenizers.put("LINEA*", lineATokenizer());
	tokenizers.put("LINEB*", lineBTokenizer());

	lineMapper.setTokenizers(tokenizers);

	Map<String, FieldSetMapper> mappers = new HashMap<>(2);
	mappers.put("USER*", userFieldSetMapper());
	mappers.put("LINE*", lineFieldSetMapper());

	lineMapper.setFieldSetMappers(mappers);

	return lineMapper;
}

以下示例展示了如何定义固定长度线标记器在 XML:spring-doc.cadn.net.cn

XML 配置
<bean id="orderFileLineMapper"
      class="org.spr...PatternMatchingCompositeLineMapper">
    <property name="tokenizers">
        <map>
            <entry key="USER*" value-ref="userTokenizer" />
            <entry key="LINEA*" value-ref="lineATokenizer" />
            <entry key="LINEB*" value-ref="lineBTokenizer" />
        </map>
    </property>
    <property name="fieldSetMappers">
        <map>
            <entry key="USER*" value-ref="userFieldSetMapper" />
            <entry key="LINE*" value-ref="lineFieldSetMapper" />
        </map>
    </property>
</bean>

在这个例子中,“LINEA” 和 “LINEB” 分别是独立的LineTokenizer实例,但它们都使用 一样FieldSetMapper.spring-doc.cadn.net.cn

模式匹配复合线映射器使用图案拼凑员#比赛方法 为了为每条线选出正确的代表。这模式匹配器允许 两个具有特殊含义的万用字符:问号(“?”)恰好匹配一个 字符,而星号(“*”)则匹配零个或多个字符。注意,在 在配置之前,所有模式以星号结尾,使其有效 行前缀。这模式匹配器总是匹配最具体的模式 无论配置顺序如何,都可能。所以如果“LINE*”和“LINEA*” 是 两者均列为图案,“LINEA”对应“LINEA*”,而“LINEB”对应 图案“LINE*”。此外,单一星号(“*”)可作为默认,通过匹配 任何没有与其他图案匹配的直线。spring-doc.cadn.net.cn

以下示例展示了如何匹配Java中其他任何模式都无法匹配的行:spring-doc.cadn.net.cn

Java 配置
...
tokenizers.put("*", defaultLineTokenizer());
...

以下示例展示了如何匹配XML中没有其他模式匹配的行:spring-doc.cadn.net.cn

XML 配置
<entry key="*" value-ref="defaultLineTokenizer" />

还有一个模式匹配复合线分词器这可以用于分词化 独自。spring-doc.cadn.net.cn

平面文件通常包含跨越多行的记录。自 处理这种情况时,需要更复杂的策略。演示一下 常见的模式可见于multiLineRecords样本。spring-doc.cadn.net.cn

平面文件中的异常处理

在许多情况下,分牌化某行可能导致异常出现。多 平面文件不完美,且包含格式错误的记录。许多用户选择 在记录问题、原始行和该行时跳过这些错误行 数。这些日志可以之后手动检查,也可以用其他批处理作业进行检查。为此 因此,Spring Batch 提供了处理解析异常的异常层级结构:FlatFileParseExceptionFlatFileFormatException.FlatFileParseException是 被FlatFileItemReader当尝试读取 a 时遇到任何错误 文件。FlatFileFormatException是被 的实现抛掷LineTokenizer接口和表示在分词化过程中遇到的更具体错误。spring-doc.cadn.net.cn

IncorrectTokenCountException

DelimitedLineTokenizer固定长度线标记器具备 具体 可用于创建野外集.然而,如果列数 名称与分词行时发现的列数不匹配,野外集无法被创造,且IncorrectTokenCountException是抛掷的,包含 遇到的标记数量及预期数量,如下例所示:spring-doc.cadn.net.cn

tokenizer.setNames(new String[] {"A", "B", "C", "D"});

try {
    tokenizer.tokenize("a,b,c");
}
catch (IncorrectTokenCountException e) {
    assertEquals(4, e.getExpectedCount());
    assertEquals(3, e.getActualCount());
}

因为分词器配置了4列名称,但只找到3个Tokens 文件,一个IncorrectTokenCountException被扔了。spring-doc.cadn.net.cn

IncorrectLineLengthException

格式为固定长度的文件在解析时有额外要求 因为与分隔格式不同,每个列必须严格遵守其预定义 宽度。如果总行长不等于该列的最宽值,则 投掷异常,如下示例所示:spring-doc.cadn.net.cn

tokenizer.setColumns(new Range[] { new Range(1, 5),
                                   new Range(6, 10),
                                   new Range(11, 15) });
try {
    tokenizer.tokenize("12345");
    fail("Expected IncorrectLineLengthException");
}
catch (IncorrectLineLengthException ex) {
    assertEquals(15, ex.getExpectedLength());
    assertEquals(5, ex.getActualLength());
}

上述分词器配置的区间为:1-5、6-10 和 11-15。因此 全线总长15条。然而,在前面的例子中,长度为5的直线 被传入,导致IncorrectLineLengthException被扔出去。抛出一个 这里的例外是,除了映射第一列,还允许处理 如果失败,行会更早且包含更多信息,而 试图在A的第2栏读取FieldSetMapper.然而,也存在一些情形 直线长度并不总是恒定的。因此,行长的验证可以 通过“严格”性质关闭,如下示例所示:spring-doc.cadn.net.cn

tokenizer.setColumns(new Range[] { new Range(1, 5), new Range(6, 10) });
tokenizer.setStrict(false);
FieldSet tokens = tokenizer.tokenize("12345");
assertEquals("12345", tokens.readString(0));
assertEquals("", tokens.readString(1));

前述示例与前例几乎相同,唯一区别是tokenizer.setStrict(false)被叫到了。该设置告诉分词器不要强制执行 分词时的行长。一个野外集现在正确生成了 和 返回。然而,它只包含剩余值的空标记。spring-doc.cadn.net.cn