`
pingfeng
  • 浏览: 59342 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

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这种实体,逻辑体,工厂互相协同,松耦合,可扩展


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics