现在的位置: 首页 > 综合 > 正文

MySQL调用JDBC查询数据全部加载的问题

2013年08月06日 ⁄ 综合 ⁄ 共 2458字 ⁄ 字号 评论关闭

转自:http://hi.baidu.com/tuozhuai/blog/item/7fe1bcee3b44832c2cf53473.html

之前在建立Statement或 PreparedStatement,您所使用的是Connection无参数的createStatement()与 preparedStatement(),这样取得的Statement其执行SQL后得到的ResultSet,将只能使用next()方法逐笔取得查询结果。

您可以在建立Statement对象时指定resultSetType,可指定的参数有 ResultSet.TYPE_FORWARD_ONLY、ResultSet.TYPE_SCROLL_INSENSITIVE与 ResultSet.TYPE_SCROLL_SENSITIVE,在不指定的情况下,预设是第一个,也就是只能使用next()来逐笔取得资料,指定第二个或第三个,则可以使用ResultSet的afterLast()、previous()、absolute()、relative()等方法。

ResultSet.TYPE_SCROLL_INSENSITIVE与ResultSet.TYPE_SCROLL_SENSITIVE 的差别在于能否取得ResultSet改变值后的资料,另外您还必须指定resultSetConcurrency,有 ResultSet.CONCUR_READ_ONLY与ResultSet.CONCUR_UPDATABLE两个参数可以设定,前者表示只能读取 ResultSet的资料,后者表示可以直接使用ResultSet来操作数据库,这会在下一个主题后说明。

createStatement()不给定参数时,预设是ResultSet.TYPE_FORWARD_ONLY、 ResultSet.CONCUR_READ_ONLY。

这边先示范如何控制ResultSet的读取游标,在建立Statement时,您使用 ResultSet.TYPE_SCROLL_INSENSITIVE及ResultSet.CONCUR_READ_ONLY即可,下面这个例子示范从查询到的资料最后一笔开始往前读取:

Statement stmt = conn.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
ResultSet result = stmt.executeQuery(
"SELECT * FROM message");
result.afterLast();
while(result.previous()) {
System.out.print(result.getString("name") + "\t");
System.out.print(result.getString("email") + "\t");
System.out.print(result.getString("subject") + "\t");
System.out.print(result.getString("time") + "\t");
System.out.println(result.getString("memo") + "\t");
}
stmt.close();
conn.close();

afterLast()会将ResultSet的读取游标移至最后一笔资料之后,您使用previous()方法往前移动读取游标。

您也可以使用absolute()方法指定查询到的资料之位置,例如absolute(4)表示第四笔资料,absoulte(10)则是第十笔资料,如果指定负数,则从最后往前数,例如absolute(-1)则是最后一笔资料,若有100笔资料,absoulte(-4)则是第97笔资料。

relative()方法则从目前游标处指定相对位置,例如若目前在第25笔数据,则relative(-2)则表示第23笔资料,而relative (4)则表示第29笔资料。

另外有还beforeFirst(),可以将游标移至数据的第一笔之前,first()可以将游标移至第一笔数据,而last()可以将游标移至最后一笔数据。

===============================================================

默认情况下,MySQL的JDBC驱动会一下子把所有row都读取下来,这在一般情况下是最优的,因为可以减少Client-Server的通信开销。但是这样也有一个问题,当数据库查询结果很大时,特别当不能全部放进内存时,就会产生性能问题。

本来,JDBC api里在Connection、Statement和ResultSet上都有设置fetchSize的方法,但是MySQL的JDBC驱动都不支持,无论你怎么设fetchSize,ResultSet都会一次性从Server读取数据。在MySQL的官方论坛上也有多个这样的问题,总结一下解决办法如下:

1.MySQL版本在5.0以上,MySQL的JDBC驱动更新到最新版本(至少5.0以上)
2.Statement一定是TYPE_FORWARD_ONLY的,并发级别是CONCUR_READ_ONLY(即创建Statement的默认参数)
3.以下两句语句选一即可:
    1).statement.setFetchSize(Integer.MIN_VALUE);
    2).((com.mysql.jdbc.Statement)stat).enableStreamingResults();

这样会一行一行地从Server读取数据,因此通信开销很大,但内存问题可以解决。官方的说法是不支持fetchSize不是MySQL的JDBC驱动的问题,而是MySQL本身就不支持。而商用数据库Oracle或DB2都是支持fetchSize的,从这里也可以看出两者的考量不同。

抱歉!评论已关闭.