zoukankan      html  css  js  c++  java
  • NHibernate从入门到精通系列(8)——一对一关联映射

      内容摘要

      单向主键关联映射

      双向主键关联映射

      唯一外键关联映射

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

      一、单向主键关联映射

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

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

    图1.1

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

      

        public class Student
        {
            
    public virtual int? ID { getset; }

            
    public virtual string Name { getset; }
        }  

        
    public class Family
        {
            
    public virtual int? ID { getset; }

            
    public virtual string Adress { getset; }

            
    public virtual Student Student { getset; }
        }
      <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 { getset; }

            
    public virtual string Name { getset; }

            
    public virtual Family Family { getset; }
        }  
    <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 { getset; }

            
    public virtual string Name { getset; }

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

        
    public class Teacher
        {
            
    public virtual int? ID { getset; }

            
    public virtual string Name { getset; }

            
    public virtual Class Class { getset; }
        }

       

     <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

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

  • 相关阅读:
    form编码方式application/x-www-form-urlencoded和multipart/form-data的区别
    CentOS开启telnet服务
    借助英语搞清会计中“借”/“贷”的含义(转载)
    乘法器的Verilog HDL实现(转载)
    Meth | 关闭mac自带apache的启动
    Meth | Git冲突:commit your changes or stash them before you can merge. 解决办法
    Meth | Git 避免重复输入用户名和密码方法
    Meth | git Please move or remove them before you can merge
    Meth | git 常用命令
    Meth | 小团队git开发模式
  • 原文地址:https://www.cnblogs.com/GoodHelper/p/nhibernate08.html
Copyright © 2011-2022 走看看