Rails 3 ActiveRecord的Relation源码剖析
Rails3的一大亮点是 AR的query 接口:
1. 程序员最爱用就是它的及接方式
Uers.where().where().order()....
2. 还有LasyLoad可以增加灵活性
=================
但这个Relation还是很让人迷惑的。尤其是一上来:
????? delegate :find, :first, :last, :all, :destroy, :destroy_all, :exists?, :delete, :delete_all, :update, :update_all, :to => :scoped
????? delegate :find_each, :find_in_batches, :to => :scoped
????? delegate :select, :group, :order, :reorder, :except, :limit, :offset, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :create_with, :to => :scoped
????? delegate :count, :average, :minimum, :maximum, :sum, :calculate, :to => :scoped
这么生猛的操作都被delegate到:scoped上。不熟悉rails的手法,更是不知所云。
NamedScope scoped
知道调用 relation,new出一个relation实例就行了
@relation ||= Relation.new(self, arel_table)
这样,所以的操作都被delegate到relation实例上。
===================
relation查询接口都在relation目录下的query_methods.rb文件中
像order这种简单谓词
?def order(*args)
????? relation = clone
????? relation.order_values += args.flatten unless args.blank?
????? relation
??? end
就直接记到实例变量,后面产生sql用。
像where就复杂一些
def where(opts, *rest)
????? relation = clone
????? relation.where_values += build_where(opts, rest) unless opts.blank?
????? relation
??? end
build_where又调用了predicate_builder来builderwhere_value。在这里也是用了arel
================================
这些query动作完后,用all, first, last来向db fetch东西,最重要的东西来了!
调用relation to_a方法
? def to_a
????? return @records if loaded?
????? @records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel.to_sql)
...
????? @records
??? end
让are生成to_sql,还用是AR的base中的connection用查询数据回来
那么arel是如何生成的呢
def build_arel
????? arel = table
????? arel = build_joins(arel, @joins_values) unless @joins_values.empty?
????? arel = collapse_wheres(arel, (@where_values - ['']).uniq)
????? arel = arel.having(*@having_values.uniq.reject{|h| h.blank?}) unless @having_values.empty?
????? arel = arel.take(connection.sanitize_limit(@limit_value)) if @limit_value
????? arel = arel.skip(@offset_value) if @offset_value
????? arel = arel.group(*@group_values.uniq.reject{|g| g.blank?}) unless @group_values.empty?
????? arel = arel.order(*@order_values.uniq.reject{|o| o.blank?}) unless @order_values.empty?
????? arel = build_select(arel, @select_values.uniq)
????? arel = arel.from(@from_value) if @from_value
????? arel = arel.lock(@lock_value) if @lock_value
????? arel
??? end
arel本身可以不断及连。
========================
到时,真相大白。
relation是一个逻辑体,可以不断及联。are是一个sql factory。 AR:base是实体,有数据库连接。
那么要hook住AR的query,只要hook在relation的to_a方法上。
========================
设计上可以参考是:
1. 及联,类似decorator模式
?
2. base, relation, arel这种实体,逻辑体,工厂互相协同,松耦合,可扩展