java - Try/Try-with-resources 和 Connection、Statement 和 ResultSet 关闭

标签 java correctness try-with-resources

我最近和我的教授讨论了如何处理基本的 jdbc 连接方案。假设我们要执行两个查询,这就是他提出的

public void doQueries() throws MyException{
    Connection con = null;
    try {
        con = DriverManager.getConnection(dataSource);
        PreparedStatement s1 = con.prepareStatement(updateSqlQuery);
        PreparedStatement s2 = con.prepareStatement(selectSqlQuery);

        // Set the parameters of the PreparedStatements and maybe do other things

        s1.executeUpdate();
        ResultSet rs = s2.executeQuery();

        rs.close();
        s2.close();
        s1.close();
    } catch (SQLException e) {
        throw new MyException(e);
    } finally {
        try {
            if (con != null) {
                con.close();
            }
        } catch (SQLException e2) {
            // Can't really do anything
        }
    }
}

我不喜欢这种方法,我有两个问题:

1.A) 我认为,如果在我们做“其他事情”的地方或在 rs.close() 行中抛出任何异常或 s2.close()然后 s1方法结束时不会关闭。我是对的吗?

1.B) 教授一直要求我明确关闭 ResultSet(即使 Statement 文档明确表示它将关闭 ResultSet)她说 Sun 推荐它。有什么理由这样做吗?

现在这是我认为是同一件事的正确代码:
public void doQueries() throws MyException{
    Connection con = null;
    PreparedStatement s1 = null;
    PreparedStatement s2 = null;
    try {
        con = DriverManager.getConnection(dataSource);
        s1 = con.prepareStatement(updateSqlQuery);
        s2 = con.prepareStatement(selectSqlQuery);

        // Set the parameters of the PreparedStatements and maybe do other things

        s1.executeUpdate();
        ResultSet rs = s2.executeQuery();

    } catch (SQLException e) {
        throw new MyException(e);
    } finally {
        try {
            if (s2 != null) {
                s2.close();
            }
        } catch (SQLException e3) {
            // Can't do nothing
        }
        try {
            if (s1 != null) {
                s1.close();
            }
        } catch (SQLException e3) {
            // Can't do nothing
        }
        try {
            if (con != null) {
                con.close();
            }
        } catch (SQLException e2) {
            // Can't do nothing
        }
    }
}

2.A) 此代码正确吗? (是否保证方法结束时全部关闭?)

2.B)这是非常大和冗长的(如果有更多的语句会变得更糟)有没有更短或更优雅的方法来做到这一点而不使用try-with-resources?

最后这是我最喜欢的代码
public void doQueries() throws MyException{
    try (Connection con = DriverManager.getConnection(dataSource);
         PreparedStatement s1 = con.prepareStatement(updateSqlQuery);
         PreparedStatement s2 = con.prepareStatement(selectSqlQuery))
    {

        // Set the parameters of the PreparedStatements and maybe do other things

        s1.executeUpdate();
        ResultSet rs = s2.executeQuery();

    } catch (SQLException e) {
        throw new MyException(e);
    }
}

3)这个代码正确吗?我认为我的教授不喜欢这种方式,因为 ResultSet 没有明确的关闭,但她告诉我,只要在文档中很清楚一切都已关闭,她就可以接受。您能否提供任何带有类似示例的官方文档的链接,或者根据文档表明此代码没有问题?

最佳答案

