SpringBatch从入门到精通-6.4 读和写处理-实战4【掘金日新计划】

持续创作,加速成长,6月更文活动来啦!| 掘金·日新计划

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第15天,点击查看活动详情

SpringBatch从入门到精通-1【掘金日新计划】

SpringBatch从入门到精通-2-StepScope作用域和用法【掘金日新计划】

SpringBatch从入门到精通-3-并行处理【掘金日新计划】

SpringBatch从入门到精通-3.2-并行处理-远程分区【掘金日新计划】

SpringBatch从入门到精通-3.3-并行处理-远程分区(消息聚合)【掘金日新计划】

SpringBatch从入门到精通-4 监控和指标【掘金日新计划】

SpringBatch从入门到精通-4.2 监控和指标-原理【掘金日新计划】

SpringBatch从入门到精通-5 数据源配置相关【掘金日新计划】

SpringBatch从入门到精通-5.2 数据源配置相关-原理【掘金日新计划】

SpringBatch从入门到精通-6 读和写处理【掘金日新计划】

SpringBatch从入门到精通-6.1 读和写处理-实战【掘金日新计划】

SpringBatch从入门到精通-6.2 读和写处理-实战2【掘金日新计划】

SpringBatch从入门到精通-6.3 读和写处理-实战3【掘金日新计划】

重用现有服务

批处理系统通常与其他应用程序样式结合使用。最常见的是在线系统,但它也可以通过移动每种应用程序样式使用的必要批量数据来支持集成甚至胖客户端应用程序。出于这个原因,许多用户希望在他们的批处理作业中重用现有的 DAO 或其他服务是很常见的。Spring 容器本身通过允许注入任何必要的类使这变得相当容易。但是,可能存在现有服务需要充当ItemReaderorItemWriter的情况,以满足另一个 Spring Batch 类的依赖关系,或者因为它确实是主要的ItemReader一步。为每个需要包装的服务编写一个适配器类是相当简单的,但因为这是一个常见的问题,Spring Batch 提供了实现: ItemReaderAdapter和ItemWriterAdapter. 这两个类都通过调用委托模式来实现标准的 Spring 方法,并且设置起来相当简单。

以下 Java 示例使用ItemReaderAdapter:

Java 配置

@Bean
public ItemReaderAdapter itemReader() {
    ItemReaderAdapter reader = new ItemReaderAdapter();
​
    reader.setTargetObject(fooService());
    reader.setTargetMethod("generateFoo");
​
    return reader;
}
​
@Bean
public FooService fooService() {
    return new FooService();
}
复制代码

需要注意的重要一点是,合同的合同targetMethod必须与合同相同read:用尽时,它会返回null。否则,它返回一个 Object. 其他任何事情都会阻止框架知道处理何时结束,导致无限循环或不正确的失败,具体取决于ItemWriter.

以下 Java 示例使用ItemWriterAdapter:

Java 配置

@Bean
public ItemWriterAdapter itemWriter() {
    ItemWriterAdapter writer = new ItemWriterAdapter();
​
    writer.setTargetObject(fooService());
    writer.setTargetMethod("processFoo");
​
    return writer;
}
​
@Bean
public FooService fooService() {
    return new FooService();
}
复制代码

防止状态持续存在

默认情况下,所有ItemReader和ItemWriter实现都将其当前状态存储在ExecutionContext提交之前。但是,这可能并不总是期望的行为。例如,许多开发人员选择通过使用进程指示器使他们的数据库读取器“可重新运行”。一个额外的列被添加到输入数据以指示它是否已被处理。当读取(或写入)特定记录时,已处理标志从 翻转false到true。然后 SQL 语句可以在子句中包含额外的语句where,例如where PROCESSED_IND = false,从而确保在重新启动的情况下只返回未处理的记录。在这种情况下,最好不要存储任何状态,例如当前行号,因为它在重新启动时无关紧要。因此,所有读取器和写入器都包含“saveState”属性。

以下 bean 定义显示了如何防止 Java 中的状态持久性:

Java 配置

@Bean
public JdbcCursorItemReader playerSummarizationSource(DataSource dataSource) {
    return new JdbcCursorItemReaderBuilder<PlayerSummary>()
                .dataSource(dataSource)
                .rowMapper(new PlayerSummaryMapper())
                .saveState(false)
                .sql("SELECT games.player_id, games.year_no, SUM(COMPLETES),"
                  + "SUM(ATTEMPTS), SUM(PASSING_YARDS), SUM(PASSING_TD),"
                  + "SUM(INTERCEPTIONS), SUM(RUSHES), SUM(RUSH_YARDS),"
                  + "SUM(RECEPTIONS), SUM(RECEPTIONS_YARDS), SUM(TOTAL_TD)"
                  + "from games, players where players.player_id ="
                  + "games.player_id group by games.player_id, games.year_no")
                .build();
​
}
复制代码

上面的ItemReader配置不会ExecutionContext在它参与的任何执行中创建任何条目。

创建自定义 ItemReaders 和 ItemWriters

自定义ItemReader示例

出于本示例的目的,我们创建了一个ItemReader从提供的列表中读取的简单实现。我们从实现最基本的合约 ItemReader,read方法开始,如下代码所示:

public class CustomItemReader<T> implements ItemReader<T> {
​
    List<T> items;
​
    public CustomItemReader(List<T> items) {
        this.items = items;
    }
​
    public T read() throws Exception, UnexpectedInputException,
       NonTransientResourceException, ParseException {
​
        if (!items.isEmpty()) {
            return items.remove(0);
        }
        return null;
    }
}
复制代码

前面的类接受一个项目列表并一次返回一个项目,从列表中删除每个项目。当列表为空时,返回null,从而满足了 ItemReader 的最基本要求,如下测试代码所示:

List<String> items = new ArrayList<>();
items.add("1");
items.add("2");
items.add("3");
​
ItemReader itemReader = new CustomItemReader<>(items);
assertEquals("1", itemReader.read());
assertEquals("2", itemReader.read());
assertEquals("3", itemReader.read());
assertNull(itemReader.read());
复制代码
使ItemReader可重启

最后的挑战是使ItemReader可重新启动。目前,如果处理被中断并重新开始,则ItemReader必须从头开始。这在许多情况下实际上是有效的,但有时最好从停止的地方重新启动批处理作业。关键的判别通常是读者是有状态的还是无状态的。无状态阅读器不需要担心可重新启动性,但有状态阅读器必须尝试在重新启动时重建其最后一个已知状态。因此,我们建议您尽可能让自定义阅读器保持无状态,这样您就不必担心可重新启动性。

如果确实需要存储状态,ItemStream则应使用该接口:

public class CustomItemReader<T> implements ItemReader<T>, ItemStream {
​
    List<T> items;
    int currentIndex = 0;
    private static final String CURRENT_INDEX = "current.index";
​
    public CustomItemReader(List<T> items) {
        this.items = items;
    }
​
    public T read() throws Exception, UnexpectedInputException,
        ParseException, NonTransientResourceException {
​
        if (currentIndex < items.size()) {
            return items.get(currentIndex++);
        }
​
        return null;
    }
​
    public void open(ExecutionContext executionContext) throws ItemStreamException {
        if (executionContext.containsKey(CURRENT_INDEX)) {
            currentIndex = new Long(executionContext.getLong(CURRENT_INDEX)).intValue();
        }
        else {
            currentIndex = 0;
        }
    }
​
    public void update(ExecutionContext executionContext) throws ItemStreamException {
        executionContext.putLong(CURRENT_INDEX, new Long(currentIndex).longValue());
    }
​
    public void close() throws ItemStreamException {}
}
复制代码

在每次调用该ItemStream update方法时,当前的索引ItemReader 都存储在提供ExecutionContext的键“current.index”中。调用该 ItemStream open方法时,ExecutionContext会检查它是否包含具有该键的条目。如果找到该键,则将当前索引移动到该位置。这是一个相当琐碎的例子,但它仍然符合一般合同:

