我对活动记录中急切加载的机制感到有些困惑。假设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}
我是否正确地认为第一个示例将执行另一个查询,因此使得急切加载毫无意义,或者是活动记录聪明到足以知道它不需要再进行另一个查询?
另外,我的第二个问题是,有一种观点认为,在某些情况下,急切加载对数据库的影响更大,有时多个小查询会比单个大型查询更有效吗?
感谢您的想法。
答案 0 :(得分: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查找页面,因此有一个哈希是有意义的。
您对数据库或多或少密集的观点是一个很好的观点,并且始终牢记在心。如果你将使用大部分返回的数据,那将是有意义的。如果你只使用一小部分数据,那么下载这些对象的所有数据是没有意义的,只是在几毫秒后将它们扔到垃圾收集中。