尝试/尝试使用资源和Connection,Statement和ResultSet关闭

Jos*_* D. 13 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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

3)这段代码是否正确?我认为我的教授不喜欢这种方式,因为没有明确关闭ResultSet,但是她告诉我她很好,只要在文档中很清楚所有关闭.您是否可以通过类似示例提供官方文档的任何链接,或者根据文档显示此代码没有问题?

Bas*_*que 12

TL;博士

  • 理论上,关闭语句会关闭结果集.
  • 实际上,一些错误的JDBC驱动程序实现无法做到这一点,这是出了名的.因此,教练的建议是她从硬敲门学校那里学到的.除非您熟悉可能为应用程序部署的每个JDBC驱动程序的每个实现,否则请使用try-with-resources自动关闭JDBC工作的每个级别,例如语句和结果集.

使用try-with-resources语法

您的所有代码都没有完全使用try-with-resources.在尝试与-资源的语法,声明和你的实例Connection,PreparedStatementResultSet括号,前括号.

虽然ResultSet在上一个代码示例中未明确关闭,但在其语句关闭时间接关闭.但是如下所述,由于JDBC驱动程序错误,它可能无法关闭.

AutoCloseable

任何此类实现的对象AutoCloseable都将自动close调用其方法.所以不需要那些finally条款.

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

你怎么知道哪些对象是可以自动关闭的,哪些不是?查看他们的类文档,看看它是否声明AutoCloseable为超级接口.相反,请参阅JavaDoc页面以AutoCloseable获取所有捆绑子接口和实现类的列表(实际上是几十个).例如,对于SQL工作中,我们看到Connection,Statement,PreparedStatement,ResultSet,和RowSet都自动关闭的,但DataSource并非如此.

请参阅Oracle Tutorial,try-with-resources语句.

代码示例

您的上一个代码示例接近良好,但应该包含DataSource在try-with-resources语句中以自动关闭.

引用DataSourceJavaDoc:

当生成它的Statement对象关闭,重新执行或用于从多个结果序列中检索下一个结果时,ResultSet对象将自动关闭.

正如你的老师一直在暗示的那样,一些JDBC驱动程序中存在严重的缺陷,这些缺陷未能达到JDBC规范的承诺,即关闭ResultSetResultSetResultSet关闭时.许多程序员养成了Statement明确关闭每个对象的习惯.

现在使用try-with-resources语法可以更轻松地完成这项额外任务.在实际工作中,您可能会尝试围绕所有PreparedStatement对象,例如ResultSet反正.所以我自己的观点是:为什么不尝试使用资源+其他?没有伤害,使您的代码更自我记录您的意图,如果您的代码遇到其中一个错误的JDBC驱动程序,它可能会有所帮助.唯一的成本是一对parens,假设你有一个尝试捕获 - 否则就位.

Oracle Tutorial中所述,AutoCloseable一起声明的多个对象将以相反的顺序关闭.

提示:try-with-resources语法允许在最后声明的资源项上使用可选分号.我将分号作为习惯包括在内,因为它能很好地读取,并且是一致的,并且有助于剪切和粘贴编辑.我把它包括在你的ResultSet线上.

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.
    }
}
Run Code Online (Sandbox Code Playgroud)

我想这种工作有一种更优雅的语法,可能是在未来的编程语言中发明的.但是现在,我们已经尝试了资源,我确实很愉快地使用它.虽然try-with-resources并不是非常优雅,但它比旧语法有了很大的改进.

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

更新:Java 9

现在,在Java 9中,您可以在try-with-resources 之前初始化资源.

  • 在2018年遇到了这个答案,这是最精美的答案之一。我遇到了类似的问题,由于有了这个答案,我的想法很明确。 (3认同)

Nat*_*hes 5

JDBC代码的有趣之处在于,您正在编写的规范并不总是很清楚实现的兼容性。有许多不同的数据库和驱动程序,并且某些驱动程序的行为比其他驱动程序更好。这倾向于使人们在谨慎方面犯错,建议诸如明确关闭所有内容之类的事情。您可以在这里仅关闭连接就可以了。仅仅为了安全起见,关闭resultSet很难争论。您没有在这里指出正在使用的数据库或驱动程序,我不想在关于驱动程序的假设中对某些实现无效的硬编码。

按顺序关闭事物确实使您容易遇到可能引发异常并导致某些关闭被跳过的问题。您对此感到担心是正确的。

请注意,这是一个玩具示例。大多数实际代码使用连接池,其中调用close方法实际上并不关闭连接,而是将连接返回到池中。因此,一旦使用池,资源可能不会关闭。如果要更改此代码以使用连接池,则必须至少返回并关闭该语句。

同样,如果您反对这种冗长的用法,那么答案是将代码隐藏在使用策略,resultSet映射器,准备好的语句设置器等的可重用实用程序中。您将重新发明Spring JDBC。

说到这:Spring JDBC显式关闭所有内容(可能是因为它需要使用尽可能多的驱动程序,并且由于某些驱动程序的行为不当而不会引起问题)。


Mar*_*ano 1

这确实是尝试资源的主要动机。请参阅 Java教程作为参考。你的教授已经过时了。如果您想处理结果集问题,您始终可以将其包含在另一个 try-with-resources 语句中。