NHibernate从入门到精通系列(8)——一对一关联映射

长平狐 发布于 2012/06/11 11:54
阅读 341
收藏 0

 

内容摘要

单向主键关联映射

双向主键关联映射

唯一外键关联映射

 

  NHibernate的一对一关联映射有三种,单向主键关联映射、双向主键关联映射、唯一外键关联映射。

 

一、单向主键关联映射

  我们模拟一个现实情况:学生(Student)和家庭(Family)的关系。在中国,目前实行计划生育,一个家庭只有一个孩子,孩子上学后就成为了学生。学生和家庭的关系可以认为是一对一的。

  让我们看一下“一对一”的表结构,如图1.1所示:

图1.1

 

让我们看一下“一对一”的实体类和映射文件:

     public   class  Student
    {
        
public   virtual   int ?  ID {  get set ; }

        
public   virtual   string  Name {  get set ; }
    }  

    
public   class  Family
    {
        
public   virtual   int ?  ID {  get set ; }

        
public   virtual   string  Adress {  get set ; }

        
public   virtual  Student Student {  get set ; }
    }

 

 

   < class  name = " Student "  table = " T_Student "  lazy = " true "   >

    
< id name = " ID "  column = " StudentID "  type = " int " >
      
< generator  class = " native " />
    
</ id >

    
< property name = " Name "  type = " string "  length = " 50 " />    
    
  
</ class >

  
< class  name = " Family "  table = " T_Family "  lazy = " true "   >

    
< id name = " ID "  column = " FamilyID "  type = " int " >
      
< generator  class = " foreign " >
        
< param name = " property " > Student </ param >
      
</ generator >
    
</ id >

    
< property name = " Adress "  type = " string "  length = " 200 " />

    
< one - to - one name = " Student "  constrained = " true " />
  
</ class >

 

其中,我们设置了“Family”类的主键生成策略为:“foreign”,含义是通过外键查询到主键值。

param的name属性设置为property,值设置为“Student”,表示通过“Student”属性查询到主键值。

然后使用<one-to-one>标签来描述“一对一”关联映射,并设置constrained属性为true,来建立一个外键约束。

 

环境建立好了,生成的表结构,如图1.2所示,我们能够观察到“T_Family”表的“FamilyID”字段既是主键又是外键。

图1.2

 

我们编写单元测试类的代码测试一下插入和查询。

 

    [TestFixture]
    
public   class  DomainTest
    {
        
private  ISessionFactory sessionFactory;

        [SetUp]
        
public   void  InitTest()
        {
            var cfg 
=   new  NHibernate.Cfg.Configuration().Configure( " Config/hibernate.cfg.xml " );
            sessionFactory 
=  cfg.BuildSessionFactory();
        }

        [Test]
        
public   void  SaveFamilyTest()
        {
            
using  (ISession session  =   this .sessionFactory.OpenSession())
            {
                var student 
=   new  Student { Name  =   " 刘冬 "  };
                var family 
=   new  Family { Adress  =   " 新疆乌鲁木齐市 " , Student  =  student };

                ITransaction tran 
=  session.BeginTransaction();
                
try
                {
                    session.Save(family);
                    tran.Commit();
                }
                
catch  (Exception ex)
                {
                    tran.Rollback();
                    
throw  ex;
                }
            }
        }

        [Test]
        
public   void  SelectFamilyTest()
        {
            
using  (ISession session  =   this .sessionFactory.OpenSession())
            {
                var family 
=  session.CreateQuery( " from Family " ).List < Family > ().First();

                Console.WriteLine(
" 家庭地址为:{0} " , family.Adress);
                Console.WriteLine(
" 学生姓名为:{0} " , family.Student.Name);
            }
        }
    }

 

插入的运行效果如图1.3所示,运行成功。奇怪的是,“Family”引用了一个临时态(Transient)的实例“Student” ,但并没有抛出异常。

这是因为<one-to-one>默认的cascade为all,这样NHibernate自动帮助我们把临时态(Transient)的实例持久化到数据库中了。

 

图1.3

 

 

查询的运行效果如图1.4所示,通过关联的属性带出了想要的信息。

图1.4

 

 

二、双向主键关联映射

我们修改一下代码,来体现双向主键关联映射:在“Student”类中加入属性“Family”,这样“Student”中有“Family”,“Family”中也有“Student”的循环引用方式就描述了双向主键关联映射的实体类结构。

