Ruby on Rails
ActiveRecordトランザクション
サーチ…
備考
トランザクションは保護ブロックで、SQL文はすべてが1つのアトミックアクションとして成功することができれば永続的です。古典的な例は、引き出しが成功した場合にのみ預金を持つことができる2つの勘定間の振替であり、その逆もあります。トランザクションは、データベースの整合性を強化し、プログラムエラーやデータベースの破壊からデータを保護します。したがって、基本的には、一緒に実行されるか、まったく実行されなければならないステートメントが多数ある場合は、トランザクション・ブロックを使用する必要があります。
基本的な例
例えば:
ActiveRecord::Base.transaction do
david.withdrawal(100)
mary.deposit(100)
end
この例では、ダビデのお金しか取っておらず、撤退も預金も例外を提起しなければ、それをメアリーに与えます。例外は、トランザクションが開始される前の状態にデータベースを戻すROLLBACKを強制します。ただし、オブジェクトのインスタンスデータはトランザクション前の状態に戻されません。
1つのトランザクション内の異なるActiveRecordクラス
トランザクションクラスメソッドは一部のActiveRecordクラスで呼び出されますが、トランザクションブロック内のオブジェクトはすべてそのクラスのインスタンスである必要はありません。これは、トランザクションがモデル単位ではなくデータベース単位の接続であるためです。
この例では、取引がAccountクラスで呼び出されても、残高レコードはトランザクションで保存されます。
Account.transaction do
balance.save!
account.save!
end
トランザクションメソッドは、モデルインスタンスメソッドとしても使用できます。たとえば、次のようにすることもできます。
balance.transaction do
balance.save!
account.save!
end
複数のデータベース接続
トランザクションは単一のデータベース接続で動作します。クラス固有のデータベースが複数ある場合、そのトランザクションはそれらの間の相互作用を保護しません。 1つの回避策は、モデルを変更した各クラスでトランザクションを開始することです。
Student.transaction do
Course.transaction do
course.enroll(student)
student.units += course.units
end
end
これは貧弱なソリューションですが、完全に分散したトランザクションはActiveRecordの対象外です。
保存と破棄は自動的にトランザクションにラップされます
#saveと#destroyの両方がトランザクションにラップされています。これにより、検証やコールバックで行うことは、保護対象のカバーの下で確実に実行されます。したがって、検証を使用してトランザクションが依存する値をチェックするか、 after_*
コールバックを含むロールバックのコールバックで例外を発生させることができます。
結果として、データベースへの変更は、操作が完了するまで接続外には見えません。たとえば、 after_save
検索エンジンのインデックスを更新しようとすると、インデクサーは更新されたレコードを表示しません。 after_commit
コールバックは、更新がコミットされるとトリガーされる唯一のコールバックです。
コールバック
トランザクションのコミットとロールバックに関連するコールバックには、 after_commit
とafter_rollback
2種類があります。
after_commit
コールバックは、トランザクションがコミットされた直後にトランザクション内で保存または破棄されたすべてのレコードで呼び出されます。 after_rollback
コールバックは、トランザクションまたはセーブポイントがロールバックされた直後に、トランザクション内で保存または破棄されたすべてのレコードで呼び出されます。
これらのコールバックは、データベースが永続的な状態にあるときにのみコールバックが実行されることが保証されるため、他のシステムとのやりとりに役立ちます。たとえば、 after_commit
は、トランザクション内からキャッシュをクリアすると、データベースが更新される前にキャッシュが再生成される可能性があるため、キャッシュをクリアするフックを入れるのに適しています。
トランザクションのロールバック
ActiveRecord::Base.transaction
は、 ActiveRecord::Rollback
例外を使用して意図的なロールバックを他の例外的な状況と区別します。通常、例外を発生させると、 .transaction
メソッドはデータベーストランザクションをロールバックし、例外を渡します。しかし、 ActiveRecord::Rollback
例外を発生させると、データベーストランザクションは例外を渡すことなくロールバックされます。
たとえば、コントローラでトランザクションをロールバックするには、次のようにします。
class BooksController < ActionController::Base
def create
Book.transaction do
book = Book.new(params[:book])
book.save!
if today_is_friday?
# The system must fail on Friday so that our support department
# won't be out of job. We silently rollback this transaction
# without telling the user.
raise ActiveRecord::Rollback, "Call tech support!"
end
end
# ActiveRecord::Rollback is the only exception that won't be passed on
# by ActiveRecord::Base.transaction, so this line will still be reached
# even on Friday.
redirect_to root_url
end
end