Java中SQL语句使用try-with-resource能否正常关闭相关资源?
问题1:loadActiveCompany执行完成后,相关资源是否会正常关闭?
你当前的写法下,PreparedStatement和ResultSet两类资源基本可以正常关闭:
- try-with-resources会自动调用所有实现了
AutoCloseable接口的资源的close()方法,你把PreparedStatement、ResultSet都放在了try的资源声明块中,执行顺序是内层ResultSet先关闭、外层PreparedStatement后关闭,符合资源释放的优先级要求。 - 但仍然存在两个风险:
- 你的
DBQuery类内部还持有_stmt、_rs的成员引用,如果后续你在DBQuery的其他方法中对已经被try-with-resources关闭的资源进行操作,会直接抛出资源已关闭的异常。 - 你当前的代码完全没有处理数据库连接的释放逻辑:
DB.getCon()拿到的连接既没有放在try-with-resources中管理,也没有手动调用close,如果你没有用数据库连接池、而是直接创建的物理连接,会出现严重的连接泄漏问题。如果DB.getCon()返回的是全局共享的单例连接,多线程场景下还会出现事务、数据混乱的并发问题。
- 你的
问题2:当前try-catch块的使用方式是否存在问题?
存在多个明显问题:
- finally块逻辑不合理:你在finally块中判断
loadFailed为true就直接return,会直接吞掉所有未捕获的异常、也会中断正常的资源清理流程。另外你catch块仅打印了一句简单提示,没有输出异常堆栈,出现问题时完全无法定位是SQL语法错误、连接故障还是参数错误。 - 事务操作逻辑错误:第一个查询块中一上来就执行
rollback(),此时还没有执行任何数据库修改操作,rollback完全无意义;且全局共享连接的情况下,你随意commit/rollback会影响其他线程的数据库操作。 - 查询结果判断缺失:执行
rsComps.next()直接取值,没有判断返回值:如果传入的companyId不存在,next()会返回false,此时直接调用getString会抛出SQLException,虽然被catch捕获,但逻辑上属于预期内的业务场景,应该单独判断处理。 - 代码缺陷:你在查询锚点的逻辑中调用了
qAnchors.getRS(),但你给出的DBQuery类中并没有定义这个方法,运行时会直接报错。
改进建议
最好让DBQuery类自己实现AutoCloseable接口,内部封装所有资源的生命周期,不要把内部资源暴露到类外,示例改造如下:
public class DBQuery implements AutoCloseable { private final String query; private PreparedStatement stmt; private ResultSet rs; private Connection con; public DBQuery (String query) throws SQLException { this.query = query; // 建议从连接池获取独立连接,不要使用全局共享连接 this.con = DB.getCon(); } public void prepare() throws SQLException { stmt = con.prepareStatement(query, Statement.RETURN_GENERATED_KEYS); } public ResultSet executeQuery() throws SQLException { if (rs != null) { rs.close(); } rs = stmt.executeQuery(); return rs; } public void setInt(int paramNum, int setVal) throws SQLException { stmt.setInt(paramNum, setVal); } public void setString(int paramNum, String setVal) throws SQLException { stmt.setString(paramNum, setVal); } public void commit() throws SQLException { con.commit(); } public void rollback() throws SQLException { con.rollback(); } @Override public void close() throws SQLException { // 按ResultSet→PreparedStatement→Connection的顺序释放资源 if (rs != null && !rs.isClosed()) { rs.close(); } if (stmt != null && !stmt.isClosed()) { stmt.close(); } if (con != null && !con.isClosed()) { // 连接池的场景下close()是归还连接到池,不是关闭物理连接 con.close(); } } }
改造后使用方式更简洁,资源管理更安全:
try (DBQuery qComps = new DBQuery("SELECT comp_name FROM comps WHERE id=?")) { qComps.prepare(); qComps.setInt(1, companyId); try (ResultSet rsComps = qComps.executeQuery()) { if (rsComps.next()) { _activeCompany.setCompanyName(rsComps.getString("comp_name")); } else { // 处理公司不存在的逻辑 } } } catch (SQLException e) { // 打印异常堆栈方便排查 e.printStackTrace(); loadFailed = true; }
内容的提问来源于stack exchange,提问作者Geoff L
相关产品推荐
相关产品推荐

