NHibernate 中支持的三种继承策略

1、使用联合主键,你的持久化类必须重载Equals()和GetHashCode()方法。
2、当持久化一个持久态的实例时,若该实例引用了一个临时态的实例会引发异常:
   object references an unsaved transient instance - save the transient instance before flushing. 
   解决方法:
   方案一:先保存临时态的实例,再保存持久实例。
   方案二:修改<many-to-one>标签中的cascade 属性为“all”,保存持久实例时会自动保存临时态的实例。
3.唯一外键关联映射实际上使用的是<many-to-one>标签,所有说默认的cascade 是“none”
4.每个类分层结构一张表(Table per class hierarchy):
   A.必须设置extends属性。
   B.子类属性必须设置not-null为false。

下面对这三种映射策略的优缺点逐一加以说明:

1. Table per concrete class

这种方式要求我们把每一个子类都对应一张表,把这个子类的所有属性(包括父类的所有属性)都映射到一张表上。

这种方式的最大的问题是对多态不能很好的支持。在数据库中关联关系一般是通过外键来体现的。如果所有的子类都单独映射到不同的表,那么子类对于父类的多态关系就不能通过一个简单的外键搞定了。这在我们的领域模型中就会产生问题。BillDetails肯定还会与其他类关联,比如User类,那么这两张表都需要外键引用到User表。

多态查询(查询并且返回所有的那些匹配查询类接口类)也会产生问题。对于父类的查询需要执行多条select语句,每一个子类一条。会严重影响性能。

还有一个严重的问题是,如果父类修改了,会导致所有子类相关列和映射文件的修改。

这种映射策略,在NHibernate中没有专门的标签,仅需要为每一个具体类建立一个新的<class>声明,然后指定一个table属性。这种映射策略,仅当真的可以放弃继承带来的好处的时候才可以使用,否则是绝对不推荐的。

  <!--Table per concrete class:每一个具体的类对应一张表,从关系模型中完全抛弃多态和继承关系-->
  <class name="CreditCard" table="T_TPCC_CREDIT_CARD">
    <id name="ID" type="string">
      <column name="CREDIT_CARD_ID" length="36" not-null="true"/>
      <generator class="assigned" />
    </id>
    <property name="Owner"  type="string">
      <column name="OWNER" length="40" not-null="false"/>
    </property>
    <property name="Number"  type="string">
      <column name="NUMBER"  length="40" not-null="false"/>
    </property>
    <property name="ExpMonth" type="string">
      <column name="EXP_MONTH"  length="40" not-null="false"/>
    </property>
    <property name="ExpYear" type="string">
      <column name="EXP_YEAR"  length="40" not-null="false"/>
    </property>
  </class>
  
  <class name="BankAccount" table="T_TPCC_BANK_ACCOUNT">
    <id name="ID" type="string">
      <column name="BANK_ACCOUNT_ID"  length="36" not-null="true"/>
      <generator class="assigned" />
    </id>
    <property name="Owner" type="string">
      <column name="OWNER"  length="40" not-null="false"/>
    </property>
    <property name="Account" type="string">
      <column name="ACCOUNT"  length="40" not-null="false"/>
    </property>
    <property name="Swift" type="string">
      <column name="SWIFT"  length="40" not-null="false"/>
    </property>
    <property name="BankName" type="string">
      <column name="BANKNAME"  length="40" not-null="false"/>
    </property>
  </class>  

2. Table per class hierarchy

这种映射策略要求将整类的继承体系完全映射到一张表中。将整个继承体系中的所有类的所有属性都一一映射到这个表的各个列上。每个子类代表表中由识别器列(discriminator column)标识的一行。

这种映射策略在三种策略中最简单,性能也最好。但是这种方式也有一个主要问题,子类定义的那些属性,对于到数据库中的列必须要被定义成可以为空。这将失去数据库对Not Null的约束,从数据完整性的角度看,问题很严重:)

在NHibernate中,我们用<subclass>元素定义table-per-class hierarchy映射。映射文件如下所示:

  <!--Table per class hierarchy:每一个继承体系对应一张表,通过不规则的关系模型支持多态,用一个识别器列(discriminator column)来标识类型信息-->
  <class name="BillingDetails" table="T_TPCH_BillingDetails" discriminator-value="BD">
    <id name="ID" type="string">
      <column name="BILLING_ID" length="36" not-null="true"/>
      <generator class="assigned" />
    </id>
    <discriminator column="BILLING_DETAILS_TYPE" type="string"/>
    <property name="Owner" type="string">
      <column name="OWNER" length="40" not-null="false"/>
    </property>
    <subclass name="CreditCard" extends="BillingDetails" discriminator-value="CC">
      <property name="Number" type="string">
        <column name="NUMBER" length="40" not-null="false"/>
      </property>
      <property name="ExpMonth" type="string">
        <column name="EXP_MONTH" length="40" not-null="false"/>
      </property>
      <property name="ExpYear" type="string">
        <column name="EXP_YEAR" length="40" not-null="false"/>
      </property>      
    </subclass>
    <subclass name="BankAccount" extends="BillingDetails" discriminator-value="BA">
      <property name="Account" type="string">
        <column name="ACCOUNT" length="40" not-null="false"/>
      </property>
      <property name="Swift" type="string">
        <column name="SWIFT" length="40" not-null="false"/>
      </property>
      <property name="BankName" type="string">
        <column name="BANKNAME" length="40" not-null="false"/>
      </property>
    </subclass>
  </class>

