SpringBatch批处理之导出数据到XML文件

JAVA 专栏收录该内容
11 篇文章 0 订阅

SpringBatch导出文数据到XML文件基本流程也是由三部分组成:itemReader,itemProcess,itemWriter;需要特殊注意的地方主要由两点:

  1. XML文件是由一组节点构成,所以批处itemReader查询出来的对象需要同XML文件的结点构造成一一对应的映射关系;
  2. SpringBatch提供了itemWriter的子类StaxEventItemWriter专门进行XML文件的输出。

1. 导出数据到XML文件基本过程

首先介绍功能开发;

1.1 XML文件是由一组节点构成,形如下,所以首先要根据节点类型构造对应的bean object。
<?xml version="1.0" encoding="UTF-8"?>
<users>
    <user>
        <name>tom</name>
        <address>Lu Jia Zui road</address>
    </user>
    <user>
        <name>tom</name>
        <address>Lu Jia Zui road</address>
    </user>
</users>  

这里我们写入XML文件,使用的是Jaxb2Marshaller组件,支持XML文件节点名称同bean的属性无需完全一致,使用@XmlElement令两者形成一一对应的关系即可。
这里使用了@XmlRootElement和@XmlElement:

  • @XmlRootElement:对应了Bean object节点组单元,即可以定义的最小的对象,如user
  • @XmlElement:Bean object属性同XML属性节点的一一对应关系,如nameaddress注解在属性的get方法上

这里Bean的属性名称不必同XML节点名称保持一致。

Bean object如下:

@XmlRootElement(name="user")
public class User{
    private String name;
    private String address;
  
    @XmlElement(name = "name")
    public String getName() {
        return name;
    }
    public void setRName(String name) {
        this.name= name;
    }

    @XmlElement(name = "address")
    public String getMarketingProgram() {
        return address;
    }

    public void setAddress(String address) {
        this.address= address;
    }
1.2 配置BatchJob

同一般的BatchJob相比较,导入XML文件主要由两处需要注意。其一,SpringBatch提供了StaxEventItemWriter写组件。其二,读取数据源需要配置item与java bean的映射关系。

StaxEventItemWriter:负责将Java对象转换为XML记录,有若干属性,必须要设置的有Marshaller,RootTagName,Resource。 他们各自的作用如下图所示。

下图中RootTagName是XML文件中的最大级别的根节点,如本文开头XML文件的==<users>,而Bean中@XmlRootTag注解则是表征最小对象的标签,如<user>==

StaxEventItemWriter结构图
下图展示了StaxEventItemWriter写入XML文件的逻辑架构图,Marshaller负责将Item对象序列化为XML格式,然后由XMLEventWriter负责将XML字符串以STAX方式写入到文件。
除此之外,StaxEventItemWriter组件还提供了headerCallBack和footerCallBack两个回调函数,能够完成更为复杂的功能。

XML文件写入的逻辑架构图(摘自刘相SpringBatch批处理框架)

RowMapper:SpringBatch提供了接口,负责提供Item同Java bean之间的映射。实际开发中只需要自定义映射类,继承该接口即可;

