2012-04-24 62 views
4

TL; DR:在AR :: Base保存事務中插入重複的連接表記錄失敗(由於唯一約束)導致保存失敗並回滾。不添加重複連接表記錄是好的。不保存是不好的。postgres數據庫錯誤強制事務重新啓動


我遷移MySQL的應用Postgres的...我曾經遵循一個模式像這樣在MySQL土地加入表記錄添加到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的事務無效,這是sadface。

這是否有更好的模式可以在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. 

...它使我的控制器超小。

+0

SELECT ... FOR SHARE出現在腦海中,並檢查它是否已經存在。我對可能重複的數據所做的事情是簡單地使每個INSERT成爲一個單獨的事務。 – freeone3000 2012-04-24 23:45:31

+0

沒錯,但是那麼我的所有邏輯都不能很好地包裝在保存調用中 – jsharpe 2012-04-24 23:55:57

+0

ActiveRecord是否允許您以任何方式訪問PostgreSQL的子事務 - 比如保存點? http://www.postgresql.org/docs/current/interactive/sql-savepoint.html ...或plpgsql中的EXCEPTION子句? http://www.postgresql.org/docs/current/interactive/plpgsql-control-structures.html#PLPGSQL-ERROR-TRAPPING – kgrittn 2012-04-25 02:22:36

回答

3

如果您在一個事務中並且從錯誤中恢復並避免使整個事務失效,則必須使用保存點。

當您使用命令SAVEPOINT一些標籤,你可以在以後運行命令ROLLBACK TO SAVEPOINT一些標籤回到那個狀態事務而忽略了保存點拍攝後的所有操作(包括錯誤)。

請在Continuing a transaction after primary key violation error看到我的其他答案更多的解釋。

+0

真棒。你是先生,是英雄。對於其他人:絕對看到上面的鏈接在這個答案。 – jsharpe 2012-06-22 19:47:34