LoginSignup
2
0

More than 1 year has passed since last update.

まとめてレコードを作成する時はトランザクションを使おう

Posted at

トランザクションとは

トランザクションとは...コンピュータ システムにおける、永続的なデータに対する不可分な一連の処理

ロールバックとは...データ更新などで障害が起こったときに、その前の状態にまで戻ることをいう

トランザクションの具体例

例えばTwitterでフォローしてる人のツイートを通知ONにしている時、

・ツイートの作成
・通知の作成

の二つはセットで作成されるべきです。ツイートだけ作成に成功する、または通知だけ作成に成功するというのはよろしくないですね。そういう時に、処理をトランザクションとして書くことで一連の処理を確実にすることができるようになります。もし、エラーが発生した場合はロールバックされ元の何も手を加えられていない状態に戻ります。

個人開発やポートフォリオの作成ですとこういった守りの開発(他にもエラーハンドリングとか)が疎かになりがちかと思いますが実務では必須なので日頃の開発から癖づけておくと良いかもです。

Railsですとトランザクションはこんな感じに書けます。

  def create
    ActiveRecord::Base.transaction do
      tweet = Tweet.create!(text: 'hoge')
      @notification_targets.each do |target|
        tweet.notifications.create!(target_id: target.id)
      end
    end
  end

トランザクションするときは例外が出るようにしよう

ロールバックはトランザクション中に例外が発生することで起こります。なので逆にいうと作成してはいけない時にちゃんと例外を出さないと不正にレコードが作成されてしまいます。

具体的にいうと以下のようなコードが悪い例です。

  def create
    ActiveRecord::Base.transaction do
      tweet = Tweet.new(text: 'hoge')
      tweet.save
      @notification_targets.each do |target|
        notification = tweet.notifications.new(target_id: target.id)
        notification.save
      end
    end
  end

saveメソッドレコードの作成に失敗してもfalseを出すのみで例外は出ません。基本, エクスクラメーションマーク(!)をつけることで作成に失敗した際例外が出るようになるので"!"をつけましょう。

そして、トランザクション失敗時の処理はrescue部分に書くのですがrescueするエラーはちゃんと指定しましょう。なんでもrescueしてしまうのは良くないので。

  def create
    ActiveRecord::Base.transaction do
      tweet = Tweet.new(text: 'hoge')
      tweet.save
      @notification_targets.each do |target|
        notification = tweet.notifications.new(target_id: target.id)
        notification.save
      end
    end
  rescue ActiveRecord::RecordInvalid => invalid
    puts invalid.record.errors
  end
2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0