Spring Batch 之 Sample(CSV文件操作)(四)

原文地址爲:Spring Batch 之 Sample(CSV文件操作)(四)

      本文將通過一個完整的實例,與大家一起討論運用Spring BatchCSV文件的讀寫操作。此實例的流程是:讀取一個含有四個字段的CSV文件(ID,Name,Age,Score),對讀取的字段做簡單的處理,然後輸出到另外一個CSV文件中。

      工程結構如下圖:

                  

      JobLaunch類用來啓動Job, CsvItemProcessor類用來對Reader取得的數據進行處理, Student類是一個POJO類,用來存放映射的數據。 inputFile.csv是數據讀取文件, outputFile.csv是數據輸出文件。

      application.xml文件配置如前篇文章,不再贅述。

      batch.xml文件中Job配置如下:

<job id="csvJob">
<step id="csvStep">
<tasklet transaction-manager="transactionManager">
<chunk reader="csvItemReader" writer="csvItemWriter" processor="csvItemProcessor" commit-interval="1">
</chunk>
</tasklet>
</step>
</job>

      這個文件裏配置了這次運行的JOB:csvJob。本Job包含一個Step,完成一個完整的CSV文件讀寫功能。分別由 csvItemReader完成CSV文件的讀操作,由 csvItemProcessor完成對取得數據的處理,由 csvItemWriter完成對CSV文件的寫操作。

      batch.xml文件中csvItemReader配置如下:

<!-- 讀取csv文件 -->
<bean:bean id="csvItemReader"
class
="org.springframework.batch.item.file.FlatFileItemReader" scope="step">
<bean:property name="resource" value="classpath:inputFile.csv"/>
<bean:property name="lineMapper">
<bean:bean
class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
<bean:property name="lineTokenizer" ref="lineTokenizer"/>
<bean:property name="fieldSetMapper">
<bean:bean
class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
<bean:property name="prototypeBeanName" value="student"></bean:property>
</bean:bean>
</bean:property>
</bean:bean>
</bean:property>
</bean:bean>

<bean:bean id="student" class="com.wanggc.springbatch.sample.csv.Student"></bean:bean>

<!-- lineTokenizer -->
<bean:bean id="lineTokenizer" class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
<bean:property name="delimiter" value=","/>
<bean:property name="names">
<bean:list>
<bean:value>ID</bean:value>
<bean:value>name</bean:value>
<bean:value>age</bean:value>
<bean:value>score</bean:value>
</bean:list>
</bean:property>
</bean:bean>

      csvItemReader實現的是Spring Batch提供FlatFileItemReader類,此類主要用於Flat文件的讀操作。它包含兩個必要的屬性 resource和 lineMapper。前者指定要讀取的文件的位置,後者是將文件的每一行映射成一個Pojo對象。其中 lineMapper也有兩個重要屬性 lineTokenizer和 fieldSetMapper, lineTokenizer將文件的一行分解成一個 FieldSet,然後由 fieldSetMapper映射成Pojo對象。

      這種方式與DB的讀操作非常類似。lineMapper類似於ResultSet,文件中的一行類似於Table中的一條記錄,被封裝成的FieldSet,類似於RowMapper。至於怎麼將一條記錄封裝,這個工作由lineTokenizer的繼承類DelimitedLineTokenizer完成。DelimitedLineTokenizer的delimiter屬性決定文件的一行數據按照什麼分解,默認的是“,”, names屬性標示分解的每個字段的名字,傳給fieldSetMapper(本實例用的是BeanWrapperFieldSetMapper)的時候,就可以按照這個名字取得相應的值。fieldSetMapper的屬性prototypeBeanName,是映射Pojo類的名字。設置了此屬性後,框架就會將lineTokenizer分解成的一個FieldSet映射成Pojo對象,映射是按照名字來完成的(lineTokenizer分解時標註的名字與Pojo對象中字段的名字對應)。

      總之,FlatFileItemReader讀取一條記錄由以下四步完成:1,從resource指定的文件中讀取一條記錄;2,lineTokenizer將這條記錄按照delimiter分解成Fileset,每個字段的名字由names屬性取得;3,將分解成的Fileset傳遞給fieldSetMapper,由其按照名字映射成Pojo對象;4,最終由FlatFileItemReader將映射成的Pojo對象返回,框架將返回的對象傳遞給Processor。

      csvItemProcessor實現的是ItemProcessor類。此類接受Reader映射成的Pojo對象,可以對此對象做相應的業務邏輯處理,然後返回,框架就會將返回的結果傳遞給Writer進行寫操作。具體實現代碼如下:

