java.sql.SQLException:在sqlite上关闭ResultSet

时间:2016-12-01 14:53:07

标签: java sqlite jdbc sqlexception

我查看了许多答案,但不幸的是没有人回答我的问题,我仍然无法弄清楚为什么我会得到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语句则不返回任何内容。

2 个答案:

答案 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)资源(ConnectionStatementResultSetPreparedStatement,...)当你确定你不再需要它们的时候。您未发布的资源是您的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();
                }
            }