3

为什么在子块中出现异常后 ActiveRecord 不回滚嵌套事务中的更改?

以下是示例:

1.


>> Client.transaction do
?>   Client.create(:name => 'Pavel')
>>   Client.transaction do
?>     Client.create(:name => 'Elena')
>>     raise ActiveRecord::Rollback
>>   end
>> end
=> nil
>> Client.all.map(&:name)
=> ["Pavel", "Elena"] # instead of []

2.


>> Client.transaction do
?>   Client.create(:name => 'Pavel')
>>   Client.transaction(:requires_new => true) do
?>     Client.create(:name => 'Elena')
>>     raise ActiveRecord::Rollback
>>   end
>> end
=> nil
>> Client.all.map(&:name)
=> ["Pavel", "Elena"] # instead of ["Pavel"]

谢谢。

Debian GNU/Linux 5.0.6;

红宝石 1.9.2;

Ruby on Rails 3.0.1;

SQLite 3.7.3。

4

2 回答 2

3

我有同样的问题,我可以完全复制你的结果。如果我在外部块中引发 ActiveRecord::Rollback,那么整个事务都会回滚,否则,什么都不会回滚。

显然,当前版本的 ActiveRecord 不知道如何使用 SQLite3 进行嵌套事务,尽管 ActiveRecord 应该使用保存点实现嵌套事务,并且 SQLite 从 3.6.8 开始支持保存点。

作为 ActiveRecord 根本不支持这进一步的证据,试试这个......

> List.connection.supports_savepoints?
=> false

Ubuntu 11.04 - Natty Narwhal;

ruby 1.8.7 (2010-04-19 patchlevel 253) [i486-linux], MBARI 0x8770, Ruby Enterprise Edition 2010.02;

Ruby on Rails 3.0.3;

sqlite3 宝石 1.3.3

SQLite 3.7.2;

于 2011-01-18T08:00:03.240 回答
0

Rails 事务实现不使用数据库用来支持嵌套事务的保存点(或类似技术)。数据库本身不支持真正的嵌套事务。

例子:

begin -- starts transaction 1
  begin -- start transaction 2

    insert into something (foo) values ('bar');

  commit -- ends transaction 1
rollback -- is ignored

第一个commitrollback总是关闭最外面的事务。

数据库实际上可以通过一种方式进行嵌套。这将使用前面提到的保存点。例子

begin -- starts transaction 1
  savepoint foo -- starts "transaction" 2

    insert into something (foo) values ('bar');

  release -- commit for transaction 2
rollback -- roll back the data of the savepoint and everything else within transaction 1

只要事务处于打开状态,您就可以在彼此之间嵌套任意数量的保存点。

但是,对于 rails 本身,有一个问题:这些函数创建并类似地将自己包装在事务中。所以你的第一个例子产生了以下 sql

begin  -- transaction.do
  begin  -- Client.create
    insert into clients ( name ) values ('Pavel')  -- Client.create
  commit  -- Client.create, closes the out-most transaction
  begin -- transaction.do
    begin  -- Client.create
      insert into clients ( name ) values ('Elena')  -- Client.create
    commit  -- Client.create, closes the out-most transaction

所以你的例外只是迟到了。

您可以修补此问题,但您必须对每个连接适配器进行修补。

PS:你可能会被--里面的 sql 弄糊涂了。这些是mysql中的单行注释。

于 2013-06-07T17:58:07.307 回答