2009-06-23 48 views
1

假設我們有兩個表之間常見的M-M的關係,如:ActiveRecord的:避免的has_many關係不一致

用戶 --- < users_tags> --- 標籤

在這篇文章中,我只關心關係user_tags,標籤:我想避免鏈接標籤可以被刪除。只有未被引用的標籤應該是可銷燬的。

愚蠢的方式做,這將是:

class Tag 
    def before_destroy 
    unless self.user_tags.empty? 
     raise "error" 
    end 
    end 
end 

但我覺得有一個潛在的競爭條件,檢查user_tags.empty之間?和實際的刪除。

第二種方法可能是在檢查是否還有任何引用之前鎖定整個表user_tags表。

而第三種方式,我能想到的會涉及更改它創建實際的參考代碼:

添加引用到users_tags:

  1. 取標籤
  2. 鎖定(以避免併發銷燬)
  3. 在users_tag中創建參考
  4. 提交

的before_destroy處理程序,然後可以:

  1. self.lock!
  2. 檢查是否有任何引用
  3. 毀滅自我
  4. 提交

有沒有更好的辦法來做到這一點?哪一個可靠/最好?我個人傾向於第二個,因爲它只需要before_destroy控制器中的邏輯,但是要鎖定整個表的成本。

編輯1:

雖然與LOCK TABLE嘗試圍繞我意識到,他們是在玩對我的交易。當使用innodb時,您可以使用事務(及其鎖定功能)或使用LOCK/UNLOCK表,這兩種世界的混合使情況變得更糟(LOCK/UNLOCK導致隱式提交,我錯過了文檔中的警告)。但這只是協議。

編輯2(幾周後):我再次與這個問題進行了抗爭。所以我想再次強調,不要使用LOCK TABLE

我現在傾向於父對象(在本例中標籤))上使用共享鎖增加孩子的時候,和FOR更新鎖定刪除。但是我仍然想知道它是否應該是這樣的(在父表中更新子表時鎖定一個Rang)。

Btw。我也意識到這個問題現在完全獨立於軌道:)。

回答

3

避免鎖定和檢查的一種方法是簡單地創建外鍵。試圖刪除在另一個表中引用的內容會導致SQL錯誤。

除此之外,你將不得不做大量的偏執狂檢查,以確保你沒有任何需要的標籤。

另一種方法是從另一個角度來處理問題。例如,作爲單個事務清除任何未使用的標記。例如:

DELETE FROM tags WHERE id NOT IN (SELECT DISTINCT(tag_id) FROM users_tags) 

這樣做的缺點是不能在模型級別執行before_destroy類型的行爲,但這對您而言可能不是問題。

+0

這就是正確的,只是使用innodbs自己的參照完整性系統會使這些事情變得更容易。 rails的方式是使用rails helper而不是定義所有的東西兩次,不確定這是否是最好的方法。 我喜歡你在刪除語句中的子查詢的方法,我懷疑有類似的東西。那聲明是原子的,對吧? – reto 2009-06-23 23:10:02

相關問題