我正在构建弹出批处理作业,修改给定数量的记录。记录ID列表是作业的输入参数。例如,一个作业可能是:修改记录ID {1,2,3,4}并在相关表上设置参数X和Y.
由于我无法将一个非常长的输入列表(典型案例,50K记录)传递给我的ItemReader,我只传递一个MyJobID,然后itemReader用来加载目标ID列表。
问题是,生成的代码出现"错误" (尽管它有效)而不是春天的精神。这是读者:
SELECT DISTINCT inventory.company, inventory.contracts
FROM inventory
WHERE inventory.company not in ('Company Name')
(
SELECT
DISTINCT inventory.company,
count(DISTINCT(concat(inventory.company, inventory.contracts))) AS Countr
FROM inventory
WHERE
inventory.company not in ('Company Name')
GROUP BY company
HAVING count(DISTINCT(concat(inventory.company, inventory.contracts))) > 1
)
ORDER BY Countr DESC
我尝试将read()方法的第一部分移动到构造函数,但此时@Autowired引用为null。之后(在读取方法上)它被初始化。
有没有更好的方法来编写ItemReader?我想移动"加载"或者这是这种情况的最佳解决方案吗?
谢谢。
答案 0 :(得分:0)
一般来说,你的做法并非错误",但可能并不理想。
首先,您可以将初始化移动到使用@PostConstruct注释的initMethod。在注入所有自动连接字段后调用此方法:
@PostConstruct
public void afterPropertiesSet() throws Exception {
itemsList = new ArrayList<Integer>();
MyJob myJob = (MyJob) jobService.loadById(jobId);
for (Integer i : myJob.getTargedIdList()) {
itemsList.add(i);
}
}
但仍然存在问题,即您一次加载所有数据。如果要处理十亿条记录,可能会耗尽内存。
所以你应该做的只是将一大块数据加载到内存中,然后在read方法中逐个返回项目。如果已返回块的所有条目,则加载下一个块并再次逐个返回其项。如果没有要加载的其他块,则从read方法返回null。
这可确保您拥有恒定的内存占用,无论您需要处理多少条记录。 (如果您看一下FlatFileItemReader,您会看到它使用BufferedReader从磁盘读取数据。虽然它与SpringBatch无关,但它的原理是相同的:它从磁盘读取一大块数据,返回如果需要更多数据,它会读取下一个数据块。
下一个问题是可重启性。如果工作在完成90%的工作后崩溃会怎样?如何重新启动作业,只处理丢失的10%? 这实际上是springbatch提供的一个功能,您所要做的就是实现ItemStream接口并实现open(),update(),close()方法。
如果你考虑这两点 - 以块的形式加载数据而不是一次性实现ItemStream接口 - 那么你最终会拥有一个充满春天气息的读者。