如何将数据库中大量数据存储为XML(内存问题)?

8

首先,我遇到了从数据库获取数据的问题,它占用了太多的内存并失败了。我已经设置了-Xmx1500M,并使用滚动ResultSet解决了这个问题。现在我需要从数据中创建一个XML文件,但是我无法将它放入一个文件中。目前,我的做法是:

while(rs.next()){
                i++;
                xmlStringBuilder.append("\n\t<row>");
                xmlStringBuilder.append("\n\t\t<ID>" + Util.transformToHTML(rs.getInt("id")) + "</ID>");
                xmlStringBuilder.append("\n\t\t<JED_ID>" + Util.transformToHTML(rs.getInt("jed_id")) + "</JED_ID>");
                xmlStringBuilder.append("\n\t\t<IME_PJ>" + Util.transformToHTML(rs.getString("ime_pj")) + "</IME_PJ>");
//etc.
                xmlStringBuilder.append("\n\t</row>");
                if (i%100000 == 0){
                                    //stores the data to a file with the name i.xml
                    storeKBR(xmlStringBuilder.toString(),i);
                    xmlStringBuilder= null;
                    xmlStringBuilder= new StringBuilder();  
                }

现在,我已经成功地获取了12个100 MB的文件。接下来,我想要将所有数据存储到一个文件中(然后进行压缩),但是如果我只是删除if部分,就会导致内存不足。我考虑过试图写入文件、关闭文件,然后再打开文件,但这样做对我帮助不大,因为当我打开文件时,我仍需将其加载到内存中。


一种实现方法是使用XML API编写多个XML文件,然后使用文件I/O合并所有文件的内容。 - Inv3r53
4个回答

3
为什么不把所有数据写入一个文件,并使用“追加”选项打开该文件呢?如果您只是要写入数据,那么无需读取文件中的所有数据。
然而,这可能是更好的解决方案:
PrintWriter writer = new PrintWriter(new BufferedOutputStream(new FileOutputStream("data.xml")));

while(rs.next()){
    i++;
    writer.print("\n\t<row>");
    writer.print("\n\t\t<ID>" + Util.transformToHTML(rs.getInt("id")) + "</ID>");
    writer.print("\n\t\t<JED_ID>" + Util.transformToHTML(rs.getInt("jed_id")) + "</JED_ID>");
    writer.print("\n\t\t<IME_PJ>" + Util.transformToHTML(rs.getString("ime_pj")) + "</IME_PJ>");
    //...

    writer.print("\n\t</row>");
}

writer.close();

BufferedOutputStream会在打印数据之前对其进行缓冲,如果默认值不适合您的需求,您可以在构造函数中指定缓冲区大小。有关详细信息,请参阅Java API:http://java.sun.com/javase/6/docs/api/


这听起来不错,但我不确定该怎么做。这是我的当前代码:fos = new FileOutputStream(new File(zipFolder + i + ".xml")); fos.write(xmlString.getBytes()); fos.flush(); fos.close(); - Andrija
它仍然需要1.5 GB的RAM,但我可以处理这么多 :) 谢谢 - Andrija
我很高兴你已经让它工作了,但一般来说,这种任务为什么不能在64M的内存中完成呢:从数据库流式传输结果是第一步(http://javaquirks.blogspot.com/2007/12/mysql-streaming-result-set.html),直接将它们写入文件是第二步。 - Tomislav Nakic-Alfirevic
事实上,这个应用程序是在周六交给我的,必须在周一之前运行,所以我没有太多的操作空间:) 一旦做完这个,我就会重新构建它,并回复您。也感谢您的评论。 - Andrija
Andrija,Daniel的解决方案不应该占用那么多的内存-可能小于64MB,但这要取决于您的其他代码。您的其他评论表明,您仍在尝试组装一些大型的String(Builder),而且还在错误地进行写入(使用.getBytes()不是一个好主意); 使用Writer(例如Daniel建议的PrintWriter)会比二进制流(即您的FileOutputStream)更好,更容易。 - Kevin Brock
正如在编辑中所看到的,代码已经被重新制作了,但我怀疑仍有改进的空间。顺便说一下,使用的zip类是http://java.sun.com/j2se/1.4.2/docs/api/java/util/zip/ZipOutputStream.html - Andrija

3
你正在内存中组装完整文件:你应该直接将数据写入文件。
此外,你可以考虑使用适当的XML API而不是将XML组装为文本文件。 这里提供了一个简短的教程(链接:这里)。

1

0

好的,代码已经重写了,我会包含整个操作:

//this is the calling/writing function; I have 8 types of "proizvod" which makes 
//8 XML files. After an XML file is created, it needs to be zipped by a custom zip class
       generateXML(tmpParam,queryRBR,proizvod.getOznaka());
   writeToZip(proizvod.getOznaka());



//inside writeToZip

    ZipEntry ze = new ZipEntry(oznaka + ".xml");
    FileOutputStream fos = new FileOutputStream(new File(zipFolder + oznaka + ".zip"));
    ZipOutputStream zos = new ZipOutputStream(fos);
    zos.putNextEntry(ze);
    FileInputStream fis = new FileInputStream(new File(zipFolder + oznaka + ".xml"));
    final byte[] buffer = new byte[1024];
    int n;
    while ((n = fis.read(buffer)) != -1)
        zos.write(buffer, 0, n);
    zos.closeEntry();
    zos.flush();
    zos.close();
    fis.close();

// inside generateXML
PrintWriter writer = new PrintWriter(new BufferedOutputStream(new FileOutputStream(zipFolder +oznaka + ".xml")));
        writer.print("\n<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
        writer.print("\n<PROSTORNE_JEDINICE>");
        stmt = cm.getConnection().createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, 
                ResultSet.CONCUR_READ_ONLY);
        String q = "";
        rs = stmt.executeQuery(q);
        if(rs != null){

            System.out.println("Početak u : " +Util.nowTime());
            while(rs.next()){
                writer.print("\n\t<row>");
                writer.print("\n\t\t<ID>" + Util.transformToHTML(rs.getInt("id")) + "</ID>");
                writer.print("\n\t\t<JED_ID>" + Util.transformToHTML(rs.getInt("jed_id")) + "</JED_ID>");
              //etc
              writer.print("\n\t</row>");
            }
            System.out.println("Kraj u : " +Util.nowTime());
        }
        writer.print("\n</PROSTORNE_JEDINICE>");

但是 generateXML 部分仍然占用了大量的内存(如果我猜得正确,它会尽可能地逐位占用),我不知道如何进行优化(使用其他方式来提供给 writer.print 函数)?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接