ExecutionContext executionContext = new ExecutionContext();
((ItemStream)itemReader).open(executionContext);
assertEquals("1", itemReader.read());
((ItemStream)itemReader).update(executionContext);
​
List<String> items = new ArrayList<>();
items.add("1");
items.add("2");
items.add("3");
itemReader = new CustomItemReader<>(items);
​
((ItemStream)itemReader).open(executionContext);
assertEquals("2", itemReader.read());
复制代码

大多数ItemReaders都有更复杂的重启逻辑。JdbcCursorItemReader例如,将最后处理的行的行 ID 存储在游标中。

还值得注意的是,其中使用的密钥ExecutionContext不应该是微不足道的。那是因为相同ExecutionContext的内容用于 a 中的所有ItemStreams内容Step。在大多数情况下,只需在键前面加上类名就足以保证唯一性。但是,在同一步骤中使用两个相同类型的极少数情况下 ItemStream(如果需要两个文件进行输出,可能会发生这种情况),需要一个更唯一的名称。出于这个原因,许多 Spring Batch ItemReader和ItemWriter实现都有一个setName()属性,可以覆盖这个键名。

自定义ItemWriter示例

实现自定义ItemWriter在许多方面与ItemReader上面的示例相似,但在足以保证其自己的示例的方式上有所不同。但是,添加可重启性本质上是相同的,因此在此示例中不涉及。与 ItemReader示例一样,List使用 a 是为了使示例尽可能简单:

public class CustomItemWriter<T> implements ItemWriter<T> {
​
    List<T> output = TransactionAwareProxyFactory.createTransactionalList();
​
    public void write(List<? extends T> items) throws Exception {
        output.addAll(items);
    }
​
    public List<T> getOutput() {
        return output;
    }
}
复制代码
使可ItemWriter重启

为了使ItemWriter可重新启动,我们将遵循与 相同的过程 ItemReader,添加和实现ItemStream接口以同步执行上下文。在示例中,我们可能必须计算处理的项目数并将其添加为页脚记录。如果我们需要这样做,我们可以 ItemStream在我们的中实现,ItemWriter以便在重新打开流时从执行上下文中重构计数器。

在许多实际情况下,customItemWriters还委托给另一个本身可重新启动的写入程序(例如,写入文件时),或者它写入事务性资源,因此不需要重新启动,因为它是无状态的。当你有一个有状态的 writer 时,你可能应该确保实现ItemStream以及ItemWriter. 还请记住,编写器的客户端需要知道ItemStream,因此您可能需要在配置中将其注册为流。

项目读取器和写入器实现

在本节中,我们将向您介绍前几节中尚未讨论的读者和作者。

装饰器

在某些情况下,用户需要将特殊行为附加到预先存在的 ItemReader. Spring Batch 提供了一些开箱即用的装饰器,可以为您的实现添加额外的ItemReader行为ItemWriter。

Spring Batch 包括以下装饰器:

SynchronizedItemStreamReader

当使用ItemReader非线程安全的时,Spring Batch 提供 SynchronizedItemStreamReader装饰器,可用于使ItemReader 线程安全。Spring Batch 提供了一个SynchronizedItemStreamReaderBuilder用于构造SynchronizedItemStreamReader.

SingleItemPeekableItemReader

Spring Batch 包含一个装饰器,该装饰器将 peek 方法添加到ItemReader. 这种查看方法让用户可以查看前面的一项。对 peek 的重复调用返回相同的项目,这是该read方法返回的下一个项目。Spring Batch 提供了一个 SingleItemPeekableItemReaderBuilder用于构造 SingleItemPeekableItemReader.

SingleItemPeekableItemReader 的 peek 方法不是线程安全的,因为不可能在多个线程中实现 peek。只有一个偷看的线程会在下一次读取调用中获得该项目。

SynchronizedItemStreamWriter

当使用ItemWriter非线程安全的时,Spring Batch 提供 SynchronizedItemStreamWriter装饰器,可用于使ItemWriter 线程安全。Spring Batch 提供了一个SynchronizedItemStreamWriterBuilder用于构造SynchronizedItemStreamWriter.

MultiResourceItemWriter

MultiResourceItemWriter包装 a并在ResourceAwareItemWriterItemStream当前资源中写入的项目数超过 时创建新的输出资源 itemCountLimitPerResource。Spring Batch 提供了一个MultiResourceItemWriterBuilder用于构造MultiResourceItemWriter.

ClassifierCompositeItemWriter

基于ClassifierCompositeItemWriter通过ItemWriter 提供的 Classifier. 如果所有委托都是线程安全的,则实现是线程安全的。Spring Batch 提供了一个ClassifierCompositeItemWriterBuilder用于构造 ClassifierCompositeItemWriter.

ClassifierCompositeItemProcessor

这ClassifierCompositeItemProcessor是一个ItemProcessor基于ItemProcessor通过提供的Classifier. Spring Batch 提供了一个 ClassifierCompositeItemProcessorBuilder用于构造 ClassifierCompositeItemProcessor.

消息读者和作家

Spring Batch 为常用的消息传递系统提供了以下阅读器和编写器:

AmqpItemReader

TheAmqpItemReader是一个ItemReader使用 anAmqpTemplate来接收或转换来自交换的消息。Spring Batch 提供了一个AmqpItemReaderBuilder用于构造AmqpItemReader.

AmqpItemWriter

是AmqpItemWriter一个ItemWriter使用AmqpTemplate发送消息到 AMQP 交换。如果提供的名称未指定,则消息将发送到无名交换器AmqpTemplate。Spring Batch 提供了一个AmqpItemWriterBuilder用于构造AmqpItemWriter.

JmsItemReader

JmsItemReader是用于 JMS的ItemReader,它使用JmsTemplate. 模板应该有一个默认目的地,用于为read() 方法提供项目。Spring Batch 提供了一个JmsItemReaderBuilder用于构造 JmsItemReader.

JmsItemWriter

JmsItemWriter是用于 JMS的ItemWriter,它使用JmsTemplate. 模板应该有一个默认目的地,用于在write(List). Spring Batch 提供了一个JmsItemWriterBuilder用于构造JmsItemWriter.

KafkaItemReader

这KafkaItemReader是ItemReader一个 Apache Kafka 主题。它可以配置为从同一主题的多个分区读取消息。它将消息偏移量存储在执行上下文中以支持重新启动功能。Spring Batch 提供了一个 KafkaItemReaderBuilder用于构造KafkaItemReader.

KafkaItemWriter

这KafkaItemWriter是ItemWriterApache Kafka 的一个,它使用一个KafkaTemplate将事件发送到默认主题。Spring Batch 提供了一个KafkaItemWriterBuilder用于构造KafkaItemWriter.

数据库阅读器

Spring Batch 提供以下数据库阅读器:

Neo4jItemReader

这Neo4jItemReader是一个ItemReader通过使用分页技术从图形数据库 Neo4j 中读取对象的方法。Spring Batch 提供了一个Neo4jItemReaderBuilder用于构造Neo4jItemReader.

MongoItemReader

这MongoItemReader是一个ItemReader使用分页技术从 MongoDB 读取文档的方法。Spring Batch 提供了一个MongoItemReaderBuilder用于构造MongoItemReader.

HibernateCursorItemReader

这HibernateCursorItemReader是一个ItemStreamReader用于读取建立在 Hibernate 之上的数据库记录。它执行 HQL 查询,然后在初始化时,在调用方法时迭代结果集read(),依次返回与当前行对应的对象。Spring Batch 提供了一个 HibernateCursorItemReaderBuilder用于构造 HibernateCursorItemReader.

HibernatePagingItemReader

这HibernatePagingItemReader是一个ItemReader用于读取建立在 Hibernate 之上的数据库记录,并且一次只能读取固定数量的项目。Spring Batch 提供了一个HibernatePagingItemReaderBuilder用于构造 HibernatePagingItemReader.

RepositoryItemReader

RepositoryItemReader是一个ItemReader使用 a 读取记录的 a PagingAndSortingRepository。Spring Batch 提供了一个RepositoryItemReaderBuilder用于构造RepositoryItemReader.

