为什么在子块中出现异常后,嵌套事务中的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。
答案 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
第一个commit
或rollback
始终关闭最外面的交易。
数据库实际上可以如何进行嵌套。这将使用前面提到的保存点。示例
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中的单行注释..