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

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

内容摘要

单向关联映射

双向关联映射

 

一、单向关联映射

1.1 单向关联映射的描述

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

图1.1.1

 

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

 

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

        
public   virtual   string  Name {  get set ; }
    }

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

        
public   virtual   string  Name {  get set ; }

        
public   virtual  IList < Student >  Students {  get set ; }
    }

 

映射文件如下:

< 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 {  get set ; }

        
public   virtual   string  Name {  get set ; }

        
public   virtual  Class Class {  get set ; }
    }

 

< 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

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


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