然后在“Student”的映射文件中加入“<one-to-one name="Family" class="Family"/>”。

 

 

    public   class  Student
    {
        
public   virtual   int ?  ID {  get set ; }

        
public   virtual   string  Name {  get set ; }

        
public   virtual  Family Family {  get set ; }
    }  

 

 

< class  name ="Student"  table ="T_Student"  lazy ="true"   >

    
< id  name ="ID"  column ="StudentID"  type ="int" >
      
< generator  class ="native" />
    
</ id >

    
< property  name ="Name"  type ="string"  length ="50" />

    
< one-to-one  name ="Family"  class ="Family" />
    
  
</ class >

 

 然后编写一个查询的单元测试方法:

        [Test]
        
public   void  SelecStudentTest()
        {
            
using  (ISession session  =   this .sessionFactory.OpenSession())
            {
                var student 
=  session.Get < Student > ( 1 );

                Console.WriteLine(
" 学生姓名为:{0} " , student.Name);
                Console.WriteLine(
" 家庭地址为:{0} " , student.Family.Adress);
            }
        }

 

运行效果如图2.1所示。奇怪的是仅生成了一条SQL语句就将两个表中的数据获取出来了。这是因为“一对一”主键关联映射默认的抓取(fetch)策略是“join”。

图2.1

 

 

三、唯一外键关联映射

唯一外键关联映射是非主键字段的“一对一”关联。

我们模拟一种“一对一”情况:一个班级对应了一个班主任老师,一个班主任老师管理一个班级。代码如下:

 

     public   class  Class
    {
        
public   virtual   int ?  ID {  get set ; }

        
public   virtual   string  Name {  get set ; }

        
///   <summary>
        
///  班主任老师
        
///   </summary>
         public   virtual  Teacher Teacher {  get set ; }
    }

    
public   class  Teacher
    {
        
public   virtual   int ?  ID {  get set ; }

        
public   virtual   string  Name {  get set ; }

        
public   virtual  Class Class {  get set ; }
    }

 

 

 

  < class  name ="Class"  table ="T_Class"   >

    
< id  name ="ID"  column ="ClassID"  type ="int" >
      
< generator  class ="native" />
    
</ id >

    
< property  name ="Name"  type ="string"  length ="50" />

    
< many-to-one  name ="Teacher"  class ="Teacher"  column ="TeacherID"  unique ="true" />
    
  
</ class >


  
< class  name ="Teacher"  table ="T_Teacher" >

    
< id  name ="ID"  column ="TeacherID"  type ="int" >
      
< generator  class ="native" />
    
</ id >

    
< property  name ="Name"  type ="string"  length ="50" />

    
< one-to-one  name ="Class"  class ="Class"  property-ref ="Teacher" />

  
</ class >

 

我们在“Class”类中使用<many-to-one>标签,并设置unique属性为true。然后在“Teacher”类中使用<one-to-one>标签,并设置属性“Class”的property-ref指向“Teacher”。

 

最后编写一个单元测试方法:

 

[Test]
        
public   void  SaveTeacherTest()
        {
            
using  (ISession session  =   this .sessionFactory.OpenSession())
            {
                var teacher 
=   new  Teacher { Name  =   " 刘冬 "  };
                var cls 
=   new  Class { Name  =   " 1班 " , Teacher  =  teacher };
                teacher.Class 
=  cls;

                ITransaction tran 
=  session.BeginTransaction();
                
try
                {
                    session.Save(teacher);
                    session.Save(cls);

                    tran.Commit();
                }
                
catch  (Exception ex)
                {
                    tran.Rollback();
                    
throw  ex;
                }
            }
        }

 

运行效果如图3.1所有,运行成功。

图3.1

 

然后观察生成的表结构,如图3.2所示,生成了唯一外键。

图3.2

 

 

 

但注意的是:唯一外键关联映射实际上使用的是<many-to-one>标签,所有说默认的cascade是“none”,这样必须确保在没有引用临时态(Transient)的实例下才能持久化数据。

 

代码下载

出处:http://www.cnblogs.com/GoodHelper/archive/2011/02/25/nhibernate08.html

欢迎转载,但需保留版权。


原文链接:http://www.cnblogs.com/GoodHelper/archive/2011/02/25/nhibernate08.html
加载中
返回顶部
顶部