我查看了许多答案,但不幸的是没有人回答我的问题,我仍然无法弄清楚为什么我会得到ResultSet closed
。
以下是导致此问题的try
代码段:
System.out.println(" System Info! @CHKMD5Files(): Found pre-existing details: "+TOTAL);
Statement stmMD5 = null;
Connection connMD5 = null;
ResultSet rs_MD5 = null;
String RESULTMD5 = null;
try {
connMD5 = DriverManager.getConnection("jdbc:sqlite:" + bkpPATH+ hostname + ".db");
stmMD5 = connMD5.createStatement();
connMD5.setAutoCommit(false);
while (ROWID <= TOTAL) {
rs_MD5 = stmMD5.executeQuery("SELECT md5 FROM details WHERE ROWID = '"+ROWID+"';");
RESULTMD5 = rs_MD5.getString("MD5");
skipBuffer.write(RESULTMD5);
skipBuffer.newLine();
skipBuffer.flush();
ROWID++;
}
System.out.println(" System Info! @CHKMD5Files(): Done with try");
} catch (Exception ex) {
System.out.println(" System Error! @CHKMD5Files (2): " + ex);
System.exit(5);
} finally {
if (stmMD5 != null){
stmMD5.close();
System.out.println(" System Info! @CHKMD5Files (2): Closing Statement(stmMD5)");
}
if (connMD5 != null) {
connMD5.close();
System.out.println(" System Info! @CHKMD5Files (2): Closing Connection(connMD5)");
}
skipBuffer.close();
}
}
看起来while循环甚至没有运行,因为如果我在循环中放置print语句则不返回任何内容。
答案 0 :(得分:2)
如果要使用ResultSet
,则必须先调用next
方法,在结果的第一行设置ResultSet
的指针。正如该方法的Javadoc所示:
ResultSet游标最初位于第一行之前;对方法的第一次调用使得第一行成为当前行; ...
因此,如果您不调用它,则表示没有结果,getString
方法(在RESULTMD5 = rs_MD5.getString("MD5");
中使用)将生成该异常。所以至少添加
rs_MD5.next()
在提取结果之前。 next
方法返回一个布尔值,以指示它是否可以到达下一行。所以你可能想要在提取任何列值之前添加if
- 语句来检查该值是否为真,因为如果next
未能到达下一行(并返回false),它将设置指向没有有效行的指针(没有左侧)如果您尝试访问结果,则会得到相同的错误(请参阅上面next
的javadoc链接)。
但遗憾的是,即使您要添加对next
的调用,您也会在第二次迭代中遇到相同的错误,因为您无法使用相同的Statement
- 对象执行不同的查询。
每个Statement
- 对象只能生成一个ResultSet
要解决这个问题,至少应该替换
rs_MD5 = stmMD5.executeQuery("SELECT md5 FROM details WHERE ROWID = '"+ROWID+"';");
带
rs_MD5 = connMD5.createStatement().executeQuery(
"SELECT md5 FROM details WHERE ROWID = '"+ROWID+"';"
);
但是我会建议你,否则因为创建一个新的Statement
- 对象非常耗时(每个都必须在执行之前编译)。因为在每次迭代中唯一改变的是ROWID
,在每次迭代时再次重新编译相同的东西将是一个可怕的浪费时间。所以请改用PreparedStatement
。它允许您使用仅编译一次的语句,并且可以通过在其中注入参数(在编译之后)重复多次。
要使用PreparedStatement
,您应该这样做:
Connection conn = ... //Connection to your database
PreparedStatement ps = conn.preparedStatement("SELECT * FROM person WHERE name = ? AND age = ?");
//First search all persons with name "J.Baoby" and age = 5
ps.setString(1, "J.Baoby");
ps.setInt(2, 5);
ResultSet set1 = ps.executeQuery();
// Do something with it
// ....
//Now I need persons with name "Henry" and age = 25
ps.setString(1, "Henry");
ps.setInt(2, 25);
ResultSet set1 = ps.executeQuery();
// Do something with it
// ...
注入这些参数的方法是使用setXXX
方法作为第一个参数“?”的索引(从1开始)你想要替换,第二个参数是值。 XXX
将替换为要注入的值的类型。您可以注入的参数类型有限,因此在注入对象之前先检查Java API。
您可以在Java API中找到有关PreparedStatement
的更多信息
或者在on this site上(还有一些例子)。
最后我想补充一点,你应该关闭所有(JDBC)资源(Connection
,Statement
,ResultSet
,PreparedStatement
,...)当你确定你不再需要它们的时候。您未发布的资源是您的JVM可能已分配给其他资源的浪费资源。不关闭你的资源是一个坏习惯,可能会导致性能下降。
答案 1 :(得分:2)
非常感谢您的所有回复,并指出我正确的路径我已设法解决问题,这是因为ROWID在开始时没有数据,这导致了我的问题。
检查是否为空的附加语句导致了我的问题:
while (ROWID <= TOTAL) {
rs_MD5 = stmMD5.executeQuery("SELECT md5 FROM details WHERE ROWID = '"+ROWID+"';");
if (!rs_MD5.next() ) {
System.out.println(" System Info! @CHKMD5Files(): No data in rowid: "+ROWID);
ROWID++;
} else {
RESULTMD5 = rs_MD5.getString("MD5");
skipBuffer.write(RESULTMD5);
skipBuffer.newLine();
skipBuffer.flush();
ROWID++;
rs_MD5.close();
}
}