Rails 3向表中添加数据库名称包括默认为eager load

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.*FROM t_usersLIMIT 1 UserRole加载(84.5ms)SELECT t_user_roles.*FROM t_user_rolesWHERE t_user_roles.user_idIN(1)

使用表名前缀:

SQL(76.8ms)SELECT DISTINCT DBNAME.t_users.id FROM DBNAME.t_usersLEFT OUTER JOIN DBNAME.t_user_rolesDBNAME.t_user_roles.user_id= DBNAME.t_users.id限制1

SQL(66.4ms)SELECT DBNAME.t_users.idAS t0_r0 , DBNAME. t_users.emailAS t0_r1 , DBNAME. t_user_roles.idAS t1_r0 , DBNAME. t_user_roles.user_idAS t1_r1 FROM DBNAME.t_usersLEFT OUTER JOIN DBNAME.t_user_rolesDBNAME.t_user_roles.user_id= DBNAME.t_users.idWHERE DBNAME.t_users.idIN(1)

换句话说,调用包的默认行为已从预加载更改为急切加载.

有谁知道为什么默认行为正在改变?必须有一些关于添加数据库名称的东西,这使Rails认为我们必须急切加载,但我不明白为什么.我也很惊讶地看到这一点,因为我认为添加数据库名称并不罕见.我可以通过将所有包含更改为预加载来强制修复我们的代码库,但我想了解这里发生了什么.有没有办法改变默认行为?

csc*_*oed 1

问题是table_name_prefix引入了一个句点。这会混淆尝试确定是否应该预加载或急切加载的逻辑。这是 Rails 3 的一个 bug,已在 Rails 4 中解决。如果您需要 Rails 3 中的特定行为,则需要显式指定preloadeager_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发生变化,在决定是预加载还是急切加载时不再使用。