编写spring-batch ItemReader的正确方法

时间:2016-12-05 17:21:15

标签: spring-batch autowired

我正在构建弹出批处理作业,修改给定数量的记录。记录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?我想移动"加载"或者这是这种情况的最佳解决方案吗?

谢谢。

1 个答案:

答案 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接口 - 那么你最终会拥有一个充满春天气息的读者。