Java JDBC操作抛出ResultSet closed异常问题咨询
问题答复
- 完全不需要在所有函数中统一ResultSet的变量名。ResultSet是每个方法内部声明的局部变量,作用域仅限当前方法,命名差异不会触发任何数据库操作异常,你遇到的
ResultSet closed报错和变量命名没有任何关系。
错误根本原因
这个报错是JDBC使用不当导致的,属于JDBC规范明确规定的标准行为:
同一个
Statement实例同一时间只能关联一个处于打开状态的ResultSet,当你调用该Statement对象执行任意新的SQL语句时,之前由这个Statement生成的ResultSet会被驱动自动关闭。
对照你的代码逻辑,报错触发路径非常清晰:
- 每个事件方法里,你先调用
statement.executeQuery(SELECT ...)拿到查询结果集ResultSet,进入while(resultSet.next())循环准备读取数据 - 循环内部你直接复用了同一个
statement对象执行UPDATE语句statement.execute("UPDATE ...") - 这行UPDATE执行的瞬间,之前SELECT查询生成的ResultSet就被JDBC驱动自动关闭了
- UPDATE执行完成后,你后续再调用
ambush.getInt(...)/fire.getInt(...)等方法读取已经关闭的ResultSet,就会直接抛出java.sql.SQLException: ResultSet closed异常。
顺带提一句,你这段代码就算不触发ResultSet关闭报错,本身也有逻辑bug:你判断血量/补给/弹药是否为0的时候,读取的是UPDATE执行前ResultSet里存的旧数值,根本检测不到更新后属性降到0的场景。
修复方案
二选一即可解决问题:
- 方案一(推荐,改动最小):遍历ResultSet时,先把所有需要用到的字段值提前读取到局部变量中保存,再执行UPDATE操作,后续所有逻辑都用提前存好的局部变量计算、判断,不再操作ResultSet对象。
以ambush方法为例,修正后的核心逻辑片段如下:
其余while (ambush.next()) { // 第一步:提前把所有需要的字段值取出来存为局部变量 int oldHealth = ambush.getInt(DatabaseConstants.Column_Health); int oldSupplies = ambush.getInt(DatabaseConstants.Column_Supplies); int oldAmmo = ambush.getInt(DatabaseConstants.Column_Ammo); int oldLocation = ambush.getInt(DatabaseConstants.Column_Location); // 计算更新后的数值 int newHealth = oldHealth - 40; int newSupplies = oldSupplies - 40; int newAmmo = oldAmmo - 40; int newLocation = oldLocation + mymovement; // 第二步:执行UPDATE语句,此时ResultSet被关闭也不会产生影响 statement.execute("UPDATE " + DatabaseConstants.Table_Troops + " SET " + DatabaseConstants.Column_Health + " = " + newHealth + ", " + DatabaseConstants.Column_Supplies + " = " + newSupplies + ", " + DatabaseConstants.Column_Ammo + " = " + newAmmo + ", " + DatabaseConstants.Column_Location + " = " + newLocation + " WHERE " + DatabaseConstants.Column_ID + " = " + myid); // 第三步:后续判断逻辑全部用局部变量,不再读取ResultSet if (mymovement == 10) { System.out.println("You have moved 10 miles - you have completed the mission"); System.out.println("Your session will now be terminated"); System.exit(0); } else if (newHealth <= 0) { System.out.println("Your health is 0%"); System.out.println("Your session will now be terminated"); System.exit(0); } else if (newSupplies <= 0) { System.out.println("Your supplies are 0%"); System.out.println("Your session will now be terminated"); System.exit(0); } else if (newAmmo <= 0) { System.out.println("Your ammunition is at 0%"); System.out.println("Your session will now be terminated"); System.exit(0); } else if (mymovement < 10) { System.out.println("Your progress has been saved - You will be re-directed to the main menu"); DatabaseConstants.showMenu(); } }fire/badweather/noEvent方法按照同样逻辑修改即可,注意对应调整不同事件的属性扣减比例。 - 方案二:执行UPDATE语句时,单独创建一个新的Statement对象来执行,不要和SELECT查询共用同一个Statement实例,两个SQL操作各自的ResultSet互不干扰,也不会触发自动关闭问题。
额外优化建议
- 你当前的资源关闭逻辑存在泄漏风险:如果SQL执行过程中抛出异常,
Connection/Statement/ResultSet的close()方法不会被执行,会持续占用数据库连接。建议使用try-with-resources语法自动管理资源,不需要手动编写close()调用。 - 四个事件方法存在大量重复的数据库连接、查询、更新、数值判断逻辑,可以把通用逻辑抽成公共方法,减少代码冗余,降低后续修改的维护成本。
内容的提问来源于stack exchange,提问作者Neet
相关产品推荐
相关产品推荐

