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

      

      内容摘要

        单向关联映射

        双向关联映射

      一、单向关联映射

      1.1 单向关联映射的描述

      让我们回顾一下之前讲的《多对一关联映射》,如图1.1.1所示,其实“一对多”关联映射就是“多对一”关联映射相反的映射。

    图1.1.1

      至于“一对多”单向关联映射的代码如下:

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

            
    public virtual string Name { getset; }
        }

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

            
    public virtual string Name { getset; }

            
    public virtual IList<Student> Students { getset; }
        }

      映射文件如下:

      

    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Domain" namespace="Domain">
      
    <class name="Student" table="T_Student" lazy="true" >
        
    <id name="ID" type="int" column="StudentID">
          
    <generator class="native"/>
        
    </id>
      
        
    <property name="Name" type="string">
          
    <column name="Name" length="50"/>
        
    </property>

      
    </class>
    </hibernate-mapping>


    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Domain" namespace="Domain">
      
    <class name="Class" table="T_Class" lazy="true" >
        
    <id name="ID" type="int" column="ClassID">
          
    <generator class="native"/>
        
    </id>
      
        
    <property name="Name" type="string">
          
    <column name="Name" length="50"/>
        
    </property>

        
    <bag name="Students">
          
    <key column="ClassID"/>
          
    <one-to-many class="Student"/>
        
    </bag>
        
      
    </class>
    </hibernate-mapping>

      我们看到“Class”类中,有名为“Students” 的属性,其类型是IList<Student>。在映射文件中,我们使用<bag>和<one-to-many>标签来描述“一对多”关联映射。

      

      1.2 单向关联映射的数据插入

       单元测试类的代码如下:

        [TestFixture]
        
    public class OneToManyTest
        {
            
    private ISessionFactory sessionFactory;

            
    public OneToManyTest()
            {
                log4net.Config.XmlConfigurator.Configure();
            }

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

            [Test]
            
    public void SaveTest()
            {
                
    using (ISession session = this.sessionFactory.OpenSession())
                {
                    var liu 
    = new Student { Name = "刘冬" };
                    var zhang 
    = new Student { Name = "张三" };

                    var cls 
    = new Class { Name = "1班" };
                    cls.Students 
    = new List<Student> { liu, zhang };

                    ITransaction tran 
    = session.BeginTransaction();
                    
    try
                    {
                        session.Save(liu);
                        session.Save(zhang);

                        session.Save(cls);

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

       我们配置log4net输出SQL语句:

      

    <?xml version="1.0"?>
    <configuration>
      
    <configSections>

        
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
      
    </configSections>


      
    <!--log4net配置-->
      
    <log4net debug="true"> 
        
    <appender name="EventLogAppender" type="log4net.Appender.EventLogAppender">
          
    <layout type="log4net.Layout.PatternLayout">
            
    <param name="ConversionPattern" value="%d [%t] %-5p %c [%x] - %m%n" />
          
    </layout>
        
    </appender> 
        
    <root>
          
    <level value="ALL" />
          
    <appender-ref ref="RollingLogFileAppender" />
        
    </root>
      
    </log4net>


      
    <startup>
        
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
      
    </startup>
    </configuration>

      运行效果如图1.2.1所示,先成“insert into”语句,然后生成“update”语句修改外键。

    图1.2.1

      从图1.2.1中,我们能够观察到,如果“一对多”的外键不允许空,就有可能插入不成功。

      我们将映射文件稍作修改:

    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Domain" namespace="Domain">
      
    <class name="Class" table="T_Class" lazy="true" >
        
    <id name="ID" type="int" column="ClassID">
          
    <generator class="native"/>
        
    </id>

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

        
    <!--设置为不可空-->
        
    <bag name="Students">
          
    <key column="ClassID" not-null="true"/>
          
    <one-to-many class="Student"/>
        
    </bag>

      
    </class>
    </hibernate-mapping>

      运行效果如图1.2.2所示,抛出“ClassID”不允许插入NULL的异常。

    图1.2.2

     

      二、双向关联映射

      2.1 双向关联映射的描述

      我们修改一下代码,来实现“一对多”双向关联映射:

      

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

            
    public virtual string Name { getset; }

            
    public virtual Class Class { getset; }
        }
    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Domain" namespace="Domain">
      
    <class name="Student" table="T_Student" lazy="true" >
        
    <id name="ID" type="int" column="StudentID">
          
    <generator class="native"/>
        
    </id>
      
        
    <property name="Name" type="string">
          
    <column name="Name" length="50"/>
        
    </property>

        
    <many-to-one name="Class" column="ClassID" />
        
      
    </class>
    </hibernate-mapping>

      “一对多”双向关联映射指的是:在“一”的这端(“Class”类这端)包含“多”的属性(“Students”);在“多”的这端包含“一”的属性(“Class”属性)。这样两个类构成的循环引用就是双向关联映射。

      2.2 双向关联映射的数据插入

      插入数据的代码如下:

      

            [Test]
            
    public void SaveTest()
            {
                
    using (ISession session = this.sessionFactory.OpenSession())
                {
                    var liu 
    = new Student { Name = "刘冬" };
                    var zhang 
    = new Student { Name = "张三" };

                    var cls 
    = new Class { Name = "1班" };
                    cls.Students 
    = new List<Student> { liu, zhang };

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

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

      运行效果如图2.2.1所示,运行成功。

    图2.2.1

      修改“Student”的映射文件,将“Class”属性修改为不允许空:

    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Domain" namespace="Domain">
      
    <class name="Student" table="T_Student" lazy="true" >
        
    <id name="ID" type="int" column="StudentID">
          
    <generator class="native"/>
        
    </id>
      
        
    <property name="Name" type="string">
          
    <column name="Name" length="50"/>
        
    </property>

        
    <!--不允许空-->
        
    <many-to-one name="Class" column="ClassID" not-null="true"/>
        
      
    </class>
    </hibernate-mapping>

       运行效果如图2.2.1所示,抛出“not-null property references a null or transient value”的异常。

    图2.2.1

      我修改单元测试代码:

            [Test]
            
    public void SaveTest()
            {
                
    using (ISession session = this.sessionFactory.OpenSession())
                {
                    var liu 
    = new Student { Name = "刘冬" };
                    var zhang 
    = new Student { Name = "张三" };

                    var cls 
    = new Class { Name = "1班" };
                    cls.Students 
    = new List<Student> { liu, zhang };

                    liu.Class 
    = cls;
                    zhang.Class 
    = cls;

                    ITransaction tran 
    = session.BeginTransaction();
                    
    try
                    {
                        
    //先保存班级
                        session.Save(cls);

                        
    //后保存学生
                        session.Save(liu);
                        session.Save(zhang);

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

      运行效果图2.2.2所示,运行成功。

    图2.2.2

      我们修改保存“Student”和“Class”实例的先后顺序:

            [Test]
            
    public void SaveTest()
            {
                
    using (ISession session = this.sessionFactory.OpenSession())
                {
                    var liu 
    = new Student { Name = "刘冬" };
                    var zhang 
    = new Student { Name = "张三" };

                    var cls 
    = new Class { Name = "1班" };
                    cls.Students 
    = new List<Student> { liu, zhang };

                    liu.Class 
    = cls;
                    zhang.Class 
    = cls;

                    ITransaction tran 
    = session.BeginTransaction();
                    
    try
                    {
                        
    //先保存学生
                        session.Save(liu);
                        session.Save(zhang);

                        
    //后保存班级
                        session.Save(cls);

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

      运行效果图2.2.3所示,同样抛出“not-null property references a null or transient value”的异常。

    图2.2.3

     

      我们修改一下“Class”的映射文件:

     

    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Domain" namespace="Domain">
      
    <class name="Class" table="T_Class" lazy="true" >
        
    <id name="ID" type="int" column="ClassID">
          
    <generator class="native"/>
        
    </id>

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

        
    <!--设置为不可空-->
        
    <bag name="Students" inverse="true" cascade="all">
          
    <key column="ClassID" not-null="true"/>
          
    <one-to-many class="Student"/>
        
    </bag>

      
    </class>
    </hibernate-mapping>

     

      然后修改单元测试的代码:

     

            [Test]
            
    public void SaveTest()
            {
                
    using (ISession session = this.sessionFactory.OpenSession())
                {
                    var liu 
    = new Student { Name = "刘冬" };
                    var zhang 
    = new Student { Name = "张三" };

                    var cls 
    = new Class { Name = "1班" };
                    cls.Students 
    = new List<Student> { liu, zhang };

                    liu.Class 
    = cls;
                    zhang.Class 
    = cls;

                    ITransaction tran 
    = session.BeginTransaction();
                    
    try
                    {
                        
    //只保存班级
                        session.Save(cls);

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

     

      运行效果如图2.2.4所示,运行成功,并且没有生成“update”语句,只生成“insert into”语句。

    图2.2.4

      这样,保存“一”的这端(“Class”实例),就能够将“一”得那端(“Student”)连带保存。其中映射文件中的“inverse”的属性是反转的意思,就是将操作交给双向关联关系中的另一端。

      我们细观察到生成的SQL语句,只生成了“insert into”语句,这样,执行效率就变的高了。

      2.3 双向关联映射的数据查询

      编写单元测试的代码:

      

            [Test]
            
    public void SelectTest()
            {
                
    using (ISession session = this.sessionFactory.OpenSession())
                {
                    var cls 
    = session.Get<Class>(1);

                    
    foreach (var item in cls.Students)
                    {
                        Console.WriteLine(
    "学生名为:{0}", item.Name);
                        Console.WriteLine(
    "班级名为:{0}", item.Class.Name);
                    }
                }
            }

      运行效果如图2.3.1所示,比较以往使用SQL语句编程的代码后,发现调用“一对多”关联映射的集合变得如此方便。

    图2.3.1

      代码下载

      出处:http://www.cnblogs.com/GoodHelper/archive/2011/03/03/nhibernate09.html

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

  • 相关阅读:
    20、Python之函数参数的使用
    19、Python之函数的基本使用
    18、Python之文件修改及f.seek的使用
    17、Python之文件处理的其他方法
    15、字符编码
    14、Python基本数据类型及内置方法(集合)
    13、Python基本数据类型及内置方法(列表、元组、字典)
    12、Python基本数据类型及内置方法(数字、字符串)
    11、Python流程控制之for循环
    10、Python流程控制之while循环
  • 原文地址:https://www.cnblogs.com/GoodHelper/p/nhibernate09.html
Copyright © 2011-2022 走看看