使用java将大量数据从数据库导出到.csv时出现问题
一、 谢谢你的关注 我想使用java将大量数据(600万行)导出到.csv文件中。该应用程序是一个swing应用程序,带有JPA,使用toplink(ojdbc14) 我尝试使用: 缓冲写入程序 随机存取文件 文件通道 等等,但内存消耗仍然很高,导致Java堆内存不足异常,尽管我将最大堆大小设置为800m(-Xmx800m) 我最新版本的源代码:使用java将大量数据从数据库导出到.csv时出现问题,java,database,memory,export-to-csv,Java,Database,Memory,Export To Csv,一、 谢谢你的关注 我想使用java将大量数据(600万行)导出到.csv文件中。该应用程序是一个swing应用程序,带有JPA,使用toplink(ojdbc14) 我尝试使用: 缓冲写入程序 随机存取文件 文件通道 等等,但内存消耗仍然很高,导致Java堆内存不足异常,尽管我将最大堆大小设置为800m(-Xmx800m) 我最新版本的源代码: ...(more lines of code) FileChannel channel = getRandomAccessFile(tempFile
...(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();
}
…(更多行代码)
FileChannel channel=getRandomAccessFile(tempFile).getChannel();
对象[][]数据=pag.getRawData()//多维数组中的数据库数据
对于(int j=0;j
由于有600万行,我不想在创建文件时将这些数据存储在内存中。我制作了许多临时文件(每个文件有5000行),在这个过程的最后,使用两个FileChannel将所有这些临时文件附加到一个文件中。但是,内存不足的例外情况是在加入之前启动的
您现在是否有导出大量数据的另一种策略
非常感谢您的回复。对不起,我的英语水平提高了xD答案是使用“流”方法-即在数据集中滚动时读一行,写一行。您需要以游标的形式获取查询结果并对其进行迭代,而不是获取整个结果集
在JPA中,使用如下代码:
ScrollableResults cursor = session.createQuery("from SomeEntity x").scroll();
while (cursor.next()) {
writeToFile(cursor);
}
这意味着您一次只有一行内存,这完全可以扩展到任意数量的行,并且使用最少的内存(无论如何速度更快)
一次获取结果集中的所有行是一种方便的方法,适用于较小的结果集(大多数情况下都是如此),但通常来说,方便是有代价的,而且并非在所有情况下都有效。最好将数据库中的数据作为页面加载,并将每个页面写入文件,然后在加载下一页之前丢弃该页。是否分析了应用程序以查看对象/类之间的内存使用情况?我怀疑这是由于持久性上下文的大小。另外,如果你能写到CSV文件的末尾,那么加入文件也不值得。感谢Jeremy和Vinet的回复。Jeremy,我创建了很多档案,因为我在最早版本的源代码中使用了BufferedWriter。但我将尝试使用一个带有FileChannel:)的文件。在Vinet中,我使用NetBeans 6.9的分析器来分析内存消耗。遗憾的是,“live results”选项卡显示内存消耗最多的类是字节数组(byte[]),也许我并不完全了解分析器。关于持久性上下文,禁用缓存可以减少内存消耗?哦,非常感谢您的回复。尽管如此,我并没有使用te createQuery()方法(我认为会话是查询类的一个实例),而是使用te createNativeQuery()方法,它执行一个复杂的查询(连接3个表)。你的方法对我的问题正确吗?(我在查询:S中没有使用实体名称)@Bohemian需要您的帮助……我正在使用hibernate,面临同样的问题。表中有200万条记录。我想将表中的所有200万条记录写入csv文件。通过上述解决方案。。。。它将引发大约200万次的查询…只是想知道它是否是这样做的正确和唯一方法。或者我们可以创建多个线程,并允许它们从表中读取以提高速度。。但问题还是出现了,比如我们如何保持某一特定记录已经通过线程读出。请帮忙。。在过去的三周里,这个问题还没有答案。@sparsh610我认为一个运营需求应该有一个运营解决方案。Java非常适合处理单个请求的应用程序。它不适合处理批量操作。我强烈建议不要为此使用java。使用shell命令调用数据库实用性函数。每个数据库都有一种将数据转储到csv的简单方法—使用它。它将是简单易懂的代码,可靠且比java中的任何操作都要快。@Bohemian非常感谢。。。我急切地期待着你的答复。。。你的回答是…在最坏的情况下,如果我们必须通过Java来实现,那么什么是最好的最快实现方法。请帮助我:)我也尝试过谷歌搜索,但没有找到太多相同的方法