【发布时间】:2023-04-11 06:57:01
【问题描述】:
上下文
我们有一个批处理作业,将本地化的国家名称(即将国家名称翻译成不同的语言)从外部复制到我们的数据库中。这个想法是在一个块中处理单个国家的所有本地化国家名称(即第一个块 - 安道尔的所有翻译,下一个块 - 阿联酋的所有翻译等)。我们使用JdbcCursorItemReader 读取外部数据 + 一些 oracle 分析函数来提供该国家可用的翻译总数:类似于
select country_code, language_code, localized_name, COUNT(1) OVER(PARTITION BY c_lng.country_code) as lng_count
from EXT_COUNTRY_LNG c_lng
order by c_lng.countty_code, c_lng.language_code
问题
因此,按块切割此输入看起来很简单:当您读取lng_count 中指定的确切行数时停止块并从下一个读取行开始一个新的,但实际上看起来并不那么简单: (
首先要尝试的是自定义完成策略。但问题是,它无权访问ItemReader 读取的最后一项——您应该明确地将其置于阅读器的上下文中,并将其重新置于策略中。不喜欢它,因为它需要额外的阅读器修改/添加阅读器侦听器。此外,我不喜欢来回序列化/反序列化相同的项目。而且我觉得JobContext/StepContext 不是存放此类数据的好地方。
还有 RepeatContext 看起来是存放此类数据的更好地方,但我无法轻松地找到它...
所以最后我们得到了这样的解决方案:
@Bean(name = "localizedCountryNamesStep")
@JobScope
public Step insertCountryStep(
final StepBuilderFactory stepBuilderFactory,
final MasterdataCountryNameReader countryNameReader,
final MasterdataCountryNameProcessor countryNameProcessor,
final MasterdataCountryNameWriter writer) {
/* Use the same fixed-commit policy, but update it's chunk size dynamically */
final SimpleCompletionPolicy policy = new SimpleCompletionPolicy();
return stepBuilderFactory.get("localizedCountryNamesStep")
.<ExtCountryLng, LocalizedCountryName> chunk(policy)
.reader(countryNameReader)
.listener(new ItemReadListener<ExtCountryLng>() {
@Override
public void beforeRead() {
// do nothing
}
@Override
public void afterRead(final ExtCountryLng item) {
/* Update the cunk size after every read: consequent reads
inside the same country = same chunk do nothing since lngCount is always the same there */
policy.setChunkSize(item.getLngCount());
}
@Override
public void onReadError(final Exception ex) {
// do nothing
}
})
.processor(countryNameProcessor)
.writer(writer)
.faultTolerant()
.skip(RuntimeException.class)
.skipLimit(Integer.MAX_VALUE) // Batch does not support unlimited skip
.retryLimit(0) // this solution disables only retry, but not recover
.build();
}
它工作正常,只需要最少的代码更改,但对我来说还是有点难看。所以我想知道,当ItemReader 上已经提供了所有必需的信息时,是否还有另一种优雅的方法可以在 Spring Batch 中执行动态块大小?
【问题讨论】:
-
afterRead 听起来不是更改块大小的正确位置,我会将其放在 afterWrite 中以对下一个块生效
-
从逻辑上讲
afterWrite听起来是对的,但是 1)在编写一个不带额外数据库查询的卡盘后,您没有该信息 2)第一个块的大小仍应以某种方式确定 - 另一个额外的数据库查询? -
您是否在处理之前清除了目标表?还是这只是一份一次性工作?
-
@DeanClark ,不,这是一个全面的“和解”:插入新记录,更新更新记录,删除删除记录。这就是为什么必须一次向作者提供与单个国家/地区相关的所有本地化国家/地区名称。
标签: java spring-batch chunks