数据库编写器

Spring Batch 提供以下数据库编写器:

Neo4jItemWriter

这Neo4jItemWriter是一个ItemWriter写入 Neo4j 数据库的实现。Spring Batch 提供了一个Neo4jItemWriterBuilder用于构造 Neo4jItemWriter.

MongoItemWriter

这MongoItemWriter是一个ItemWriter使用 Spring Data 的实现写入 MongoDB 存储的实现MongoOperations。Spring Batch 提供了一个 MongoItemWriterBuilder用于构造MongoItemWriter.

RepositoryItemWriter

这RepositoryItemWriter是来自 Spring Data的ItemWriter包装器。CrudRepositorySpring Batch 提供了一个RepositoryItemWriterBuilder用于构造RepositoryItemWriter.

HibernateItemWriter

它HibernateItemWriter使用ItemWriterHibernate 会话来保存或更新不属于当前 Hibernate 会话的实体。Spring Batch 提供了一个HibernateItemWriterBuilder用于构造HibernateItemWriter.

JdbcBatchItemWriter

这JdbcBatchItemWriter是一个ItemWriter使用批处理功能 NamedParameterJdbcTemplate为所有提供的项目执行批处理语句的方法。Spring Batch 提供了一个JdbcBatchItemWriterBuilder用于构造 JdbcBatchItemWriter.

JpaItemWriter

这JpaItemWriter是一个ItemWriter使用 JPAEntityManagerFactory来合并不属于持久性上下文的任何实体。Spring Batch 提供了一个 JpaItemWriterBuilder用于构造JpaItemWriter.

GemfireItemWriter

GemfireItemWriter是一个ItemWriter使用 aGemfireTemplate将 GemFire 中的项目作为键/值对存储的一个。Spring Batch 提供了一个GemfireItemWriterBuilder 用于构造GemfireItemWriter.

专业读者

Spring Batch 提供以下专业阅读器:

LdifReader

从LdifReadera 读取 LDIF(LDAP 数据交换格式)记录Resource,解析它们,并LdapAttribute为每个read执行返回一个对象。Spring Batch 提供了一个LdifReaderBuilder用于构造LdifReader.

MappingLdifReader

从MappingLdifReadera 读取 LDIF(LDAP 数据交换格式)记录 Resource,解析它们,然后将每个 LDIF 记录映射到 POJO(普通旧 Java 对象)。每次读取都会返回一个 POJO。Spring Batch 提供了一个MappingLdifReaderBuilder用于构造MappingLdifReader.

AvroItemReader

从资源中AvroItemReader读取序列化的 Avro 数据。每次读取都会返回一个由 Java 类或 Avro Schema 指定的类型的实例。可以选择将阅读器配置为嵌入或不嵌入 Avro 模式的输入。Spring Batch 提供了一个AvroItemReaderBuilder用于构造AvroItemReader.

专业作家

Spring Batch 提供以下专业编写器:

SimpleMailMessageItemWriter

SimpleMailMessageItemWriter是一个ItemWriter可以发送邮件的消息。它将消息的实际发送委托给MailSender. Spring Batch 提供了一个SimpleMailMessageItemWriterBuilder用于构造 SimpleMailMessageItemWriter.

AvroItemWriter

根据AvroItemWrite给定的类型或模式将 Java 对象序列化为 WriteableResource。可以选择将编写器配置为在输出中嵌入或不嵌入 Avro 模式。Spring Batch 提供了一个AvroItemWriterBuilder用于构造AvroItemWriter.

专用处理器

Spring Batch 提供以下专用处理器:

ScriptItemProcessor

这ScriptItemProcessor是一个ItemProcessor将要处理的当前项目传递给提供的脚本,并且脚本的结果由处理器返回。Spring Batch 提供了一个ScriptItemProcessorBuilder用于构造 ScriptItemProcessor.

代码位置: github.com/jackssybin/…

猜你喜欢

转载自juejin.im/post/7110609720318099493
今日推荐