Java Spring批处理以聚合值并写入单个值

Java Spring批处理以聚合值并写入单个值,java,spring,spring-batch,Java,Spring,Spring Batch,我正在使用SpringBatch,我需要实现以下目标 读取包含日期和金额等详细信息的csv文件 合计同一日期所有金额的总和 保留一个带有日期和总和的条目 我过去使用过批处理,我想到了以下方法。创建一个包含2个步骤的批处理 第1步: Reader:使用FlatFileItemReader遍历整个文件 处理器:使用键作为日期和值作为金额填充地图。如果存在条目,则获取该值并将其添加到新值中 作者:没有操作作者,因为我不想写 第二步: 读卡器:循环浏览地图的值 作者:坚持这些价值观 我能够完成步骤1,在

我正在使用SpringBatch,我需要实现以下目标

  • 读取包含日期和金额等详细信息的csv文件
  • 合计同一日期所有金额的总和
  • 保留一个带有日期和总和的条目
  • 我过去使用过批处理,我想到了以下方法。创建一个包含2个步骤的批处理

    第1步:

  • Reader:使用FlatFileItemReader遍历整个文件
  • 处理器:使用键作为日期和值作为金额填充地图。如果存在条目,则获取该值并将其添加到新值中
  • 作者:没有操作作者,因为我不想写
  • 第二步:

  • 读卡器:循环浏览地图的值
  • 作者:坚持这些价值观
  • 我能够完成步骤1,在那里我填充了
    地图
    。已使用
    @JobScope

    我被困在如何创建步骤2的读取器上,而步骤2只需要读取值列表。我尝试了
    ListItemReader
    ,但无法从
    ListItemReader
    访问
    Map

    请提供解决方案,或者您是否有更好的方法来解决此问题

    谢谢

    选项1: 如果您的CV已经按日期排序,则可以实现一个组读取器,该读取器读取行,直到键值更改为止。之后,整个组可以作为一个项目传递给处理器

    这样的组读取器可能如下所示:

      private SingleItemPeekableItemReader<I> reader;
      private ItemReader<I> peekReaderDelegate;
    
      @Override
      public void afterPropertiesSet() throws Exception {
        Assert.notNull(peekReaderDelegate, "The 'itemReader' may not be null");
        this.reader= new SingleItemPeekableItemReader<I>();
        this.reader.setDelegate(peekReaderDelegate);
      }
    
      @Override
      // GroupDTO is just a simple container. It is also possible to use
      // List<I> instead of GroupDTO<I>
      public GroupDTO<I> read() throws Exception {
        State state = State.NEW; // a simple enum with the states NEW, READING, and COMPLETE
        GroupDTO<I> group = null;
        I item = null;
    
        while (state != State.COMPLETE) {
          item = reader.read();
    
          switch (state) {
            case NEW: {
              if (item == null) {
                // end reached
                state = State.COMPLETE;
                break;
              }
    
              group = new GroupDTO<I>();
              group.addItem(item);
              state = State.READING;
              I nextItem = reader.peek();
              // isGroupBreak returns true, if 'item' and 'nextItem' do NOT belong to the same group
              if (nextItem == null || getGroupBreakStrategy.isGroupBreak(item, nextItem)) {
                state = State.COMPLETE;
              }
              break;
            }
            case READING: {
              group.addItem(item);
    
              // peek and check if there the peeked entry has a new date
              I nextItem = peekEntry();
              // isGroupBreak returns true, if 'item' and 'nextItem' do NOT belong to the same group
              if (nextItem == null || getGroupBreakStrategy.isGroupBreak(item, nextItem)) {
                state = State.COMPLETE;
              }
              break;
            }
            default: {
              throw new org.springframework.expression.ParseException(groupCounter, "ParsingError: Reader is in an invalid state");
            }
          }
        }
    
        return group;
      }
    
    私有SingleItemPeakableItemReader;
    私有项目阅读器peekReaderDelegate;
    @凌驾
    public void afterPropertieSet()引发异常{
    Assert.notNull(peekReaderDelegate,“itemReader”不能为null);
    this.reader=新的SingleItemPeakableItemReader();
    this.reader.setDelegate(peekReaderDelegate);
    }
    @凌驾
    //GroupDTO只是一个简单的容器。也可以使用
    //列表而不是GroupDTO
    public GroupDTO read()引发异常{
    State State=State.NEW;//状态为NEW、READING和COMPLETE的简单枚举
    GroupDTO group=null;
    I项=空;
    while(state!=state.COMPLETE){
    item=reader.read();
    开关(状态){
    新案例:{
    如果(项==null){
    //到达终点
    state=state.COMPLETE;
    打破
    }
    group=新的GroupDTO();
    组.附加项(项目);
    state=state.READING;
    I nextItem=reader.peek();
    //如果“item”和“nextItem”不属于同一组,isGroupBreak将返回true
    if(nextItem==null | | getGroupBreakStrategy.isGroupBreak(项,nextItem)){
    state=state.COMPLETE;
    }
    打破
    }
    案例解读:{
    组.附加项(项目);
    //查看并检查查看的条目是否有新日期
    I nextItem=peek条目();
    //如果“item”和“nextItem”不属于同一组,isGroupBreak将返回true
    if(nextItem==null | | getGroupBreakStrategy.isGroupBreak(项,nextItem)){
    state=state.COMPLETE;
    }
    打破
    }
    默认值:{
    抛出新的org.springframework.expression.ParseException(groupCounter,“ParsingError:Reader处于无效状态”);
    }
    }
    }
    返回组;
    }
    
    您需要一个SingleItemPeakableItemReader,以便预读下一个元素。这一个包裹了你的普通读者

    备选案文2: 第一步正如您所建议的,但只需为第二步编写一个tasklet即可。不需要使用reader-process-writer方法,而是可以使用一个简单的tasklet将地图的内容写入文件

    备选案文3: 如果你真的想在第二步中使用读写器-处理器-编写器的方法,那就编写你自己的读写器,在你的地图上迭代

    类似(我没有测试该代码):

    公共类MapReader实现ItemReader{
    私有映射容器;
    
    private Iterator请发布代码的相关部分…您是如何声明映射的,步骤2的读取器是什么样子的?您可以尝试以下方法(2个选项StepContext或使用Java Bean)
    public class MapReader implements ItemReader {
    
         private MapContainer container;
         private Iterator<Map.Entry<Date, Integer> mapIterator;
    
         @PostConstruct
         public void afterPropertiesSet() {
            Assert.notNull(container);
            iterator = container.getMap().entry().iterator;
         }
    
         public void setMapContainer(MapContainer container) {
             this.container = container;
         }
    
         public Map.Entry<Date, Integer> read() {
            if (iterator.hasNext()) {
               return iterator.next();
            }
            return null;
          }
    }
    
    @Component
    public class MapContainer {
        private Map<Date, Integer> data = new Hashmap<>();
    
        public Map<Date, Integer> getMap() {
            return data;
        }
    
        // add modifier method as needed for step 1
    
    }