一对多。典型的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,
1)
NOT
NULL,
[
EmpName
]
[
varchar
](
50)
NOT
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
=
ON)
ON
[
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,
1)
NOT
NULL,
[
DepName
]
[
varchar
](
50)
NOT
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
=
ON)
ON
[
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 {
get;
set; }
public
virtual
string EmpName {
get;
set; }
}
}
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 {
get;
set; }
public
virtual
string DepName {
get;
set; }
/*
因为一个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!