我可以让 belongs_to 关联默认使用预先加载吗?
Can I make a belongs_to association use eager loading by default?
我正在连接到我公司的一个 SQL 服务器数据库,并尝试设置 ActiveRecord,这样我就可以像对待 Rails 对象一样对待它们。
我有这两个型号:
class Change < ActiveRecord::Base
belongs_to :affected_contact, class_name: "Contact"
end
class Contact
# Contact's primary key is a binary UUID; I can't change this
end
我正在尝试获取某个特定更改的受影响联系人。通常情况下,这是一个简单的案例,但是:
Change.first.affected_contact
Change Load (52.6ms) EXEC sp_executesql N'SELECT TOP (1) [chg].* FROM [chg] ORDER BY [chg].[id] ASC'
Contact Load (28.0ms) EXEC sp_executesql N'SELECT TOP (1) [ca_contact].* FROM [ca_contact] WHERE [ca_contact].[contact_uuid] = @0', N'@0 binary', @0 = 0xfcf9a8ac6381aa4386c9b10ee382e10b [["contact_uuid", "<16 bytes of binary data>"]]
=> nil
...这不是我想要的!然而,如果我首先预先加载连接,它会起作用:
Change.eager_load(:affected_contact).first.affected_contact
SQL (34.4ms) EXEC sp_executesql N'SELECT TOP (1) holy_crap_theres_a_lot_of_columns FROM [chg] LEFT OUTER JOIN [ca_contact] ON [ca_contact].[contact_uuid] = [chg].[affected_contact] ORDER BY [chg].[id] ASC'
=> #<Contact contact_uuid: "\xFC\xF9\xA8\xACc\x81\xAAC\x86\xC9\xB1\x0E\xE3\x82\xE1\v", ... >
事实上,如果我以任何方式强制在 JOIN
子句中进行匹配,它将起作用,但 belongs_to
似乎改用 WHERE
子句,并且nil
是我能得到的最好的回应(很多时候,字符串和它的二进制类型之间存在转换错误)。
有没有办法确保通过 JOIN
子句的预先加载默认发生在 belongs_to
关联上?
使用 includes
应该可以解决您的问题。这是因为 includes
将 preload
或 eager_load
取决于您的其他条件。
我发现 #find_by_contact_uuid
(contact_uuid
是主键)有效,而 #find
由于某种原因无效。这导致了这个被实施。
我最终重写了 Active Record 提供的关联方法:
module AssociationMethods
def self.included(base)
base.reflect_on_all_associations(:belong_to).each do |a|
define_method a.name do
# #find_by_<uuid_pk> seems to work where #find doesn't
a.klass.send "find_by_#{a.association_primary_key}", self[a.foreign_key]
end
end
base.reflect_on_all_associations(:has_many).each do |a|
define_method a.name do
a.klass.where(a.foreign_key => self.send(a.association_primary_key))
end
end
end
end
class Contact
has_many :changes, foreign_key: :affected_contact_id
include AssociationMethods # include *after* all associations are defined
end
class Change
belongs_to :affected_contact, class_name: 'Contact'
include AssociationMethods
end
它没有涵盖 Active Record 在设置关联时提供的所有内容,但它似乎可以解决问题。
我正在连接到我公司的一个 SQL 服务器数据库,并尝试设置 ActiveRecord,这样我就可以像对待 Rails 对象一样对待它们。
我有这两个型号:
class Change < ActiveRecord::Base
belongs_to :affected_contact, class_name: "Contact"
end
class Contact
# Contact's primary key is a binary UUID; I can't change this
end
我正在尝试获取某个特定更改的受影响联系人。通常情况下,这是一个简单的案例,但是:
Change.first.affected_contact
Change Load (52.6ms) EXEC sp_executesql N'SELECT TOP (1) [chg].* FROM [chg] ORDER BY [chg].[id] ASC'
Contact Load (28.0ms) EXEC sp_executesql N'SELECT TOP (1) [ca_contact].* FROM [ca_contact] WHERE [ca_contact].[contact_uuid] = @0', N'@0 binary', @0 = 0xfcf9a8ac6381aa4386c9b10ee382e10b [["contact_uuid", "<16 bytes of binary data>"]]
=> nil
...这不是我想要的!然而,如果我首先预先加载连接,它会起作用:
Change.eager_load(:affected_contact).first.affected_contact
SQL (34.4ms) EXEC sp_executesql N'SELECT TOP (1) holy_crap_theres_a_lot_of_columns FROM [chg] LEFT OUTER JOIN [ca_contact] ON [ca_contact].[contact_uuid] = [chg].[affected_contact] ORDER BY [chg].[id] ASC'
=> #<Contact contact_uuid: "\xFC\xF9\xA8\xACc\x81\xAAC\x86\xC9\xB1\x0E\xE3\x82\xE1\v", ... >
事实上,如果我以任何方式强制在 JOIN
子句中进行匹配,它将起作用,但 belongs_to
似乎改用 WHERE
子句,并且nil
是我能得到的最好的回应(很多时候,字符串和它的二进制类型之间存在转换错误)。
有没有办法确保通过 JOIN
子句的预先加载默认发生在 belongs_to
关联上?
使用 includes
应该可以解决您的问题。这是因为 includes
将 preload
或 eager_load
取决于您的其他条件。
我发现 #find_by_contact_uuid
(contact_uuid
是主键)有效,而 #find
由于某种原因无效。这导致了这个被实施。
我最终重写了 Active Record 提供的关联方法:
module AssociationMethods
def self.included(base)
base.reflect_on_all_associations(:belong_to).each do |a|
define_method a.name do
# #find_by_<uuid_pk> seems to work where #find doesn't
a.klass.send "find_by_#{a.association_primary_key}", self[a.foreign_key]
end
end
base.reflect_on_all_associations(:has_many).each do |a|
define_method a.name do
a.klass.where(a.foreign_key => self.send(a.association_primary_key))
end
end
end
end
class Contact
has_many :changes, foreign_key: :affected_contact_id
include AssociationMethods # include *after* all associations are defined
end
class Change
belongs_to :affected_contact, class_name: 'Contact'
include AssociationMethods
end
它没有涵盖 Active Record 在设置关联时提供的所有内容,但它似乎可以解决问题。