嵌套事务 - 回滚场景

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

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

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


相关链接:https://dev59.com/3nVC5IYBdhLWcg3wtzkQ 和 http://www.postgresql.org/docs/8.3/interactive/tutorial-transactions.html(搜索“savepoint”) - matt b
4个回答

11

简短回答:不支持。长回答如下。

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

JTA中对嵌套事务的支持

首先,如果你正在使用JTA,则取决于事务管理器是否支持嵌套事务。如果在已经关联了事务的线程中尝试启动新事务,则可能会由不支持嵌套事务的事务管理器(Transaction Manager)抛出NotSupportedException异常。

根据Java Transaction API 1.1规范:

 

3.2.1 启动事务

    

TransactionManager.begin方法启动全局事务,并将事务上下文与调用线程关联。如果Transaction Manager实现不支持嵌套事务,则当调用线程已经关联到事务时,TransactionManager.begin方法将引发NotSupportedException异常。

JDBC中对嵌套事务的支持

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

自动提交的影响

默认情况下,除非JDBC驱动程序明确偏离此前提,否则所有获得的连接都设置为自动提交。如果启用此功能,则数据库中通过连接进行的所有更改都会在执行时自动提交,这自动排除了具有嵌套事务的范围。

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

尤其是如果您与JDBC API或自己启动新的JTA事务隔离开来,您可能还想检查您的框架如何支持嵌套事务。


根据上述描述嵌套事务支持如何在各种场景中实现,似乎在您的代码中回滚将回滚与连接对象相关联的所有更改。


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异常时,当前事务将回滚到保存点,并且剩余的事务可以继续进行。如果没有回滚,后续的事务将会失败。


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();
  }
}

0
根据您的代码,在A()中开始了一个事务。然后跳转到B(),在那里再次开始事务,这将提交所有先前的事务。然后在B()的结尾,事务被明确提交。此时,您的所有代码都已提交。现在代码返回到A()并处理剩余的代码。如果出现异常,则仅回滚B()调用后的此部分。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接