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 驱动程序),您有以下三种选择:
在执行语句之前禁用自动提交:
conn.setAutoCommit(false);
通过执行 as
使第一个语句在提交后保持不变
Statement stmt = conn.createStatement(
ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY,
ResultSet.HOLD_CURSORS_OVER_COMMIT);
这将导致生成的结果集在(自动)提交后保持打开状态。但请注意,在这种模式下,Jaybird 将获取内存中的整个结果集,而不是分批获取。
- 与前面的选项类似,您可以通过指定连接 属性
defaultResultSetHoldable
集,将 Jaybird 配置为默认使用可保存的结果集。另见 Default holdable result sets。设置此 属性 将影响所有语句和结果集。
第一个选项更可取。
顺便说一句,我强烈建议您开始使用 try-with-resources:您当前的代码容易受到资源泄漏的影响。
如 Kayaman 的评论所述,您应该考虑重写您的查询,使其只是一个查询。您当前正在创建所谓的 N+1 查询问题(执行一个语句,然后对该语句的每一行执行另一个语句)。在查询中使用联接将允许您将其作为单个查询执行。作为警告,您的查询容易受到存储 SQL 注入的攻击,因为您将一个值连接到查询字符串中。
我尝试从 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 ofCLOSE_CURSORS_AT_COMMIT
and an implicit or explicit commit occurs
使用 Jaybird(Firebird JDBC 驱动程序),您有以下三种选择:
在执行语句之前禁用自动提交:
conn.setAutoCommit(false);
通过执行 as
使第一个语句在提交后保持不变Statement stmt = conn.createStatement( ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT);
这将导致生成的结果集在(自动)提交后保持打开状态。但请注意,在这种模式下,Jaybird 将获取内存中的整个结果集,而不是分批获取。
- 与前面的选项类似,您可以通过指定连接 属性
defaultResultSetHoldable
集,将 Jaybird 配置为默认使用可保存的结果集。另见 Default holdable result sets。设置此 属性 将影响所有语句和结果集。
第一个选项更可取。
顺便说一句,我强烈建议您开始使用 try-with-resources:您当前的代码容易受到资源泄漏的影响。
如 Kayaman 的评论所述,您应该考虑重写您的查询,使其只是一个查询。您当前正在创建所谓的 N+1 查询问题(执行一个语句,然后对该语句的每一行执行另一个语句)。在查询中使用联接将允许您将其作为单个查询执行。作为警告,您的查询容易受到存储 SQL 注入的攻击,因为您将一个值连接到查询字符串中。