数据处理——实现数据校验

spring batch提供了对数据校验的功能,先读取数据,然后校验数据,最后写入数据。
在spring batch中实现检验,先定义一个类去实现Validator接口,然后Spring batch提供了一个ValidatingItemProcessor类,用于去执行Validator的接口类。实质ValidatingItemProcessor也是实现了ItemProcessor接口,执行校验就相当于执行一次数据处理。

示例:从下面文件中reader数据,然后process,最后进行writer数据。在进行process时又分了两个子process进行数据校验和处理。在进行process时先检验数据,name中手否包含”l”和”t”字符,合法的数据把name转化为大写。

ID,NAME,AGE
1,lzj,28
2,tom,20
3,terry,30
4,lerry,18
5,bob,25
6,linda,27
7,marry,39
8,long,22
9,kin,33
10,jiken,40

在进行配置job程序时,首先创建每条数据对应的bean和创建一个校验器。

public class User implements Serializable{

    private String id;
    private String name;
    private String age;
    ……省略set/get和toString方法
}

创建校验器,实现spring batch的的校验接口即可

import org.springframework.batch.item.validator.ValidationException;
import org.springframework.batch.item.validator.Validator;

/*该检验器只校验name中含有"l"和"t"的数据行*/
public class UserValidator implements Validator<User>{

    @Override
    public void validate(User user) throws ValidationException {
        if (!user.getName().contains("l") && !user.getName().contains("t")) {
            throw new ValidationException("参数不合法!");
        }
    }

}

配置该job作业如下:

import java.util.ArrayList;
import java.util.List;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.LineMapper;
import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper;
import org.springframework.batch.item.file.mapping.DefaultLineMapper;
import org.springframework.batch.item.file.mapping.FieldSetMapper;
import org.springframework.batch.item.file.transform.DelimitedLineTokenizer;
import org.springframework.batch.item.file.transform.FieldSet;
import org.springframework.batch.item.file.transform.LineTokenizer;
import org.springframework.batch.item.support.CompositeItemProcessor;
import org.springframework.batch.item.validator.ValidatingItemProcessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.validation.BindException;

@Configuration
public class BatchConfig {

    @Autowired
    private StepBuilderFactory stepBuilderFactory;

    @Autowired
    private JobBuilderFactory jobBuilderFactory;

    /*1、创建一个Job作业*/
    @Bean
    public Job fileReaderJob() throws Exception{
        return jobBuilderFactory.get("fileReaderJob")
        .start(chunkStep())
        .build();
    }

    //2、创建一个step*/
    @Bean
    public Step chunkStep() throws Exception{
        return stepBuilderFactory.get("chunkStep")
                .<User, User>chunk(2)                                           //每4次提交一次
                .reader(fileItemReader())                           //读取文件,并把文件中每行数据映射到工程中的User bean中
                .processor(itemProcess())
                .writer(list -> list.forEach(System.out::println))
                .allowStartIfComplete(true)
                .build();
    }

    //3、配置要读取文件的特性*/
    @Bean
    public ItemReader<User> fileItemReader(){
        FlatFileItemReader<User> reader= new FlatFileItemReader<>();
        reader.setResource(new ClassPathResource("/data/User.txt")); //指定读取文件的位置
        reader.setLinesToSkip(1);                                    //文件的第一行不用读取,跳过第一行
        reader.setLineMapper(userLineMapper());                      //设置lineMapper,用于把文件中的每行数据映射到工程的bean中
        return reader;
    }

    //4、定义一个LineMapper,用于把文件中的每行数据映射到工程的bean中*/
    private LineMapper<User> userLineMapper(){
        DefaultLineMapper<User> lineMapper = new DefaultLineMapper<>();
        lineMapper.setLineTokenizer(userLineTokenizer());        //设置文件的解析器
        lineMapper.setFieldSetMapper(new UserFieldStepMapper()); //设置文件的映射器
        lineMapper.afterPropertiesSet();                         //完成文件的配置
        return lineMapper;
    }

    //5、定义文件的解析器,解析文件中的ID、NAME、AGE三列*/
    private LineTokenizer userLineTokenizer(){
        DelimitedLineTokenizer tokenizer = new DelimitedLineTokenizer();
        tokenizer.setNames(new String[]{"ID", "NAME", "AGE"});
        return tokenizer;
    }

    //6、实现FieldSetMapper,用于从User.txt文件中读取数据,用读取的数据创建工程中的User对象*/
    private static class UserFieldStepMapper implements FieldSetMapper<User>{

        /*一个fieldSed对应文件中的一行数据*/
        @Override
        public User mapFieldSet(FieldSet fieldSet) throws BindException {
            // TODO Auto-generated method stub
            return new User(fieldSet.readString("ID"), 
                    fieldSet.readString("NAME"), 
                    fieldSet.readString("AGE"));
        }

    }

    /*创建process: process中共包括两个process,userValidateItemProcess和UserProcess*/
    @Bean
    public ItemProcessor<User, User> itemProcess() throws Exception{
        List<ItemProcessor<User, User>> list = new ArrayList<>();

        ValidatingItemProcessor<User> userValidateItemProcess = 
                new ValidatingItemProcessor<>(new UserValidator());
        userValidateItemProcess.setFilter(false); //设置false,说明不过滤,当校验出错时,直接抛出异常,不再继续执行下去
        list.add(userValidateItemProcess);  //首先执行userValidateItemProcess
        list.add(new UserProcess());        //然后执new UserProcess()。执行process是有顺序的,先放入list的先执行,这一点从源码中可以看出。
        CompositeItemProcessor<User, User> compositeItemProcessor =
                new CompositeItemProcessor<>();
        compositeItemProcessor.setDelegates(list);
        compositeItemProcessor.afterPropertiesSet(); //对list校验是否为空

        return compositeItemProcessor;
    }

    /*该process把姓名由小写变为大写*/
    private class UserProcess implements ItemProcessor<User, User>{

        @Override
        public User process(User item) throws Exception {
            return new User(item.getId(), item.getName().toUpperCase(), item.getAge());
        }

    }

}

运行该job,输出内容如下:

……省略日志
User [id=1, name=LZJ, age=28]
User [id=2, name=TOM, age=20]
User [id=3, name=TERRY, age=30]
User [id=4, name=LERRY, age=18]
2018-03-10 18:39:13.495 ERROR 10304 --- [           main] o.s.batch.core.step.AbstractStep         : Encountered an error executing step chunkStep in job fileReaderJob

org.springframework.batch.item.validator.ValidationException: 参数不合法!
……

从输出内容看,前4条检验成功,并把name由小写变为大写。校验到第5条时,抛出异常,job终止,不再继续执行。

如果把userValidateItemProcess.setFilter(true); 表示过滤,校验失败后过滤出去,继续向下执行。重新运行该Job,输出内容如下:
结果只写入如下两条数据:

……
User [id=6, name=LINDA, age=27]
User [id=8, name=LONG, age=22]
……

是因为上一次在第5条数据失败终止,现在重新启动从失败出重新执行,结果把后面满足条件的两条数据输出来。如果在setFilter(true)条件下再重新运行一次的话,就会输出全部满足校验的数据了:

……
User [id=1, name=LZJ, age=28]
User [id=2, name=TOM, age=20]
User [id=3, name=TERRY, age=30]
User [id=4, name=LERRY, age=18]
User [id=6, name=LINDA, age=27]
User [id=8, name=LONG, age=22]
……
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值