Single table inheritance:單表繼承

[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來修改缺省名稱)。這意味着一個繼承看起來是這樣的:

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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章