java.sql.SQLException: 执行两条语句时结果集在Firebird上关闭

java.sql.SQLException: The result set is closed on Firebird when executing two statements

我尝试从 firebird 2.5 DB 进行 2 个查询,我使用 2 个单独的 Statement 对象,但是当在第二个查询程序中尝试使用第一个查询的数据时,出现错误 java.sql.SQLException: The result set is closed.

conn = DriverManager.getConnection(
                    strURL,
                    strUser, strPassword);


            if (conn == null) {
                System.err.println("Could not connect to database");
            }

            Statement stmt = conn.createStatement();
            Statement statement = conn.createStatement();

            ResultSet rs = stmt.executeQuery(strSQL);
            ResultSet rs2 = null;

            try {
                while (rs.next()) {
                    String strSQL2 = "SELECT PATIENT_NAME_R, PATIENT_DOB, PATIENT_ID, PATIENT_SEX, PATIENT_ADDRESS_CITY, PATIENT_ADDRESS_SHF FROM PATIENTS WHERE PATIENT_UID = " + rs.getObject(1);
                    rs2 = statement.executeQuery(strSQL2);
                    try {
                        while (rs2.next()) {
                            System.out.println("СПРАВКА");
                            System.out.println("Ф.И.О.: " + rs2.getObject(1).toString().trim() + " Дата рождения: " + rs2.getObject(2));
                            System.out.println("СНИЛС: " + rs2.getObject(3));
                            System.out.println("Адрес: " + rs2.getObject(5) + " " + rs2.getObject(6));
                            System.out.println("Врач: " + rs.getString("STUDY_MD"));
                            System.out.println("----------------------------------------------------------------");
                        }
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }

在 while (rs2.next()) 行 System.out.println("Врач: " + rs.getString("STUDY_MD")) 中,我得到错误

java.sql.SQLException: The result set is closed
at org.firebirdsql.jdbc.AbstractResultSet.checkOpen(AbstractResultSet.java:297)
at org.firebirdsql.jdbc.AbstractResultSet.getField(AbstractResultSet.java:788)
at org.firebirdsql.jdbc.AbstractResultSet.getString(AbstractResultSet.java:844)
at sample.Main$MedicalCert.run(Main.java:156)
at java.lang.Thread.run(Thread.java:748)

问题是您正在自动提交的连接上执行两个语句。在自动提交模式下,当您执行一条语句时,其他语句创建的结果集将被关闭。

JDBC 4.3 specification 中所述,在第 15.2.5 节 关闭 ResultSet 对象:

A ResultSet object is implicitly closed when

  • The associated Statement object is re-executed
  • The ResultSet is created with a Holdability of CLOSE_CURSORS_AT_COMMIT and an implicit or explicit commit occurs

使用 Jaybird(Firebird JDBC 驱动程序),您有以下三种选择:

  1. 在执行语句之前禁用自动提交:

    conn.setAutoCommit(false);
    
  2. 通过执行 as

    使第一个语句在提交后保持不变
    Statement stmt = conn.createStatement(
            ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY,
            ResultSet.HOLD_CURSORS_OVER_COMMIT);
    

    这将导致生成的结果集在(自动)提交后保持打开状态。但请注意,在这种模式下,Jaybird 将获取内存中的整个结果集,而不是分批获取。

  3. 与前面的选项类似,您可以通过指定连接 属性 defaultResultSetHoldable 集,将 Jaybird 配置为默认使用可保存的结果集。另见 Default holdable result sets。设置此 属性 将影响所有语句和结果集。

第一个选项更可取。

顺便说一句,我强烈建议您开始使用 try-with-resources:您当前的代码容易受到资源泄漏的影响。

如 Kayaman 的评论所述,您应该考虑重写您的查询,使其只是一个查询。您当前正在创建所谓的 N+1 查询问题(执行一个语句,然后对该语句的每一行执行另一个语句)。在查询中使用联接将允许您将其作为单个查询执行。作为警告,您的查询容易受到存储 SQL 注入的攻击,因为您将一个值连接到查询字符串中。