JDBC中的嵌套事务

时间:2014-06-11 10:35:51

标签: java jdbc transactions

假设想到一个场景:

function a(){
      create savepoint s1
      insert x1
      insert x2
      b()
      insert x4
     if we get an error rollback to s1
     else commit.
}

function b(){
      create savepoint s2
      insert x3
     if we get an error rollback to s2
     else commit
}

我的问题是假设我的b()传递没有错误,并且在插入x4时出现问题,所以我解雇了回滚。所以我的问题是它是否也将还原作为b()的一部分插入的东西,即x3。

2 个答案:

答案 0 :(得分:1)

方法b()根本不应该提交,commit()只应在一切完成后才允许。

您应该将场景修改为:

function a(){
    create savepoint s1
    insert x1
    insert x2
    b()
    insert x4
    if we get an error rollback to s1
    else commit.
}

function b(){
    create savepoint s2
    insert x3
    if we get an error rollback to s2
}

唯一的区别是删除b()中的提交分支。 Connection将保留保存点s2,直到回滚到s1或提交。

请注意,如果函数a()本身也是较大事务的一部分,那么else commit也应该被删除。如果a()是事务的开始,那么您应该回滚(而不是rollback to s1)。

对评论的回应

如果您希望这些可以独立调用或可组合,那么我建议您使用某种形式的事务管理器,并将其传递给方法调用。

说出

的内容
interface TransactionManager() {
    void commit();

    void rollback();

    TransactionManager subTransaction();
}

class RootTransactionManager() implements TransactionManager {
    private final Connection connection;

    RootTransactionManager(Connection connection) {
        this.connection = connection;
    }

    public void commit() {
        connection.commit();
    }

    public void rollback() {
        connection.rollback();
    }

    public TransactionManager subTransaction() {
        return new SubTransactionManager(this);
    }

    Connection getConnection() {
        return connection;
    }
}

class SubTransactionManager implements TransactionManager() {
    private final RootTransactionManager manager;
    private final SavePoint savePoint;

    SubTransactionManager (RootTransactionManager manager) {
        this.manager = manager;
        savePoint = manager.getConnection().setSavePoint();
    }

    public void commit() {
        // do nothing (semantics of Connection.releaseSavePoint not useful for nesting)
    }

    public void rollback() {
        connection.rollback(savePoint);
    }

    public TransactionManager subTransaction() {
        return new SubTransactionManager(manager);
    }
}

您可以使用它来实现您的方法:

void someOtherMethod() {
    Connection connection; // = ...
    a(new RootTransactionManager(connection));
}

void a(TransactionManager tm) {
    try {
        // do stuff
        b(tm.subTransaction);
        // do stuff
        tm.commit();
    } catch (SQLException ex) {
        tm.rollback();
    }
}

void b(TransactionManager tm) {
    try {
        // do stuff
        tm.commit();
    } catch (SQLException ex) {
        tm.rollback();
    }
}

这只是一个快速草图,如果我真的花了更多时间,我可能会想出一些完全不同的东西。

答案 1 :(得分:0)

它将在最后一个保存点之前还原事物,即不会还原插入X1,X2和b()的修改。

理想情况下,为了保持事务的原子性,我们不应该在它之间发出commit语句。只有在所有操作都成功完成后才会发出Commit()。但是,如果某些内容失败,则应发出回滚(通常在catch块中)。