【问题标题】:Flatten processing result in spring batch展平弹簧批次中的处理结果
【发布时间】:2017-03-15 16:47:30
【问题描述】:

有谁知道我如何在 spring-batch (3.0.7) 中平展返回实体列表的处理器的结果? 示例:

我有一个返回列表的处理器

public class MyProcessor implements ItemProcessor < Long , List <Entity>> {
    public List<Entity> process ( Long id )
}

现在所有以下处理器/编写器都需要在列表 上工作。有没有办法将结果平展为简单的实体,以便给定步骤中的进一步处理器可以在单个实体上工作?

唯一的方法是通过写入器以某种方式持久化列表,然后创建一个单独的步骤来读取持久化的数据。

提前致谢!

【问题讨论】:

  • 有趣的问题。我有完全相同的问题。我想知道为什么这个问题被否决了两次。

标签: java spring-batch


【解决方案1】:

如您所知,spring-batch 中的处理器可以与复合处理器链接。在链中,您可以在处理器之间更改处理类型,但当然两个“邻居”处理器的输入和输出类型必须匹配。

但是,输入输出输出类型始终被视为一项。因此,如果处理器的输出类型是列表,则该列表被视为一项。因此,下面的处理器需要有一个 InputType“List”,如果一个 writer 跟随,那么 Writer 需要一个 List-of-List 作为其 write-method 的类型。 此外,处理器不能乘以它的元素。每个输入元素只能有一个输出项。

基本上,拥有这样的链条并没有错

Reader<Integer>
ProcessorA<Integer,List<Integer>>
ProcessorB<List<Integer>,List<Integer>>
Writer<List<Integer>> (which leads to a write-method write(List<List<Integer>> items)

根据具体情况,可能会有更好的解决方案。 您可以通过使用 wrapper-processors 和 wrapper-writer 来减轻影响(例如可重用性),例如以下代码示例:

public class ListWrapperProcessor<I,O> implements ItemProcessor<List<I>, List<O>> {

    ItemProcessor<I,O> delegate;

    public void setDelegate(ItemProcessor<I,O> delegate) {
        this.delegate = delegate;
    }


    public List<O> process(List<I> itemList) {
        List<O> outputList = new ArrayList<>();

        for (I item : itemList){    
           O outputItem = delegate.process(item);
           if (outputItem!=null) {
               outputList.add(outputItem);
           }
        }

        if (outputList.isEmpty()) {
            return null;
        }

        return outputList;
    }

}


public class ListOfListItemWriter<T> implements InitializingBean, ItemStreamWriter<List<T>> {

    private ItemStreamWriter<T> itemWriter;

    @Override
    public void write(List<? extends List<T>> listOfLists) throws Exception {
        if (listOfLists.isEmpty()) {
            return;
        }

        List<T> all = listOfLists.stream().flatMap(Collection::stream).collect(Collectors.toList());

        itemWriter.write(all);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        Assert.notNull(itemWriter, "The 'itemWriter' may not be null");
    }

    public void setItemWriter(ItemStreamWriter<T> itemWriter) {
        this.itemWriter = itemWriter;
    }

    @Override
    public void close() {
        this.itemWriter.close();
    }

    @Override
    public void open(ExecutionContext executionContext) {
        this.itemWriter.open(executionContext);
    }

    @Override
    public void update(ExecutionContext executionContext) {
        this.itemWriter.update(executionContext);
    }
}

使用此类包装器,您仍然可以实现“普通”处理器和编写器,然后使用此类包装器将“列表”处理移出它们。

【讨论】:

  • 感谢您的理解解释。我以类似的方式实现了我的案例(使用包装器对象更容易跟踪重试/错误处理)这不是我一开始所期望的(因为需要专门针对这种情况实现计数/跳过功能)但我想那里由于弹簧批次的限制,没有办法做得更好。
【解决方案2】:

除非您能提供令人信服的理由,否则没有理由将列表列表发送到您的 ItemWriter。这不是ItemProcessor 的使用方式。相反,您应该创建/配置和ItemReader 以返回一个具有相关对象的对象。

例如,如果您正在从数据库中读取数据,则可以使用 HibernateCursorItemReader 和如下所示的查询:

"from ParentEntity parent left join fetch parent.childrenEntities"

您的数据模型应该有一个带有 Long id 的父表,您当前正在将其传递给您的 ItemProcessor,因此请利用它来发挥您的优势。然后,读者将传回ParentEntity 对象,每个对象都带有一组ChildEntity 对象。

【讨论】:

  • 我知道项目处理器/编写器不应该与列表一起使用,但我得到的情况如下:我在数据库中获得了一个带有 id 的表,我一个一个地查询,然后对于我调用的每个 id可以返回多个项目的远程休息服务(在处理器中) - 这迫使我返回列表。
  • 不幸的是,这打破了Reader-&gt;Processor-&gt;Writer 的模式。相反,我会配置一个自定义 ItemReader 来执行这些休息调用,并一次将 1 个项目返回给您的处理器。 Hansjoerg 的回答也可以,但我不希望将List&lt;List&lt;T&gt;&gt; 传递给我的作者。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-03
  • 1970-01-01
  • 2019-12-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多