Spring Batch:如何获取读取的所有行的错误?

时间:2017-07-07 10:56:23

标签: spring-batch

我正在使用FlatFileItemReader来读取文件。我插入了DefaultLineMapper和我自己的自定义FieldSetMapper(myMapper)。

目前在myMapper中,当发生错误时我只是记录它。我想为文件中的所有行累积所有错误,然后将它们保存到文件中。

我正在考虑实现自己的Tasklet。但是根据我的阅读,如果您的步骤进行面向块的处理,建议仅执行此操作。

另一种选择是使用ItemListenerSupport或ItemReadListener并实现onReadError()方法。但是,如果我这样做,我不确定如何访问一个包含所有行的所有错误列表的全局/共享对象。

我一直在这两个选项之间来回试图让他们工作,没有太大的成功。任何建议都非常感激。

*****编辑*****

我的代码不是任何非标准我不认为。我定义错误日志Job Param:

Map<String, JobParameter> jobParametersMap ...
jobParametersMap.put("errorsFile", new JobParameter(errorsFileURI));

我的xml配置如下所示:

<job ...>
  <step ...>
  <step id="import">
    <tasklet>
      <chunk reader="importReader" writer="importWriter" .../>
    </tasklet>
  </step>
</job>

<bean id="importReader" class="MyImportReader" scope="step">
  <property name="resource" .../>
  <property name="lineMapper">
    <bean class = "...DefaultLineMapper">
      ...
      <property name="fieldSetMapper" ref="importMapper"/>
    </bean>
  </property>
  <property name="errorsFile" value="#jobParameters['errorsFile']}"/>
</bean>

<bean id="importWriter" ...scope="step">
  ...
  <property name="errorsFile" value="#jobParameters['errorsFile']}"/>
</bean>

Reader类扩展了FlatFileItemReader并实现了ItemReadListener。该编写器实现了BatchLoadableWriter和StepExecutionListener。

正如您所看到的,我将errorsFile传递给Reader和Writer。 Writer已经使用了errorsFile一段时间,而我只是将它添加到Reader中。这两个类都有一个用于errorsFile的getter / setter。

它们之间的区别在于,在Writer中,@ Overridden write()方法验证然后在文件中写入所有项。因此所有错误会立即写入errorsFile。此外,如果存在错误,则设置标志(hasErrors),并在@Overridden afterStep()方法中检查该标志的值。如果为true,则返回ExitStatus.FAILED。

而使用Reader,对每个Item调用一次doRead()方法。如果有错误,我可以将它写入errorsFile,而我可以设置一个像Writer那样的标志。但该标志将仅为该行/项设置

所以我想说我导入了10行。前5个有错误,后5个没有错误。当调用afterRead()时,它将检查最后处理的Item的标志值,该值没有错误,因此hasErrors将为false。不好。或者最好覆盖onReadError()。但是什么会导致调用该方法,Mapper中的错误?

有些东西告诉我实现自己的Reader,和/或让它实现ItemReadListener可能不是解决这个问题的方法。对我来说,似乎我需要将一些或所有这些逻辑放在Reader的“父”中......这将是......一个Tasklet?但是我已经在网上和其他地方读过,不推荐实现自己的Tasklet来执行块处理;它应该只用于简单的任务。

我不知所措......

2 个答案:

答案 0 :(得分:1)

只是跟进这个问题,以防它可以帮助其他人。

最后,我能够通过实现自定义LineMapper并在该类中实现我想要的目标。 mapLine(String line,int lineNumber)方法,将lineNumber保存到executionContext:

public class MyLineMapper implements LineMapper<MyPojo>,
  InitializingBean, StepExecutionListener {

  private ExecutionContext _executionContext;

  public MyPojo mapLine(String line, int lineNumber)
    throws Exception {

  _executionContext.put("lineNumber", lineNumber);

  MyPojo myPojo = fieldSetMapper.mapFieldSet(tokenizer.tokenize(line));
  return myPojo;
}

由于我需要访问ExecutionContext,因此我使该类也实现了StepExecutionListener。

然后在我的自定义FieldMapper中,我还实现了StepExecutionListener,因此我可以从ExecutionContext中获取lineNumber,并使用它来记录行号错误:

public class MyFieldMapper implements LineMapper<MyPojo>,
  InitializingBean, StepExecutionListener {

  private ExecutionContext _executionContext;

  @Override
  public MyPojo mapFieldSet(final FieldSet fieldSet)
    throws BindException {

    String currentLineNumber =
      (_executionContext.get("lineNumber") != null) ? String
      .valueOf(_executionContext.get("lineNumber")) : "-";

    if (some kind of error) {
      logError(currentLineNumber, errorMsg);

然后我在我的Writer的beforeWrite()方法中检查是否存在errorFile。如果它存在,那意味着在读取/验证时会出现某种错误,并且我会抛出异常。

这样我可以记录所有读取/验证错误,我的csv文件的所有行,退出并停止处理发生第一次错误。

希望有一天能帮助别人!

答案 1 :(得分:0)

我认为你应该考虑使用步骤和工作范围。从您的阅读器中,您可以将错误详细信息保存到这些范围,然后在稍后阶段引用这些信息。我在这里记录太多信息时要小心。

http://docs.spring.io/spring-batch/reference/html/configureStep.html#step-scope

您在作业开始时,生成并命名错误文件并将其保存到作业/步骤范围。如果您的Reader有错误,它可以将详细信息写入文件。在该过程结束时,您仍然可以使用记录的详细信息引用错误文件名。