raj*_*aja 10 activerecord ruby-on-rails ruby-on-rails-3
资料来源:
我的项目正朝着拥有多个数据库(目前在同一台服务器上)的方向发展,我希望能够在这些数据库之间加入.为了做到这一点,我需要将数据库名称添加到表前缀,如下所示:
class FirstBase < ActiveRecord::Base
def self.table_name_prefix
"DBNAME.t_"
end
establish_connection :firstdb
end
class User < FirstBase
has_many :user_roles
end
class UserRole < FirstBase
belongs_to :user
end
Run Code Online (Sandbox Code Playgroud)
添加表名前缀似乎会影响同一查询上包含的默认行为,即使在同一个数据库中也是如此.考虑User.includes(:user_roles).first
没有表名前缀:
用户加载(67.1ms)SELECT
t_users.*FROMt_usersLIMIT 1 UserRole加载(84.5ms)SELECTt_user_roles.*FROMt_user_rolesWHEREt_user_roles.user_idIN(1)
使用表名前缀:
SQL(76.8ms)SELECT DISTINCT
DBNAME.t_users.id FROMDBNAME.t_usersLEFT OUTER JOINDBNAME.t_user_roles开DBNAME.t_user_roles.user_id=DBNAME.t_users.id限制1SQL(66.4ms)SELECT
DBNAME.t_users.idAS t0_r0 ,DBNAME.t_users.DBNAME.t_user_roles.idAS t1_r0 ,DBNAME.t_user_roles.user_idAS t1_r1 FROMDBNAME.t_usersLEFT OUTER JOINDBNAME.t_user_roles开DBNAME.t_user_roles.user_id=DBNAME.t_users.idWHEREDBNAME.t_users.idIN(1)
换句话说,调用包的默认行为已从预加载更改为急切加载.
有谁知道为什么默认行为正在改变?必须有一些关于添加数据库名称的东西,这使Rails认为我们必须急切加载,但我不明白为什么.我也很惊讶地看到这一点,因为我认为添加数据库名称并不罕见.我可以通过将所有包含更改为预加载来强制修复我们的代码库,但我想了解这里发生了什么.有没有办法改变默认行为?
问题是table_name_prefix引入了一个句点。这会混淆尝试确定是否应该预加载或急切加载的逻辑。这是 Rails 3 的一个 bug,已在 Rails 4 中解决。如果您需要 Rails 3 中的特定行为,则需要显式指定preload或eager_load按照您在问题中指出的那样。
在ActiveRecord::Relation中,exec_queries调用eager_loading?来决定是否应该预先加载。此调用references_eager_loaded_tables?用于tables_in_string尝试在 SQL 查询中查找不属于连接表一部分的表名:
# ActiveRecord::Relation#references_eager_loaded_tables?
(tables_in_string(to_sql) - joined_tables).any?
Run Code Online (Sandbox Code Playgroud)
该tables_in_string方法存在缺陷,因为它并不总是正确解析 SQL。此代码可用于查看 SQL 查询中它认为的表名称:
relation = User.includes(:user_roles)
relation.send(:tables_in_string, relation.to_sql)
Run Code Online (Sandbox Code Playgroud)
使用DBNAME.t_表名前缀,这将给出["DBNAME", "t_users"]表名,这是错误的。它应该给["DBNAME.t_users"].
当点/句点处于条件值时,ActiveRecord 查询中记录了类似的问题。这导致ActiveRecord::Relationtables_in_string发生变化,在决定是预加载还是急切加载时不再使用。
| 归档时间: |
|
| 查看次数: |
389 次 |
| 最近记录: |