使用java将大量数据从数据库导出到.csv时出现问题

时间:2011-08-09 21:08:59

标签: java database memory export-to-csv

我,谢谢你的关注。

我想使用java将大量数据,真正的大量数据(600万行)导出到.csv文件中。该应用程序是一个swing应用程序,JPA,使用toplink(ojdbc14)。

我试过用:

的BufferedWriter RandomAccessFile的 FileChannel

等等,但内存消耗仍然非常高,导致Java Heap Out of Memory Exception,尽管我将最大堆大小设置为800m(-Xmx800m)。

我的源代码的最后一个版本:

...(more lines of code)

FileChannel channel = getRandomAccessFile(tempFile).getChannel();
Object[][] data = pag.getRawData(); //Database data in a multidimentional array

            for (int j = 0; j < data.length; j++) {
                write(data[j], channel); //write data[j] (an array) into the channel
                freeStringLine(data[j]); //data[j] is an array, this method sets all positions =null
                data[j] = null;//sets reference in null
            }

            channel.force(false); //force writing in file system (HD)
            channel.close(); //Close the channel
            pag = null; 

...(more lines of code)

 private void write(Object[] row, FileChannel channel) throws DatabaseException {
    if (byteBuff == null) {
        byteBuff = ByteBuffer.allocateDirect(1024 * 1024);
    }
    for (int j = 0; j < row.length; j++) {
        if (j < row.length - 1) {
            if (row[j] != null) {
                byteBuff.put(row[j].toString().getBytes());
            }
            byteBuff.put(SPLITER_BYTES);
        } else {
            if (row[j] != null) {
                byteBuff.put(row[j].toString().getBytes());
            }
        }
    }
    byteBuff.put("\n".toString().getBytes());        
    byteBuff.flip();
    try {
        channel.write(byteBuff);
    } catch (IOException ex) {
        throw new DatabaseException("Imposible escribir en archivo temporal de exportación : " + ex.getMessage(), ex.getCause());
    }
    byteBuff.clear();
}

有600万行,我不想在创建文件时将这些数据存储在内存中。我创建了许多临时文件(每个文件有5000行),在最后的过程中,使用两个FileChannel将所有这些临时文件附加到一个文件中。但是,在加入之前会启动缺少内存的例外。

你现在有另一种出口大量数据的策略吗?

非常感谢任何回复。对不起我的英语,我正在改进xD

1 个答案:

答案 0 :(得分:3)

答案是使用“流”方法 - 即读取一行,在滚动数据集时写入一行。您需要将查询结果作为游标获取并遍历它,而不是获取整个结果集。

在JPA中,使用以下代码:

ScrollableResults cursor = session.createQuery("from SomeEntity x").scroll();

while (cursor.next()) {
    writeToFile(cursor);
}

这意味着你一次只能在内存中有一行,它可以完全扩展到任意数量的行并使用最少的内存(无论如何它都更快)。

在结果集中一次获取所有行是一种方便的方法,适用于小结果集(大多数情况下),但通常情况下,便利性是有代价的,并不适用于所有情况。