其中discriminator并不是持久类的属性,它仅是用来区分继承体系中的各个持久类的,它仅供NHibernate内部自己使用 。这一列的名字是BILLING_DETAILS_TYPE,在这个例子中这一列的值会被置为CC和BD,NHibernate会自动地得到或者设置这一列的值。也就是说表中的每一行代表继承体系中的一个类,用BILLING_DETAILS_TYPE的不同的值来区分,CC就表示这一行对应CreditCard这个类。每一个子类都有自己的subclass元素。子类的属性被映射到BILLING_DETAILS表的列中。值得注意的是,各个子类的属性必须设置not-null属性为true,因为,CreditCard子类实例不可能全部拥有其他子类实例的属性。<subclass>可以嵌套其他的<subclass>,直到整个的继承体系全部映射到表中。

3.  Table per subclass

第三种就是用外键关系来表示继承关系。在这种策略中,每一个子类,父类都会单独映射到一张表中。和第一种策略不同的是,这种策略的表中仅包含类中自己定义的那些属性(不包含继承下来的属性)。

如果CreditCard子类的实例被持久化,那么BillingDetails父类中定义的那些属性也要被持久化到BILLING_DETAILS表中新的一行中,CreditCard子类中自己定义的那几个属性(不包括继承的属性)会被持久化到CREDIT_CARD表新的一行中。这两个新行通过共享的主键相关联。之后子类的实例从数据库中被取出的时候,需要join子类和父类分别对应得那几张表。

这种策略最大的好处就是关系模型完全标准化,关系模型和领域模型完全一致。在NHibernate中用<joined-subclass>来表示这种策略,映射文件如下:

<!--Table per subclass:每一个子类对应一张表,通过外键关系来表示出继承关系-->
 <class name="BillingDetails" table="T_TPS_BILLING_DETAIL">
    <id name="ID" type="string">
      <column name="BILLING_ID" length="36" not-null="true"/>
      <generator class="assigned" />
    </id>
    <property name="Owner" type="string">
      <column name="OWNER" length="40" not-null="false"/>
    </property>
    <joined-subclass name="CreditCard" table="CREDIT_CARD">
      <key column="CREDIT_CARD_ID" />
      <property name="Number" type="string">
        <column name="NUMBER" length="40" not-null="false"/>
      </property>
      <property name="ExpMonth" type="string">
        <column name="EXP_MONTH" length="40" not-null="false"/>
      </property>
      <property name="ExpYear" type="string">
        <column name="EXP_YEAR" length="40" not-null="false"/>
      </property>
    </joined-subclass>
    <joined-subclass name="BankAccount" table="T_TPS_BANK_ACCOUNT">
      <key column="BANK_ACCOUNT_ID" />
      <property name="Account" type="string">
        <column name="ACCOUNT" length="40" not-null="false"/>
      </property>
      <property name="Swift" type="string">
        <column name="SWIFT" length="40" not-null="false"/>
      </property>
      <property name="BankName" type="string">
        <column name="BANKNAME" length="40" not-null="false"/>
      </property>
    </joined-subclass>
  </class>
 

在这种策略中是不需要识别器列(discriminator)的。<joined-subclass>元素用来映射子类。在CREDIT_CARD表中,主键是必须的,它需要外键关联到BILLING_DETAILS表的主键。<joined-subclass>也可以嵌套其他的<joined-subclass>元素,但不能包含<subclass>元素。

这种策略明显比其他策略要复杂,性能也要低得多,因为需要大量的join语句。尤其继承关系比较复杂的情况下,问题更加严重。