 public class UserFieldMapper implements RowMapper<User> {
    @Override
    public User mapRow(ResultSet rs, int i) throws SQLException {
        User regUser = new User();
        regUser.setName(rs.getString("name"));
        regUser.setAddress(rs.getString("address"));
        return regUser;
    }
}

以上就是文件导出之前需要准备的配置文件,下面我们进行BatchJob的配置:以从数据库查询数据导入XML文件为例。

//配置BatchJob
@Bean
Job exportUserJob(JobBuilderFactory jobBuilderFactory,Step userToXmlStep) {
    return jobBuilderFactory.get("exportuserJob").incrementer(new RunIdIncrementer())
        .start(userToXmlStep).build();
} 
//配置Step,导出XML文件使用StaxEventItemWriter组件
@Bean
Step userToXmlStep(ItemReader<User> userToXmlItemReader,
                           StaxEventItemWriter<User> xmlFileItemWriter,
                           StepBuilderFactory stepBuilderFactory) {
    return stepBuilderFactory.get("userToXmlStep")
            .<User, User>chunk(10)
            .reader(userToXmlItemReader)
            .writer(xmlFileItemWriter)
            .build();
}

//配置ItemReader,
@Bean
@StepScope
ItemReader<User> userToXmlItemReader(DataSource dataSource) {
    JdbcPagingItemReader<User> databaseReader = new JdbcPagingItemReader<>();
    databaseReader.setDataSource(dataSource);
    databaseReader.setPageSize(10);
    //!重要:配置数据库到DTO的映射关系
    userFieldMapper userFieldMapper = new UserFieldMapper();
    databaseReader.setRowMapper(userFieldMapper);

    PagingQueryProvider queryProvider = createQueryProvider(jobParameters);
    databaseReader.setQueryProvider(queryProvider);

    return databaseReader;
}

private PagingQueryProvider createQueryProvider(Map jobParameters) {
    MySqlPagingQueryProvider queryProvider = new MySqlPagingQueryProvider();
    queryProvider.setSelectClause("SELECT name, address");
    queryProvider.setFromClause("FROM user");
    queryProvider.setSortKeys(sortByLastActivityDateDesc());

    return queryProvider;
}
//Config result order
private Map<String, Order> sortByLastActivityDateDesc() {
    Map<String, Order> sortConfiguration = new LinkedHashMap<>();
    sortConfiguration.put("name", Order.ASCENDING);
    return sortConfiguration;
}
//TODO,这里省略了ItemProcss
//配置ItemWriter
StaxEventItemWriter<User> toXmlItemWriter() {
    StaxEventItemWriter<User> writer = new StaxEventItemWriter<>();
    //配置文件写入的编码方式
    writer.setEncoding("UTF-16");
    //配置XML文件的根节点
    writer.setRootTagName("Users");
    //writer.setHeaderCallback(new HeaderCallback());
    //writer.setFooterCallback(new FooterCallback());
    writer.setMarshaller(marshaller());
    return writer;
}

//配置Marshaller
private Jaxb2Marshaller marshaller() {
    Jaxb2Marshaller xml = new Jaxb2Marshaller();
    //Item映射到的绑定的Bean
    xml.setClassesToBeBound(User.class);
    return xml;
}

以上是SpringBatch导出数据到XML文件的基本开发技术。

在实际的开发过程中,也会遇到各种各样的问题,在这里仅介绍文件的XML文件的缩进的问题。

2. 格式缩进

问题描述:项目开发中,导出的XML文件都写在了一行,并没有实现分行与格式缩进,试了设置Jaxb2Marshaller 的格式Marshaller.JAXB_FORMATTED_OUTPUT属性,都不生效。最终找到了如下的解决方案。

解决方案:自定义StaxEventItemWriter,借助带有缩进功能的IndentingXMLEventWriter改写XML文件的写组件。

核心代码如下:

 //自定义带有缩进功能的StaxEventItemWriter XML文件写组件;
public class IndentingStaxEventItemWriter<T> extends StaxEventItemWriter<T> {
    private boolean indent = true;
    @Override
    protected XMLEventWriter createXmlEventWriter(XMLOutputFactory outputFactory, Writer writer) throws XMLStreamException {
        if (indent) {
            return new IndentingXMLEventWriter( super.createXmlEventWriter( outputFactory, writer ) );
        } else {
            return super.createXmlEventWriter( outputFactory, writer );
        }
    }
    public boolean isIndent() {
        return indent;
    }
    public void setIndent(boolean indent) {
        this.indent = indent;
    }
}
//改写上面的ItemWriter,将StaxEventItemWriter替换成带有缩进的写 IndentingStaxEventItemWriter
@Bean
@StepScope
IndentingStaxEventItemWriter<User> userToXmlItemWriter() {
  	IndentingStaxEventItemWriter<Registration> writer = new IndentingStaxEventItemWriter<>();
  	 writer.setEncoding("UTF-16");
  	 writer.setRootTagName("Users");
  	 writer.setMarshaller(marshaller());
  	 String exportFilePath = sftpBiz.getOutTempDir();
  	 writer.setResource("D://exportData.xml"));
   	return writer;
}

值得注意的是,上面的功能改写,需要借助stax-utils.jar;pom.xml导入jar。

<dependency>
      <groupId>net.java.dev.stax-utils</groupId>
      <artifactId>stax-utils</artifactId>
      <version>20070216</version>
  </dependency>

3. 多个<rootTag> 的问题

有的时候,基于项目的需要,可能要写入的XML文档结构较为复杂,如多<rootTag>;这个这时候就可以考虑使用headerCallBack和footerCallBack两个回调函数。具体内容具体分析,这里就不介绍了。

本文中涉及的代码都是核心功能的介绍,不是完整的项目代码,本文示例代码都托管在GitHub;若有需要,可以在评论区留言,或联系作者QQ370731702.。
基于以上的分享技术点,欢迎同行的评论,留言和扶正。

  • 1
    点赞
  • 4
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值