2016-12-05 55 views
0

我正在构建修改给定数量记录的弹簧批处理作业。记录ID列表是作业的输入参数。例如,一项工作可能是:修改记录ID {1,2,3,4}并在相关表上设置参数X和Y.编写弹簧式批处理的正确方法ItemReader

因为我无法将潜在的很长的输入列表(tipical个案,50K记录)传递给我的ItemReader,所以我只传递一个MyJobID,然后itemReader用来加载目标ID列表。

问题是,结果代码出现“错误”(虽然它的工作原理),而不是春季批次的精神。这里的读者:

@Scope(value = "step", proxyMode = ScopedProxyMode.INTERFACES) 
@Component 
public class MyItemReader implements ItemReader<Integer> { 

    @Autowired 
    private JobService jobService; 
    private List<Integer> itemsList; 
    private Long jobId; 

    @Autowired 
    public MyItemReader(@Value("#{jobParameters['jobId']}") final Long jobId) { 
     this.jobId = jobId; 
     this.itemsList = null; 
    } 

    @Override 
    public Integer read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException { 

     // First pass: Load the list. 
     if (itemsList == null) { 
      itemsList = new ArrayList<Integer>(); 

      MyJob myJob = (MyJob) jobService.loadById(jobId); 

      for (Integer i : myJob.getTargedIdList()) { 
       itemsList.add(i); 
      } 
     } 

     // Serve one at a time: 
     if (itemsList.isEmpty()) { 
      return null; 
     } else { 
      return itemsList.remove(0); 
     } 
    } 
} 

我试图read()方法的第一部分移动到构造,但@Autowired引用是空在这一点上。之后(在读取方法上),它被初始化。

有没有更好的方法来编写ItemReader?我想移动“负载”或者这是该场景的最佳解决方案?

谢谢。

+0

显示jobService.loadById()的代码,我会回来反馈。 –

回答

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方法返回null。

这可确保您拥有恒定的内存占用量,而不管有多少记录需要处理。 (如果你看看FlatFileItemReader,你会发现它使用BufferedReader从磁盘读取数据,虽然它与SpringBatch无关,但它的原理相同:它从磁盘读取大量数据,返回,如果需要更多数据,它将读取下一个数据块)。

下一个问题是可重启性。如果在完成90%的工作后工作崩溃会发生什么?如何重新开始工作,只处理缺失的10%? 这实际上是springbatch提供的一个功能,您只需要实现ItemStream接口并实现open(),update(),close()方法。

如果你考虑这两点 - 一次加载数据块而不是一次性实现ItemStream接口 - 最终你会得到一个春天的读者。

+0

优秀的答案。谢谢! – tggm