【发布时间】:2011-08-25 18:01:26
【问题描述】:
我正在尝试将一个大的 ResulSet(~1mm 行)写入单个文件。在 Java 1.6 中是否有首选/有效的方法来执行此操作?
【问题讨论】:
-
~1mm 有多大?只是好奇
-
@Bozho:该副本的答案没有考虑任何 JDBC 细节。
我正在尝试将一个大的 ResulSet(~1mm 行)写入单个文件。在 Java 1.6 中是否有首选/有效的方法来执行此操作?
【问题讨论】:
这取决于使用的 JDBC 驱动程序。您需要指示 JDBC 驱动程序不预先将整个 ResultSet 加载到 Java 的内存中,而是在每次 next() 调用时按行加载它。然后,在ResultSet#next() 循环中,您需要立即将数据写入文件,而不是将其保存在List 或其他东西中。
不清楚您使用的是什么 JDBC 驱动程序,但例如,可以指示 MySQL JDBC 驱动程序按照MySQL JDBC driver documentation 的以下方式按每行提供结果集:
结果集
默认情况下,ResultSet 被完全检索并存储在内存中。在大多数情况下这是最有效的操作方式,并且由于 MySQL 网络协议的设计更容易实现。如果您正在使用具有大量行或大值的 ResultSet,并且无法在 JVM 中为所需的内存分配堆空间,您可以告诉驱动程序一次将结果流回一行。
要启用此功能,您需要通过以下方式创建一个 Statement 实例:
stmt = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); stmt.setFetchSize(Integer.MIN_VALUE);
这是一个具体的启动示例:
try (
PrintWriter writer = new PrintWriter(new OutputStreamWriter(new BufferedOutputStream(new FileOutputStream("/records.txt")), "UTF-8"));
Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
) {
statement.setFetchSize(Integer.MIN_VALUE);
try (ResultSet resultSet = statement.executeQuery("SELECT col1, col2, col3 FROM sometable")) {
while (resultSet.next()) {
writer.append(resultSet.getString("col1")).append(",")
.append(resultSet.getString("col2")).append(",")
.append(resultSet.getString("col3")).println();
}
}
}
顺便说一句,我会先检查数据库是否没有内置的 SQL 支持,这样可以更有效地完成此操作。例如,MySQL 对此有一个SELECT INTO OUTFILE construct。
SELECT的SELECT ... INTO OUTFILE 'file_name'形式将选定的行写入文件。该文件是在服务器主机上创建的,因此您必须具有FILE权限才能使用此语法。 file_name 不能是现有文件,这样可以防止/etc/passwd和数据库表等文件被破坏。从 MySQL 5.1.6 开始,character_set_filesystem系统变量控制文件名的解释。
【讨论】:
BufferedOutputStream缓冲FileOutputStream,如我的代码示例所示。这会有很大的不同。如果 Oracle 不支持 SELECT INTO OUTFILE 之类的东西,我已经环顾四周,但似乎你必须为此编写一个 proc。我不是Oracle DB,所以我不能详细说明,对不起。
public static void writeResultSetToWriter(ResultSet resultSet, PrintWriter writer) throws SQLException
{
ResultSetMetaData metadata = resultSet.getMetaData();
int numColumns = metadata.getColumnCount();
int numRows = 0;
while(resultSet.next()) //iterate rows
{
++numRows;
JSONObject obj = new JSONObject(); //extends HashMap
for (int i = 1; i <= numColumns; ++i) //iterate columns
{
String column_name = metadata.getColumnName(i);
obj.put(column_name, resultSet.getObject(column_name));
}
writer.println(obj.toJSONString());
if(numRows % 1000 == 0)
writer.flush();
}
【讨论】: