NHibernate简单使用介绍
1、在数据库中新建表格,并插入记录,SQL如下:
USE WFC_DB GO create table Students ( Id int primary key IDENTITY(1,1) not null, Name varchar(255), Age int, Score int ) GO insert into Students values('cheng', 10, 60) insert into Students values('liu', 11, 80)
注意:表的Id字段设为自增长,即 IDENTITY(1,1)
2、新建类库项目,添加实体类与映射文件
实体类 Student.cs:
public class Student { public virtual int Id { get; set; } public virtual string Name { get; set; } public virtual int Age { get; set; } public virtual int Score { get; set; } }
注意:属性需为virtual,否则报错
映射文件Student.hbm.xml
<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHbernateLib" namespace="NHbernateLib"> <!--<class name="NHbernateLib.Student, NHbernateLib" table="Students">--> <class name="Student" table="Students"> <id name="Id"> <generator class="native"></generator> </id> <property name="Name"></property> <property name="Age"></property> <property name="Score"></property> </class> </hibernate-mapping>
注意:1、映射文件须以.hbm.xml作为后缀,这是NHibernate所要求的;文件的前缀没有要求,如可以命名为Students.hbm.xml,但最好以实体名为前缀
2、映射文件设为嵌入的资源,方法为:右击该文件,点击属性->选择生成操作,设为嵌入的资源
3、注意标签<hibernate-mapping>中需注明程序集与命名空间,如果不注明则需在标签<class> name属性中明确类名与程序集,如注释
4、如果运行时报该映射文件编译错误,则说明文件内容有误,最有可能的地方是类所属的程序集未指明
3、添加NHibernate程序集引用与主配置文件
程序集可在NHibernate官网上下载已编译好的dll,也可下载源码自己编译成dll
复制NHibernate.dll、NHibernate.xml到该项目文件夹中,并引用前者(后者为注释文件,智能提示)
注意:在很多博客中,作者指出需引用NHibernate官网提供的所有dll,但本例为NHibernate最简单应用,只需引用NHibernate.dll即可
配置文件hibernate.cfg.xml采用源码中配置模板,本来采用SQL Server 2008,配置内容如下:
<?xml version="1.0" encoding="utf-8"?> <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2" > <session-factory> <property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property> <property name="dialect">NHibernate.Dialect.MsSql2008Dialect</property> <!-- 以下两项可在配置文件中指明,也可在代码中动态指定 --> <!--<property name="connection.connection_string">server=yqzhu-peter;database=WFC_DB;uid=sa;pwd=ABcd1234;Connect Timeout=100</property>--> <!--<mapping assembly="NHibernateLib"/>--> </session-factory> </hibernate-configuration>
注意:1、主配置文件文件名hibernate.cfg.xml固定,不可更改
2、文件属性设为始终复制,方法为:右击该文件,点击属性->选择复制到输出目录,设为始终复制,这样主配置文件位置可随意放置
4、添加数据库操作接口类,如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using NHibernate; using NHibernate.Cfg; namespace NHbernateLib { public interface IStudentDao { object Save(Student entity); void Update(Student entity); void Delete(Student entity); Student Get(object id); Student Load(object id); void SaveOrUpdate(Student entity); } public class StudentDao : IStudentDao { private ISessionFactory sessionFactory; public StudentDao() { var configuration = new Configuration(); configuration.Configure(); configuration.SetProperty("connection.connection_string", "server=yqzhu-peter;database=WFC_DB;uid=sa;pwd=ABcd1234;Connect Timeout=100"); configuration.AddAssembly(typeof(Student).Assembly); sessionFactory = configuration.BuildSessionFactory(); } public object Save(Student entity) { using(ISession session = sessionFactory.OpenSession()) { var id = session.Save(entity); session.Flush(); return id; } } public void Update(Student entity) { using (ISession session = sessionFactory.OpenSession()) { session.Update(entity); session.Flush(); } } public void Delete(Student entity) { using (ISession session = sessionFactory.OpenSession()) { session.Delete(entity); session.Flush(); } } public Student Get(object id) { using (ISession session = sessionFactory.OpenSession()) { return session.Get<Student>(id); } } public Student Load(object id) { using (ISession session = sessionFactory.OpenSession()) { return session.Load<Student>(id); } } public void SaveOrUpdate(Student entity) { using (ISession session = sessionFactory.OpenSession()) { session.SaveOrUpdate(entity); session.Flush(); } } } }
5、在解决方案中添加控制台项目,在该项目中引用类库, program.cs如下:
static void Main(string[] args) { IStudentDao studentDao = new StudentDao(); Student student = new Student() { Id = 100, Age = 10, Name = "yang", Score = 90 }; //var obj = studentDao.Save(student); //Console.WriteLine(obj.ToString()); Student student2 = studentDao.Get(2); Console.WriteLine(student2.Score); student2.Score = 50; studentDao.SaveOrUpdate(student2); student2 = studentDao.Get(2); Console.WriteLine(student2.Score); //Student student2 = studentDao.Get(1); //Console.WriteLine(student2.Score); //studentDao.Delete(student2); Console.ReadKey(); }
运行该项目,即可进行测试
(本例包含NUnit测试代码,版本为NUnit 2.6.4, 可到官网下载安装包,其中load方法测试有问题,原因为load为延迟加载,对数据的访问必须有session存在)
作为本博客的补充,可参考NHibernate使用之详细图解
NHibernate使用注意事项:
1、Get与Load的区别是:前者立即加载,后者延迟加载(默认情况下)
2、当数据表关系存在(如多对一),使用Get获取对其他表的引用是延迟加载的
3、NHibernate延迟加载须有Session存在,且配置为Lazy=true(默认),超过session访问延迟加载项时将会出错
一个封装好的NHibernate实例NHibernateDemo2.rar
表关系详解:
一对一:
1、数据库中建立数据表
create table T_Student ( StudentID int IDENTITY(1,1), Name varchar(25) ) create table T_Family ( FamilyID int, Adress varchar(25) )
注意:在配置文件Student.hbm.xml中,虽然配置T_Family的外键,但数据库中没有必要添加该限制
2、添加类及相应配置配置
public class Student { public virtual int ID { get; set; } public virtual string Name { get; set; } } public class Family { public virtual int ID { get; set; } public virtual string Adress { get; set; } public virtual Student Student { get; set; } }
<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHbernateLib" namespace="NHbernateLib"> <class name="Student" table="T_Student"> <id name="ID" column="StudentID"> <generator class="native"/> </id> <property name="Name"/> </class> <class name="Family" table="T_Family"> <id name="ID" column="FamilyID"> <generator class="foreign"> <param name="property">Student</param> </generator> </id> <property name="Adress"/> <one-to-one name="Student" constrained="true"/> </class> </hibernate-mapping>
需要注意的是:Family类中Student属性没有必要与类同名,如为Pupile,此时配置中<generator>中的参数应为Pupile,即与属性名相同(而非类型名)
3、NUnit测试
[TestFixture] public class StudentTest { private ISessionFactory sessionFactory; [TestFixtureSetUp] public void Init() { var configuration = new Configuration(); configuration.Configure(); configuration.SetProperty("connection.connection_string", "server=yqzhu-peter;database=WFC_DB;uid=sa;pwd=ABcd1234;Connect Timeout=100"); configuration.AddAssembly(typeof(Student).Assembly); sessionFactory = configuration.BuildSessionFactory(); } [Test] public void OneToOneAddTest() { using (ISession session = sessionFactory.OpenSession()) { using (ITransaction tran = session.BeginTransaction()) { Student student = new Student() { Name = "liu" }; Family family = new Family() { Adress = "xiamen", Student = student}; session.SaveOrUpdate(family); tran.Commit(); } } } [Test] public void OneToOneGetTest() { using (ISession session = sessionFactory.OpenSession()) { using (ITransaction tran = session.BeginTransaction()) { Family family = session.Get<Family>(4); Console.WriteLine(family.Adress) ; Console.WriteLine(family.Student.Name); tran.Commit(); } } } }
源码中包含双向引用的实例ChildTest,对应SQL
create table T_Child ( ChildID int IDENTITY(1,1), Name varchar(25) ) create table T_Mom ( MomID int, Name varchar(25) )
关于一对一关系需注意的事项:
1、<one-to-one>默认的cascade为all,因此在默认情况下可将临时态(Transient)的实例持久化到数据库中
2、在双向一对一关系中,从ID设为数据库自动生成的类的方向保存数据时(实例中的Child),必须设置该类所引用的类引用自身(即循环引用),否则将无法同时保存所引用的类,但从另一方向(实例中的Mom)则不存在该问题,但良好的实践是两种情况都设为循环引用
一对多:
1、数据库中建立数据表
create table T_Student ( StudentID int IDENTITY(1,1), Name varchar(25), FamilyID int ) create table T_Family ( FamilyID int IDENTITY(1,1), Adress varchar(25) )
注意:配置中Id生成方式设为native,在数据库中创建数据表时需要将Id设为自增长
2、添加类及相应配置配置
public class Student { public virtual int ID { get; set; } public virtual string Name { get; set; } } public class Family { public virtual int ID { get; set; } public virtual string Adress { get; set; } public virtual IList<Student> Students { get; set; } }
<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHbernateLib" namespace="NHbernateLib"> <class name="Student" table="T_Student"> <id name="ID" column="StudentID"> <generator class="native"/> </id> <property name="Name"/> </class> <class name="Family" table="T_Family"> <id name="ID" column="FamilyID"> <generator class="native"/> </id> <property name="Adress"/> <bag name="Students" cascade="all"> <key column="FamilyID"/> <one-to-many class="Student" /> </bag> </class> </hibernate-mapping>
需要注意的是:key中column值为表T_Student中的字段,如果该字段值为Family,此时要将column值设为Family
3、NUnit测试
[TestFixture] public class StudentTest { private ISessionFactory sessionFactory; [TestFixtureSetUp] public void Init() { var configuration = new Configuration(); configuration.Configure(); configuration.SetProperty("connection.connection_string", "server=yqzhu-peter;database=WFC_DB;uid=sa;pwd=ABcd1234;Connect Timeout=100"); configuration.AddAssembly(typeof(Student).Assembly); sessionFactory = configuration.BuildSessionFactory(); } [Test] public void OneToManyAddTest() { using (ISession session = sessionFactory.OpenSession()) { using (ITransaction tran = session.BeginTransaction()) { Student student1 = new Student() { Name = "liu" }; Student student2 = new Student() { Name = "cheng" }; Family family = new Family() { Adress = "xiamen" }; family.Students = new List<Student>() { student1, student2 }; //session.SaveOrUpdate(student1); //session.SaveOrUpdate(student2); session.SaveOrUpdate(family); tran.Commit(); } } } [Test] public void OneToManyGetTest() { using (ISession session = sessionFactory.OpenSession()) { using (ITransaction tran = session.BeginTransaction()) { Family family = session.Get<Family>(8); Console.WriteLine(family.Adress); Console.WriteLine(family.Students.Count); tran.Commit(); } } } }
源码中包含了多对一(ChildTest),一对多与多对一双向关系(PupilTest)的实例
ChildTest对应QSL:
create table T_Child ( ChildID int IDENTITY(1,1), Name varchar(25), MomID int ) create table T_Mom ( MomID int IDENTITY(1,1), Name varchar(25) )
PupilTest对应QSL:
create table T_Pupil ( PupilID int IDENTITY(1,1), Name varchar(25), ClassID int ) create table T_Class ( ClassID int IDENTITY(1,1), Name varchar(25) )
关于一对多,多对一关系需注意的事项:
1、<bag>与<many-to-one>默认的cascade为none,因此需显式指定为all,方可保存临时态(Transient)实例
2、这两种关系不存在一对一中需要循环引用的问题
多对多:
1、数据库中建立数据表
create table T_User ( UserID int IDENTITY(1,1), Name varchar(25) ) create table T_User_Role ( RoleID int, UserID int ) create table T_User_Role ( RoleId int, UserId int )
2、添加类及相应配置配置
public class Role { public virtual int ID { get; set; } public virtual string Name { get; set; } public virtual IList<User> Users { get; set; } } public class User { public virtual int ID { get; set; } public virtual string Name { get; set; } public virtual IList<Role> Roles { get; set; } }
<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHbernateLib" namespace="NHbernateLib"> <class name="Role" table="T_Role"> <id name="ID" column="RoleID"> <generator class="native"/> </id> <property name="Name" /> <bag name="Users" table="T_User_Role" cascade="all"> <key column="RoleId"/> <many-to-many class="User" column="UserId"/> </bag> </class> <class name="User" table="T_User" > <id name="ID" column="UserID"> <generator class="native"/> </id> <property name="Name" /> <bag name="Roles" table="T_User_Role" cascade="all"> <key column="UserId"/> <many-to-many class="Role" column="RoleId"/> </bag> </class> </hibernate-mapping>
需要注意的是:key与<many-to-many>中column值为表T_User_Role中的字段,如果该字段值为user, role,此时要将column值分别设为user, role
3、NUnit测试
public class RoleTest { private ISessionFactory sessionFactory; [TestFixtureSetUp] public void Init() { var configuration = new Configuration(); configuration.Configure(); configuration.SetProperty("connection.connection_string", "server=yqzhu-peter;database=WFC_DB;uid=sa;pwd=ABcd1234;Connect Timeout=100"); configuration.AddAssembly(typeof(Role).Assembly); sessionFactory = configuration.BuildSessionFactory(); } [Test] public void ManyToManySaveTest() { using (ISession session = sessionFactory.OpenSession()) { using (ITransaction tran = session.BeginTransaction()) { Role role1 = new Role() { Name = "Administer" }; Role role2 = new Role() { Name = "User" }; Role role3 = new Role() { Name = "Visitor" }; User user = new User() { Name = "cheng" }; user.Roles = new List<Role>() { role1, role2 }; //session.SaveOrUpdate(role1); //session.SaveOrUpdate(role2); //session.SaveOrUpdate(role3); session.SaveOrUpdate(user); tran.Commit(); } } } [Test] public void ManyToManyGetTest() { using (ISession session = sessionFactory.OpenSession()) { using (ITransaction tran = session.BeginTransaction()) { Role role = session.Get<Role>(8); Console.WriteLine(role.Name); Console.WriteLine(role.Users.Count); tran.Commit(); } } } }
关于多对多,注意<bag>默认的cascade为none,因此需显式指定为all,方可保存临时态(Transient)实例
关于NHIbernate详细介绍可参考刘冬的博客