嵌套事务 – 回滚方案

A(){ con.begin; ......... ......... B(); ........ ........(con.rollback;) con.commit; } B{ con.begin; ....... ....... con.commit; } 

在上面的代码中,我在A()开始一个新的DB事务。 它成功执行了一些事务。 在那之后B()开始执行并且它也成功执行了一些事务,现在控制返回到A()。 此时会发生一些exception,我会进行回滚。 我想知道B()中成功的事务是否会回滚。

简短的回答,没有。 答案如下。

在Java中支持嵌套事务取决于各种变量。

支持JTA中的嵌套事务

首先,如果您使用的是JTA,则事务管理器最多可以支持嵌套事务。 如果尝试在已与事务关联的线程中启动新事务,则任何开始事务的尝试都可能导致事务管理器(不支持嵌套事务)抛出NotSupportedException。

从Java Transaction API 1.1规范:

3.2.1开始交易

TransactionManager.begin方法启动全局事务,并将事务上下文与调用线程相关联。 如果事务管理器实现不支持嵌套事务,则当调用线程已与事务关联时,TransactionManager.begin方法会发生NotSupportedException。

支持JDBC中的嵌套事务

JDBC 3.0引入了Savepoint类,它或多或少类似于数据库中保存点的概念。 必须使用返回Savepoint实例的Connection.setSavepoint()方法初始化保存点。 可以使用Connection.rollback(Savepoint svpt)方法在稍后的时间点回滚到此保存点。 当然,所有这些取决于您是否使用支持JDBC 3.0的驱动程序,该驱动程序支持保存点的设置并回滚到它们。

自动提交的影响

默认情况下,获取的所有连接都设置为自动提交,除非JDBC驱动程序在此前面存在明显偏差。 此function(如果启用)会自动排除嵌套事务的范围,因为通过连接在数据库中进行的所有更改都会在执行时自动提交。

如果禁用自动提交function,并选择显式提交和回滚事务,则提交事务始终会提交连接执行的所有更改,直到该时间点为止。 请注意,为提交选择的更改不能由程序员定义 – 直到该瞬间的所有更改都被选择用于提交,无论它们是在一种方法还是另一种方法中执行。 唯一的出路是定义保存点,或者破解你的方式通过JDBC驱动程序 – 驱动程序通常会提交由与线程关联的连接执行的所有更改,因此启动新线程(这很糟糕)并在其中获取新连接,经常给你一个新的交易背景。

您可能还想检查框架如何为嵌套事务提供支持,特别是如果您与JDBC API隔离或自行启动新的JTA事务。


基于以上关于如何在各种场景中实现嵌套事务支持的描述,似乎代码中的回滚将回滚与Connection对象关联的所有更改。

这看起来像是糟糕的交易管理我害怕。 如果你处理从调用者到A和B的提交和回滚会很好。

 A() { //business code A B(); //more business code A } B() { //business code B } DoA() { try { con.begin(); A(); con.commit(); } catch(Exception e) { con.rollback(); } } DoB() { try { con.begin(); B(); con.commit(); } catch(Exception e) { con.rollback(); } } 

根据您的代码,在A()中您开始交易。 然后跳转到B(),再次启动事务,然后将提交所有先前的事务。 然后在B()结束时,显式提交事务。 此时,您的所有代码都已提交。 现在代码返回A()并处理剩余的代码。 如果发生exception,只会回滚B()调用后的这一部分。

您可以在Postgres 8及更高版本中使用Java.SQL的内置SavePoint函数。

 Connection conn = null; Savepoint save = null; DatabaseManager mgr = DatabaseManager.getInstance(); try { conn = mgr.getConnection(); proc = conn.prepareCall("{ call writeStuff(?, ?) }"); //Set DB parameters proc.setInt(1, stuffToSave); proc.setString(2, moreStuff); //Set savepoint here: save = conn.setSavepoint(); //Try to execute the query proc.execute(); //Release the savepoint, otherwise buffer memory will be eaten conn.releaseSavepoint(save); } catch (SQLException e) { //You may want to log the first one only. //This block will attempt to rollback try { //Rollback to the Savepoint of prior transaction: conn.rollback(save); } catch (SQLException e1) { e1.printStackTrace(); } } 

发生SQLexception时,当前事务将回滚到SavePoint,并且可能会发生其余事务。 如果没有回滚,后续事务将失败。