我正在使用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来执行块处理;它应该只用于简单的任务。
我不知所措......
答案 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有错误,它可以将详细信息写入文件。在该过程结束时,您仍然可以使用记录的详细信息引用错误文件名。