涤生之Enjoy code

General principles of programmer

导航

NHibernate学习之一:Many2One遇到的问题

博客园有不少优秀的NHibernate起步的教程,但很多的都是官方StartKit的教程,关于如何来解决初学者所遇到的问题上,以及对NHibernate使用中一些关键点如何理解上所述甚少。本文是我自己在学习NHiberNate中所遇到的一些关键点以及一些问题的记录。希望对我或与我有相同困惑的人有所帮助。

 

写了一个示例程序,来实现Many2One,示例程序组成:Order与OrderDetail,对应的Hbm与cs文件如下:

数据库如下:

表结构


 Order.hbm.xml

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">

  
<class name="NHibernateStudy.Domain.Order, NHibernateStudy.Domain" table="TBL_ORDER"
     lazy
="false">
    
<id name="Id" column="ID" type="Guid"
        unsaved-value
="00000000-0000-0000-0000-000000000000">
      
<generator class="guid" />
    
</id>

    
<property name="UserName" column="USERNAME" type="String" length="40"/>
    
<property name="OrderTime" column="ORDERTIME" type="DateTime"></property>
    
<property name="TotalAmount" column="TOTALAMOUNT" type="Decimal"/>

    
<bag name="Items" lazy="true" inverse="true" cascade="all-delete-orphan">
      
<key column="ORDER_ID"></key>
      
<one-to-many class="NHibernateStudy.Domain.OrderDetail, NHibernateStudy.Domain"/>
      
    
</bag>   
  
</class>

</hibernate-mapping>

OrderDetail.hbm.xml

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">

  
<class name="NHibernateStudy.Domain.OrderDetail, NHibernateStudy.Domain" table="TBL_ORDER_DETAIL"
     lazy
="false">
    
<id name="Id" column="ID" type="Guid"
        unsaved-value
="00000000-0000-0000-0000-000000000000">
      
<generator class="guid" />
    
</id>
    
<many-to-one name="Order" class ="NHibernateStudy.Domain.Order, NHibernateStudy.Domain" column="ORDER_ID" not-null="true"/>
    
    
    
<property name="ItemName" column="ITEM_NAME" type="String" length="40"/>
    
<property name="Amount" column="AMOUNT" type="Decimal"/>
    
  
</class>

</hibernate-mapping>

Order.cs

OrderDetail.cs


 

在写程序中共遇到如下的疑惑处,均已经解决。现记录如下

第一点:写Hbm中遇到的问题 

实现Many2One有两种实现方法,一种是只在父类的Hbm中增加信息,不改变子表的Hbm的信息。但这种方法有如下缺点

1) 子表中父表的关联字段即FK不可为空

2) 在每次更新子表的时候都是分两步走的Insert然后Update,存在效率问题,且第一次Insert的时候FK为空。

    由于存在这两个方面的问题,所以一般不采用这种方法。关于上面两个缺陷请看下文(下文来自NHiberNate的自带文档)

Suppose we start with a simple <one-to-many> association from Parent to Child. 
<set name="Children">
    
<key column="parent_id" />
    
<one-to-many class="Child" />
</set>
If we were to execute the following code 
Parent p 
= ..;
Child c 
= new Child();
p.Children.Add(c);
session.Save(c);
session.Flush();
NHibernate would issue two SQL statements: 
an INSERT to create the record 
for c
an UPDATE to create the link from p to c 
This 
is not only inefficient, but also violates any NOT NULL constraint on the parent_id column.

所以一般采用第二种方法,我自己写的示例程序也是采用这种方法的。 

首先看条目类的HBM文件,在子类的hbm文件加入:

<many-to-one name="Order" class ="NHibernateStudy.Domain.Order, NHibernateStudy.Domain" column="ORDER_ID" not-null="true"/>

 

备注:

1)     条目类的Hbm文件用many-to-one来与主类相联系

2)     name属性,表示在条目类中,主类的Properties名称

3)     class属性,表示主类的类名,与名称空间

4)     column属性,表示在条目类对应的条目表中与主类对应的父表关联字段。简单点说就是条目表中对应主表的那个外键

5)     not-null属性,表示是否为空。

 

看主类的HBM文件,在主类的Hbm文件加入

<bag name="Items" lazy="true" inverse="true" cascade="all-delete-orphan">

      
<key column="ORDER_ID"></key>

      
<one-to-many class="NHibernateStudy.Domain.OrderDetail, NHibernateStudy.Domain"/>

