2011-06-30 23 views
4

型號:如何在使用after_initialize回調時修復ActiveRecord(Rails 3)中的n + 1查詢問題?

class Project < ActiveRecord::Base 
    has_many :user_roles 
    after_initialize :add_user_roles 

    def add_user_roles 
    UserRoles.all.each do |ur| 
     self.user_roles << ur unless self.user_roles.include?(ur) 
    end 
    end 
end 

聲明說找到項目:

@projects = Project.includes(:user_roles) 

所以你可以看到,我告訴它包括在查詢中的用戶角色關聯。但是,我仍然看到n + 1個查詢問題:它爲每個項目找到一次角色。

如果我從回調中刪除了self.user_roles的用法並查看日誌,我可以看到它在2個查詢中找到項目及其用戶角色 - 一個用於項目,另一個用於角色project_id in (1,2,3,4,5...,n)

有沒有辦法解決這個問題?

讓我澄清一點:雖然我願意根據需要解決具體情況,但我更喜歡關注如何解決問題的答案。我有能力編寫一個kludge來獲取我想要的狀態的數據,而不使用after_initialize回調,因此不會進入n + 1查詢問題。不過,我寧願不這樣做,所以我更喜歡回答一般問題,而不是我的具體例子。

回答

1

即使急於加載關聯並不提供after_initialize(他們得到加載後的記錄被初始化)。看到這個問題的Rails的一些討論:

https://github.com/rails/rails/issues/13156

相關原來的問題:它看起來像每一個項目都會有相同的一組UserRole對象。我猜想有一個has_many :through已被消毒,但即使如此,Project有沒有完整的結果?我沒有看到如何ProjectUserRole實際上這裏連接 - 從什麼在本例中是可見的,這一點:

class Project < ActiveRecord::Base 
    def user_roles 
    UserRole.all 
    end 
end 

將完成同樣的事情after_initialize ...

+0

它已經足夠長,我不記得*爲什麼*。我認爲這可能是因爲角色是可以複製到項目中的全系統模型,並且名稱在那裏發生了變化,所以是有可能缺失的HABTM反映了這一點。 –

+0

在任何情況下,由於存在Rails問題,並且它看起來不會「固定」,所以我會接受這是正確的答案:不可能。 –

1

這可能是由每次初始化每個對象時都運行的after_initialize回調引起的。如果回調點是爲每個用戶自動分配每個角色(除非已經分配),那麼您可以通過before_save過濾器代替。這樣,在執行Project.includes(:user_roles)查找器時代碼將不會運行。

+0

我之所以選擇after_initialize而不是before_save是因爲我希望項目在數據庫中獲得新角色(如果他們沒有這些角色),即使項目再也不會保存了。例如,如果在系統中彈出一個新角色,則現有項目下次從數據庫中查詢時應自動擁有該角色。那有意義嗎? –

6

看看軌預先加載 http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations

同時使用includes

User.find(2).includes(:assets)#will load all assets with user 

加載對象,你可以加載的關聯,也可以在模型中指定要急於負荷協會

應用程序/模型/user.rb

class User< AR::Base 
    has_many :posts,:include=>:comments 
end 

class Post < AR::Base 
    has_many :comments 
    belongs_to :user 
end 

現在u.posts每個將加載評論張貼

+0

嗨Naveed,在原來的問題中,我提到我已經在使用渴望的loadin,實際上在那裏有一個使用includes方法的行。 ('Project.includes(:user_roles)')問題不是急切的加載 - 它是急切的加載,同時也使用'after_initialize'。你有辦法解決這個問題嗎? –

+0

嗨,'UserRole'的首要本質不適合'after_initialize'回調,因爲每當用戶對象被啓動時它就會被調用。這個回調的最佳位置是'before_save,after_save或after_create' .. ..現在你如果你調用'User.all',那麼每個用戶都會調用add_user_roles,這又是查詢表單數據庫來加載用戶角色。與此我建議移動'add_user_roles'保存然後急切的加載將正常工作。 – Naveed

+0

我很欣賞你在說什麼,但你能想象任何人在任何情況下都想使用after_initialize,同時也需要加載關聯嗎?這樣的建議無助於他們。在引發這個問題的情況下,我們確實不希望在保存活動中運行它,因爲保存很少發生,並且因爲如果系統中添加了新角色,所有項目都需要在下一次時反映出來他們被加載。 –

相關問題