2012-04-24 12 views
4

TL; DR:AR :: Baseセーブトランザクション内に重複するジョインテーブルレコードを挿入すると、(ユニークな制約のため)セーブおよびロールバックの原因となります。重複する結合テーブルレコードを追加しないでください。保存していないと悪いです。postgresデータベースエラーがトランザクションの再開を強制する


私はDBへの参加・テーブルをレコードを追加するのmysql-土地で、このようなパターンの何かを追跡するために使用...のpostgresにmysqlのアプリを移行しています:

呼び出さ
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 

しかし、これでポストグレスが爆発します。ここで良い説明があります:

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

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

は基本的に#saveは、トランザクションを開始し、重複したレコードの挿入原因データベース例外。これは#saveのトランザクションを無効にします。これはsadfaceです。

これはポストグルーランドで使用できるより良いパターンがありますか?

ありがとうございます!


編集:

私はしっかりと取引を保存する、それはシリーズ内でこのロジックを保つために理にかなっていると信じて...パターンは次のようになります。

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. 

...それは私を作りますコントローラは超小型です。

+0

SELECT ...のために、共有が心に浮かび、それがすでにあるかどうかを確認します。重複している可能性のあるデータに対して行ったことは、各INSERTを個別のトランザクションにすることだけでした。 – freeone3000

+0

そうですが、すべてのロジックがセーブコールの内側にうまく包まれない – jsharpe

+0

ActiveRecordは、savepointsのようなPostgreSQLのサブトランザクションにアクセスする方法を教えてくれますか? plpgsqlのhttp://www.postgresql.org/docs/current/interactive/sql-savepoint.html ...またはEXCEPTION節? http://www.postgresql.org/docs/current/interactive/plpgsql-control-structures.html#PLPGSQL-ERROR-TRAPPING – kgrittn

答えて

3

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

コマンドSAVEPOINTいくつかのラベルを使用すると、後でトランザクションでその状態に戻るには、いくつかのラベルをSAVEPOINTとセーブポイントが取られた後にすべてのアクションを無視するコマンドROLLBACKを実行することができます(エラーを含みます)。

詳しくは、Continuing a transaction after primary key violation errorで私の他の回答をご覧ください。

+0

素晴らしい。あなたは、英雄です。他人のために:この答えの上のリンクを確かに見てください。 – jsharpe

関連する問題