4

TL;DR: AR::Base 保存トランザクション内に重複した結合テーブル レコードを挿入すると、(一意の制約が原因で) 保存が失敗し、ロールバックが発生します。重複した結合テーブル レコードを追加しなくても問題ありません。貯めないことは悪いことです。


私はmysqlアプリをpostgresに移行しています...私はmysql-landで次のようなパターンに従って、結合テーブルレコードをDBに追加していました:

class EventsSeries < ActiveRecord::Base
  #  UNIQUE KEY `index_events_series_on_event_id_and_series_id` (`event_id`,`series_id`)
  belongs_to :event
  belongs_to :series
end

class Series < ActiveRecord::Base

  has_many :events_series
  before_validation :add_new_event

private

  def add_new_event
    # boils down to something like this
    EventSeries.new.tap do |es|
      es.event_id = 1
      es.series_id = 1
      begin
        es.save!
      rescue ActiveRecord::RecordNotUnique
        # Great it exists
        # this isn't really a problem
        # please move on
      end
    end
  end
end

次のように呼び出されます。

Series.first.save 
# should not blow up on duplicate join record, cause i don't care

ただし、postgres はこれで爆発します。ここに良い説明があります:

http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html

...「例外処理とロールバック」セクション (警告を参照)

基本的に #save はトランザクションを開始し、重複したレコードの挿入によりデータベース例外が発生し、#save のトランザクションが無効になります。これは悲しいことです。

postgres-land で使用できる、より良いパターンはありますか?

ありがとう!


編集:

このロジックをシリーズの保存トランザクション内に保持することは理にかなっていると固く信じています...パターンは次のようになります。

s = Series.new
s.new_event_id = 123 # this is just an attr_accessor
s.save # callbacks on Series know how to add the new event.

...それは私のコントローラーを超小型にします。

4

1 に答える 1

3

トランザクション内にいて、エラーから回復し、トランザクション全体の無効化を回避する場合は、セーブポイントを使用する必要があります。

コマンドSAVEPOINTsome-labelを使用すると、後でコマンドROLLBACK TO SAVEPOINT some-labelを実行して、トランザクションでその状態に戻り、セーブポイントが実行された後のすべてのアクション(エラーを含む)を無視できます。

説明の詳細については、主キー違反エラー後のトランザクションの続行に関する他の回答を参照してください。

于 2012-04-26T22:34:14.447 に答える