前面關于Spring Batch的文章,講述了SpringBatch對CSV檔案的讀寫操作、對XML檔案的操作,以及對固定長格式檔案的操作。這些事例,同一個Reader讀取的都是相同格式的資料,最終寫入一個檔案。如果遇到下面這樣的資料,并想将學生資訊和商品資訊分類後寫入兩個檔案,應該如何處理呢?
student,200001,ZhangSan,18,78
goodsPNH001011000200.1zhangshana2011/12/18 01:12:36
student,200002,LiSi,19,79
goodsPNH001022000300.1zhangshanb2011/12/19 01:12:36
student,200003,WangWu,20,80
goodsPNH001033000400.1zhangshanc2011/12/20 01:12:36
* 以student開頭的資料代表學生資訊,以goods開頭代表商品資訊
這次将和大家一起探讨Spring Batch讀取複合格式的資料,然後寫入不同的檔案的處理方式。
工程結構如下圖:

applicationContext.xml和log4j.xml前文已經叙述過,在此不做贅述。
本執行個體的核心配置檔案batch.mxl内容如下:
1 <?xml version="1.0" encoding="UTF-8"?>
2 <bean:beans xmlns="http://www.springframework.org/schema/batch"
3 xmlns:bean="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx"
5 xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
6 xmlns:util="http://www.springframework.org/schema/util"
7 xsi:schemaLocation="http://www.springframework.org/schema/beans
8 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
9 http://www.springframework.org/schema/tx
10 http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
11 http://www.springframework.org/schema/aop
12 http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
13 http://www.springframework.org/schema/context
14 http://www.springframework.org/schema/context/spring-context-2.5.xsd
15 http://www.springframework.org/schema/batch
16 http://www.springframework.org/schema/batch/spring-batch-2.1.xsd
17 http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
18
19 <bean:import resource="applicationContext.xml" />
20 <!-- Job的配置資訊 -->
21 <job id="multiTypeSingleFileJob">
22 <step id="xmlFileReadAndWriterStep">
23 <tasklet>
24 <chunk reader="multiTypesItemReader" writer="multiTypesItemWriter"
25 commit-interval="1">
26 <streams>
27 <stream ref="studentWriter" />
28 <stream ref="goodsWriter" />
29 </streams>
30 </chunk>
31 </tasklet>
32 </step>
33 </job>
34
35 <!-- 不同格式資料的檔案讀取 -->
36 <bean:bean id="multiTypesItemReader"
37 class="org.springframework.batch.item.file.FlatFileItemReader" scope="step">
38 <bean:property name="resource"
39 value="file:#{jobParameters['inputFilePath']}" />
40 <bean:property name="lineMapper">
41 <bean:bean
42 class="org.springframework.batch.item.file.mapping.PatternMatchingCompositeLineMapper">
43 <bean:property name="tokenizers">
44 <bean:map>
45 <bean:entry key="student*" value-ref="studentTokenizer" />
46 <bean:entry key="goods*" value-ref="goodsTokenizer" />
47 </bean:map>
48 </bean:property>
49 <bean:property name="fieldSetMappers">
50 <bean:map>
51 <bean:entry key="student*" value-ref="studentFieldSetMapper" />
52 <bean:entry key="goods*" value-ref="goodsFieldSetMapper" />
53 </bean:map>
54 </bean:property>
55 </bean:bean>
56 </bean:property>
57 </bean:bean>
58 <bean:bean id="studentTokenizer"
59 class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
60 <bean:property name="delimiter" value="," />
61 <bean:property name="names">
62 <bean:list>
63 <bean:value>student</bean:value>
64 <bean:value>ID</bean:value>
65 <bean:value>name</bean:value>
66 <bean:value>age</bean:value>
67 <bean:value>score</bean:value>
68 </bean:list>
69 </bean:property>
70 </bean:bean>
71 <bean:bean id="studentFieldSetMapper"
72 class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
73 <bean:property name="prototypeBeanName" value="student" />
74 <bean:property name="distanceLimit" value="100" />
75 </bean:bean>
76 <!-- 學生Pojo類 -->
77 <bean:bean id="student"
78 class="com.wanggc.springbatch.sample.multitypessinglefile.pojo.Student"
79 scope="prototype" />
80
81 <bean:bean id="goodsTokenizer"
82 class="org.springframework.batch.item.file.transform.FixedLengthTokenizer">
83 <bean:property name="columns" value="6-13,14-17,18-22,23-32,33-" />
84 <bean:property name="names"
85 value="isin,quantity,price,customer,buyDay" />
86 </bean:bean>
87 <bean:bean id="goodsFieldSetMapper"
88 class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
89 <bean:property name="prototypeBeanName" value="goods" />
90 </bean:bean>
91 <!-- 商品Pojo類 -->
92 <bean:bean id="goods"
93 class="com.wanggc.springbatch.sample.multitypessinglefile.pojo.Goods"
94 scope="prototype" />
95
96 <bean:bean id="multiTypesItemWriter"
97 class="com.wanggc.springbatch.sample.multitypessinglefile.MultiItemWriter">
98 <bean:property name="delegates">
99 <bean:list>
100 <bean:ref bean="studentWriter" />
101 <bean:ref bean="goodsWriter" />
102 </bean:list>
103 </bean:property>
104 </bean:bean>
105 <!-- 學生資訊的寫 -->
106 <bean:bean id="studentWriter"
107 class="org.springframework.batch.item.file.FlatFileItemWriter" scope="step">
108 <bean:property name="resource"
109 value="file:#{jobParameters['outputFilePathStudent']}" />
110 <bean:property name="lineAggregator">
111 <bean:bean
112 class="org.springframework.batch.item.file.transform.FormatterLineAggregator">
113 <bean:property name="fieldExtractor">
114 <bean:bean
115 class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
116 <bean:property name="names" value="ID,name,age,score" />
117 </bean:bean>
118 </bean:property>
119 <bean:property name="format" value="%-9s%-9s%3d%-2.0f" />
120 </bean:bean>
121 </bean:property>
122 </bean:bean>
123 <!-- 商品資訊的寫 -->
124 <bean:bean id="goodsWriter"
125 class="org.springframework.batch.item.file.FlatFileItemWriter" scope="step">
126 <bean:property name="resource"
127 value="file:#{jobParameters['outputFilePathGoods']}" />
128 <bean:property name="lineAggregator">
129 <bean:bean
130 class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
131 <bean:property name="fieldExtractor">
132 <bean:bean
133 class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
134 <bean:property name="names"
135 value="isin,quantity,price,customer,buyDay" />
136 </bean:bean>
137 </bean:property>
138 </bean:bean>
139 </bean:property>
140 </bean:bean>
141 </bean:beans>
複制代碼
21-33行配置了Job的基本資訊。
36-57行配置了Reader的基本資訊。FlatFileItemReader的lineMapper屬性使用SpringBatch核心類PatternMatchingCompositeLineMapper的時候,會将讀取的記錄按照不同的方式映射成我們的Pojo對象。當然首先我們要配置不同的tokenizers(43-48)和fieldSetMappers(49-54),并告訴它目前的記錄按照那條原則去解析和映射。如45行所示,我們指定key為student*的時候,用studentTokenizer去解析成fieldset,用studentFieldSetMapper将studentTokenizer解析好的fieldset記錄映射成Student對象。我們指定的key,其實也就是student開頭的記錄,*是通配符。PatternMatchingCompositeLineMapper支援兩種通配符:*和?,前者代表多個字元,後者僅代表一個字元。至于student和goods資訊如何映射成pojo對象,前面的文章中已經做過詳細的介紹,這裡就不做贅述了。
96-104行配置了Writer的基本資訊。Writer也是使用代理的方式,學生資訊使用106-122行定義的studentWriter按照固定長的格式寫入學生資訊檔案中,商品資訊使用124-141行定義的goodsWriter按照CSV的格式寫入商品資訊檔案中。MultiItemWriter的代碼很簡單,就不做詳細解釋了。如下:
package com.wanggc.springbatch.sample.multitypessinglefile;
import java.util.ArrayList;
import java.util.List;
import org.springframework.batch.item.ItemWriter;
import com.wanggc.springbatch.sample.multitypessinglefile.pojo.Goods;
import com.wanggc.springbatch.sample.multitypessinglefile.pojo.Student;
/**
* 寫處理類。
*
* @author Wanggc
*
* @param <T>
*/
@SuppressWarnings("unchecked")
public class MultiItemWriter<T> implements ItemWriter<T> {
/** 寫代理 */
private List<ItemWriter<? super T>> delegates;
public void setDelegates(List<ItemWriter<? super T>> delegates) {
this.delegates = delegates;
}
@Override
public void write(List<? extends T> items) throws Exception {
// 學生資訊的Writer
ItemWriter studentWriter = (ItemWriter) delegates.get(0);
// 商品資訊的Writer
ItemWriter goodsWriter = (ItemWriter) delegates.get(1);
// 學生資訊
List<Student> studentList = new ArrayList<Student>();
// 商品資訊
List<Goods> goodsList = new ArrayList<Goods>();
// 将傳過來的資訊按照不同的類型添加到不同的List中
for (int i = 0; i < items.size(); i++) {
if ("Student".equals(items.get(i).getClass().getSimpleName())) {
studentList.add((Student) items.get(i));
} else {
goodsList.add((Goods) items.get(i));
}
}
// 如果學生List中有資料,就執行學生資訊的寫
if (studentList.size() > 0) {
studentWriter.write(studentList);
}
// 如果商品List中有資料,就執行商品資訊的寫
if (goodsList.size() > 0) {
goodsWriter.write(goodsList);
}
}
}
複制代碼
至此,複合檔案的讀寫操作已經讨論結束了。注意執行個體沒有配置Processor。下面是一些輔助檔案的資訊。
student和goods類的資訊與前面文章一樣,就不再貼出代碼了。
Job啟動的代碼如下:
package com.wanggc.springbatch.sample.multitypessinglefile;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Launch {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(
"batch.xml");
JobLauncher launcher = (JobLauncher) context.getBean("jobLauncher");
Job job = (Job) context.getBean("multiTypeSingleFileJob");
try {
// JOB實行
JobExecution result = launcher.run(
job,
new JobParametersBuilder()
.addString("inputFilePath",
"C:\\testData\\multiTypesInput.txt")
.addString("outputFilePathStudent",
"C:\\testData\\student.txt")
.addString("outputFilePathGoods",
"C:\\testData\\goods.csv")
.toJobParameters());
// 運作結果輸出
System.out.println(result.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
}
複制代碼
Input檔案内容如下圖:
處理結果的學生資訊檔案如下圖:
處理結果的商品資訊檔案如下圖:
Spring Batch對複合格式檔案的讀寫操作就讨論到這裡。至此,Spring Batch對檔案簡單操作的讨論也告一段落,下次将讨論Spring Batch讀寫DB的操作。