tl;博士

  • 理论上关闭语句会关闭结果集。
  • 在实践中,众所周知,一些有缺陷的 JDBC 驱动程序实现未能做到这一点。因此,您的导师的建议是她从 Hard Knocks 学校学到的。除非您熟悉每个 JDBC driver 的每个实现可能会为您的应用部署,使用 try-with-resources自动关闭您 JDBC 的每一层诸如语句和结果集之类的工作。

  • 使用 try-with-resources 语法

    您的代码都没有完全使用 try-with-resources .在 try-with-resources 语法中,您声明并实例化您的 Connection , PreparedStatement , 和 ResultSet在括号中,在大括号之前。见 Tutorial by Oracle .

    而您的 ResultSet在您的最后一个代码示例中没有明确关闭,它应该在其语句关闭时间接关闭。但如下所述,它可能不会因为 JDBC 驱动程序错误而关闭。
    AutoCloseable
    任何实现 AutoCloseable 的此类对象将自动拥有他们的 close方法调用。所以不需要那些 finally条款。

    对于阅读本文的人文专业,是的,Java 团队拼错了“可关闭”。

    你怎么知道哪些对象是可自动关闭的,哪些不是?查看他们的类文档,看看它是否声明 AutoCloseable 作为 super 接口(interface)。相反,请参阅 the JavaDoc page for AutoCloseable 获取所有捆绑子接口(interface)和实现类的列表(实际上有几十个)。

    例如,对于 SQL 工作,我们看到 Connection , Statement , PreparedStatement , ResultSet , 和 RowSet 都是自动关闭的,但 DataSource 不是。这是有道理的,如 DataSource存储有关潜在资源(数据库连接)的数据,但本身不是资源。 DataSource永远不会“打开”,因此无需关闭。

    请参见 Oracle 教程,The try-with-resources Statement .

    代码示例

    你的最后一个代码示例已经接近好,但应该已经包装了 ResultSet在 try-with-resources 语句中自动关闭。

    报价 ResultSet Java文档:

    A ResultSet object is automatically closed when the Statement object that generated it is closed, re-executed, or used to retrieve the next result from a sequence of multiple results.



    正如您的老师所暗示的那样,某些 JDBC 驱动程序存在严重缺陷,未能兑现 JDBC 规范关闭 ResultSet 的 promise 。当其 StatementPreparedStatement关闭了。所以很多程序员养成了关闭每一个的习惯ResultSet明确的对象。

    现在使用 try-with-resources 语法可以更轻松地完成这项额外任务。在实际工作中,您可能会在所有 AutoCloseable 周围尝试其他方法。对象,例如 ResultSet无论如何。所以我自己的观点是:为什么不把它做成一个 try-with-resources + else?没有伤害,使您的代码更能自我记录您的意图,如果您的代码遇到那些有问题的 JDBC 驱动程序之一,它可能会有所帮助。 唯一的成本是一对括号 ,假设您无论如何都会有一个 try-catch-else 。

    Oracle Tutorial 中所述, 多个 AutoCloseable一起声明的对象将是 以相反顺序关闭 ,正如我们希望的那样。

    提示:try-with-resources 语法允许在最后声明的资源项上使用可选的分号。我把分号作为一种习惯,因为它对我的眼睛来说很好读,是一致的,并且便于剪切和粘贴编辑。我将它包含在您的 PreparedStatement s2 中线。
    public void doQueries() throws MyException{
        // First try-with-resources.
        try ( Connection con = DriverManager.getConnection( dataSource ) ;
              PreparedStatement s1 = con.prepareStatement( updateSqlQuery ) ;
              PreparedStatement s2 = con.prepareStatement( selectSqlQuery ) ;
        ) {
    
            … Set parameters of PreparedStatements, etc.
    
            s1.executeUpdate() ;
    
            // Second try-with-resources, nested within first.
            try (
                ResultSet rs = s2.executeQuery() ;
            ) {
                … process ResultSet
            } catch ( SQLException e2 ) {  
                … handle exception related to ResultSet.
            }
    
        } catch ( SQLException e ) {  
            … handle exception related to Connection or PreparedStatements.
        }
    }
    

    我想这种工作有一种更优雅的语法,可能会在 future 的编程语言中发明。但是现在,我们有 try-with-resources,我确实很乐意使用它。虽然 try-with-resources 并不完美,但它比旧语法有了很大的改进。

    顺便说一句,Oracle 建议使用 DataSource 获取连接的实现而不是 DriverManager 在您的代码中看到的方法。使用 DataSource在整个代码中,可以更轻松地切换驱动程序或切换到连接池。查看您的 JDBC 驱动程序是否提供了 DataSource 的实现。 .

    更新:Java 9

    现在在 Java 9 中,您可以在 try-with-resources 之前初始化资源。见 this article .这种灵活性在某些情况下可能很有用。

    关于java - Try/Try-with-resources 和 Connection、Statement 和 ResultSet 关闭,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22671697/

    相关文章:

    java - 在 Java 6 中模拟 try-with-resources 的最佳方法是什么?

    java - 在 nUnit 中,Hamcrest 的 Matchers.containsInAnyOrder(Matcher... matchers) 等价于什么?

    java - java中MapReduce中大量全局变量的存储

    java - Mockito 方法为使用的变量抛出空指针异常

    c - 带返回值的 Switch 语句——代码正确性

    java - 尝试使用 JedisPool 中的链接资源

    java - 对 CloseableHttpClient 使用 try-with-resource block 是否也会关闭返回的 CloseableHttpResponse?

    java - 如何使用 jackson 获取 json 响应?

    algorithm - 正式验证算法的正确性

    algorithm - 通过归纳法证明算法正确