</bag>

关于用哪种表示集合,见下面的(http://wljcan.cnblogs.com/archive/2004/06/30/19713.html)

 

在Nhibernate中经常遇到one-to-many和many-to-many的关系映射,用一些集合类来保存关联的many集合,这些集合类包括: IList、Array和IDictionary。其在map文件中对应的元素为 list(IList)、set(IDictionary)、bag(IList)、map(IDictionary)和array(Array)。

        其中比较特殊的是List和Array,它们需要在关联表中用一个单独字段来保存列表(List)或数组(Array)的索引(如childRecord[i]中的i),虽然list可以实现对无序元素的访问,但是在nhibernate中还是必须要提供索引。这样就出现一个问题:如果表中没有这样的index字段,将无法使用array,这样可能降低性能,因为使用IList的时候需要 boxing 和unboxing。

   

        另外,在使用的过程中发现使用 list无法与datagrid绑定,而bag却可以。

       
<bag name="Students" lazy="true" inverse="true" >

          
<key column="TeacherID"/> 

          
<one-to-many class="testMSSql.student, testMSSql" />

        
</bag>

 

       
<list name="Students" lazy="true" inverse="true" >

          
<key column="TeacherID"/> 

          
<index column="id" />

          
<one-to-many class="testMSSql.student, testMSSql" />

        
</list>

 

备注:1)<bag>表示集合,集合的区别见上

        2)lazy = true,表示延迟加载。经试验,此处的lazy与下面的lazy是有区别的

 

<class name="NHibernateStudy.Domain.OrderDetail, NHibernateStudy.Domain" table="TBL_ORDER_DETAIL"     lazy="false">

 

如果在类中写 lazy= true,那么此类的所有属性均要为 public virtual。而在关联时,写上lazy=true,则无此要求。但按照建议,还是将类的所有属性写成virtual要好。

3) inverse = true,表示将主类与条目类之间的关系控制移交给条目类来完成。即

Parent p = (Parent) session.Load(typeof(Parent), pid);

Child c 
= new Child();

c.Parent 
= p;

p.Children.Add(c);

session.Save(c);

session.Flush();

必须要写 c.Parent = p;来完成主类与条目类之间的关系,没有此语句,仅仅将条目类放入主类中的语句p.Children.Add(c),无法完成主类与条目类的关联。

为了简化这个过程,可以在主类中加入如下函数

public void AddChild(Child c)
{
    c.Parent 
= this;
    children.Add(c);
}

Now, the code to add a Child looks like 

Parent p 
= (Parent) session.Load(typeof(Parent), pid);
Child c 
= new Child();
p.AddChild(c);
session.Save(c);
session.Flush();
 

4) cascade表示级联使用的场合

5) 属性 Key column表示主表与条目表的关联字段。记住是这个关联字段在条目表中的名称。即无论是bag中还是条目类的hbm文件中的many-to-one中所指定的column都是指条目表中外键字段的名称。

6) 属性one-to-many,指定条目类

第二点: 写程序中遇到的问题

1) 在条目类中放入一个引用的主类的变量。变量名称即为在条目类中many-to-one中name属性指定的值。刚开始由于还是以数据库的思维考虑,放入了一个主类的ID,汗一个。

2) 父类中放一个条目类的IList。在父类中使用范型的ILIst来完成条目类的引用效率更高,相关代码如下:

using System.Collections.Generic;

private IList<OrderDetail> itmes;

public virtual IList<OrderDetail> Items

        
{

            
get return this.itmes; }

            
set this.itmes = value; }

        }


3) 在父类的构造函数中要建立子类所用的IList实例。刚开始,忘记new此实例导致在加入条目类的时候出现空引用。代码如下:

public Order()

        
{

            
this.itmes = new List<OrderDetail>() ;

        }


 

3) 编译时遇到的问题

1) 是遇到空引用的问题,问题源自2-3

出现了 mappingException,找不到对应的EntityClass。――未将对应的hbm文件作为嵌入资源。即设置文件*.hbm.xml的生成操作属性为“嵌入的资源”。感谢(http://www.cnblogs.com/david-chan/archive/2006/02/18/333230.aspx

posted on 2007-07-18 15:43  涤生  阅读(1699)  评论(4编辑  收藏  举报