今天早上我看到一个针对完全不同受众的交易问题,作为一个知名Java新闻网站上的第一条头条新闻出现。除了让我和我的同事们对错误和事务开怀大笑之外,它还触及了一个让我在酒吧打赌中赢得几杯啤酒的问题,并让学生在培训期间感到困惑。这个问题与以下(简化)代码相关

Connection con = DriverManager.getConnection(url, name, pwd);
con.setAutoCommit(false);
Statement st = con.prepareStatement("delete VITAL_DATA");
st.executeUpdate();
con.close();

假设VITAL_DATA在执行此代码之前包含数据,在调用con.close()?

之后,它是否仍然包含这些数据?

答案是:这取决于!

如果此代码针对Oracle数据库执行VITAL_DATA将不再包含数据,因为Oracle会隐式调用commit()如果您的连接有 剩余 的更改。

这时人们开始与我争论,说我疯了!不可能,因为所有相信事务的良性和其ACID属性的开发者都会说,在没有显式调用commit()时,不应该将任何内容提交到数据库——否则将是一种罪过。

我想Oracle喜欢犯罪,并且这一点也得到了文档记录。

/Page 3-14, Oracle9i JDBC Developers Guide and Reference/ 包含以下文本

If auto-commit mode is disabled and you close the connection
without explicitly committing or rolling back your last changes,
then an implicit <code>COMMIT</code> operation is executed.

我从一个Oracle技术专家那里听到,这种行为是旧OCI库的遗留问题——这是否是真的我不知道;但这对大多数我向其展示过这种行为的人来说确实是个惊喜(包括我自己第一次遇到这个问题时)。

在几年前发现这个问题后,我查阅了JDBC规范,看看谁应该为这种行为负责。

有趣的部分在于,我找不到关于 JDBC 中 close() 方法行为的任何信息,除了从 /JDBC 4.0/ 规范中找到的以下文本

When auto-commit is disabled, each transaction must be explicitly committed by
calling the Connection method commit or explicitly rolled back by calling the
Connection method rollback, respectively.

close() 方法的JavaDoc说明

Releases this Connection object's database and JDBC resources
immediately instead of waiting for them to be automatically released

对于一个天真地相信 ACID 的人,我会说 Oracle 在这个问题上是错的,但请注意规范只提到了事务的行为?它并没有明确说明JavaDoc不允许提交数据,只是说应该释放资源(它确实做到了!)

因此,从我的角度来看,Oracle 在这里处于边缘状态,但显然没有违反规范。请注意,这可能在其他数据库中也会发生,但在我使用过的其他主要数据库中从未发生过。

学到的教训?始终显式(或声明式)提交或回滚你的事务!


返回顶部