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("",null); } handler.character(value.toCharArray(),value.length()); handler.endElement("",ROW_ELEMENT_NAME); } handler.endElement("",rootElementName); handler.endDocument(); fos.close(); } } } 上面的方法执行完的同时,文件输出流fos也完整地结束了写文件的工作并关闭,从此,再大的结果集我们都不怕了。 BTW:XML的标签不能以数字开头,本例略去了对节点名是否合法的判断。
/////////////////////// 众所周知XML已经成不同应用程序之间数据交换的事实上的标准。在实际工作中,我们经常需要把JDBC返回的结果集(ResultSet)转化为XML表达形式,便于把数据传送到其他的应用程序。这里提供一个简单的例子,它可以把ResultSet转化为XML格式的文本,并存放在字符串(String)作为返回结果。这个程序通用之处在于它与选用的数据库结构无关。就是说,如果数据库结构发生了变化,本文提供的程序也可以正确运行。如果你有相同的需要,希望本文能给您一点帮助和启发。
Java代码
//////////////////
2012-12-01 13:32
数据查询结果集ResultSet转成xml格式的文件和xml String的方法1.将ResultSet转成xmL文件: public static Document toDocument(ResultSet rs) 2.将查询结果集ResultSet 转换成String类型的xml方法: public static String convertResultSetToXML(ResultSet rs){ public String Getxmlstring(String sqlsentance){ dbconn(); ResultSet rs1=dataset(sqlsentance); String xmlstr = convertResultSetToXML(rs1); return xmlstr; } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |