18

我的任务是将一个巨大的表转换为自定义 XML 文件。我将使用 Java 来完成这项工作。

如果我简单地发出“SELECT * FROM customer”,它可能会返回大量数据,最终导致 OOM。我想知道,有没有一种方法可以在记录可用后立即处理,然后在 sql 检索过程中从内存中删除记录?

--- 2009 年 7 月 13 日编辑

让我详细说明我的问题。我有 1 个数据库服务器和 1 个应用程序服务器。当我在应用程序中发出选择查询时,数据将从数据库服务器传输到应用程序服务器。

我相信(如果我错了,请纠正我)ResultSet 需要等到接收到查询中的所有记录。即使我们将 fetch size 设置为 4,对于 1000 条记录的表,我们最终在应用服务器的堆内存中仍然有 1000 条记录,对吗?获取大小仅影响往返数据库服务器的次数。

我的问题是,如何在它到达应用服务器后立即开始处理这 4 条(或任意数量)记录,并处理它以释放应用服务器中的内存?

4

6 回答 6

6

通过更多信息,我可以获得更有帮助的答案。

如果您使用的是 MySQL:

stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,
       java.sql.ResultSet.CONCUR_READ_ONLY);
stmt.setFetchSize(Integer.MIN_VALUE);

来自http://www.oracle.com/technology/tech/java/sqlj_jdbc/htdocs/jdbc_faq.html

java.util.Properties info = new java.util.Properties();
info.put ("user", "scott");
info.put ("password","tiger");
info.put ("defaultRowPrefetch","15");
getConnection ("jdbc:oracle:oci:@",info);
于 2009-07-10T04:49:56.830 回答
4

我认为您可以使用与此相同的解决方案。可滚动的结果集。

于 2009-07-10T04:51:33.097 回答
4

如果您使用的是 JDBC,则可以使用带有游标的 ResultSet,一次遍历一条记录。您需要确保将您的 XML 一次写入一条记录的文件,而不是使用 DOM 来构建 XML。

于 2009-07-10T04:52:02.037 回答
4

我从经验中学到的一条经验法则是,您永远不要将数据库中的所有数据都带到您的应用程序服务器。您可以做的一件事是实施一个过程来分页您的数据。

您可以带来一页包含大约 1000-5000 条记录的数据,处理它们,然后再次获取下一页的数据。

于 2009-07-10T04:52:30.910 回答
1

导出整个表的概念。(专家提示:我知道它的缺点。)

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
public class FullTableExport {
    public static String toXML(String s) {
        if (s != null) {
            StringBuilder b = new StringBuilder(s.length());
            for (int i = 0, count = s.length(); i < count; i++) {
                char c = s.charAt(i);
                switch (c) {
                case '<':
                    b.append("&lt;");
                    break;
                case '>':
                    b.append("&gt;");
                    break;
                case '\'':
                    b.append("&#39;");
                    break;
                case '"':
                    b.append("&quot;");
                    break;
                case '&':
                    b.append("&amp;");
                    break;
                default:
                    b.append(c);
                }
            }
            return b.toString();
        }
        return "";
    }
    public static void main(String[] args) throws Exception {
        String table = "CUSTOMER";
        int batch = 100;

        Class.forName("oracle.jdbc.driver.OracleDriver");
        Connection conn = DriverManager.getConnection(
            "jdbc:oracle:thin:@server:orcl", "user", "pass");
        PreparedStatement pstmt = conn.prepareStatement(
            "SELECT /*+FIRST_ROWS(" + batch + ") */ * FROM " + table);
        ResultSet rs = pstmt.executeQuery();
        rs.setFetchSize(batch);
        ResultSetMetaData rsm = rs.getMetaData();
        File output = new File("result.xml");
        PrintWriter out = new PrintWriter(new BufferedWriter(
            new OutputStreamWriter(
            new FileOutputStream(output), "UTF-8")), false);
        out.printf("<?xml version='1.0' encoding='UTF-8'?>%n");
        out.printf("<table name='%s'>%n", toXML(table));
        int j = 1;
        while (rs.next()) {
            out.printf("\t<row id='%d'>%n", j++);
            for (int i = 1; i <= rsm.getColumnCount(); i++) {
                out.printf("\t\t<col name='%s'>%s</col>%n", 
                    toXML(rsm.getColumnName(i)), 
                    toXML(rs.getString(i)));
            }
            out.printf("\t</row>%n");
        }
        out.printf("</table>%n", table);
        out.flush();
    }
}

编辑缺点(感谢@JS):

  • ojdbc 之外没有使用外部库
  • 什么都没有关闭
  • 抛出一个通用异常
  • 这是一个主要的方法
  • 使用 print 生成 XML
  • Oracle 特定的 SQL
  • 纯文本密码
  • 有些列在字符串表示中看起来很尴尬
  • UTF-8 太国际化了
  • XML 结构占用空间大
于 2009-07-10T08:14:24.403 回答
0

OOM错误发生在哪个阶段,是在数据检索还是将数据处理为XML文件?

如果是它的数据检索,批量获取数据。首先获取总行数,按主键对选择进行排序,并将选择的行限制为可咀嚼的大小。

如果是在创建 XML 文件时,将每个客户的 XML 节点发送到 System.out.println,不要将其保存在内存中。通过命令行启动程序并将所有输出重定向到文件;

java MyConverter > results.txt

当您遍历记录时,所有内容都保存在文件中。

于 2009-07-10T06:18:41.113 回答