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 はセーブポイントを使用してネストされたトランザクションを実装するはずであり、SQLite は 3.6.8 以降セーブポイントをサポートしているにもかかわらず、現在のバージョンの ActiveRecord は SQLite3 でネストされたトランザクションを実行する方法を認識していないようです。

これがまだ ActiveRecord でサポートされていないというさらなる証拠として、これを試してください...

> List.connection.supports_savepoints?
=> false

Ubuntu 11.04 - Natty Narwhal;

ruby 1.8.7 (2010-04-19 パッチレベル 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

最初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 自体には問題があります。create などの関数は、トランザクション内にラップされます。したがって、最初の例では次の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 に答える