[align=center][size=x-large]Single table inheritance:单表继承[/size][/align]
转于:[url]http://my4java.itpub.net/post/9983/78535[/url]
一、介绍:
关系数据库不支持继承,所以在将对象映射到数据库时,我们必须考虑如何在关系表中表现我们完美的继承结构。当映射到一个关系数据库时,我们试图最小化在多个表内处理一个继承体系时快速增长的结合。单表继承则将一个继承体系的所有类映射到单个表的字段中。
在一个关系数据库内至少可以有三种形式来表现继承体系。Martin Fowler 在[url]http://www.martinfowler.com/eaaCatalog/[/url]中做了简短的描述。
1、Class Table Inheritance:继承体系中的每个类都由单个表来表现。
2、Single Table Inheritance:继承体系中的所有类都由一个单独表中的列来表现。
3、Concrete Table Inheritance:继承体系中的每个具体类由单个表来表现。
可参考下面的链接:
1、 [url]http://www.martinfowler.com/eaaCatalog/classTableInheritance.html[/url]
2、 [url]http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html[/url]
3、 [url]http://www.martinfowler.com/eaaCatalog/concreteTableInheritance.html[/url]
二、“活动记录”的支持:
“活动记录”使用第二种途径来支持继承,这意味着你必须添加一个string列,通过在列中存储类的名字,而此列缺省地称为,“type”(可以通过覆写Base.inheritance_column来修改缺省名称)。这意味着一个继承看起来是这样的:
当你完成Firm.create(:name => "37signals"),这个记录将被保存在companies表中且type="firm"。然后你可以使用Company.find(:first, "name = ‘37signals’")来获取此行,并且它会返回一个Firm对象。
如果你没有在你的表内定义type列,则不会触发单表继承。这种情况下,它只是像普通子类一样工作没什么魔术可用。
注意,所有的属性必须位于同一表内。
[color=indigo][b]注意:Rails在使用STI类之前必须能看到包含它们的文件,否则你会得到“uninitialized constant”错误。如果类的名字与包含它的文件的名称不是同样的话,你可能要有麻烦。如果你在'employees.rb'文件内有个“模型”为Manager。在这种情况下,Rails将不能从类名字中分析出文件名。
解决这个问题最简单的方式是添加“model :employees”到你的application.rb“控制器”中,那里'employees'是不带扩展名的,包含STI类的文件名(所以解决上面问题,“模型”应该包含文件名'employees.rb‘才可以。)这会强迫Rails加载此文件并看到你在其内定义的所有“模型”,然后在你使用时,Rails已经知道了你的STI类。[/b][/color]
使用Employee[:type]来访问子表信息,不要使用Employee.type。type是一个Ruby废弃的方法,所以直接访问它来设置或修改行的type会导致陌生的Ruby消息。
在使用“单个表继承时“有个明显的约束。两个子类不能有同样名字但不同type的属性,那样两个属性将被映射为同一个列。
三、例子:
转于:[url]http://my4java.itpub.net/post/9983/78535[/url]
一、介绍:
关系数据库不支持继承,所以在将对象映射到数据库时,我们必须考虑如何在关系表中表现我们完美的继承结构。当映射到一个关系数据库时,我们试图最小化在多个表内处理一个继承体系时快速增长的结合。单表继承则将一个继承体系的所有类映射到单个表的字段中。
在一个关系数据库内至少可以有三种形式来表现继承体系。Martin Fowler 在[url]http://www.martinfowler.com/eaaCatalog/[/url]中做了简短的描述。
1、Class Table Inheritance:继承体系中的每个类都由单个表来表现。
2、Single Table Inheritance:继承体系中的所有类都由一个单独表中的列来表现。
3、Concrete Table Inheritance:继承体系中的每个具体类由单个表来表现。
可参考下面的链接:
1、 [url]http://www.martinfowler.com/eaaCatalog/classTableInheritance.html[/url]
2、 [url]http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html[/url]
3、 [url]http://www.martinfowler.com/eaaCatalog/concreteTableInheritance.html[/url]
二、“活动记录”的支持:
“活动记录”使用第二种途径来支持继承,这意味着你必须添加一个string列,通过在列中存储类的名字,而此列缺省地称为,“type”(可以通过覆写Base.inheritance_column来修改缺省名称)。这意味着一个继承看起来是这样的:
class Company < ActiveRecord::Base; end
class Firm < Company; end
class Client < Company; end
class PriorityClient < Client; end
当你完成Firm.create(:name => "37signals"),这个记录将被保存在companies表中且type="firm"。然后你可以使用Company.find(:first, "name = ‘37signals’")来获取此行,并且它会返回一个Firm对象。
如果你没有在你的表内定义type列,则不会触发单表继承。这种情况下,它只是像普通子类一样工作没什么魔术可用。
注意,所有的属性必须位于同一表内。
[color=indigo][b]注意:Rails在使用STI类之前必须能看到包含它们的文件,否则你会得到“uninitialized constant”错误。如果类的名字与包含它的文件的名称不是同样的话,你可能要有麻烦。如果你在'employees.rb'文件内有个“模型”为Manager。在这种情况下,Rails将不能从类名字中分析出文件名。
解决这个问题最简单的方式是添加“model :employees”到你的application.rb“控制器”中,那里'employees'是不带扩展名的,包含STI类的文件名(所以解决上面问题,“模型”应该包含文件名'employees.rb‘才可以。)这会强迫Rails加载此文件并看到你在其内定义的所有“模型”,然后在你使用时,Rails已经知道了你的STI类。[/b][/color]
使用Employee[:type]来访问子表信息,不要使用Employee.type。type是一个Ruby废弃的方法,所以直接访问它来设置或修改行的type会导致陌生的Ruby消息。
在使用“单个表继承时“有个明显的约束。两个子类不能有同样名字但不同type的属性,那样两个属性将被映射为同一个列。
三、例子:
# simplified
def new
case @params[:person_type]
when "Manager"
@person = Manager.new
when "Slave"
@person = Slave.new
end
end
在你的“视图”中,完成:
<%= hidden_field_tag "person_type", @person[:type] %>
然后是create方法:
#again, simplified
def create
case @params[:person_type]
when "Manager"
@person = Manager.new(@params[:person])
when "Slave"
@person = Slave.new(@params[:person])
end
if @person.save
redirect_to :action => :list
else
render_action :new
end
end
甚至你可以在你的Person类中创建一个factory类方法。
class Person < ActiveRecord::Base
def self.factory(type, params = nil)
case type
when "Manager"
return Manager.new(params)
when "Slave"
return Slave.new(params)
else
return nil
end
end
end
然后,你可以在new和create方法内完成
def new
@person = Person.factory(@params[:person_type])
end
def create
@person = Person.factory(@params[:person_type], @params[:person])
if @person.save
...
end
end
你也可以使用下面方式来创建一个子类(thanks to Sean Hussey/Ezra)
person_type = "Manager"
@person = eval(person_type + ".new")
或者使用 constantize:
person_type = "Manager"
@person = person_type.constantize.new
如果你的“type”列的文本没有精确地匹配你的类名字,你可能得这样做:
class ETH < ListData; end
Ethnicity = ETH