我最近和我的教授讨论了如何处理基本的 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;博士
使用 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 。当其 Statement
或 PreparedStatement
关闭了。所以很多程序员养成了关闭每一个的习惯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/