Ruby on rails活动记录查询哪一个是有效的

时间:2017-11-27 08:35:56

标签: ruby-on-rails ruby oop activerecord ruby-on-rails-5

我最近正在开展一个项目,在这个项目中,我选择了两种获得相同结果的方法。这是类结构:

class Book < ApplicationRecord
  belongs_to :author
end

class Author < ApplicationRecord
  has_many :books
end

作者有姓,姓。我希望将给定书籍的作者全名作为实例方法。

在简单的活动记录术语中,由于书籍与作者相关联,我们可以按如下方式获取书籍的作者姓名:

例如在Book类中,我们有:

class Book < ApplicationRecord
  belongs_to :author

  def author_name
    "#{author.first_name} #{author.last_name}"
  end

end

我们得到了结果!

但是,根据最小化依赖关系(POODR Book),未来易于更改和更好的面向对象设计的目标,本书不应该知道作者的属性。它应该通过接口与作者对象进行交互。

因此,Book不应该是负责获取作者姓名的人。作者类应该。

class Book < ApplicationRecord
  belongs_to :author

  def author_name
    get_author_name(self.author_id)
  end

  private

  #minimizing class dependecies by providing private methods as external interfaces
  def get_author_name(author_id)
    Author.get_author_name_from_id(author_id)
  end

end

class Author < ApplicationRecord
    has_many :books

    #class methods which provides a gate-way for other classes to communicate through interfaces, thus reducing coupling.   

    def self.get_author_name_from_id(id)
        author = self.find_by_id(id)
        author == nil ? "Author Record Not Found" : "#{author.first_name.titleize} #{author.last_name.titleize}" 
    end

end

现在,本书只是与作者和作者提供的公共界面进行交互,正在处理从其属性中获取全名的责任,这是一个更好的设计。

我尝试在控制台中将查询作为两个单独的方法运行:

class Book < ApplicationRecord
  def author_name
    get_author_name(self.author_id)
  end

  def author_name2
    "#{author.last_name} + #{author.first_name}"
  end
end

结果如下所示: enter image description here

看起来两者都运行相同的查询。

我的问题是

  1. rails是否将在Book类中调用的author.last_name转换为 与作者内部的Author.find_by_id(author_id).last_name相同的SQL查询 如果数据量较大,作者类(通过Book类传递消息)?
  2. 如果数据量更大,哪一个性能更高?
  3. 不从Book类调用author.last_name违反设计 原则?

2 个答案:

答案 0 :(得分:1)

1)显然不是,它执行书籍的加入&amp;作者表。您所做的内容需要2个查询,而不是1 join您将拥有book.find(id)author.find(book.author_id)

2)JOIN应该更快。

3)由于last_name是一个公共接口,因此绝对不违反设计原则。如果您从外部访问作者的姓氏,那将违反原则:Book.find(1).author.last_name - 这是一件坏事。正确的是:Book.find(1).authors_last_name - 并在Model类中访问作者的姓名。

您提供的示例似乎过于复杂。

根据您分享的示例,您只想获得该书作者的全名。因此,分担责任的想法是正确的,但在Author类中应该是简单的实例方法full_name,如:

class Author < ApplicationRecord
  has_many :books

  def full_name
    "#{author.first_name.titleize} #{author.last_name.titleize}"
  end
end

class Book < ActiveRecord::Base
  belongs_to :author

  def author_name
    author.full_name
  end
end

请注意,此代码中没有直接查询。一旦你需要某个地方的作者姓名(在一个视图中,在api响应中等),Rails将使最优化的查询成为可能(但取决于你的用例,例如,如果你调用遍历书籍,它可能是无效的并在循环中调用author

答案 1 :(得分:1)

  1. 如果数据量较大,rails会将在Book类中调用的author.last_name转换为与Author.find_by_id(author_id).last_name相同的SQL查询(在Book类中通过消息传递)吗?
  2. 依赖于调用因子,就像在您的示例中一样,两者都将生成相同的查询。但是,如果您在获取图书/作者时有一个include \ join子句,则两者都会生成不同的查询。

    根据rails约定,不建议使用Author.find_by_id(author_id).last_name,因为无论何时调用该方法,它总是会在数据库上触发查询。应该使用rails的关联接口来调用相关对象上的方法,该方法可以智能地从内存中识别对象,或者如果不在内存中则从数据库中获取它。

    1. 如果数据量更大,哪一个更有效?

      author.last_name更好,因为如果使用它将处理连接,包含和memoization子句,并避免N + 1查询问题。

    2. 不从Book类调用author.last_name违反了设计原则吗?

      ,您甚至可以使用@Steve建议的委托。