• 将一个JDBC的ResultSet转成XML并输出到文件


    一·、需求

    写一个功能类,能将一个给定的sql select语句的执行结果集按照一定格式生成xml文件。

    比如,一个sql语句"select * from star;"的执行结果是这样的:

    ---------------------------

    name     age   gender 

    ---------------------------

    James    26     male

    Bryant   33     male

    要求生成后的xml的根节点名叫"star"且每条数据使用一个<row>标签来代表,就像下面的这样:

    <?xml version="1.0" encoding="UTF-8" standalone="no" ?>

    <star xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <row>

    <name>James</name>

    <age>26</age>

    <gender>male</gender>

    </row>

    <row>

    <name>Bryant</name>

    <age>33</age>

    <gender>male</gender>

    </row>

    </star>

    二、早期作法

    早期分析这个需求的时候,很自然的将需求分成了两块功能来完成,一块用来生成xml,另一块用来将xml输出到文件。

    1、生成xml:

    生成xml的方式的总体思路是,首先通过JDBC连接执行sql得到结果集,然后遍历结果集将内容填充到一个

    org.w3c.dom.Document对象,最后使用javax.xml.transform.Transformer将Document对象转换成xml。

    @Component("xmlMaker")
    public class XmlMaker{
      private static DataSource datasource;
      private static final String XSDNS="http://ww.w3.org/2001/XMLSchema";
      private static final String ROW_ELEMENT_NAME="row";
    private String content; @Resource(name
    ="mydatasource") public void setDataSource(DataSource dataSource){ this.dataSource=datasource; } public String generateXML(String sql, String rootElementName){ Connection con = null; PreparedStatement ps = null; ResultSet rs = null; String result = null; try{ con = dataSource.getConnection();
    // 创建一个可滚动的只读结果集,普通类型的结果集的游标无法自由上下移动 ps
    = con.prepareStatement(sql,ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY); rs = ps.executeQuery(); result = makeXMLFromResultSet(rs,rootElementName); } catch(Exception e){ //todo }finally{ try{ if(null != rs){ rs.close(); } if(null != ps){ ps.close(); } if(null != con){ con.close(); } }catch(SQLException e){ //todo } }
    return result; }
    private String makeXMLFromResultSet(ResultSet rs,String rootElementName) throws Exception{ Document doc = resultSet2Dom(rs, rootElementName); String ret = null; StringWriter sw = new StringWriter(); Transformer t = null; try{ TransformerFactory tf = TransformerFactory.newInstance(); t = tf.newTransformer(); t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
    t.setOutputProperty(OutputKeys.METHOD, "xml");
    t.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
    t.setOutputProperty(OutputKeys.INDENT, "yes");
          DOMSource domSource = new DOMSource(doc);
    StreamResult sr = new StreamResult(sw);
    transformer.transform(domSource,sr);
    content = sw.toString();
         }catch(Exception e){
    //todo
    }finally{
    doc = null;
    try{
    sw.close();
    }catch(IOException e){
    }
    }
    return content;
    }

    private Document resultSet2Dom(ResultSet rs,String rootElementName){
    Document myDocument = null;
    try{
    myDocument = ((DocumentBuilderFactory.newInstance()).newDocumentBuilder()).newDocument();
    }catch(ParserConfigurationException pce){
    //todo
    }
    Element root = myDocument.createElement(rootElementName);
    root.setAttribute("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance");
    myDocument.appendChild(root);
    ResultSetMetaData rsmd = rs.getMetaData();
    Element element,row;
    String value;
    if(rs.next()){
    rs.previous();//使用rs.next()来判断结果集是否有至少一条数据,如果有就将游标退回初始位置准备开始遍历。
    while(rs.isLast() == false){
    rs.next();
    row = myDocument.createElement(ROW_ELEMENT_NAME);
    root.appendChild(row);
    for(int i=1;i<=rsmd.getColumnCount();i++){
    element = myDocument.createElement(rsmd.getColumnLabel(i).toLowerCase());
    int columnType = rsmd.getColumnType();//此处得到列类型是方便对特殊类型数据的处理,比如当数据是浮点型时四舍五入。本例略
    value = rs.getString(i);
    if(value == null){
    element.setAttribute("xsi:nil","true");
    }else{
    element.appendChild(myDocument.createTextNode(value));
    }
    row.appendChild(element);
    }
    }
    return myDocument;
    }
    }

    2、将xml写成文件

    public class FileMaker{
    public void static writeFile(String filePath,Sring fileName,String content){
    File fileDirectory = new File(filePath);
    File targetFile = new File(filePath + File.separator + fileName);
    if(!(fileDirectory.isDirectory())){
    fileDirectory.mkdirs();//如果传过来的文件路径不存在,就先创建这个路径
    }
    if(!(targetFile.isFile())){
    try{
    targetFile.createNewFile();//如果目标文件不存在就创建文件
    }catch(IOException e){
    //todo
    }
    }
    FileOutputStream fos = null;
    try{
    fos = new FileOutputStream(targetFile);
    org.apache.commons.io.IOUtils.write(content,fos,"UTF-8");
    }catch(IOException e){
    //todo
    }finally{
    IOUtils.closeQuietly(fos);
    }
    }
    }

     三、遇到问题

    在数据量小时,这种做法能正常工作,但有一天别人在使用的时候系统卡死了,Debug后发现在结果集过大(当时有三百万条数据)时,内存溢出了。因为依照上面的做法,需要将一个有三百万条数据的结果集转成一个Dom对象放在内存中。于是我加大内存,终

    于挨过了生成xml这个环节,得到了一个庞大的字符串content。但由于Dom对象的引用虽然被指向了null但它之前所占用的内存并不可能立即释放,所以在写文件时内存又不够了,又溢出。其实,加内存并不是解决问题的办法,因为数据量不固定,这终究是一

    个不健壮的程序。

    四、修改方案,解决问题

    将结果集硬生生的打造成一个大Dom对象的方式已被证明不可行,我考虑在遍历结果集的同时边读边写文件。

    感谢 http://www.clipclip.org/wqmd/clips/detail/1204498 的作者。

    使用SAX的方式在遍历结果集的同时生成xml文件。

    public void resultSet2XML(ResultSet rs, String rootElementName,String filePath) throws Exception{
    SAXTransformerFactory fac = (SAXTransformerFactory)SAXTransfomerFactory.newInstance();
    TransformerHandler handler = fac.newTransformerHandler();
    Transformer transformer = handler.getTransformer();
    Transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION,"no");
    Transformer.setOutputProperty(OutputKeys.METHOD,"xml");
    Transformer.setOutputProperty(OutputKeys.ENCODING,"UTF-8");
    Transformer.setOutputProperty(OutputKeys.INDENT,"yes");
    Transformer.setOutputProperty(OutputKeys.STANDALONE,"no");
    FileOutputStream fos = new FileOutputStream(filePath);
    Result resultxml = new StreamResult(fos);
    handler.setResult(resultxml);
    ResultSetMetaData rsmd = rs.getMetaData();
    String value = "";
    AttributeImpl rootElementAttribute = new AttributesImpl();
    rootElementAttribute.addAttribute("","","xmlns:xsi","","http://www.w3.org/2001/XMLSchema-instance");
    handler.startDocument();
    handler.startElement("","",rootElementName,rootElementAttribute);
    if(rs.next()){
    rs.previous();
    while(rs.isLast() == false){
    rs.next();
    handler.startElement("","",ROW_ELEMENT_NAME,null);
    for(int i=1;i<=rsmd.getColumnCount();i++){
    int columnType = rsmd.getColumnType();
    value = rs.getString(i);
    String columnName = rsmd.getColumnLabel(i).toLowerCase();
    if(value == null){
    AttributesImpl tempAttribute = new AttributesImpl();
    tempAttribute.addAttribute("","","xsi:nil","","true");
    handler.startElement("","",columnName,tempAttribute);
    }else{
    handler.startElement("","",columnName,null);
    }
    handler.character(value.toCharArray(),0,value.length());
    handler.endElement("","",ROW_ELEMENT_NAME);
    }
    handler.endElement("","",rootElementName);
    handler.endDocument();
    fos.close();
    }
    }
    }

    上面的方法执行完的同时,文件输出流fos也完整地结束了写文件的工作并关闭,从此,再大的结果集我们都不怕了。

     BTW:XML的标签不能以数字开头,本例略去了对节点名是否合法的判断。

  • 相关阅读:
    CentOS怎样强制卸载PHP以及自定义安装PHP
    HTMLparser 笔记
    linux如何查看CPU,内存,机器型号,网卡信息
    PC机做ISCSI存储服务器故障
    [ Python
    [ Python
    [ Python
    [ Python
    [ Python
    [ Python
  • 原文地址:https://www.cnblogs.com/mabaishui/p/2850565.html
Copyright © 2020-2023  润新知