【问题标题】:Spring Batch: How to get errors for all lines read?Spring Batch:如何获取所有读取行的错误?
【发布时间】:2017-07-07 10:56:22
【问题描述】:

我正在使用 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。 writer 实现 BatchLoadableWriter 和 StepExecutionListener。

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

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

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

假设我导入了 10 行。前 5 个有错误,后 5 个没有。当 afterRead() 被调用时,它将检查最后处理的 Item 的标志值,它没有错误,因此 hasErrors 将为 false。不好。或者也许重写 onReadError() 会更好。但是什么会导致该方法被调用,Mapper 中的一个错误?

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

我很茫然……

【问题讨论】:

  • 您能否在当前配置 Spring 批处理作业的位置发布一些代码。它使给出答案更容易。

标签: spring-batch


【解决方案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 是否存在。如果存在,则意味着在读取/验证时发生了某种错误,我会抛出异常。

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

希望有一天这对其他人有所帮助!

【讨论】:

    【解决方案2】:

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

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

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

    【讨论】:

    • 但这需要实现我自己的阅读器吗?或者我可以在 FieldMapper 中执行此操作吗?我想我的问题是,我在哪里粘贴这段代码?对文件中的每一行执行 Reader(默认的 FlatFileItemReader)。如果读取/验证失败,我想将错误添加到错误列表中。我应该把将 List 放入 Job 或 Step 范围的代码放在哪里?当所有处理完成后,我应该将代码放在哪里,然后将错误打印到文件中?
    • 其实,我知道如何将 List 作为作业参数传递。但是我应该把从范围中获取列表的代码放在哪里 1)添加到它(对于每次读取)和 2)打印出它的内容(在所有读取/处理完成之后)。对于(1)我想我必须实现我自己的扩展 FlatFileItemReader 的 Reader 并在 doRead() 中获取 List 并添加到它。至于(2),我不知道该放在哪里。或者也许我可以在 ItemReadListener 中做 (1)?对于(2)我不确定要使用什么听众。无论如何,我不知道侦听器是否可以访问范围内的参数...
    • 好吧,来了解一下,你不能将 List 作为 JobParam 传递!真可惜。所以我将不得不使用emeraldjava的错误文件idea。但我仍然需要知道整个读取是否有任何实际错误,如果有,返回错误状态,以及文件链接.我不想打开文件并检查行以了解是否有错误。仍然不确定在哪里执行此操作... StepListener?在阅读器/映射器中抛出异常?但如果我这样做,执行将停止。我想读取/验证 all 行,如果有错误,then 抛出异常 ...
    猜你喜欢
    • 1970-01-01
    • 2021-07-10
    • 2020-08-15
    • 1970-01-01
    • 1970-01-01
    • 2023-03-13
    • 1970-01-01
    • 2014-09-05
    • 1970-01-01
    相关资源
    最近更新 更多