确保关闭数据库对象的首选方法是什么?

时间:2015-06-12 22:13:41

标签: java jdbc

我对数据库交互方面的最佳实践感到好奇。我一直在使用一种模式,我相信它可以确保在完成它们之后关闭所有适当的对象。然而,一位同事最近重新编写了我的代码,其中包括“确保我们始终关闭数据库对象”的注释。由于某种原因,我需要知道一种模式是否比另一种模式“更好”。我以某种方式错误使用的模式是什么?一种模式比另一种模式有优势吗?

我一直关注的模式:

public void doStuff() {
    try {
        final Connection connection = this.getConnection();

        try {
            final PreparedStatement ps = connection.prepareStatement("SELECT COLA, COLB FROM TBL WHERE COLC = ?");

            try {
                ps.setString(1, "asdf");
                final ResultSet rs = ps.executeQuery();
                try {
                    // get data from rs 
                } finally {
                    rs.close();
                }
            } finally {
                ps.close();
            }
        } finally {
            connection.close();
        }
    } catch (SQLException e) {
        // do something with the error
    }
}

我的同事修改我的代码的模式:

public void doStuff() {
    Connection connection = null;
    PreparedStatement ps = null;
    ResultSet rs = null;
    try {
        connection = this.getConnection();
        ps = connection.prepareStatement("SELECT COLA, COLB FROM TBL WHERE COLC = ?");
        ps.setString(1, "asdf");
        rs = ps.executeQuery();
        // get data from rs
    } finally {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                // do something with the error
            }

        }
        if (ps!= null) {
            try {
                ps.close();
            } catch (SQLException e) {
                // do something with the error
            }
        }
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                // do something with the error
            }
        }

    }
}

4 个答案:

答案 0 :(得分:5)

如果您使用的是Java 6或更早版本,请使用后者,因为它更易于阅读和维护。请注意,后者可以通过一些重构来改进,以便在每次调用try-catch方法时处理繁琐的close

如果您使用的是Java 7或更高版本,请使用try-with-resources

try (Connection con = ...;
     PreparedStatement pstmt = ...) {
    pstmt.setXyz(...);
    ResultSet rs = pstmt.executeQuery();
    //read data from resultset
    //and then close it
    rs.close();
} catch (Exception e) {
    //handle the exception properly...
}

如果您想确保关闭ResultSet,可以使用嵌套try-with-resources

try (Connection con = ...;
     PreparedStatement pstmt = ...) {
    pstmt.setXyz(...);
    try(ResultSet rs = pstmt.executeQuery()) {
        //read data from resultset
    }
} catch (Exception e) {
    //handle the exception properly...
}

答案 1 :(得分:1)

后者更容易阅读;深层筑巢很难说。

我更喜欢closeables周围的安全包装器,例如,如果closeable为null,它们什么都不做。这也使主线代码更容易阅读。

当然,Luigi的回答在Java 7中最有意义。

答案 2 :(得分:0)

将数据库资源的关闭抽象为专用的管理器对象通常更简单,更简洁,该对象将包含任何可能被抛出的NPE等。

作为开源项目OpenFire的一部分存在一个写得很好的文章:

https://github.com/igniterealtime/Openfire/blob/master/src/java/org/jivesoftware/database/DbConnectionManager.java#L243

此DbConnectionManager中的示例辅助方法:

public static void closeResultSet(ResultSet rs) {
    if (rs != null) {
        try {
                rs.close();
            }
        catch (SQLException e) {
            Log.error(e.getMessage(), e);
        }
    }
}

因此,在finally块中,您只需将资源传递回管理器,它就会处理丑陋的逻辑以测试空值并捕获异常等。

像:

Connection con = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
    con = DbConnectionManager.getConnection();
    ps = con.prepareStatement(yourStatement);
    rs = ps.executeQuery();
    if (rs != null) {
        while (rs.next()) {
            // do stuff
        }
    }
} catch (SQLException e) {
    LOG.error(e.getMessage(), e);
} finally {
    DbConnectionManager.closeConnection(rs, ps, con);
}

答案 3 :(得分:-5)

模拟Go的延迟声明:D

try(Defer defer = new Defer())
{
    Connection connection = ...;
    defer.add( connection::close );

    ....

    Path tmpFile = ...;
    defer.add( ()->Files.delete(tmpFile) );

    ....

} // Defer.close() => executing registered actions, from last to first

如何实施Defer留给读者的练习:)