[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).进行级联,但是要慎用。

然后我们来测试查询。

 

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("-------------------");
                
//    }
                
//}
            }
        }

 结果

 

只显示一条语句就是查询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: 编译测试。

 

怎么样 是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;
            }
        }

 

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

 

 OK 没问题

 

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

结果

 

部门名称显示出来了。但是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;
            }
        }

 

 

 结果:

 

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

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

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

 

 

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

 

 

posted @ 2012-03-15 11:21  子夜.  Views(3187)  Comments(2Edit  收藏  举报