JDBC Statement.closeOnCompletion() 应该在下次执行时关闭语句吗?
JDBC Statement.closeOnCompletion() should close statement on next execution?
JDBC API 有以下与 ResultSet 相关的注释:
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.
在JDBC4.3规范中:
13.1.4 Closing Statement Objects
Closing a Statement object will close and invalidate any instances of ResultSet produced by that Statement object.
至此就清楚了,关闭一个statement对象,应该关闭ResultSet
Statement 的 JavaDoc 有这样的注释:
All execution methods in the Statement interface implicitly close a
current ResultSet object of the statement if an open one exists.
现在,问题是应该如何表现Statement.closeOnCompletion()?
Specifies that this Statement will be closed when all its dependent
result sets are closed. If execution of the Statement does not produce
any result sets, this method has no effect.
Note: ... However, a call to closeOnCompletion does effect both the subsequent
execution of statements, and statements that currently have open,
dependent, result sets.
声明应该允许重新执行?或者重新执行应该在第二次执行时关闭语句?
以测试为例:
@Test
public void testCloseOnCompletionMultipleExecutionResultSets() throws SQLException {
Statement statement = conn.createStatement();
ResultSet rs1 = statement.executeQuery("SELECT 1");
assertFalse("rs1 should be open", rs1.isClosed());
statement.closeOnCompletion();
// Should the second execution throw an SQLException with "Statement closed"?
// or it should work and the statement be closed until the second rs is closed?
ResultSet rs2 = statement.executeQuery("SELECT 2"); // fail or not?
assertTrue("rs1 should be closed by rs2", rs1.isClosed());
assertFalse("rs2 should be open", rs2.isClosed());
assertFalse("statement should be open", statement.isClosed());
rs2.close(); // Only close the statement here since is the last rs open.
assertTrue("statement should be closed", statement.isClosed());
}
通过调用 closeOnCompletion()
表示您希望语句在关闭其最后一个结果集('completes' 执行)后关闭,因此语句将在执行完成后自行关闭.对于非结果集生成语句,它没有作用(你应该自己调用 close()
)。对于生成结果集的语句,一旦关闭最后一个结果集,执行就完成了。主要用例是当您 return 要处理的结果集,但不想跟踪其语句时。
假设对同一语句的另一次执行将关闭该语句中任何打开的结果集,启用 closeOnCompletion
,结果将自动关闭该语句。
发出 closeOnCompletion
信号将是用户错误,但随后通过执行另一个语句继续使用该语句,因此引发 SQLException
是合适的。
顺便说一句,正文
However, a call to closeOnCompletion
does effect both the subsequent
execution of statements, and statements that currently have open,
dependent, result sets.
意味着如果你调用closeOnCompletion
它将影响下一次产生结果集的执行,或者如果你当前有一个结果集打开,则当前执行。
Lance Andersen(JDBC 规范负责人)和 Douglas Surber(JSR-221JDBC代表Oracle的专家组成员),具体来说:
The overall intent was to deal with code similar to:
ResultSet rs = foo();
while(rs.next() {
/*do something */
}
rs.close();
public ResultSet foo() {
Statement stmt = con.createStatement();
stmt.closeOnCompletion();
ResultSet rs = stmt.executeQuery(aQuery);
return rs
}
We did spend a lot of time on this back in 2009 and took quite a bit
of time to reach agreement on the current wording. However, it does
look like there is the potential for some additional word smithing.
(安徒生,https://mail.openjdk.java.net/pipermail/jdbc-spec-discuss/2021-February/000542.html)
I wrote the original proposal. The intent was to handle the case Lance
described. It was not intended to allow multiple executions of the
Statement. So while the language may not be as clear as it needs to
be, the case Filipe described should throw on the second execution of
the Statement. At least that was my intent.
(道格拉斯·瑟伯,https://mail.openjdk.java.net/pipermail/jdbc-spec-discuss/2021-February/000543.html)
As Douglas points out at the time of the discussion, the JDBC EG
consensus was the Statement would be closed. If applications did not
want this behavior they should not call Statement::closeOnCompletion
and if they were not sure if the method had been invoked, they could
always call Statement::isCloseOnCompletion to make a decision
programatically.
This area as you are aware is messy enough and the intent was to
address a common use case which lead to common issues for applications
and not further complicate things.
(安徒生,https://mail.openjdk.java.net/pipermail/jdbc-spec-discuss/2021-February/000548.html)
听起来这个问题是从 'someone implementing JDBC' 的相当奇特的立场提出的,而不是以用户的身份提出的。感谢您为维护 postgres JDBC 绑定所做的努力:)
@MarkRotteveel 在这里提供了很多背景信息。不过还有更重要的一点:
来自 javadoc:
from Statement.closeOnCompletion
: Specifies that this Statement will be closed when all its dependent result sets are closed.
from Statement.getResultSet
: Retrieves the current result as a ResultSet object.
这些似乎是相互排斥的:一个建议单个语句可以同时激活多个结果集,另一个建议 当前结果与ResultSet 本身的 javadoc(当生成它的 Statement 对象关闭时,ResultSet 对象自动关闭,重新执行,或用于检索下一个结果多个结果的序列。) 这表明最多只能有一个结果集。
我猜第二种解释更正确;它不仅由文档支持,而且由方法结构支持(假设有一个 getResultSet
方法没有 return 结果集数组,即使没有文档也有一些 'proof'只能有一个的概念),并在更多地方与更多 javadoc 相结合。
但是,有一种方法可以解释这一点,避免不得不用 openjdk.org 提交有关此冲突文档的错误:JDBC 为实现者添加自定义方法留出了空间 确实分拆了多个结果集,所有结果集都处于活动状态,尽管这意味着 getResultSet()
应该 return 的内容仍然是个谜。我很确定 postgres JDBC 驱动程序没有任何这样的方法,这使它成为一个有争议的问题。
由此得出一个简单的结论:
如果新的结果集是从标记为 close on completion 并且之前已经创建过结果集的语句创建的,则:
- 之前的结果集必须关闭。
- 然后 [A] 然后关闭语句,因此必须抛出 SQLException:尝试执行关闭的语句,--或--
- [B]由于隐式关闭了原来的resultset,语句未关闭,新创建的ResultSet现在有属性的: “如果你关闭我,你就关闭了声明”。在旧的 ResultSet 上调用
close()
应该是空操作,并且如果这是选择的路由,则 不应该 关闭语句:它不再是活动结果集,因此javadoc 很清楚,(重新)关闭这个与 close on completion 函数无关。
文档对我来说似乎不清楚这两个选项中的哪一个是正确的,但我认为这并不重要:这显然是程序员想要的行为 (B),或者是异常是不可避免的,并且程序员将很快收到通知,这种从 close on complete 语句中旋转多个结果集的情况永远不会起作用。
在实践中,这一点大概几乎完全没有实际意义:在像 JDBI/JOOQ 和 try-with-resources 这样的框架之间,close on complete 只有一个真正有用的功能:允许代码 return 从新创建的语句对象创建的 ResultSet,并且 return 将关闭它的责任转移给调用者。
那个来电者可以然后做:
resultSet.getStatement().execute(...);
但这将是恶意的白痴:鉴于您不知道它是什么类型的语句,您不知道您可以用它做什么(普通简 Statement
允许 execute(String)
,但是 PreparedStatement
被明确指定为在您尝试调用该方法时立即抛出。'just throw' 是非常好的行为,这已经足够奇怪了。
您问题的答案必须是“否是”。
在 JDBC 专家提供的说明后,我正在更改我的原始回复。
因此,closeOnCompletion
确实必须关闭语句,而不管结果集是如何关闭的,即由用户显式关闭或在后续执行中使用语句时隐式关闭,并且因此必须抛出异常。
不过,还有一个问题有待澄清。该方法的 JavaDoc 说:
Specifies that this Statement will be closed when all its dependent result sets are closed. If execution of the Statement does not produce any result sets, this method has no effect.
第一句没有疑问,但是第二句不太符合原意,即无论是否产生结果集are/have,都不允许重新执行这条语句与否。
这将我们引向另一个极端情况:如果我们调用 closeOnCompletion()
在 语句执行(一次或多次)并且没有结果集时会发生什么从以前的执行或所有结果集已经关闭?规范中的注释说:
However, a call to closeOnCompletion
does effect both the subsequent execution of statements, and statements that currently have open, dependent, result sets.
所以,可能是语句处于“重置”状态,就像新创建的语句一样,有可能再执行一次;或者它必须在调用 closeOnCompletion()
时立即关闭,就好像它是在前一个 execute
之前或在关闭最后一个结果集之前调用的一样。
如果有其他说明,我会添加更多相关细节。
JDBC API 有以下与 ResultSet 相关的注释:
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.
在JDBC4.3规范中:
13.1.4 Closing Statement Objects
Closing a Statement object will close and invalidate any instances of ResultSet produced by that Statement object.
至此就清楚了,关闭一个statement对象,应该关闭ResultSet
Statement 的 JavaDoc 有这样的注释:
All execution methods in the Statement interface implicitly close a current ResultSet object of the statement if an open one exists.
现在,问题是应该如何表现Statement.closeOnCompletion()?
Specifies that this Statement will be closed when all its dependent result sets are closed. If execution of the Statement does not produce any result sets, this method has no effect.
Note: ... However, a call to closeOnCompletion does effect both the subsequent execution of statements, and statements that currently have open, dependent, result sets.
声明应该允许重新执行?或者重新执行应该在第二次执行时关闭语句?
以测试为例:
@Test
public void testCloseOnCompletionMultipleExecutionResultSets() throws SQLException {
Statement statement = conn.createStatement();
ResultSet rs1 = statement.executeQuery("SELECT 1");
assertFalse("rs1 should be open", rs1.isClosed());
statement.closeOnCompletion();
// Should the second execution throw an SQLException with "Statement closed"?
// or it should work and the statement be closed until the second rs is closed?
ResultSet rs2 = statement.executeQuery("SELECT 2"); // fail or not?
assertTrue("rs1 should be closed by rs2", rs1.isClosed());
assertFalse("rs2 should be open", rs2.isClosed());
assertFalse("statement should be open", statement.isClosed());
rs2.close(); // Only close the statement here since is the last rs open.
assertTrue("statement should be closed", statement.isClosed());
}
通过调用 closeOnCompletion()
表示您希望语句在关闭其最后一个结果集('completes' 执行)后关闭,因此语句将在执行完成后自行关闭.对于非结果集生成语句,它没有作用(你应该自己调用 close()
)。对于生成结果集的语句,一旦关闭最后一个结果集,执行就完成了。主要用例是当您 return 要处理的结果集,但不想跟踪其语句时。
假设对同一语句的另一次执行将关闭该语句中任何打开的结果集,启用 closeOnCompletion
,结果将自动关闭该语句。
发出 closeOnCompletion
信号将是用户错误,但随后通过执行另一个语句继续使用该语句,因此引发 SQLException
是合适的。
顺便说一句,正文
However, a call to
closeOnCompletion
does effect both the subsequent execution of statements, and statements that currently have open, dependent, result sets.
意味着如果你调用closeOnCompletion
它将影响下一次产生结果集的执行,或者如果你当前有一个结果集打开,则当前执行。
Lance Andersen(JDBC 规范负责人)和 Douglas Surber(JSR-221JDBC代表Oracle的专家组成员),具体来说:
The overall intent was to deal with code similar to:
ResultSet rs = foo(); while(rs.next() { /*do something */ } rs.close(); public ResultSet foo() { Statement stmt = con.createStatement(); stmt.closeOnCompletion(); ResultSet rs = stmt.executeQuery(aQuery); return rs }
We did spend a lot of time on this back in 2009 and took quite a bit of time to reach agreement on the current wording. However, it does look like there is the potential for some additional word smithing.
(安徒生,https://mail.openjdk.java.net/pipermail/jdbc-spec-discuss/2021-February/000542.html)
I wrote the original proposal. The intent was to handle the case Lance described. It was not intended to allow multiple executions of the Statement. So while the language may not be as clear as it needs to be, the case Filipe described should throw on the second execution of the Statement. At least that was my intent.
(道格拉斯·瑟伯,https://mail.openjdk.java.net/pipermail/jdbc-spec-discuss/2021-February/000543.html)
As Douglas points out at the time of the discussion, the JDBC EG consensus was the Statement would be closed. If applications did not want this behavior they should not call Statement::closeOnCompletion and if they were not sure if the method had been invoked, they could always call Statement::isCloseOnCompletion to make a decision programatically.
This area as you are aware is messy enough and the intent was to address a common use case which lead to common issues for applications and not further complicate things.
(安徒生,https://mail.openjdk.java.net/pipermail/jdbc-spec-discuss/2021-February/000548.html)
听起来这个问题是从 'someone implementing JDBC' 的相当奇特的立场提出的,而不是以用户的身份提出的。感谢您为维护 postgres JDBC 绑定所做的努力:)
@MarkRotteveel 在这里提供了很多背景信息。不过还有更重要的一点:
来自 javadoc:
from
Statement.closeOnCompletion
: Specifies that this Statement will be closed when all its dependent result sets are closed.
from
Statement.getResultSet
: Retrieves the current result as a ResultSet object.
这些似乎是相互排斥的:一个建议单个语句可以同时激活多个结果集,另一个建议 当前结果与ResultSet 本身的 javadoc(当生成它的 Statement 对象关闭时,ResultSet 对象自动关闭,重新执行,或用于检索下一个结果多个结果的序列。) 这表明最多只能有一个结果集。
我猜第二种解释更正确;它不仅由文档支持,而且由方法结构支持(假设有一个 getResultSet
方法没有 return 结果集数组,即使没有文档也有一些 'proof'只能有一个的概念),并在更多地方与更多 javadoc 相结合。
但是,有一种方法可以解释这一点,避免不得不用 openjdk.org 提交有关此冲突文档的错误:JDBC 为实现者添加自定义方法留出了空间 确实分拆了多个结果集,所有结果集都处于活动状态,尽管这意味着 getResultSet()
应该 return 的内容仍然是个谜。我很确定 postgres JDBC 驱动程序没有任何这样的方法,这使它成为一个有争议的问题。
由此得出一个简单的结论:
如果新的结果集是从标记为 close on completion 并且之前已经创建过结果集的语句创建的,则:
- 之前的结果集必须关闭。
- 然后 [A] 然后关闭语句,因此必须抛出 SQLException:尝试执行关闭的语句,--或--
- [B]由于隐式关闭了原来的resultset,语句未关闭,新创建的ResultSet现在有属性的: “如果你关闭我,你就关闭了声明”。在旧的 ResultSet 上调用
close()
应该是空操作,并且如果这是选择的路由,则 不应该 关闭语句:它不再是活动结果集,因此javadoc 很清楚,(重新)关闭这个与 close on completion 函数无关。
文档对我来说似乎不清楚这两个选项中的哪一个是正确的,但我认为这并不重要:这显然是程序员想要的行为 (B),或者是异常是不可避免的,并且程序员将很快收到通知,这种从 close on complete 语句中旋转多个结果集的情况永远不会起作用。
在实践中,这一点大概几乎完全没有实际意义:在像 JDBI/JOOQ 和 try-with-resources 这样的框架之间,close on complete 只有一个真正有用的功能:允许代码 return 从新创建的语句对象创建的 ResultSet,并且 return 将关闭它的责任转移给调用者。
那个来电者可以然后做:
resultSet.getStatement().execute(...);
但这将是恶意的白痴:鉴于您不知道它是什么类型的语句,您不知道您可以用它做什么(普通简 Statement
允许 execute(String)
,但是 PreparedStatement
被明确指定为在您尝试调用该方法时立即抛出。'just throw' 是非常好的行为,这已经足够奇怪了。
您问题的答案必须是“否是”。
在 JDBC 专家提供的说明后,我正在更改我的原始回复。
因此,closeOnCompletion
确实必须关闭语句,而不管结果集是如何关闭的,即由用户显式关闭或在后续执行中使用语句时隐式关闭,并且因此必须抛出异常。
不过,还有一个问题有待澄清。该方法的 JavaDoc 说:
Specifies that this Statement will be closed when all its dependent result sets are closed. If execution of the Statement does not produce any result sets, this method has no effect.
第一句没有疑问,但是第二句不太符合原意,即无论是否产生结果集are/have,都不允许重新执行这条语句与否。
这将我们引向另一个极端情况:如果我们调用 closeOnCompletion()
在 语句执行(一次或多次)并且没有结果集时会发生什么从以前的执行或所有结果集已经关闭?规范中的注释说:
However, a call to
closeOnCompletion
does effect both the subsequent execution of statements, and statements that currently have open, dependent, result sets.
所以,可能是语句处于“重置”状态,就像新创建的语句一样,有可能再执行一次;或者它必须在调用 closeOnCompletion()
时立即关闭,就好像它是在前一个 execute
之前或在关闭最后一个结果集之前调用的一样。
如果有其他说明,我会添加更多相关细节。