サーチ…


備考

トランザクションは保護ブロックで、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_commitafter_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


Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow