嵌套事务 - 回滚方案

时间:2010-09-24 11:25:30

标签: java postgresql

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

    B{
    con.begin;
    .......
    .......
    con.commit;
    }

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

4 个答案:

答案 0 :(得分:11)

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

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

支持JTA中的嵌套事务

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

来自Java Transaction API 1.1规范:

  

3.2.1开始交易

     

TransactionManager.begin方法启动   全球交易和员工   与事务上下文   调用线程。如果是交易   经理实施没有   支持嵌套事务,   TransactionManager.begin   methodthrowsthe NotSupportedException   当调用线程已经存在时   与交易相关联。

支持JDBC中的嵌套事务

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

自动提交的影响

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

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

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


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

答案 1 :(得分:0)

我害怕看起来糟糕的交易管理。如果你处理从调用者到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();
  }
}

答案 2 :(得分:0)

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

答案 3 :(得分:0)

您可以在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();
    }
}

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