当前位置: 代码迷 >> Ruby/Rails >> ActiveRecord-联接多张表之单表继承
  详细解决方案

ActiveRecord-联接多张表之单表继承

热度:531   发布时间:2016-04-29 02:08:48.0
ActiveRecord-连接多张表之单表继承

ActiveRecord-连接多张表之单表继承

1. 基本概念

Rails提供了两种机制,可以将复杂的面向对象模型映射为关系模型,即所谓的单表继承(single-table inheritance)和多态关联(polymorphic associations,也有人称为多表继承)。

2. 单表继承

在使用面向对象开发时,经常会用到类和继承,如应用程序中涉及不同角色的人员(People):顾客(Customer)、员工(Employee)和经理(Manager)等等。其中有一些属性是共有的,另一些属性是特有的。因此,创建模型:Customer类和Employee类,且都是People的子类,Manager类是Employee的子类。子类会继承父类的所有属性和行为。

这些都是代码中的模型体现,在关系型数据库是如何体现的?见如下代码:

# model的定义class People < ActiveRecord::Baseendclass Customer < Peopleendclass Employee < People    belongs_to :boss, class_name: 'Employee', foreign_key: 'reports_to'endclass Manager < Employeeend
# 迁移文件的定义,即关系型数据库的体现create_table :people, :force => true do |t|    t.column :type, :string    # common attributes    t.column :name, :string    t.column :email, :string    # attributes for type=Customer    t.column :balance, :decimal, :precision => 10, :scale => 2    # attributes for type=Employee    t.column :reports_to, :integer    t.column :dept, :integer    # attributes for type=Manager    # noneend
# 添加人员boss = Manager.create({name: 'Jobs', email: [email protected]', dept: 1})empl = Employee.new({name: 'David', email: [email protected]', dept: 2})empl.boss = bossempl.saveuser = Customer.create({name: 'Jim', email: [email protected]', balance: 100})

由上面的代码可以看出类定义中使用了继承,而对于不同角色的人员,均保存于people表,对于boss和empl对象,都是没有user的balance属性的,即这两个对象的balance字段都是null,而对于user对象的dept和reports_to字段同样是null,这样就实现了一个单表继承,相比于我们使用各种其它方法会简单很多。但是问题是:

2.1 ActiveRecord如何如此简单的提供了单表继承?

通过迁移文件中可以看到,在people表中添加了type字段。通过上图也可以看到,每条记录的type字段都有值,而且为用户的角色。ActiveRecord是约定好了使用type字段来描述对象所属的类型。

针对此问题,可以详见Martin Fowler在《企业应用架构模式》的介绍。

2.2 如果一个新加入的开发者使用People添加了人员呢?

是的,也许一个新加入项目组的同学使用People添加了人员,即使是测试,也会感觉到代码不是那么严谨,例如:

god = Person.create({name: 'God', email: [email protected]'})

发生了什么?God真的是神啊,type为空,所以,我们一定不想在表中见到神,按如下三种方法均可以解决:

  • 在People中实现一个名为abstract_class?的类方法,并使其返回true,这样,就可以达到目的了。不过它带来的问题是:1.ActiveRecord永远不会尝试寻找对应于抽象类的数据库表,这是对我们有利的,2.抽象类的子类会被当作各自独立的ActiveRecord模型类,即各自映射到一张独立的数据库表。这就达不到我们对公共属性抽取的目的了。这种方法不完美
  • 使用Ruby模块来包含这些需要共享的功能,然后将模块混入所谓的子类。这是书中提供的方法,也感觉不完美,没有了继承的感觉
  • 能否在父类中添加什么使其只可读不可写呢?
2.3 单表继承的优点和缺点

单表继承中所有的属性都存在于一张表中,这样真的好吗?如果子类存在的差异较大,且属性数据较大,如果仍然存在于同一张表,就会产生很多问题。这时可以学习多态继承了。

5. 参考

  • 《Web开发敏捷之道》第2版#P341-连接多张表