Java 从 Spring Batch ItemProcessor 返回多个项目

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24004959/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 09:38:47  来源:igfitidea点击:

Return multiple items from spring batch ItemProcessor

javaspringspring-batchbatch-processingitemprocessor

提问by Fabio

I'm writing a spring batch job and in one of my step I have the following code for the processor:

我正在编写一个 spring 批处理作业,在我的步骤之一中,我为处理器编写了以下代码:

@Component
public class SubscriberProcessor implements ItemProcessor<NewsletterSubscriber, Account>, InitializingBean {

    @Autowired
    private AccountService service;

    @Override public Account process(NewsletterSubscriber item) throws Exception {
        if (!Strings.isNullOrEmpty(item.getId())) {
            return service.getAccount(item.getId());
        }
        // search with email address
        List<Account> accounts = service.findByEmail(item.getEmail());
        checkState(accounts.size() <= 1, "Found more than one account with email %s", item.getEmail());
        return accounts.isEmpty() ? null : accounts.get(0);
    }

    @Override public void afterPropertiesSet() throws Exception {
        Assert.notNull(service, "account service must be set");
    }
}

The above code works but I've found out that there are some edge cases where having more than one Accountper NewsletterSubscriberis allowed. So I need to remove the state check and to pass more than one Accountto the item writer.

上面的代码有效,但我发现在某些边缘情况下,Account每个NewsletterSubscriber允许超过一个。所以我需要删除状态检查并将多个传递Account给项目编写器。

One solution I found is to change both ItemProcessorand ItemWriterto deal with List<Account>type instead of Accountbut this have two drawbacks:

我发现的一种解决方案是同时更改ItemProcessorItemWriter处理List<Account>类型而不是,Account但这有两个缺点:

  • Code and tests are uglier and harder to write and maintain because of nested lists in writer
  • Most important more than one Accountobject may be written in the same transaction because a list given to writer may contain multiple accounts and I'd like to avoid this.
  • 由于 writer 中的嵌套列表,代码和测试更难看,更难编写和维护
  • 最重要的Account是,在同一个事务中可能会写入多个对象,因为提供给 writer 的列表可能包含多个帐户,我想避免这种情况。

Is there any way, maybe using a listener, or replacing some internal component used by spring batch to avoid lists in processor?

有什么办法,也许使用侦听器,或者替换 spring 批处理使用的一些内部组件来避免处理器中的列表?

Update

更新

I've opened an issue on spring Jirafor this problem.

我已经针对这个问题在 spring Jira 上打开了一个问题。

I'm looking into isCompleteand getAdjustedOutputsmethods in FaultTolerantChunkProcessorwhich are marked as extension points in SimpleChunkProcessorto see if I can use them in some way to achieve my goal.

我正在研究isCompletegetAdjustedOutputs方法,在FaultTolerantChunkProcessor这些方法中被标记为扩展点,SimpleChunkProcessor看看我是否可以以某种方式使用它们来实现我的目标。

Any hint is welcome.

欢迎任何提示。

回答by Michael Minella

There isn't a way to return more than one item per call to an ItemProcessorin Spring Batch without getting pretty far into the weeds. If you really want to know where the relationship between an ItemProcessorand ItemWriterexits (not recommended), take a look at the implementations of the ChunkProcessorinterface. While the simple case (SimpleChunkProcessor) isn't that bad, if you use any of the fault tolerant logic (skip/retry via FaultTolerantChunkProcessor), it get's very unwieldily quick.

没有一种方法可以ItemProcessor在不深入杂草的情况下每次调用返回一个以上的项目到Spring Batch 中。如果你真的想知道anItemProcessorItemWriterexit的关系在哪里(不推荐),看看ChunkProcessor接口的实现。虽然简单的情况 ( SimpleChunkProcessor) 并没有那么糟糕,但如果您使用任何容错逻辑(通过 跳过/重试FaultTolerantChunkProcessor),它会变得非常笨拙。

A much simpler option would be to move this logic to an ItemReaderthat does this enrichment before returning the item. Wrap whatever ItemReaderyou're using in a custom ItemReaderimplementation that does the service lookup before returning the item. In this case, instead of returning a NewsletterSubscriberfrom the reader, you'd be returning an Accountbased on the previous information.

一个更简单的选择是ItemReader在返回项目之前将此逻辑移至执行此扩充的 。将ItemReader您正在使用的任何内容包装在一个自定义ItemReader实现中,该实现在返回项目之前执行服务查找。在这种情况下,不是NewsletterSubscriber从读取器返回 a,而是Account根据先前的信息返回 a 。

回答by Esben

Instead of returning an Account you return create an AccountWrapper or Collection. The Writer obviously must take this into account :)

您返回创建一个 AccountWrapper 或 Collection,而不是返回一个 Account。作家显然必须考虑到这一点:)

回答by Matt Broekhuis

Item Processor takes one thing in, and returns a list

项目处理器接收一件事,并返回一个列表

MyItemProcessor implements ItemProcessor<SingleThing,List<ExtractedThingFromSingleThing>> {
    public List<ExtractedThingFromSingleThing> process(SingleThing thing) {
    //parse and convert to list
    }
}

Wrap the downstream writer to iron things out. This way stuff downstream from this writer doesn't have to work with lists.

包装下游作家以解决问题。这样,作者下游的东西就不必使用列表了。

@StepScope
public class ItemListWriter<T> implements ItemWriter<List<T>> {
    private ItemWriter<T> wrapped;

    public ItemListWriter(ItemWriter<T> wrapped) {
        this.wrapped = wrapped;
    }

    @Override
    public void write(List<? extends List<T>> items) throws Exception {
        for (List<T> subList : items) {
            wrapped.write(subList);
        }
    }
}

回答by Salah Atwa

You can made transformer to transform your Pojo( Pojo object from file) to your Entity By making the following code :

您可以通过制作以下代码来制作转换器以将您的 Pojo(来自文件的 Pojo 对象)转换为您的实体:

public class Intializer {

public static LGInfo initializeEntity() throws Exception {
    Constructor<LGInfo> constr1 = LGInfo.class.getConstructor();
    LGInfo info = constr1.newInstance();
    return info;
}
}

And in your item Processor

并在您的项目处理器中

public class LgItemProcessor<LgBulkLine, LGInfo> implements ItemProcessor<LgBulkLine, LGInfo> {

private static final Log log = LogFactory.getLog(LgItemProcessor.class);

@SuppressWarnings("unchecked")
@Override
public LGInfo process(LgBulkLine item) throws Exception {
    log.info(item);
    return (LGInfo) Intializer.initializeEntity();
}

}