FlatFileItemWriter应写入与输入文件同名的输出文件 [英] FlatFileItemWriter should write output file named same as input file

查看:14
本文介绍了FlatFileItemWriter应写入与输入文件同名的输出文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个Spring批处理作业,它从目录中读取匹配命名模式的文件,执行一些处理,并在输入文件中写回每行的处理状态。编写器必须生成与输入文件同名的输出文件。我将模式"files-*.txt"传递给MultiResourceItemReader,并期望FlatFileItemWriter使用输入文件的名称。如何在上下文XML文件中指定此约束?

Reader Bean

<bean id="multiResourceReader" class="org.springframework.batch.item.file.MultiResourceItemReader" scope="step">
  <property name="resources" value="file:#{jobParameters['cwd']}/#{jobParameters['inputFolder']}/file-*.txt" />
  <property name="delegate" ref="itemReader" />
</bean>
<bean id="itemReader" class="org.springframework.batch.item.file.FlatFileItemReader" scope="step">

        <property name="lineMapper">
          <bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">

            <property name="lineTokenizer">
              <bean class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
                <property name="delimiter" value="," />
                <property name="names" value="paramA,paramB" />
              </bean>
            </property>
            <property name="fieldSetMapper" >
              <bean class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
                <property name="targetType" value="a.b.c.d.MyObject" />
              </bean>
            </property>
          </bean>
        </property>
    </bean>

我尝试过这样做:在我的FlatFileItemReader实现中,将输入文件路径作为字符串属性添加到ExecutionContext的映射中。在我的FlatFileItemWriter实现中-覆盖setResource,并从ExecutionContext实际创建一个Resource对象。这样可以吗?

我认为此解决方案可能会产生错误输出,因为MultiResourceItemReader从第一个文件读取了一串项,如果该集与您的完成策略不匹配,它将从下一个资源读取;在这种情况下,您将拥有来自两个不同来源的项,itemReader推荐答案不会同步以写入正确的输出资源。itemReader 您可以尝试解决leta.b.c.d.MyObject实现org.springframework.batch.item.ResourceAware,并编写自定义多资源项编写器,并在资源更改时管理资源打开/关闭(此解决方案不是微不足道的,因为当然需要状态保存以实现可重启)。

class MyItemWriter implements ItemWriter<MyObject>,ItemStream {
  private FlatFileItemWriter delegate;
  private String resourceName;

  public open(ExecutionContext e) {
    resourceName = e.getString("resourceName");
    delegate.open(e);
  }
  public close(ExecutionContext e) {
    e.putString("resourceName",resourceName);
    delegate.close(e);
  }
  public update(ExecutionContext e) {
    e.putString("resourceName",resourceName);
    delegate.update(e);
  }
  public void write(List<? extends MyObject> items) {
    for(final MyObject o : items) {
      String itemResource = o.getResource().toString();
      if(!itemResource.equals(resourceName)) {
        closeDelegate();
        openDelegate();
      }
      writeToDelegate(o);
    }
  }
}

当然,这没有经过测试,这只是一个想法。
另一种方法则完全不同,它使用decider以这种方式模拟循环:

  1. 创建用于存储file:#{jobParameters['cwd']}/#{jobParameters['inputFolder']}/file-*.txt中的资源的Bean(例如,名为ResourceHolder);该Bean不会存储到执行上下文中,而是在每次作业启动时使用JobExecutionListener填充,因为执行上下文应该保持较小。
  2. 使用决策器模拟循环!将当前资源的名称从决策器存储到执行上下文中;您还需要存储要移动到下一个资源或停止作业的当前资源的索引
  3. 读取/处理/写入步骤使用单个文件,即从决策器存储到执行上下文中的文件

<batch:job id="job">
  <batch:decision decider="decider" id="decider">
    <batch:next on="CONTINUABLE" to="readWriteStep" />
    <batch:end on="FINISHED" />
  </batch:decision>
  <batch:step id="readWriteStep" reader="itemReader" write="itemWriter" next="decider" />
  <batch:listeners>
    <batch:listener class="ResourceHolderFiller">
      <property name="resources" value="file:#{jobParameters['cwd']}/#{jobParameters['inputFolder']}/file-*.txt" />
      <property name="resourceHolderBean" ref="resourceHolderBean" />
    </batch:listener>
  </batch:listeners>
</batch:job>
<bean id="resourceHolder" class="ResourceHolderBean" />
<bean id="decider" class="MyDecider">
  <property name="resourceHolderBean" ref="resourceHolderBean" />
</bean>

class ResourceHolderBean
{
  Resource[] resource;

  public void setResource(Resource[] resource) {...}
  public Resource[] getResource() {...}
}
class MyDecider implements JobExecutionDecider {
  ResourceHolderBean resourceBean;

  public void setResourceBean(ResourceHolderBean resBean){...}

  FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution) {
    Integer resourceIndex = jobExecution.getExecutionContext().getInt("resourceIndex");
    if(resourceIndex == null)
    {
      resourceIndex = 0;
    }
    if(resourceIndex == resourceBean.getResources().size()) {
      return new FlowExecutionStatus(RepeatStatus.FINISHED.name());
    }
    else {
      jobExecution.getExecutionContext().putInt("resourceIndex",resourceIndex);
      // This is the key your reader/writer must use as inpu/output filename
      jobExecution.getExecutionContext().putString("resourceName",resourceBean.getResources()[resourceIndex].toString());
    }
  }
}

ResourceHolderFiller是将资源注入ResourceHolderBean Bean的作业侦听器。

希望这能有所帮助。

这篇关于FlatFileItemWriter应写入与输入文件同名的输出文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