天天看點

Spring Batch 之 Sample(複合格式檔案的讀、多檔案的寫)(七)

前面關于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讀取複合格式的資料,然後寫入不同的檔案的處理方式。

      工程結構如下圖:

Spring Batch 之 Sample(複合格式檔案的讀、多檔案的寫)(七)

       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 之 Sample(複合格式檔案的讀、多檔案的寫)(七)

      處理結果的學生資訊檔案如下圖:

Spring Batch 之 Sample(複合格式檔案的讀、多檔案的寫)(七)

     處理結果的商品資訊檔案如下圖:

Spring Batch 之 Sample(複合格式檔案的讀、多檔案的寫)(七)

      Spring Batch對複合格式檔案的讀寫操作就讨論到這裡。至此,Spring Batch對檔案簡單操作的讨論也告一段落,下次将讨論Spring Batch讀寫DB的操作。

Spring Batch 之 Sample(複合格式檔案的讀、多檔案的寫)(七)