2014-07-08 96 views
0

我有一個春天批處理作業讀取文件匹配從目錄命名模式,進行一些處理,並寫回每行處理的狀態中輸入文件輸出文件。作者必須生成與輸入文件具有相同名稱的輸出文件。對於MultiResourceItemReader,我傳遞模式:「files - *。txt」,並期望FlatFileItemWriter使用輸入文件的名稱。如何在上下文xml文件中指定此約束?FlatFileItemWriter應該編寫一個名爲相同的輸入文件

讀者豆

<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的。在我的FlatFileItemWriter實現中 - 覆蓋setResource並實際從ExecutionContext中創建一個Resource對象。這樣好嗎?

回答

1

我認爲這個解決方案可能會產生錯誤的輸出,因爲MultiResourceItemReader從第一個文件讀取一堆項目,如果這個設置與您的完成策略不匹配,它會從下一個資源讀取;在這種情況下,您將擁有來自兩個不同來源的項目,並且itemReader bean不會同步寫入正確的輸出資源。
您可以嘗試解決讓a.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(稱爲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豆。

希望能有所幫助。

+1

謝謝你的詳細答覆@LucaBassoRicci。我用你的第一個建議。我們的Maven項目結構限制了我的Spring-batch-2.1.9版基礎結構,因此我在a.b.c.d.MyObject上編寫了自己的'ResourceAware'類接口。 – chetan

相關問題