使用 ORM 給系統(tǒng)的某些實(shí)體引入一個(gè)父類(lèi)

需要在一個(gè)已經(jīng)在運(yùn)行中的系統(tǒng)中引入一個(gè)(新增的)父類(lèi)的情況很常見(jiàn)驼壶。

假設(shè)在這個(gè)系統(tǒng)中我們?cè)染鸵呀?jīng)使用了 ORM 框架水慨,而使用的 ORM 框架可能支持繼承映射特性间校。繼承映射大致有三種方式:

  • TPH:Table Per Hierarchy
  • TPT:Table Per Type
  • TPC:Table Per Concrete Type

如何在這幾種方式中進(jìn)行選擇肛跌?對(duì)于一個(gè)已經(jīng)在運(yùn)行的系統(tǒng)來(lái)說(shuō)刊棕,其中有一個(gè)重要因素需要考慮:怎么減少對(duì)數(shù)據(jù)庫(kù)模型(Schema)的修改个曙?

顯然锈嫩,使用 TPC 方式對(duì)數(shù)據(jù)庫(kù)模型(Schema)的影響最小受楼。

不要小看這一點(diǎn)。有人說(shuō)呼寸,SQL 數(shù)據(jù)庫(kù)之所以取得巨大的成功艳汽,是因?yàn)樗峁┝艘粋€(gè)標(biāo)準(zhǔn)的集成機(jī)制。往往很多代碼对雪、系統(tǒng)都是和 SQL 數(shù)據(jù)庫(kù)模型耦合在一起的河狐,對(duì)數(shù)據(jù)庫(kù) Schema 的修改往往牽一發(fā)而動(dòng)全身。對(duì)于已經(jīng)在使用的系統(tǒng)瑟捣,重構(gòu)還是一小步一小步地來(lái)好一些馋艺。下面我們就先看看怎么用 TPC 的方式來(lái)搞吧。

我們假設(shè)現(xiàn)在系統(tǒng)中有兩個(gè)類(lèi)迈套,一個(gè)是 Store(門(mén)店)捐祠,一個(gè)是 Agency(代理)。

其中 Store 的代碼如下:

using System;

namespace NHibernateTest.Domain.Model
{
    public class Store
    {
        public Store ()
        {
        }

        public virtual int StoreId { get; set; }
        public virtual string StoreName { get; set; }
        public virtual string Location { get; set; }
            
    }
}

另外一個(gè)類(lèi) Agency 的代碼如下:

using System;

namespace NHibernateTest.Domain.Model
{
    public class Agency
    {
        public Agency ()
        {
        }

        public virtual int AgencyId { get; set; }
        public virtual string AgencyName { get; set; }
        public virtual string Location { get; set; }            
    }
}

假設(shè)我們的系統(tǒng)的使用了 NHiberante 作為 ORM 框架桑李。Store 的 OR 映射文件:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" 
                   assembly="NHibernateTest" 
                   namespace="NHibernateTest.Domain.Model">
  
  <class name="Store" table="Stores">
    <id name="StoreId" column="Id">
      <generator class="native" />
    </id>

    <property name="StoreName" />

    <property name="Location" />

  </class>
  
</hibernate-mapping>

Agency 的映射文件如下:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" 
                   assembly="NHibernateTest" 
                   namespace="NHibernateTest.Domain.Model">
  
  <class name="Agency" table="Agencies">
    <id name="AgencyId" column="Id">
      <generator class="native" />
    </id>

    <property name="AgencyName" />

    <property name="Location" />

  </class>
  
</hibernate-mapping>

現(xiàn)在踱蛀,我們發(fā)現(xiàn)需要引入一個(gè)父類(lèi) Organization(組織)。

至于為什么要引入贵白?這里不多解釋?zhuān)?qǐng)自行腦補(bǔ)吧星岗。

如果使用 TPC 方式,對(duì)代碼的修改非常簡(jiǎn)單戒洼。

Organization 的代碼:

using System;

namespace NHibernateTest.Domain.Model
{
    public abstract class Organization
    {
        static Random rand = new Random ();

        public static int   NextOrganizationId ()
        {
            return rand.Next ();
        }

        public Organization ()
        {
        }

        public virtual int OrganizationId { get; set; }

        public virtual string Name { get; set; }

        public virtual string Location { get; set; }

    }
}

對(duì) Store 的修改如下:

using System;

namespace NHibernateTest.Domain.Model
{
    public class Store : Organization
    {
        public Store ()
        {
        }

