在实际项目开发的中,经常会遇到这样的场景:由于数据量很大,会将数据批量导入到多个文件中,从而避免因单个文件数据量过大而带来不好的影响;这种场景下主要关注两个因素:第一,控制每一个文件的写入条数,一旦要写的数据超过这个条数,就会再创建一个新的格式完全一样的文件;第二,多文件的文件名称要保持一致,且呈递增格式。
MultiResourceItemWriter
SpringBatch提供了MultiResourceItemWriter<T>组件,可以很便捷地将数据写入到多文件中。
如图,该组件主要由三元素构成,其具体作用如下:
- Resource:指定文件路径和名称;
- ItemCountLimitPerResource:每一个文件能够写入的最大条数;
- ResourceSuffixCreator:文件名称后缀生成器,默认是SimpleResourceSuffixCreator,即在文件名追加(. + index);
- ResourceAwareItemWriterItemStream:具体的文件写入实现类,如写入XML,TXT等。
这里需要指出的是,程序中指定的Resource是原始设置的文件路径+名称,如D:/demo.txt;而使用MultiResourceItemWriter写入到多文件后,就会对该文件名成就行一定的修改,若采用默认的名称后缀生成器,最终的文件名称就是D:/demo.txt.1。
实际开发流程
这里核心的ItemWriter功能,ItemReader和ItemProcess同上节SpringBatch批处理之导出数据到XML文件 。
如下面的代码,只需使用MultiResourceItemWriter替换ItemWriter,然后设置好每个文件的大小,代理的实际写bean,资源路径和文件后缀名生成器等。
@Bean
@StepScope
MultiResourceItemWriter<Registration> multiXmlFileItemWriter() {
MultiResourceItemWriter<Registration> writer = new MultiResourceItemWriter<Registration>();
//设置每个文件写入文件条数大小
writer.setItemCountLimitPerResource(10);
//设置实际写如的ItemWriter
writer.setDelegate(registrationToXmlItemWriter());
//设置文件资源路径
writer.setResource(new FileSystemResource("D://xml.xml"));
//设置文件后缀名生成器,自定义
writer.setResourceSuffixCreator(new FileNameCreator());
return writer;
}
下面是具体的文件写使用的方式:这里就以导出数据到XML文件为例。
@Bean
StaxEventItemWriter<Registration> registrationToXmlItemWriter() {
StaxEventItemWriter<Registration> writer = new StaxEventItemWriter<>();
writer.setEncoding("UTF-16");
writer.setRootTagName("RegistrationFile");
writer.setHeaderCallback(new HeaderCallback());
writer.setFooterCallback(new FooterCallback());
writer.setMarshaller(marshaller());
return writer;
}
以上就是数据导入到多文件的基本实现流程,SpringBatch提供的开箱即用功能,的确很方便。
实际开发中遇到的问题
基于以上关于MultiResourceItemWriter的介绍,我们可以很方便的完成项目中的需求。同时也会遇到各种问题,这里介绍我在实际开发中遇到的两个问题。
- ItemCountLimitPerResource设置无效:
我们知道SpringBatch批处理是的最小处理单位是Chunk(单位:批),它的事务控制也是以批为单位,一批数据是放在一起进行读和写操作。如下代码,设置chunk为1000。
@Bean
Step registrationToXmlStep(ItemReader<Registration> registrationToXmlItemReader,
MultiResourceItemWriter<Registration> multiXmlFileItemWriter,
StepBuilderFactory stepBuilderFactory) {
return stepBuilderFactory.get("registrationToXmlStep")
//设置批的大小1000
.<Registration, Registration>chunk(1000)
.reader(registrationToXmlItemReader)
.writer(multiXmlFileItemWriter)
.build();
}
我们设置每个文件的ItemCountLimitPerResource属性时,就要考虑chunk的大小,若每个文件写入的条数小于chunk值时,这时候设置的ItemCountLimitPerResource属性就无效,每个文件的大小就是根据chunk的大小而定了。
所以ItemCountLimitPerResource的值要大于chunk的值,且前者时后者的整数倍。
- 自定义多文件文件名格式
多文件的文件名是根据index呈增量呈现的。如demo.txt.1,demo.txt.2,依次递推。
SpringBatch提供了ResourceSuffixCreator,可以根据具体需求实现自定义后缀名格式,如下代码:
public class CustomerResourceSuffixCreator implements ResourceSuffixCreator {
public SimpleResourceSuffixCreator() {
}
public String getSuffix(int index) {
return "_" + index;
}
}
值得注意的是无论是使用默认的,还是自定义的ResourceSuffixCreator,都是修改的文件的后缀名,最终的效果都是在文件名后面添加index。实际项目中往往要求的文件名都是在文件名内部进行递增index,而不是仅仅在修改后缀,如demo_1_user.txt,demo_2_user.txt,以此类推。这个时候ResourceSuffixCreator就不能满足我们的需求。
经过对源码的探究,发现可以自定义MultiResourceItemWriter来实现该需求,如ExtMultiResourceItemWriter<T>,该组件同原生的MultiResourceItemWriter<T>相比只是修改了文件生成的方法。核心代码如下:
private File setResourceToDelegate() throws IOException {
//创建多文件文件名的方法A
String path = this.resource.getFile().getAbsolutePath() + this.suffixCreator.getSuffix(this.resourceIndex);
File file = new File(path);
this.delegate.setResource(new FileSystemResource(file));
return file;
}
上面的方法是MultiResourceItemWriter生成文件名的地方,我们只需要重新自定义ExtMultiResourceItemWriter,然后在上面生成文件名的方法处,根据需求生成相应的文件名称即可。
---------SpringBatch批处理电子书下载地址百度网盘
链接: https://pan.baidu.com/s/1kCDsHuOPXHGJ0EjWleDalw 提取码: vvra
- 以上内容为实际开发中所总结,本着介绍主要思想思路的原则,仅贴出了核心的代码,如需完整代码或者有不同的见解,敬请在评论和留言以扶正。作者QQ:370731702。