Rails 3.嵌套事务。儿童街区的例外情况

时间:2010-11-11 11:03:38

标签: ruby-on-rails activerecord transactions sqlite nested-transactions

为什么在子块中出现异常后,嵌套事务中的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;

Ruby 1.9.2;

Ruby on Rails 3.0.1;

SQLite 3.7.3。

2 个答案:

答案 0 :(得分: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 gem 1.3.3

SQLite 3.7.2;

答案 1 :(得分:0)

rails事务实现不使用数据库用于支持嵌套事务的 savepoints (或类似技术)。 数据库本身不支持真正的嵌套事务。

示例:

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中的单行注释..