这个 JDBC 代码容易泄露吗?
Is this JDBC code prone to leaking?
我已经实现了这个 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;
}
我认为这段代码中的某些异常可能会产生连接泄漏:
if(isMarketSpecific)
{
statement.setString(1, this.asms);
statement.setString(2, this.country);
}
由于已经获取了PreparedStatement
,但没有赋值给ps
,所以不会被try-with-resources关闭。有些驱动程序会关闭 connection.close()
上的每个依赖资源,但其他驱动程序会要求您明确 close()
每个资源。这些驱动程序可能会泄漏。
我同意@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
}
我已经实现了这个 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;
}
我认为这段代码中的某些异常可能会产生连接泄漏:
if(isMarketSpecific)
{
statement.setString(1, this.asms);
statement.setString(2, this.country);
}
由于已经获取了PreparedStatement
,但没有赋值给ps
,所以不会被try-with-resources关闭。有些驱动程序会关闭 connection.close()
上的每个依赖资源,但其他驱动程序会要求您明确 close()
每个资源。这些驱动程序可能会泄漏。
我同意@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
}