Rails 4 对象的预加载
Rails 4 eager load for an object
我正在尝试从实例化对象中加载关联,即不是将关联与父对象一起读取...
User.includes(:characters).first
...推迟它,直到我决定它真的需要它并做类似的事情:
u = User.first
# Other stuff ...
u.eager_load(:characters)
在Rails3中我用这个方法增强了ActiveRecord:
def eager_load(*args)
ActiveRecord::Associations::Preloader.new(self, *args).run
end
而且效果很好。 Rails 4 稍微修改了这部分,我将方法更新为:
def eager_load(*args)
ActiveRecord::Associations::Preloader.new.preload(self, *args)
end
不幸的是,它现在做了一些奇怪的事情。看一看:
2.1.2 :001 > u = User.first
[2015-01-06 23:18:03] DEBUG ActiveRecord::Base : User Load (0.3ms) SELECT `users`.* FROM `users` ORDER BY `users`.`id` ASC LIMIT 1
=> #<User id: 1, ...>
2.1.2 :002 > u.eager_load :characters
[2015-01-06 23:18:07] DEBUG ActiveRecord::Base : Character Load (0.2ms) SELECT `characters`.* FROM `characters` WHERE `characters`.`user_id` IN (1)
[2015-01-06 23:18:07] DEBUG ActiveRecord::Base : Character Load (0.3ms) SELECT `characters`.* FROM `characters`
[2015-01-06 23:18:07] DEBUG ActiveRecord::Base : Character Load (0.2ms) SELECT `characters`.* FROM `characters`
=> [#<ActiveRecord::Associations::Preloader::HasMany:0x00000007c26d28 @klass=Character(id: integer, ...), @owners=[#<User id: ...], @reflection=#<ActiveRecord::Reflection::HasManyReflection:0x0000000496aa60 @name=:characters, ...(LOTS of stuff here)...]
请特别注意所有记录的两倍 SELECT。有没有办法修复此行为或其他一些方法来做我想做的事?
为了防止重复查询,您实际上应该预先加载您认为会用到的所有内容。所以,你应该像这个例子一样在 documentation:
上嵌套你的 include 命令
users = User.includes(:address, friends: [:address, :followers])
您还可以添加更深层次的关系,例如:
users = User.includes(:address, {friends: [:address, :followers]})
我遇到了同样的问题,回溯我的步骤,我注意到这可能是控制台上 运行 这段代码的产物。每个表达式都会被评估,但我们只对副产品感兴趣:如果是这种情况,请尝试:
u.eager_load(:characters); nil
我希望它有所帮助,我目前正在研究这个,所以这是我迄今为止唯一的观察。
我正在尝试从实例化对象中加载关联,即不是将关联与父对象一起读取...
User.includes(:characters).first
...推迟它,直到我决定它真的需要它并做类似的事情:
u = User.first
# Other stuff ...
u.eager_load(:characters)
在Rails3中我用这个方法增强了ActiveRecord:
def eager_load(*args)
ActiveRecord::Associations::Preloader.new(self, *args).run
end
而且效果很好。 Rails 4 稍微修改了这部分,我将方法更新为:
def eager_load(*args)
ActiveRecord::Associations::Preloader.new.preload(self, *args)
end
不幸的是,它现在做了一些奇怪的事情。看一看:
2.1.2 :001 > u = User.first
[2015-01-06 23:18:03] DEBUG ActiveRecord::Base : User Load (0.3ms) SELECT `users`.* FROM `users` ORDER BY `users`.`id` ASC LIMIT 1
=> #<User id: 1, ...>
2.1.2 :002 > u.eager_load :characters
[2015-01-06 23:18:07] DEBUG ActiveRecord::Base : Character Load (0.2ms) SELECT `characters`.* FROM `characters` WHERE `characters`.`user_id` IN (1)
[2015-01-06 23:18:07] DEBUG ActiveRecord::Base : Character Load (0.3ms) SELECT `characters`.* FROM `characters`
[2015-01-06 23:18:07] DEBUG ActiveRecord::Base : Character Load (0.2ms) SELECT `characters`.* FROM `characters`
=> [#<ActiveRecord::Associations::Preloader::HasMany:0x00000007c26d28 @klass=Character(id: integer, ...), @owners=[#<User id: ...], @reflection=#<ActiveRecord::Reflection::HasManyReflection:0x0000000496aa60 @name=:characters, ...(LOTS of stuff here)...]
请特别注意所有记录的两倍 SELECT。有没有办法修复此行为或其他一些方法来做我想做的事?
为了防止重复查询,您实际上应该预先加载您认为会用到的所有内容。所以,你应该像这个例子一样在 documentation:
上嵌套你的 include 命令users = User.includes(:address, friends: [:address, :followers])
您还可以添加更深层次的关系,例如:
users = User.includes(:address, {friends: [:address, :followers]})
我遇到了同样的问题,回溯我的步骤,我注意到这可能是控制台上 运行 这段代码的产物。每个表达式都会被评估,但我们只对副产品感兴趣:如果是这种情况,请尝试:
u.eager_load(:characters); nil
我希望它有所帮助,我目前正在研究这个,所以这是我迄今为止唯一的观察。