        //      public virtual int StoreId { get; set; }
        //
        //      public virtual string StoreName { get; set; }
        //
        //      public virtual string Location { get; set; }

        public virtual int StoreId { 
            get { return OrganizationId; }
            set { OrganizationId = value; }
        }

        public virtual string StoreName { 
            get { return Name; }
            set { Name = value; } 
        }
            
    }
}


對(duì) Agency 的修改如下:

using System;

namespace NHibernateTest.Domain.Model
{
    public class Agency : Organization
    {
        public Agency ()
        {
        }

        //      public virtual int AgencyId { get; set; }
        //
        //      public virtual string AgencyName { get; set; }
        //
        //      public virtual string Location { get; set; }

        public virtual int AgencyId {
            get{ return OrganizationId; }
            set{ OrganizationId = value; }
        }

        public virtual string AgencyName {
            get { return Name; } 
            set { Name = value; } 
        }
            
    }
}


原來(lái) Store 和 Agency 的映射文件可以干掉了。新的 Organization 的映射文件如下:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" 
                   assembly="NHibernateTest" 
                   namespace="NHibernateTest.Domain.Model">
  
  <class name="Organization" abstract="true">
    <id name="OrganizationId" column="Id">
      <generator class="assigned" />
    </id>

    <union-subclass name="Agency"
        table="Agencies">

      <property name="Name" column="AgencyName"/>

      <property name="Location" />

    </union-subclass>

    <union-subclass name="Store"
        table="Stores">

      <property name="Name" column="StoreName"/>

      <property name="Location" />

    </union-subclass>


  </class>
  
</hibernate-mapping>

測(cè)試代碼片段如下:

        public static void TestOrgs ()
        {

            using (ISession session = _sessionFactory.OpenSession ())
            using (ITransaction transaction = session.BeginTransaction ()) {

                Store s = new Store ();
                //***********
                s.OrganizationId = Organization.NextOrganizationId ();
                //
                s.StoreName = Guid.NewGuid ().ToString ();
                s.Location = Guid.NewGuid ().ToString ();
                session.Save (s);

                Agency a = new Agency ();
                //***********
                a.OrganizationId = Organization.NextOrganizationId ();
                //
                a.AgencyName = Guid.NewGuid ().ToString ();
                a.Location = Guid.NewGuid ().ToString ();
                session.Save (a);

                transaction.Commit ();
            }

        }

上面假設(shè)原來(lái) Store 和 Agency 兩個(gè)實(shí)體在數(shù)據(jù)庫(kù)中的主鍵列名都是 Id允华,分別映射到代碼中 StoreId 和 AgencyId圈浇。這種情況下,我們看看使用 TPC 方式為它們引入一個(gè)共同的父類(lèi)靴寂,我們做了哪些修改:

  • 修改了 Id 的產(chǎn)生機(jī)制磷蜀。

    這里出于演示的目的,將 Organization(Store 和 Agency)的 Id 改成了 assigned 方式百炬,并在 Organization 的代碼中寫(xiě)了一個(gè)演示性的 Id 生成方法 NextOrganizationId(隨機(jī)產(chǎn)生一個(gè)整數(shù)作為 Id褐隆,不要用在正式的生產(chǎn)代碼中)。其實(shí)還有其他的 Id 產(chǎn)生機(jī)制可以選的剖踊;

  • 新增了一個(gè) Organization 抽象父類(lèi)庶弃。將 Store 和 Agency 概念相同的屬性提升到這個(gè)父類(lèi)中;

  • 修改了 Store 和 Agency 的代碼德澈,讓它們分別繼承 Organization歇攻,并將它們?cè)械囊恍傩栽L(fǎng)問(wèn)操作委托給父類(lèi)實(shí)現(xiàn)。

除此之外梆造,就沒(méi)有別的了缴守。特別需要注意的是:數(shù)據(jù)庫(kù) Schema 可能幾乎沒(méi)有做修改。這里說(shuō)可能幾乎,是因?yàn)椋涸瓉?lái)的 Id 產(chǎn)生器是 native 的屡穗,而對(duì)不同的底層數(shù)據(jù)庫(kù)來(lái)著贴捡,native 的實(shí)現(xiàn)機(jī)制可能是不一樣的。比如 MS SQL Server 和 MySQL 默認(rèn)是使用了自增類(lèi)型的字段村砂。這時(shí)候就需要把 Id 字段的自增屬性去掉烂斋。

當(dāng)然,這里說(shuō)的只是數(shù)據(jù)庫(kù)的 Schema (幾乎)不變箍镜,但是系統(tǒng)遺留的已有數(shù)據(jù)(Store 和 Agency)可能存在 Id 沖突源祈,所以數(shù)據(jù)遷移還是要做的。