package com.wanggc.springbatch.sample.csv;

import org.springframework.batch.item.ItemProcessor;
import org.springframework.stereotype.Component;

/**
* ItemProcessor類。
*/
@Component("csvItemProcessor")
public class CsvItemProcessor implements ItemProcessor<Student, Student> {

/**
* 對取到的數據進行簡單的處理。
*
*
@param student
* 處理前的數據。
*
@return 處理後的數據。
*
@exception Exception
* 處理是發生的任何異常。
*/
@Override
public Student process(Student student) throws Exception {
/* 合併ID和名字 */
student.setName(student.getID() + "--" + student.getName());
/* 年齡加2 */
student.setAge(student.getAge() + 2);
/* 分數加10 */
student.setScore(student.getScore() + 10);
/* 將處理後的結果傳遞給writer */
return student;
}
}

       batch.xml文件中csvItemReader配置如下:

<!-- 寫CSV文件 -->
<bean:bean id="csvItemWriter"
class
="org.springframework.batch.item.file.FlatFileItemWriter" scope="step">
<bean:property name="resource" value="file:src/outputFile.csv"/>
<bean:property name="lineAggregator">
<bean:bean
class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
<bean:property name="delimiter" value=","></bean:property>
<bean:property name="fieldExtractor">
<bean:bean
class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
<bean:property name="names" value="name,age,score"></bean:property>
</bean:bean>
</bean:property>
</bean:bean>
</bean:property>
</bean:bean>

      csvItemWriter實現的是FlatFileItemWriter類。此類與FlatFileItemReader類相似,也有兩個重要的屬性:resource和lineAggregator。前者是要輸出的文件的路徑,後者和lineTokenizer類似。lineAggregator(本實例用DelimitedLineAggregator類)也有兩個重要的屬性:delimiter和fieldExtractor。Delimiter標示輸出的字段以什麼分割,後者將Pojo對象組裝成由Pojo對象的字段組成的一個字符串。同樣FlatFileItemWriter寫一條記錄也有以下四步完成:1,Processor傳遞過來一個對象給lineAggregator;2,lineAggregator將其這個對象轉化成一個數組;3,再由lineAggregator的屬性fieldExtractor將數組轉化成按照delimiter分割一個字符串;4,將這個字符串輸出。

      這樣,一條數據的讀、處理、寫操作就基本完成了。當然,讀和寫也可以自己寫類來處理,只是要注意繼承FlatFileItemReader和FlatFileItemWriter就可以了。

      實例中用到的Student類代碼如下:

package com.wanggc.springbatch.sample.csv;

/** Pojo類_Student */
public class Student {
/** ID */
private String ID = "";
/** 名字 */
private String name = "";
/** 年齡 */
private int age = 0;
/** 分數 */
private float score = 0;
/*getter 和setter已刪除*/
}

      實例中用到的輸入數據如下:

               

      實例輸出結果如下:    

               

      本文的配置要注意以下兩點:

      1,  注意Writer的resource要寫成“file:******”形式,不能用“classpath:******”形式。

      2,  如果將Job配置中commit-interval屬性配置爲大於1時,每次commit的都是最後一條記錄,前面讀取的被覆蓋了。具體原因不明,如果將Reader的fieldSetMapper屬性自己重寫,就可以解決這個問題。(注:student bean添加scope屬性可以解決此問題:scope:"prototype".2011/12/16)

      下次,將和大家一起討論關於XML文件的讀寫問題。


轉載請註明本文地址:Spring Batch 之 Sample(CSV文件操作)(四)
發佈了0 篇原創文章 · 獲贊 0 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章