SonarQube提示需关闭PreparedStatement但已在finally实现仍报错怎么办
问题解决方法
告警触发原因
你的现有代码存在明确的资源泄露风险,并非SonarQube误报:
PreparedStatement的初始化语句this.connection.prepareStatement(sql)、参数赋值语句preparedStatement.setString(1, id)都写在try块外部,如果这两行代码抛出SQLException,代码不会进入后续的try-finally逻辑,PreparedStatement资源完全不会被关闭- 执行查询得到的
ResultSet资源没有做任何关闭处理,同样存在资源泄露风险
最优解决方案
Java 7及以上版本中,JDBC的Connection、Statement、ResultSet都实现了AutoCloseable接口,直接使用try-with-resources语法即可自动处理资源关闭,完全符合SonarQube的规则要求,修改后代码如下:
public String getUserEmail(String id) throws SQLException { String emailAddress = null; String sql = "select email from my_table where id=?"; // 将PreparedStatement、ResultSet都放在try-with-resources声明中,执行结束会自动按逆序关闭资源 try (PreparedStatement preparedStatement = this.connection.prepareStatement(sql)) { preparedStatement.setString(1, id); try (ResultSet rs = preparedStatement.executeQuery()) { while (rs.next()) { emailAddress = rs.getString("email"); } } } catch(SQLException e) { throw new TuringClientException("Failed to getUserEmail. ", e); } return emailAddress; }
如果需要兼容Java 6及更早版本,必须手动处理资源的非空判断、异常捕获,写法如下:
public String getUserEmail(String id) throws SQLException { String emailAddress = null; String sql = "select email from my_table where id=?"; PreparedStatement preparedStatement = null; ResultSet rs = null; try { preparedStatement = this.connection.prepareStatement(sql); preparedStatement.setString(1, id); rs = preparedStatement.executeQuery(); while (rs.next()) { emailAddress = rs.getString("email"); } } catch(SQLException e) { throw new TuringClientException("Failed to getUserEmail. ", e); } finally { // 先关闭ResultSet,再关闭Statement,关闭操作单独捕获异常避免覆盖业务异常 if (rs != null) { try { rs.close(); } catch (SQLException e) { // 可按业务需求打印日志或忽略 } } if (preparedStatement != null) { try { preparedStatement.close(); } catch (SQLException e) { // 可按业务需求打印日志或忽略 } } } return emailAddress; }
两种方案修改后都可以消除SonarQube的告警,优先推荐使用try-with-resources的写法,代码更简洁且不会出现手动关闭的逻辑疏漏。
内容的提问来源于stack exchange,提问作者fri6aug
相关产品推荐
相关产品推荐

