昨天 [Day 16] 關聯資料表加載,解決 N+1 Query,我們得到了includes
這個方法可以解決N+1 query
,但是背後的原理是什麼?
就是我今天的主題。
class Product < ApplicationRecord
has_many :styles, class_name: 'ProductStyle' # 關聯取名為styles
end
class ProductStyle < ApplicationRecord
belongs_to :product
end
雖然只是簡單的加上has_many
關聯,Rails已經幫我們產生很多黑魔法,包含最常用的關聯方法:styles
,只要Product.last.styles
就可以呼叫出最後一個商品的所有款式。
其實:styles
方法會先看記憶體cache
裡面是不是已經有@association_cache
這個實體變數
,有的話就直接從@association_cache
裡面把資料拉出來,如果沒有才用SQL語法跟DB要資料。
@association_cache
來比較是否使用eager_loading
(預加載),兩種情況的@association_cache
eager_loading
product = Product.last
product.instance_variable_get(:@association_cache) # => {}
@association_cache
回傳一個空的hash
,所以@association_cache
預設值是空hash
eager_loading
product = Product.includes(:styles).first
product.instance_variable_get(:@association_cache)
# => {:styles=>#<ActiveRecord::Associations::HasManyAssociation:0x00007fa3083dc978...
# ...落落長...省略...@association_ids=nil, @association_scope=nil>}
@association_cache
回傳一個key是:styles
的hash
class Type < ApplicationRecord
end
class Product < ApplicationRecord
has_many :styles, class_name: 'ProductStyle'
has_many :types
end
一次關聯兩個資料表
product = Product.includes([:styles, :types]).last
product.instance_eval{@association_cache}.keys
# => [:styles, :types]
@association_cache
會用關聯的名字作為一個個hash_key,把預加載的資料放在對應的hash value。
透過includes
方法把資料cache
在@association_cache
裡,就可以減少query資料庫的次數,是非常好用的技巧。