app/controllers/test_controller.rb
class TestController < ApplicationController
def index
ActiveRecord::Base.transaction do
company = Company.create(name: 'test')
raise ActiveRecord::Rollback
end
end
end
説明
\n1トランザクションとは
\nトランザクションは、複数のデータベース操作をまとめて「全て成功」または「全て失敗」として扱うための仕組みです。一連の処理の途中で問題が発生した場合、それまでに行われた変更をすべて元に戻すことができます。
\n \nポイント
\n 重要: トランザクションは同一のコントローラー内で複数のモデルの操作などを行うときに使用し、例外などの中にraise ActiveRecord::Rollbackを記入しておくことで不都合が起きた時にActiveRecord::Base.transaction do内のすべてのデータベースの変更を取り消すことができます。
2基本的な使い方
\nトランザクションの基本的な構文は以下の通りです:
\n \n\n
\n \n ActiveRecord::Base.transaction do\n # データベースの操作\n # 問題が発生したらロールバック\n raise ActiveRecord::Rollback\nend\n
上記の例では、raise ActiveRecord::Rollbackの部分が動作するとデータベースのデータがActiveRecord::Base.transaction doの前の状態に戻ります。
3実践的な例
\n注文処理で、商品の在庫を減らしつつ注文データを作成する例:
\n \n\n
\n \n def create_order\n ActiveRecord::Base.transaction do\n # 商品の在庫を減らす\n @product = Product.find(params[:product_id])\n if @product.stock >= params[:quantity].to_i\n @product.stock -= params[:quantity].to_i\n @product.save!\n \n # 注文データを作成\n @order = Order.new(\n user_id: current_user.id,\n product_id: @product.id,\n quantity: params[:quantity].to_i\n )\n \n # 注文データが保存できなければロールバック\n unless @order.save\n raise ActiveRecord::Rollback\n end\n \n # 支払い処理\n payment_result = process_payment(params[:payment_info])\n \n # 支払いが失敗したらロールバック\n unless payment_result\n raise ActiveRecord::Rollback\n end\n else\n # 在庫不足の場合もロールバック\n raise ActiveRecord::Rollback\n end\n end\nend\n
この例では、商品在庫の確認・更新、注文データの作成、支払い処理のどの段階でも問題が発生したら、raise ActiveRecord::Rollbackによってすべての変更がなかったことになります。
4条件付きロールバック
\n条件に応じてロールバックを行う例:
\n \n\n
\n\n\n\n ActiveRecord::Base.transaction do\n # 会社情報を更新\n @company = Company.find(params[:id])\n @company.update!(name: params[:name])\n \n # 関連する全社員の部署情報も更新\n @company.employees.each do |employee|\n # 条件に合わない社員がいればロールバック\n if employee.department == "経理" && params[:allow_accounting_change] != "1"\n flash[:alert] = "経理部門の社員情報は変更できません"\n raise ActiveRecord::Rollback\n end\n \n employee.update!(department: params[:new_department])\n end\n \n # ここまで到達すればトランザクション完了(コミット)\n flash[:notice] = "会社情報と社員情報を更新しました"\nend\n
5エラーハンドリングとの組み合わせ
\nトランザクションとエラーハンドリングを組み合わせた例:
\n \n\n
\n\n\nbegin\n ActiveRecord::Base.transaction do\n # 1つ目のモデルを作成\n @user = User.create!(user_params)\n \n # 2つ目のモデルを作成\n @profile = @user.build_profile(profile_params)\n @profile.save!\n \n # 外部APIを呼び出し\n api_response = external_signup_api(@user)\n \n # API呼び出しが失敗したらロールバック\n if api_response[:status] != "success"\n raise "API Error: #{api_response[:message]}"\n end\n end\n \n # トランザクション成功時の処理\n redirect_to user_path(@user), notice: "ユーザー登録が完了しました"\n \nrescue ActiveRecord::RecordInvalid => e\n # バリデーションエラー時の処理\n flash.now[:alert] = "登録に失敗しました: #{e.message}"\n render :new\n \nrescue StandardError => e\n # その他のエラー時の処理\n logger.error "ユーザー登録エラー: #{e.message}"\n flash.now[:alert] = "システムエラーが発生しました"\n render :new\nend\n ポイント
\n 補足: トランザクションは、アトミック(不可分)な操作が必要な場面で使用します。例えば、銀行での送金処理(引き落としと入金が必ず両方成功するか両方失敗する)や、複数のテーブルに関連するデータを一貫して作成・更新する場合などに有効です。
\n