はじめに
こんにちは株式会社TOKOSのナオキです!
今回は、Railsのtransaction
とraise
を使った例外処理について解説します。
この記事では、以下の内容について取り上げています。
transaction
メソッドの説明と基本的な使い方raise
メソッドの説明と基本的な使い方transaction
とraise
を組み合わせた使い方
- Rails初学者の方
transaction
内で例外処理を行いたい方
transactionメソッドの説明と基本的な使い方
transaction
メソッドとは、複数のSQL文を1つのまとまった処理として扱い、全てのSQL文が正常に実行された場合にのみデータベースの変更が反映されるメソッドです。
途中でエラーや例外が発生した場合はそれまでに行われた処理が全て取り消され、データベースの状態が元に戻るためデータの不整合を防ぐことができます!
例えば、銀行の送金処理を考えてみましょう。
A銀行からB銀行に送金処理を行いたい場合、A銀行からの引き落としとB銀行への入金がセットで行われなければなりません。もしA銀行からの引き落としが完了したあとにB銀行への入金する過程でエラーが発生してしまったらデータの不整合が起こってしまいます。transaction
メソッドを使用すれば、これらの処理が全て正常に行われる(コミット)か、どちらも行われない(ロールバック)かになります。
例えば、UserモデルとShopモデルがあるとします。transaction
メソッドはActiveRecordのクラスメソッドです。つまりUser
やShop
などのActiveRecodeモデル自体やActiveRecord::Base
を通して使用することができます。ActiveRecord::Base
を使用し2つのモデルを同時に更新をする必要がある場合下記のように記述します。
ActiveRecord::Base.transaction do
# transaction内での複数の処理
user.save!
shop.save!
end
上記のコードでは、user.save!
とshop.save!
の2つのデータベースの更新を1つのtransaction
の中で行っています。transaction
内の全ての処理が問題なく実行されればデータベースの更新が完了します。
user.save!
とshop.save!
のどちらかの処理で何かしらの問題がが発生したら、save!
が例外を発生させます。
なのでどちらのモデルも更新されることはないです。
save!
の!
をつけない場合は、例外が発生しないので処理は実行されます。
raiseメソッドの説明と基本的な使い方
raise
メソッドとは、処理の中で意図的に例外を発生させることのできるメソッドです。
通常、プログラムの実行中に予期せぬ例外が発生すると、その時点で処理が止まり、Rubyがエラーメッセージを表示して何が問題だったのかを教えてくれます。
一方で、raise
を使用するとプログラム自体に問題がなくても、実行中に意図的に例外を発生されることができます。
これにより、異常な状態を検知し、適切なエラーハンドリングやデバッグを行いやすくなります!
raise
メソッドはデフォルトではRuntimeError
を発生させます。
下記のようにメソッドの第一引数に例外クラス、第二引数に文字列を渡すとRuntimeError
以外の例外を発生させることができ、第二引数の文字列がエラーメッセージになります。
引数に文字列だけ渡すとRuntimeError
になります!
raise ArgumentError, "無効な値です。"
def hoge(age)
raise "年齢をマイナス値にすることはできません" if age < 0
puts "彼は#{age}歳です。"
end
hoge(-5)
上記は年齢がマイナス値の場合にraise
でRuntimeError
を発生させエラーメッセージで「年齢をマイナス値にすることはできません」と表示させます。
def foo(a, b)
raise ArgumentError, "0による除算はできません。" if b == 0
a / b
end
foo(9, 0)
上記はfooの第二引数が0の場合にraise
でArgumentError
を発生させエラーメッセージで「0による除算はできません。」と表示させています。
transactionとraiseを組み合わせた使い方
transaction
内で例外が発生する場合だけでなく、プログラム上で特定の条件が満たされなかった場合に意図的に例外を発生させたい場合もがあります。
ここでraise
メソッドを使用します。raise
を使用することで、意図的に例外を発生させてtransaction
を途中で停止しロールバックを行えます。
raise
の第一引数に入れられる例外クラスにActiveRecord::Rollback
があります。ActiveRecord::Rollback
はtransaction
を途中で中止し、行った更新を元に戻す(ロールバック)ために使用される特別な例外クラスです。
通常のraise
による例外(RuntimeError
)などはプログラム全体を停止させますが、ActiveRecord::Rollback
は特別で、transaction
のロールバックだけを行います。なのでtransaction
が中断されてもプログラム全体は停止せず残りの処理を行うことができます。
ActiveRecord::Base.transaction do
user.save!
if shop.invalid?
raise ActiveRecord::Rollback, "店舗情報が無効です"
end
shop.save!
end
上記は、user
とshop
を同時に更新しようとしているコードで、invalid?
メソッドを使用してshop
のバリデーションを通過したか、してないかを確認しています。
もしバリデーションを通過できなかったらロールバックされます。
これにより整合性が保たれます!
さいごに
Railsでのtransactionとraiseを使用した例外処理は、データの整合性を守るために非常に重要です。
適切にtransactionを使用することで例外発生時にデータのロールバックが保証されるので不整合な状態になりません。
また、raiseを使用したエラーハンドリングを組み合わせることで、例外の原因をいち早く特定でき問題を回避することが可能になります。
ぜひ使用してみてください。