我有一项任务是将一个巨大的表转换为自定义XML文件。我将使用Java来完成这项工作。
如果我只是发出“SELECT * FROM customer”,它可能会返回最终导致OOM的大量数据。我想知道,有没有办法可以在记录可用后立即处理,并在sql检索过程中从内存中删除记录?
---于2009年7月13日编辑
让我详细说明我的问题。我有1个db服务器和1个应用服务器。 当我在应用程序中发出select查询时,数据将从db服务器传送到app server。
我相信(如果我错了,请纠正我)ResultSet需要等到接收查询中的所有记录。即使我们将获取大小设置为4,对于1000记录表,我们仍然最终在应用服务器的堆内存中有1000条记录,这是正确的吗?获取大小仅影响从/向数据库服务器的往返次数。
我的问题是,如何在到达app服务器后立即开始处理该4(或任何数字)记录,并将其丢弃以释放应用服务器中的内存?
答案 0 :(得分: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);
答案 1 :(得分:4)
我认为您可以使用与this one相同的解决方案。可滚动的结果集。
答案 2 :(得分:4)
如果您使用JDBC,则可以使用带有游标的ResultSet,您可以一次迭代一条记录。您需要确保一次将XML写入一个文件,而不是使用DOM来构建XML。
答案 3 :(得分:4)
我从我的经验中学到的一条经验法则是,您永远不会将数据库中的所有数据都带到您的应用服务器。您可以做的一件事是实施一个程序来分页数据。
您可以带一页包含大约1000-5000条记录的数据,处理它们,然后再次获取下一页的数据。
答案 4 :(得分: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("<");
break;
case '>':
b.append(">");
break;
case '\'':
b.append("'");
break;
case '"':
b.append(""");
break;
case '&':
b.append("&");
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();
}
}
编辑缺点(感谢@ J.S。):
答案 5 :(得分:0)
在哪个阶段发生OOM错误,是在数据检索上还是在处理数据到XML文件?
如果是数据检索,请批量获取数据。首先获取总行数,按主键对选择进行排序,并将选定的行限制为可咀嚼的大小。
如果在创建XML文件时,将每个客户的XML节点发送到System.out.println,请不要将其保存在内存中。通过commad行启动程序并将所有输出重定向到文件;
java MyConverter > results.txt
当您循环浏览记录时,所有内容都会保存在文件中。