【问题标题】:Is there a good way to temporarily prevent ActiveRecord from performing queries?有没有一种暂时阻止 ActiveRecord 执行查询的好方法?
【发布时间】:2014-10-26 04:24:12
【问题描述】:

我有一组 ActiveRecord 模型,我正在预加载它们以避免 N+1 问题。为了帮助这个过程,我希望能够在我拥有(概念上)如下所示的块的地方进行一些手动测试:

# Prepare some data
data = Foo.includes(:bar, {baz: :quux}).find(27)

ActiveRecord.raise_error_if_any_queries_occur
perform_some_work_with_data(data)
ActiveRecord.back_to_normal

这似乎应该有办法做到这一点,但我没有任何运气。有其他人这样做过吗?

【问题讨论】:

  • 如果你想推迟查询,你为什么要马上做.find(27)
  • @lurker -- 这是事先发生的。我不想推迟查询,我想提前完成,然后在我正在工作的部分中,我想避免隐式查询。

标签: ruby-on-rails ruby activerecord


【解决方案1】:

您可以断开 ActiveRecord 与数据库的连接,并在完成后重新连接:

data = Foo.includes(:bar, {baz: :quux}).find(27)

Foo.connection.disconnect!
perform_some_work_with_data(data)
Foo.connection.reconnect!

如果在 ActiveRecord 断开连接时尝试对数据库运行查询,则会引发错误。

【讨论】:

    【解决方案2】:

    您可以通过以下方式与数据库断开连接:

    ActiveRecord::Base.connection.disconnect!
    Rails.logger.info('Disconnected from database')
    
    perform_some_work_with_data(data)
    
    ActiveRecord::Base.establish_connection
    Rails.logger.info('Connected to database')
    

    但我觉得这有点奇怪。而且我更喜欢使用bullet gem 进行 N+1 查询检测。

    【讨论】:

      【解决方案3】:

      rails 中的查询有时会调用此处定义的select_all(arel, name = nil, binds = []) 方法: https://github.com/rails/rails/blob/89a7187cc0d893da67f53d3215a33043905d68ed/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb#L22

      您可以看到,该方法将处理对select(sql, name = nil, binds = []) 方法的查询。此外,由于使用了 undef_method :select ,此方法处理对其父方法的查询,您可以在其定义中选择: https://github.com/rails/rails/blob/89a7187cc0d893da67f53d3215a33043905d68ed/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb#L354

      最后,这意味着执行的 select 方法是 ConnectionAdapter 方法,例如 posgresql 适配器,如第 946 行所示: https://github.com/rails/rails/blob/89a7187cc0d893da67f53d3215a33043905d68ed/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb

      所以,第一个选项: -您可以编辑其中一个文件以包含一些它们被调用的信息,例如在日志中。

      其次,我不知道它是否可行: - 您可以重写select_all(arel, name = nil, binds = []) 以引发异常,例如:

        def select_all(arel, name = nil, binds = [])
          raise "error"
        end
      

      当您想取消此覆盖时,您可以使用以下方法取消定义您的方法:

      undef_method :select_all
      

      所以你会将 select_all 传递给父级。

      但同样,我不知道第二个是否有效,但第一个肯定有效,尽管编辑 rails 核心以包含日志功能非常具有侵入性。

      【讨论】:

        【解决方案4】:

        如果您删除find(27) 并使用where(id: 27),您可以将.to_sql 附加到末尾。

        data = Foo.includes(:bar, {baz: :quux}).where(id: 27).to_sql
        

        然后稍后执行它:

        Foo.find_by_sql(data)
        

        您也可以使用Arel 完成相同的操作。 Arel 将让您构建更复杂的查询,您可以在需要时执行这些查询。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2023-01-21
          • 1970-01-01
          • 2019-10-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多