0

我已经实现了这个 JDBC 代码,并且在周末我们遇到了一个问题,即连接可能会泄漏,并且数据库功能停止工作。虽然我不确定这是否是问题所在,但我想通过 Stack Overflow 社区传递它,看看是否有任何代码确实容易泄漏。我相信我已经通过 Try with resources 缩小了所有范围,但也许我错过了一些东西。所有帮助表示赞赏。

 public ArrayList<LinkedHashMap> runReportQuery(final DataSource dataSource, final String asms, final String country)  {    

      final ArrayList<LinkedHashMap> reportList = new ArrayList<>();

      this.executeQuery(dataSource, "select * from table where id =5", "customerReportType", true,  reportList);


            return reportList;
        }
        private void executeQuery(final DataSource queryDataSource, final String sql, final String reportType,
                                  final Boolean isMarketSpecific,  final ArrayList<LinkedHashMap> reportList ){

            try(Connection conn = queryDataSource.getConnection();
                PreparedStatement ps = this.createPreparedStatement(conn, isMarketSpecific, sql);
                ResultSet rs = ps.executeQuery();
            ) {
                while(rs.next()){
                    final LinkedHashMap<String, String> reportMap = new LinkedHashMap<>();
                    //Creating report string that adds columns and respective values to report - This way we do not need to deal with DTO object creation logic
                    String reportString=  "";
                    //Iterate through each column, add column and respective data.
                    for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++)
                        reportString = reportString + rs.getMetaData().getColumnLabel(i) + ": " + rs.getString(i) +"!$";
                    reportMap.put("reportItem", reportType + "!$"+  reportString);
                    reportList.add(reportMap);
                }
            }
            catch (final SQLException e){
                LOG.info("SqlException Occured", e);
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        }

        private PreparedStatement createPreparedStatement(final Connection con, final boolean isMarketSpecific, final String sql) throws SQLException {
            final PreparedStatement statement = con.prepareStatement(sql);
            if(isMarketSpecific)
            {
                statement.setString(1, this.asms);
                statement.setString(2, this.country);
            }
            return statement;
        }
4

2 回答 2

3

我认为此代码中的某些异常可能会产生连接泄漏的可能性很小:

if(isMarketSpecific)
{
       statement.setString(1, this.asms);
       statement.setString(2, this.country);
}

由于PreparedStatement已经获得了 ,但没有分配给ps,因此它不会被 try-with-resources 关闭。一些驱动程序关闭 上的每个依赖资源connection.close(),但其他驱动程序要求您显式地显示close()每个资源。这些驱动程序可能会泄漏。

于 2017-09-05T13:55:32.687 回答
1

我同意@gpeche 的诊断。你可以像这样修复它:

private PreparedStatement createPreparedStatement(final Connection con, 
           final boolean isMarketSpecific, final String sql) 
           throws SQLException {
    final PreparedStatement statement = con.prepareStatement(sql);
    try {
        if (isMarketSpecific) {
            statement.setString(1, this.asms);
            statement.setString(2, this.country);
        }
        return statement;
    } catch (SQLException | RuntimeException | Error e) {
        try {
            statement.close();
        catch (SQLException e) {
            // squash
        }
        throw e;
    }
}

但正如你所看到的,这非常冗长。所以我认为更好的解决方案是重组executeQuery以使用嵌套的 try-with-resource 语句;例如

try (Connection conn = queryDataSource.getConnection();
    PreparedStatement ps = con.prepareStatement(sql)) {
    if (isMarketSpecific) { // This could be a separate method ...
        statement.setString(1, this.asms);
        statement.setString(2, this.country);
    }
    try (ResultSet rs = ps.executeQuery()) {
        while(rs.next()){
            // etc
        }
    }
}
catch (final SQLException e){
    // etc
}
于 2017-09-05T14:29:29.260 回答