我有一个问题。我在我的 java 项目中使用以下 MySQL 驱动程序:
// SET THE MYSQL DRIVER
Class.forName("com.mysql.cj.jdbc.Driver");
SqlConn sqlConn = new SqlConn();
在 SqlConn
类中,我有以下功能:
public ResultSet executeQuery(String query) {
Statement stmt = null;
ResultSet rs = null;
try {
stmt = conn.createStatement();
if (stmt.execute(query)) {
rs = stmt.getResultSet();
}
// Now do something with the ResultSet ....
} catch (SQLException ex) {
System.out.println("SQLException: " + ex.getMessage());
System.out.println("SQLState: " + ex.getSQLState());
System.out.println("VendorError: " + ex.getErrorCode());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException ignored) {
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException sqlEx) {
} // ignore
stmt = null;
}
}
return rs;
}
该函数的使用方式如下:
ResultSet result = sqlConn.executeQuery("SELECT Market, Coin FROM Wallets GROUP BY Market, Coin ORDER BY Market, Coin;");
但是当我想像这样循环它时:
while (result.next()) {
System.out.println(result.getString("Market"));
System.out.println(result.getString("Coin"));
System.out.println();
}
我收到以下错误:
Exception in thread "main" java.sql.SQLException: Operation not allowed after ResultSet closed
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:129)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:89)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:63)
at com.mysql.cj.jdbc.result.ResultSetImpl.checkClosed(ResultSetImpl.java:464)
at com.mysql.cj.jdbc.result.ResultSetImpl.next(ResultSetImpl.java:1744)
at com.company.drivers.SimulatorDriver.main(SimulatorDriver.java:82)
我做错了什么以及如何解决这个问题?
最佳答案
在您粘贴的代码中,您首先创建一个连接,然后无法关闭该连接(资源泄漏!),然后创建一个语句和一个结果集。在返回之前,您总是(通过finally block )关闭这些。因此,此方法创建一个 ResultSet,它立即关闭并返回这个现在关闭的结果集。
所有这些东西都是 20 多年前的想法。尝试用一些好的方法来掩盖 JDBC 是完全有意义的,但绝对没有必要自己发明这个轮子;使用JOOQ或JDBI它为你做了这件事。
如果您坚持使用此代码,请注意此代码:
在管理资源时使用 try-with-resources,不要使用 finally block 。
所有 3 件事都需要关闭(Connection、Statement 和 ResultSet),但请注意 close() 会传播:如果关闭结果集,则只是关闭结果集,但如果关闭语句,则也会关闭它生成的任何结果集,如果关闭连接,您也会依次关闭它生成的任何语句(以及所有结果集)。这使得编写这样的方法实际上是不可能的(你如何管理如何关闭事物)?因此,研究 lambda。无论如何,您都需要 lambda 来重试。
声明几乎完全没有用。当您进行参数化查询时(即
SELECT * FROM users WHERE username = ...用户名刚刚在此处输入到网络表单中的用户名...
,您不能使用以下任何一个:您可以'不要在连接用户名的地方创建一个字符串,因为如果在表单上输入的用户名是whatever' OR 1 == 1; DROP TABLE users CASCADE; EXEC 'format C:/y';
?不,唯一的方法是PreparedStatement
,它支持参数化,但不会立即让您遭受 SQL 注入(inject)攻击。说真的,JOOQ 或 JDBI 做到了这一切,而且做得更好。
您不需要
Class.forName("com.mysql.cj.jdbc.Driver");
- 20 年来都不需要它。这种“从无到有,结果集”的模型行不通。 DB 有事务;有一个更大的上下文(事务),通常包含多个查询。您需要重新设计此 API 以考虑到这一点:要么将连接对象传递给executeQuery 方法,要么创建您自己的对象来表示连接,并且它将具有查询方法。 JOOQ 和 JDBI 也涵盖了这一点。假设非灾难性危险的隔离级别,即使是一系列只读查询也需要事务。
关于java - 无法在 Java 中循环 ResultSet,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66561983/