[Nhibernate] one-to-many

   一对多。典型的Department和Employee. 二者关系一对多(一个部门下有很多员工)

   SQL建立脚本:

  

Create Table
USE  [ ziyeNhiDB ]
GO
/* ***** Object:  Table [dbo].[Emp]    Script Date: 10/08/2011 13:59:11 ***** */
SET ANSI_NULLS  ON
GO
SET QUOTED_IDENTIFIER  ON
GO
SET ANSI_PADDING  ON
GO
CREATE  TABLE  [ dbo ]. [ Emp ](
     [ EmpId ]  [ int ]  IDENTITY( 1, 1NOT  NULL,
     [ EmpName ]  [ varchar ]( 50NOT  NULL,
     [ DepId ]  [ int ]  NULL,
  CONSTRAINT  [ PK_Emp ]  PRIMARY  KEY  CLUSTERED 
(
     [ EmpId ]  ASC
) WITH (PAD_INDEX   =  OFF, STATISTICS_NORECOMPUTE   =  OFF, IGNORE_DUP_KEY  =  OFF, ALLOW_ROW_LOCKS   =  ON, ALLOW_PAGE_LOCKS   =  ONON  [ PRIMARY ]
ON  [ PRIMARY ]

GO
SET ANSI_PADDING  OFF
GO
ALTER  TABLE  [ dbo ]. [ Emp ]   WITH  CHECK  ADD   CONSTRAINT  [ FKD9930B51E340AA6 ]  FOREIGN  KEY( [ DepId ])
REFERENCES  [ dbo ]. [ Dep ] ( [ DepId ])
GO
ALTER  TABLE  [ dbo ]. [ Emp ]  CHECK  CONSTRAINT  [ FKD9930B51E340AA6 ]


USE  [ ziyeNhiDB ]
GO
/* ***** Object:  Table [dbo].[Dep]    Script Date: 10/08/2011 13:59:49 ***** */
SET ANSI_NULLS  ON
GO
SET QUOTED_IDENTIFIER  ON
GO
SET ANSI_PADDING  ON
GO
CREATE  TABLE  [ dbo ]. [ Dep ](
     [ DepId ]  [ int ]  IDENTITY( 1, 1NOT  NULL,
     [ DepName ]  [ varchar ]( 50NOT  NULL,
  CONSTRAINT  [ PK_Dep ]  PRIMARY  KEY  CLUSTERED 
(
     [ DepId ]  ASC
) WITH (PAD_INDEX   =  OFF, STATISTICS_NORECOMPUTE   =  OFF, IGNORE_DUP_KEY  =  OFF, ALLOW_ROW_LOCKS   =  ON, ALLOW_PAGE_LOCKS   =  ONON  [ PRIMARY ]
ON  [ PRIMARY ]

GO
SET ANSI_PADDING  OFF

   建立好了随便插入点数据。Employee表中有一个Department外键。可以理解为两张表的关系是由employee表联系的。(后面中的inverse会提到)

   两个实体类

  

Employee
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Iesi.Collections.Generic;

namespace ZiyeNhi.Entities
{
     public  class Employee
    {
         public  virtual  int EmpId {  getset; }

         public  virtual  string EmpName {  getset; }
    }
}

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Iesi.Collections.Generic;

namespace ZiyeNhi.Entities
{
     public  class Department
    {
         public  virtual  int DepId {  getset; }

         public  virtual  string DepName {  getset; }

         /*  因为一个Department里有多个employee
         * 这里用ISet集合
         * 命名空间Iesi.Collections.Generic;
         
*/

         private ISet<Employee> employees;

         public  virtual ISet<Employee> Employees
        {
             // 至于这个get为什么要这么写。留给大家猜一猜。
             get
            {
                 if (employees ==  null)
                {
                    employees =  new HashedSet<Employee>();
                }
                 return employees;
            }
             set { employees = value; }
        }
    }
}

接下来配置文件(XML要改成Embedded Resource)

Employee.hbm.xml
<? xml version="1.0" encoding="utf-8"  ?>
< hibernate-mapping
  
xmlns ="urn:nhibernate-mapping-2.2"
  namespace
="ZiyeNhi.Entities"
  assembly
="ZiyeNhi.Entities" >

   < class  name ="Employee"  table ="Emp" >
     < id  name ="EmpId"  column ="EmpId"  type ="int" >
       < generator  class ="identity" >
       </ generator >
     </ id >

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

</ hibernate-mapping >
<? xml version="1.0" encoding="utf-8"  ?>
< hibernate-mapping
  
xmlns ="urn:nhibernate-mapping-2.2"
  namespace
="ZiyeNhi.Entities"
  assembly
="ZiyeNhi.Entities" >

   < class  name ="Department"  table ="Dep" >
     < id  name ="DepId"  column ="DepId"  type ="int" >
       < generator  class ="identity" >
       </ generator >
     </ id >
     < property  name ="DepName"  column ="DepName"  type ="string"  length ="50" ></ property >
     <!-- <one-to-many> -->
     <!-- 这里有一个inverse="true",还有一个lazy="true" 要好好把握后面会介绍 -->
     < set  name ="Employees"  table ="Emp"  inverse ="true"  lazy ="true"   >
       <!-- 这key中的column到底是主表的主键,还是子表的外键。
      自己试下就知道了。一般这两个键的名字都写一样的就懒得管到底是主键还是外键
-->
       < key  column ="DepId"   ></ key >
       <!-- 一对多,class指向Employee -->
       < one-to-many  class ="Employee" />
     </ set >
   </ class >

</ hibernate-mapping >

 一对多的关系配完了,一个部门有N个Employee.

 测试代码

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using NUnit.Framework;
using NHibernate;
using ZiyeNhi.Entities;
using Iesi.Collections;
using Iesi.Collections.Generic;

namespace ZiyeNhi.Test
{
    [TestFixture]
     public  class DepOneToManyEmp
    {
         private ISessionFactory sessionFactory;

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

        [Test]
         public  void SaveDepartmentInfo()
        {
             using (ISession session =  this.sessionFactory.OpenSession())
            {
                var emp1 =  new Employee() { EmpName =  " Bill Gates " };
                var emp2 =  new Employee() { EmpName =  " Paul Allen " };
                var department =  new Department() { DepName =  " microsoft " };
                emp1.Department = department;
                emp2.Department = department;
                ITransaction transaction = session.BeginTransaction();
                 try
                {
                    session.Save(department);
                    session.Save(emp1);
                    session.Save(emp2);
                    transaction.Commit();
                }
                 catch (Exception ex)
                {
                    transaction.Rollback();
                     throw ex;
                }
            }
        }

        [Test]
         public  void GetDepartmentInfo()
        {
             using (ISession session =  this.sessionFactory.OpenSession())
            {
                var department = session.CreateQuery( " from Department ").List<Department>().FirstOrDefault();

                Console.WriteLine( " 部门名称:{0} ", department.DepName);

                Console.WriteLine( " 部门人数:{0} ", department.Employees.Count.ToString());

                 if (department.Employees.Count >  0)
                {
                     foreach (Employee emp  in department.Employees)
                    {
                        Console.WriteLine( " 员工姓名:{0} ", emp.EmpName);

                        Console.WriteLine( " 部门数量:{0} ", department.Employees.First().Department.DepName);

                        Console.WriteLine( " ------------------- ");
                    }
                }
            }
        }
    }
}

先SAVE一些信息到数据库中。

如果不喜欢这样用。可以用(cascade).进行级联,但是要慎用。

然后我们来测试查询。

 [Nhibernate] one-to-many_第1张图片

2条SQL语句

第一条取出了部门的信息。然后把部门的名字显示出来了

第二条查出了部门里的Employee信息和count.然后把他们显示出来。

下面我把测试代码变一下。

[Test]
         public  void GetDepartmentInfo()
        {
             using (ISession session =  this.sessionFactory.OpenSession())
            {
                var department = session.CreateQuery( " from Department ").List<Department>().FirstOrDefault();

                Console.WriteLine( " 部门名称:{0} ", department.DepName);

                 // Console.WriteLine("部门人数:{0}", department.Employees.Count.ToString());

                
// if (department.Employees.Count > 0)
                
// {
                
//     foreach (Employee emp in department.Employees)
                
//     {
                
//         Console.WriteLine("员工姓名:{0}", emp.EmpName);

                
//         Console.WriteLine("部门数量:{0}", department.Employees.First().Department.DepName);

                
//         Console.WriteLine("-------------------");
                
//     }
                
// }
            }
        }

 结果

[Nhibernate] one-to-many_第2张图片 

只显示一条语句就是查询Department信息。

那肯定啊,因为没有查询Employee的信息 他怎么会生成2条SQL语句呢。

 在把配置文件改一下:

Department.hbm.xml
<? xml version="1.0" encoding="utf-8"  ?>
< hibernate-mapping
  
xmlns ="urn:nhibernate-mapping-2.2"
  namespace
="ZiyeNhi.Entities"
  assembly
="ZiyeNhi.Entities" >

   < class  name ="Department"  table ="Dep" >
     < id  name ="DepId"  column ="DepId"  type ="int" >
       < generator  class ="identity" >
       </ generator >
     </ id >
     < property  name ="DepName"  column ="DepName"  type ="string"  length ="50" ></ property >

     <!-- <one-to-many> -->
     < set  name ="Employees"  table ="Emp"  inverse ="true"  lazy ="false"   >
       < key  column ="DepId"   ></ key >
       < one-to-many  class ="Employee" />
     </ set >
   </ class >

</ hibernate-mapping >

 lazy改为了false: 编译测试。

[Nhibernate] one-to-many_第3张图片 

怎么样 是2条吧。

lazy="true",就是说启用了department下的employee属性为延迟加载。当我们调用这个属性的时候才加载;

比如:

 

 Console.WriteLine("部门人数:{0}", department.Employees.Count.ToString());

我们调用了department.Employees.Count.ToString();这个时候才加载

如果设置为lazy="false",无论你调用或者没有调用它都会加载。 这玩意高级。

好了。下面我们在来改一下。

 

[Test]
         public  void GetDepartmentInfo()
        {
            var department = GetDepartment();
            Console.WriteLine( " 部门名称:{0} ", department.DepName);
             // Console.WriteLine("部门人数:{0}", department.Employees.Count.ToString());
        }
         public Department GetDepartment()
        {
             using (ISession session =  this.sessionFactory.OpenSession())
            {
                var department = session.CreateQuery( " from Department ").List<Department>().FirstOrDefault();
                 return department;
            }
        }

 

 先把部门人数注释了。运行结果

[Nhibernate] one-to-many_第4张图片 

 OK 没问题

 

然后把注释去掉,获取部门名称和人数。

结果

[Nhibernate] one-to-many_第5张图片 

部门名称显示出来了。但是Count没有加载出来。

语句太长我把没显示出来的信息贴出来。

 

***** ZiyeNhi.Test.DepOneToManyEmp.GetDepartmentInfo
NHibernate: select department0_.DepId as DepId3_, department0_.DepName as DepName3_ from Dep department0_
部门名称:microsoft
14:10:13,148 ERROR [TestRunnerThread] LazyInitializationException [(null)]- Initializing[ZiyeNhi.Entities.Department#185]- failed to lazily initialize a collection of role: ZiyeNhi.Entities.Department.Employees, no session or session was closed
NHibernate.LazyInitializationException: Initializing[ZiyeNhi.Entities.Department#185]-failed to lazily initialize a collection of role: ZiyeNhi.Entities.Department.Employees, no session or session was closed

 

no session or session was closed

 

就是说session已经关闭了。为什么呢。因为我们通过

 

 

public Department GetDepartment()
        {
             using (ISession session =  this.sessionFactory.OpenSession())
            {
                var department = session.CreateQuery( " from Department ").List<Department>().FirstOrDefault();
                 return department;
            }
        }

 

 

 这个方法来获取Department的 获取到的对象session就关闭了(用了using)

 那么我在调用它的Employee的时候就会失效。session关闭;lazy失效。如果把lazy="true"设置为lazy="false",那是没有问题的。

 那意思就是说我们调用它的属性就必须在session没有关闭之前加载(调用它的属性)。

 

 

[Test]
         public  void GetDepartmentInfo()
        {
            var department = GetDepartment();
            Console.WriteLine( " 部门名称:{0} ", department.DepName);
            Console.WriteLine( " 部门人数:{0} ", department.Employees.Count.ToString());
        }
         public Department GetDepartment()
        {
             using (ISession session =  this.sessionFactory.OpenSession())
            {
                var department = session.CreateQuery( " from Department ").List<Department>().FirstOrDefault();
                 int count = department.Employees.Count;//这句
                 return department;
            }
        }

 

 

 结果:

[Nhibernate] one-to-many_第6张图片 

 OK了吧。在session没有关闭之前我们加载他的属性就可以了。

 如果在分层的情况下呢。业务层去调用数据层。难道还要在数据层把这些属性加载好?这样就达不到想要的效果了。

 要么把session提到业务层?要么手动控制session的状态?

 

 

 高手总是在最后出现->spring.net!

 

 

你可能感兴趣的:(one-to-many)