1

我对在活动记录中急切加载的机制有点困惑。假设一个Book模型有很多Pages,我使用这个查询来取一本书:

@book = Book.find book_id, :include => :pages

现在这是我感到困惑的地方。我的理解是@book.pages已经加载并且不会执行另一个查询。但是假设我想找到一个特定的页面,我会怎么做?

@book.pages.find page_id

# OR...

@book.pages.to_ary.find{|p| p.id == page_id}

我是否认为第一个示例将执行另一个查询,因此使急切加载毫无意义,或者活动记录足够聪明,知道它不需要执行另一个查询?

另外,我的第二个问题是,是否有一个论点是在某些情况下,急切加载在数据库上更加密集,有时多个小查询会比单个大查询更有效?

谢谢你的想法。

4

1 回答 1

2

当 ActiveRecord 急切地加载关联时,对象内部深处发生的事情是设置了一个实例变量。就是这样,没有魔法。Enumerable 和 ActiveRecord::AssociationProxy 提供的所有方法只需查看实例变量以了解它是否已加载,然后继续执行它的业务。

当您调用#find 时,您并未加载集合:您正在搜索特定实例。您不是在与集合本身交谈。

你的第二个例子是要走的路,但我会以不同的方式做:

@book.pages.detect {|p| p.id == page_id}

或者,在这里我假设有关您的应用程序的一些事情,我会以更好的方式来做:

class BooksController < ApplicationController
  def show
    @book = Book.find(params[:id], :include => :pages)
    @pages_by_id = @book.pages.index_by(&:id)
  end
end

# app/views/books/show.html.erb
Page: <%= @pages_by_id[page_id].number %>

请注意,我正在使用#index_by,它返回一个哈希,其中键是评估块的结果,值是原始对象。由于您似乎只想按 ID 查找页面,因此对这些页面进行哈希处理是有意义的。

您关于数据库或多或少密集的观点是一个很好的观点,并且始终牢记在心。如果您将使用大部分返回的数据,那将是有意义的。如果您只使用一小部分数据,那么下载这些对象的所有数据是没有意义的,只是在几毫秒后将它们扔到垃圾收集器中。

于 2010-05-14T15:24:40.077 回答