H2数据库报The object is already closed[90007-200]如何预防?
H2数据库[90007-200]对象已关闭报错预防方案
已知问题补充
你提供的业务代码除了连接提前关闭的问题外,还存在两处会触发该报错的风险点:
- JDBC资源未做主动生命周期管控,依赖GC回收资源的逻辑不可靠,很容易出现连接、Statement、ResultSet被提前释放的情况
- 同个Statement对象混用查询和更新操作:执行
st.execute()更新语句时,JDBC规范会自动关闭该Statement关联的未关闭ResultSet对象,也会触发对象已关闭报错 - 代码存在变量名笔误:定义的ResultSet对象名为
rest,但判断条件中误写为rset,需要优先修正该问题
可落地的预防方案
- 严格遵循JDBC资源管理规范,使用try-with-resources语法自动释放资源,避免手动关闭遗漏,改造参考代码如下:
// conn为从连接池获取的有效数据库连接 String selectSql = "SELECT amount, `month`, `year` FROM monthly_summary WHERE id=(SELECT MAX(id) FROM monthly_summary)"; try ( Statement querySt = conn.createStatement(); ResultSet rest = querySt.executeQuery(selectSql); ) { if (rest.next() && rest.getInt("month") == month && rest.getInt("year") == year) { double oldAmount = rest.getDouble("amount"); // 单独创建Statement执行更新操作,避免影响已打开的ResultSet try (Statement updateSt = conn.createStatement()) { updateSt.execute("UPDATE monthly_summary SET amount =" + (oldAmount + amount)); } } else { try (Statement insertSt = conn.createStatement()) { insertSt.execute("INSERT INTO monthly_summary(amount, `month`, `year`) VALUES (" + amount + "," + month + "," + year + ")"); } } } catch (SQLException e) { // 自行补充异常告警、事务回滚逻辑 e.printStackTrace(); }
- 优先使用
PreparedStatement代替普通Statement执行SQL,既可以避免SQL注入风险,也能更清晰的管控每个SQL操作对应的资源 - 如果你使用了数据库连接池,调整连接池配置提升可靠性:
- 开启获取连接时的有效性校验:配置
testOnBorrow=true,使用SELECT 1作为H2数据库的校验SQL,每次取连接时先确认连接存活 - 合理设置连接最大空闲时间,避免业务执行过程中连接被池端主动回收
- 开启获取连接时的有效性校验:配置
- 禁止跨业务方法复用同一个Connection、Statement对象,每次数据库操作从连接池取新连接,操作完成后立即归还,避免长连接持有过程中出现网络断开、数据库端主动关闭连接的问题
- 针对H2数据库做专项配置:如果使用文件模式的H2,在JDBC URL末尾添加
;DB_CLOSE_ON_EXIT=FALSE,避免JVM退出钩子提前关闭数据库连接;如果使用内存模式,排查业务逻辑中是否存在主动关闭全局连接的代码。
内容的提问来源于stack exchange,提问作者mikegr3p
相关产品推荐
相关产品推荐

