嵌套事务

9 投票
3 回答
9239 浏览
提问于 2025-04-15 19:44

我想知道在一个事务里面再打开一个事务是否安全,或者说这样做是否被推荐?

我有一个方法:

def foo():
    session.begin
    try:
          stuffs
    except Exception, e:
         session.rollback()
         raise e
    session.commit()

还有一个方法在一个事务里面调用了第一个方法:

def bar():
    stuffs
    try:
         foo()   #<<<< there it is :)
         stuffs
    except Exception, e:
        session.rollback()
        raise e
    session.commit()

如果在foo方法里出现了异常,所有的操作都会被撤销吗?其他的操作会正常工作吗?谢谢!!

3 个回答

0

在PostgreSQL中,嵌套事务是可以正常工作的。

确实,你不会收到错误提示(只是一个警告),这点没错。但是,你不能只提交内层事务而回滚外层事务,外层事务也会回滚内层事务。

开始一个事务;

往x表里插入一个值('John');

开始一个内层事务; -- 这里会有警告!

往y表里插入一个值('Jane');

提交内层事务; -- 提交内层事务

回滚; -- 这会回滚两个插入操作,而不仅仅是第一个,也就是在"x"表里的那个

据我所知,Oracle是少数几个支持这种操作的数据库之一。

0

你不能这样做,PostgreSQL 不支持子事务。你可以考虑使用保存点,但那是另外一回事。

19

在SQLAlchemy中,有两种方法可以实现事务的嵌套。第一种是虚拟事务,SQLAlchemy会记录你发起了多少个开始事务的命令,只有在最外层的事务提交时,才会真正提交。而如果你需要回滚(撤销)操作,系统会立即执行回滚。因为这个事务是虚拟的,也就是说数据库并不知道你有嵌套的事务,所以在回滚之后,你不能对这个会话做任何操作,直到你把所有外层的事务也回滚掉。要使用虚拟事务,你需要在调用begin()时添加subtransactions=True这个参数。这个功能的设计是为了让你在一些可能相互调用的函数中使用事务控制,而不需要担心自己是否在一个事务中。为了让这个功能正常工作,你需要把会话配置为autocommit=True,并且在事务函数中总是调用session.begin(subtransactions=True)

第二种嵌套事务的方法是使用真实的嵌套事务。这是通过保存点(savepoints)来实现的。如果你回滚一个嵌套事务,那么在这个事务中所做的所有更改都会被撤销,但外层的事务仍然可以使用,外层事务所做的更改依然有效。要使用嵌套事务,你可以调用session.begin(nested=True)或者session.begin_nested()。并不是所有的数据库都支持嵌套事务。SQLAlchemy的测试库配置函数sqlalchemy.test.requires.savepoints对此支持情况有说明:

    emits_warning_on('mssql', 'Savepoint support in mssql is experimental and may lead to data loss.'),
    no_support('access', 'not supported by database'),
    no_support('sqlite', 'not supported by database'),
    no_support('sybase', 'FIXME: guessing, needs confirmation'),
    exclude('mysql', '<', (5, 0, 3), 'not supported by database')

在PostgreSQL中,SQLAlchemy的嵌套事务工作得很好。

撰写回答