实体类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Domain
{
    public abstract class BillingDetails
    {
        private string _id;
        private string _owner;

        public BillingDetails()
        {
        }
        public BillingDetails(string id, string owner)
        {
            this._id = id;
            this._owner = owner;
        }
        public virtual string ID
        {
            get { return this._id; }
            set { this._id = value; }
        }
        public virtual string Owner
        {
            get { return this._owner; }
            set { this._owner = value; }
        }

        protected abstract void Init();
    }

    public class CreditCard : BillingDetails
    {
        private string _number;
        private string _expYear;
        private string _expMonth;

        public CreditCard()
        {
        }
        public CreditCard(string id, string owner, string number, string month, string year)
            : base(id, owner)
        {
            this._number = number;
            this._expMonth = month;
            this._expYear = year;
        }
        public virtual string Number
        {
            get { return this._number; }
            set { this._number = value; }
        }
        public virtual string ExpMonth
        {
            get { return this._expMonth; }
            set { this._expMonth = value; }
        }
        public virtual string ExpYear
        {
            get { return this._expYear; }
            set { this._expYear = value; }
        }

        protected override void Init()
        {
        }
    }

    public class BankAccount : BillingDetails
    {
        private string _account;
        private string _bankName;
        private string _swift;

        public BankAccount()
        {
        }
        public BankAccount(string id, string owner, string account, string bank, string swift)
            : base(id, owner)
        {
            this._account = account;
            this._bankName = bank;
            this._swift = swift;
        }
        public virtual string Account
        {
            get { return this._account; }
            set { this._account = value; }
        }
        public virtual string Swift
        {
            get { return this._swift; }
            set { this._swift = value; }
        }
        public virtual string BankName
        {
            get { return this._bankName; }
            set { this._bankName = value; }
        }

        protected override void Init()
        {
        }
    }
}

测试方法:
        [TestMethod]
        public void TestBillingSave()
        {
            using (ISession session = this.sessionFactory.OpenSession())
            {
                CreditCard card1 = new CreditCard("00000000-0000-0000-0000-000000000001", "ruijc", "aaa", "8", "2008");
                CreditCard card2 = new CreditCard("00000000-0000-0000-0000-000000000002", "betty", "aab", "8", "2008");
                BankAccount account1 = new BankAccount("10000000-0000-0000-0000-000000000001", "ruijc", "aac", "12", "2008");
                BankAccount account2 = new BankAccount("10000000-0000-0000-0000-000000000002", "betty", "aaa", "12", "2008");
                using (ITransaction tran = session.BeginTransaction())
                {
                    session.SaveOrUpdate(card1);
                    session.SaveOrUpdate(card2);
                    session.SaveOrUpdate(account1);
                    session.SaveOrUpdate(account2);
                    tran.Commit();
                }

                ICriteria criteria = session.CreateCriteria(typeof(BillingDetails));
                criteria.Add(Expression.Eq("Owner", "ruijc"));
                IList<BillingDetails> billings = criteria.List<BillingDetails>();
                foreach (BillingDetails bill in billings)
                {
                    Debug.WriteLine(bill.ID);
                }
                Debug.WriteLine("------------------------------------");
                ICriteria criteria1 = session.CreateCriteria(typeof(CreditCard));
                criteria1.Add(Expression.Eq("Owner", "ruijc"));
                IList<CreditCard> creditCard = criteria1.List<CreditCard>();
                foreach (CreditCard obj in creditCard)
                {
                    Debug.WriteLine(obj.Owner);
                }
            }
        }


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
课程通过实际项目融入常用开发技术架构,讲授风格独特,提供详细上课日志及答疑,赠送配套的项目架构源码注释详细清晰且达通俗,均能直接在实际项目应用,正真的物超所值,价格实惠任务作业:综合运用《C#/.Net企业级系统架构设计实战精讲教程》课程所学知识技能设计一个学生成绩管理系统的架构。要求:1.系统基于MVC的三层架构,各层单独建不同的解决方案文件夹。2.采用Model First开发方式,设计架构时只需要设计学生(TbStudent)和课程(TbCourse)。学生必须有的字段是ID、stuName、age;课程必须有的字段是ID、courseName、content。3.数据访问层采用Entity Framework或NHibernate来实现,必须封装对上述的增删改查方法。4.必须依赖接口编程,也就是必须要有数据访问层的接口层、业务逻辑层的接口层等接口层。层层之间必须减少依赖,可以通过简单工厂或抽象工厂。5.至少采用简单工厂、抽象工厂、Spring.Net等技术的2种来减少层与层之间的依赖等。6.封装出DbSession类,让它拥有所有Dal层实例和SaveChanges方法。7.设计出数据访问层及业务逻辑层主要类的T4模板,以便实体增加时自动生成相应的类。8.现层要设计相关的控制器和视图来验证设计的系统架构代码的正确性,必须含有验证增删改查的方法。9.开发平台一定要是Visual Studio平台,采用C#开发语言,数据库为SQL Server。10.提交整个系统架构的源文件及生成的数据库文件。(注意: 作业需写在CSDN博客,请把作业链接贴在评论区,老师会定期逐个批改~~)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值