ActiveRecord的lazy loading与eager loading

看来大家还对ActiveRecord的lazy loading和eager loading不是很清楚
[color=red]
ActiveRecord默认是lazy loading的,而加上:include选项后可以指定eager loading一些字段
[b]:include - specify second-order associations that should be eager loaded when the collection is loaded.[/b]
[/color]
看例子:
[code]
Post.find(:all, :include => :comments)
# => SELECT ... FROM posts LEFT OUTER JOIN comments ON ...
[/code]

我再把active_recordassociations.rb的源代码翻出来给大家看看:
1,base.rb
[code]
def with_scope(method_scoping = {}, action = :merge, &block)
method_scoping = method_scoping.method_scoping if method_scoping.respond_to?(:method_scoping)

# Dup first and second level of hash (method and params).
method_scoping = method_scoping.inject({}) do |hash, (method, params)|
hash[method] = (params == true) ? params : params.dup
hash
end

method_scoping.assert_valid_keys([ :find, :create ])

if f = method_scoping[:find]
f.assert_valid_keys([ :conditions, :joins, :select, :include, :from, :offset, :limit, :order, :readonly, :lock ])
f[:readonly] = true if !f[:joins].blank? && !f.has_key?(:readonly)
end

# Merge scopings
if action == :merge && current_scoped_methods
method_scoping = current_scoped_methods.inject(method_scoping) do |hash, (method, params)|
case hash[method]
when Hash
if method == :find
(hash[method].keys + params.keys).uniq.each do |key|
merge = hash[method][key] && params[key] # merge if both scopes have the same key
if key == :conditions && merge
hash[method][key] = [params[key], hash[method][key]].collect{ |sql| "( %s )" % sanitize_sql(sql) }.join(" AND ")
elsif key == :include && merge
hash[method][key] = merge_includes(hash[method][key], params[key]).uniq
else
hash[method][key] = hash[method][key] || params[key]
end
end
else
hash[method] = params.merge(hash[method])
end
else
hash[method] = params
end
hash
end
end

self.scoped_methods << method_scoping

begin
yield
ensure
self.scoped_methods.pop
end
end

def find_every(options)
records = scoped?(:find, :include) || options[:include] ?
find_with_associations(options) :
find_by_sql(construct_finder_sql(options))

records.each { |record| record.readonly! } if options[:readonly]

records
end
[/code]

2,associations.rb
[code]
module ActiveRecord
module Associations
module ClassMethods
private
def find_with_associations(options = {})
catch :invalid_query do
join_dependency = JoinDependency.new(self, merge_includes(scope(:find, :include), options[:include]), options[:joins])
rows = select_all_rows(options, join_dependency)
return join_dependency.instantiate(rows)
end
[]
end

def select_all_rows(options, join_dependency)
connection.select_all(
construct_finder_sql_with_included_associations(options, join_dependency),
"#{name} Load Including Associations"
)
end

def construct_finder_sql_with_included_associations(options, join_dependency)
scope = scope(:find)
sql = "SELECT #{column_aliases(join_dependency)} FROM #{(scope && scope[:from]) || options[:from] || table_name} "
sql << join_dependency.join_associations.collect{|join| join.association_join }.join

add_joins!(sql, options, scope)
add_conditions!(sql, options[:conditions], scope)
add_limited_ids_condition!(sql, options, join_dependency) if !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit])

sql << "GROUP BY #{options[:group]} " if options[:group]

add_order!(sql, options[:order], scope)
add_limit!(sql, options, scope) if using_limitable_reflections?(join_dependency.reflections)
add_lock!(sql, options, scope)

return sanitize_sql(sql)
end

class JoinDependency

class JoinAssociation < JoinBase
def association_join
join = case reflection.macro
when :has_and_belongs_to_many
" LEFT OUTER JOIN %s ON %s.%s = %s.%s " % [
table_alias_for(options[:join_table], aliased_join_table_name),
aliased_join_table_name,
options[:foreign_key] || reflection.active_record.to_s.classify.foreign_key,
parent.aliased_table_name, reflection.active_record.primary_key] +
" LEFT OUTER JOIN %s ON %s.%s = %s.%s " % [
table_name_and_alias, aliased_table_name, klass.primary_key,
aliased_join_table_name, options[:association_foreign_key] || klass.table_name.classify.foreign_key
]
when :has_many, :has_one
...
when :belongs_to
...
else
...
join
end
end

end
[/code]
调用流程是这样的:
[b]ActiveRecord.find* -> with_scope -> find_every -> find_with_associations -> select_all_rows -> construct_finder_sql_with_included_associations -> association_join[/b]
如果提供:include选项,则调用find_with_associations,即eager loading,否则调用find_by_sql,为lazy loading
最后我们看到JoinAssociation的association_join方法使用LEFT OUTER JOIN来做关联查询,做eager loading

一切都弄清楚了吧?

BTW:对于open source项目,遇到问题时看看source code是很好的solution
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值