還有色迂,如果原來(lái)的(Store 和 Agency 對(duì)應(yīng)的)兩個(gè)數(shù)據(jù)表中香缺,Id 的字段名不是 Id,而是 StoreId 和 AgencyId歇僧,那 Schema 可能就需要改了图张。

以上是使用 NHiberante 舉例,EF 應(yīng)該也差不多诈悍。關(guān)于 EF 的繼承映射支持祸轮,可以自行 Google。也可以看看這篇文章:
http://www.cnblogs.com/oppoic/p/ef_tph_tpt_tpc_inheritance.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末侥钳,一起剝皮案震驚了整個(gè)濱河市适袜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌舷夺,老刑警劉巖苦酱,帶你破解...
    沈念sama閱讀 222,378評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異给猾,居然都是意外死亡疫萤,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,970評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)敢伸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)扯饶,“玉大人,你說(shuō)我怎么就攤上這事池颈∥残颍” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,983評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵躯砰,是天一觀的道長(zhǎng)蹲诀。 經(jīng)常有香客問(wèn)我,道長(zhǎng)弃揽,這世上最難降的妖魔是什么脯爪? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,938評(píng)論 1 299
  • 正文 為了忘掉前任则北,我火速辦了婚禮,結(jié)果婚禮上痕慢,老公的妹妹穿的比我還像新娘尚揣。我一直安慰自己,他們只是感情好掖举,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,955評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布快骗。 她就那樣靜靜地躺著,像睡著了一般塔次。 火紅的嫁衣襯著肌膚如雪方篮。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,549評(píng)論 1 312
  • 那天励负,我揣著相機(jī)與錄音藕溅,去河邊找鬼。 笑死继榆,一個(gè)胖子當(dāng)著我的面吹牛巾表,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播略吨,決...
    沈念sama閱讀 41,063評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼集币,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了翠忠?” 一聲冷哼從身側(cè)響起鞠苟,我...
    開(kāi)封第一講書(shū)人閱讀 39,991評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎秽之,沒(méi)想到半個(gè)月后偶妖,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,522評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡政溃,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,604評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了态秧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片董虱。...
    茶點(diǎn)故事閱讀 40,742評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖申鱼,靈堂內(nèi)的尸體忽然破棺而出愤诱,到底是詐尸還是另有隱情,我是刑警寧澤捐友,帶...
    沈念sama閱讀 36,413評(píng)論 5 351
  • 正文 年R本政府宣布淫半,位于F島的核電站,受9級(jí)特大地震影響匣砖,放射性物質(zhì)發(fā)生泄漏科吭。R本人自食惡果不足惜昏滴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,094評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望对人。 院中可真熱鬧谣殊,春花似錦、人聲如沸牺弄。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,572評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)势告。三九已至蛇捌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間咱台,已是汗流浹背络拌。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,671評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留吵护,地道東北人盒音。 一個(gè)月前我還...
    沈念sama閱讀 49,159評(píng)論 3 378
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像馅而,于是被迫代替她去往敵國(guó)和親祥诽。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,747評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理瓮恭,服務(wù)發(fā)現(xiàn)雄坪,斷路器,智...
    卡卡羅2017閱讀 134,714評(píng)論 18 139
  • 太長(zhǎng)了屯蹦,還是轉(zhuǎn)載吧...今天在看博客的時(shí)候维哈,無(wú)意中發(fā)現(xiàn)了@Trinea在GitHub上的一個(gè)項(xiàng)目Android開(kāi)源...
    龐哈哈哈12138閱讀 20,218評(píng)論 3 283
  • 轉(zhuǎn)載:1號(hào)店訂單系統(tǒng)水平分庫(kù)的實(shí)踐之路以及關(guān)鍵步驟 隨著大型互聯(lián)網(wǎng)應(yīng)用的發(fā)展,海量數(shù)據(jù)的存儲(chǔ)和訪(fǎng)問(wèn)成為系統(tǒng)設(shè)計(jì)的瓶...
    meng_philip123閱讀 5,838評(píng)論 0 41
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)登澜、插件阔挠、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,123評(píng)論 4 61
  • 活在當(dāng)下,努力過(guò)好每一天脑蠕。(^.^) 暑假的結(jié)束就代表新學(xué)期的開(kāi)始购撼。中考失敗的我,只有到表哥所在的學(xué)校就讀谴仙。第一次...
    請(qǐng)問(wèn)通天二苑閱讀 479評(píng)論 1 2