2016-12-05 91 views
0

我正在構建修改給定數量記錄的彈簧批處理作業。記錄ID列表是作業的輸入參數。例如,一項工作可能是:修改記錄ID {1,2,3,4}並在相關表上設置參數X和Y.編寫彈簧式批處理的正確方法ItemReader

因爲我無法將潛在的很長的輸入列表(tipical個案,50K記錄)傳遞給我的ItemReader,所以我只傳遞一個MyJobID,然後itemReader用來加載目標ID列表。

問題是,結果代碼出現「錯誤」(雖然它的工作原理),而不是春季批次的精神。這裏的讀者:

@Scope(value = "step", proxyMode = ScopedProxyMode.INTERFACES) 
@Component 
public class MyItemReader implements ItemReader<Integer> { 

    @Autowired 
    private JobService jobService; 
    private List<Integer> itemsList; 
    private Long jobId; 

    @Autowired 
    public MyItemReader(@Value("#{jobParameters['jobId']}") final Long jobId) { 
     this.jobId = jobId; 
     this.itemsList = null; 
    } 

    @Override 
    public Integer read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException { 

     // First pass: Load the list. 
     if (itemsList == null) { 
      itemsList = new ArrayList<Integer>(); 

      MyJob myJob = (MyJob) jobService.loadById(jobId); 

      for (Integer i : myJob.getTargedIdList()) { 
       itemsList.add(i); 
      } 
     } 

     // Serve one at a time: 
     if (itemsList.isEmpty()) { 
      return null; 
     } else { 
      return itemsList.remove(0); 
     } 
    } 
} 

我試圖read()方法的第一部分移動到構造,但@Autowired引用是空在這一點上。之後(在讀取方法上),它被初始化。

有沒有更好的方法來編寫ItemReader?我想移動「負載」或者這是該場景的最佳解決方案?

謝謝。

+0

顯示jobService.loadById()的代碼,我會回來反饋。 –

回答

0

通常,您的方法並非「錯誤」,但可能並不理想。

首先,您可以將初始化移動到使用@PostConstruct註釋的initMethod。自動連接所有字段已被注射之後,該方法被稱爲:

@PostConstruct 
public void afterPropertiesSet() throws Exception { 
    itemsList = new ArrayList<Integer>(); 
    MyJob myJob = (MyJob) jobService.loadById(jobId); 

    for (Integer i : myJob.getTargedIdList()) { 
     itemsList.add(i); 
    } 
} 

不過還是有問題,你一次加載所有數據。如果你有十億條記錄要處理,這可能會炸燬記憶。

所以你應該做的是隻加載你的數據塊到內存中,然後在你的讀取方法中逐一返回項目。如果塊的所有條目都已返回,則加載下一個塊並再次返回它的項目。如果沒有其他塊被加載,則從read方法返回null。

這可確保您擁有恆定的內存佔用量,而不管有多少記錄需要處理。 (如果你看看FlatFileItemReader,你會發現它使用BufferedReader從磁盤讀取數據,雖然它與SpringBatch無關,但它的原理相同:它從磁盤讀取大量數據,返回,如果需要更多數據,它將讀取下一個數據塊)。

下一個問題是可重啓性。如果在完成90%的工作後工作崩潰會發生什麼?如何重新開始工作,只處理缺失的10%? 這實際上是springbatch提供的一個功能,您只需要實現ItemStream接口並實現open(),update(),close()方法。

如果你考慮這兩點 - 一次加載數據塊而不是一次性實現ItemStream接口 - 最終你會得到一個春天的讀者。

+0

優秀的答案。謝謝! – tggm