NHIbernate学习总结
什么是NHIbernate?
Nhibernate是一个ORM工具
ORM就是一种把数据库的数据结构,通过映射文件描述其关系,将程序中的对象与数据库关联起来的技术,方便我们在程序中,通过操作对象的方式对数据库进行增增删改查操作
而NHibernate就是帮助实现这种技术的工具
市面上这种工具还有很多:比如SQLSugar、EF、Dapper
EF:vs自带,使用方便,开发效率高,通过创建对应的实体对象,在dbcontext中添加实体与数据库表的映射,就可以实现对数据库的增删改查,但性能一般
NHibernate:开源、免费、批量读写等特点,个人感觉入门门槛比较高,基本上就是配置又配置,操作流程就是配置、创建持久化类、创建持久化类的映射文件,在映射文件中,配置一对一、一对多、多对多等表关系
SqlSugar:开源、国人开发、面向.net平台、支持.net core
Dapper:轻量、速度快、多数据库支持
NHibernate如何使用?
NHibernate在我看来就是配置+实体+映射
配置:
- 添加引用:使用第三方工具的第一步都是先通过nuget或者手动下载的形式,先将目标工具引用到项目中。
-
为NHibernate配置数据库连接信息,在web.config中添加配置元素:
<configSections> -- 添加配置节点名称;
<hibernate-configuration> -- 里面写具体的信息,例如数据库连接字符串
dialect(方言)-- 是必须设置的,用于匹配当前使用的数据库SQL脚本上的差异
Mapping – 声明了持久化类即映射文件所在的应用程序集
web.config
除了这种方式,还可以<hibernate-configuration>的内容单独配置到hibernate.cfg.xml中
实体:
创建简单的.net对象类,使用get,set属性来访问数据成员
public class Person
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
配置映射:
创建了类后,还需要创建映射文件:**.hbm.xml,将类和数据库表及字段关联起来,映射文件和实体通常是成对出现的
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="DemoEntity" assembly="DemoEntity">
<class name="Person" table="T_Persons" lazy="false">
<id name="Id" column="Id" type="Int32">
<generator class="native" />
</id>
<property name="Name" access="property" type="String">
<column name="Name" />
</property>
</class>
</hibernate-mapping>
映射文件中的元素与映实体中的属性一一对应
property :对应实体类中的属性名;id:表示唯一标识;column:属性对应的表的列名;
使用:
做完上面准备工作之后,就可以开始通过ISession从数据库中存取数据了。
ISession从ISessionFactory中创建,ISessionFactory是什么呢?
我们可以把它当做存放数据库的工厂,我们需要操作一个数据库,则通过在工厂中创建一个实例来获取一个数据库
ISessionFactory sessionFactory = new Configuration().Configure().BuildSessionFactory();
ISessionFactory 通常只被初始化一次,通常会先实现一个帮助类:NHibernateHelper
private const string CurrentSessionKey = "nhibernate.current_session";
private static readonly ISessionFactory sessionFactory;
static NHibernateHelper()
{
sessionFactory = new Configuration().Configure().BuildSessionFactory();
}
public static ISession GetCurrentSession()
{
HttpContext context = HttpContext.Current;
ISession currentSession = context.Items[CurrentSessionKey] as ISession;
if (currentSession == null)
{
currentSession = sessionFactory.OpenSession();
context.Items[CurrentSessionKey] = currentSession;
}
return currentSession;
}
以下是保存命令:
using (isession = NHibernateHelper.GetCurrentSession())
{
using (var trans = isession.BeginTransaction())
{
var p=new Person();
p.Name="xxx";
isession.Save(pmodel);
trans.Commit();
}
}
查询出列表数据:
using (isession = NHibernateHelper.GetCurrentSession())
{
ICriteria crit = isession.CreateCriteria(typeof(Person));
IList<Person> plist = crit.List<Person>();
return View(plist.ToArray());
}
或者根据id获取:
var pnew = isession.Get<Person>(model.Id);
又或者使用CreateSQLQuery直接执行SQL语句:
var curhouseholders = isession.CreateSQLQuery(string.Format(@"select * from householder where name like '%{0}%'", model.Name)).AddEntity(typeof(HouseHolder));
Var HouseHolders = curhouseholders.List<HouseHolder>();
AddEntity()方法可以使CreateSQLQuery()返回值按照指定类型转化为HouseHolder.
当不使用AddEntity()方法时,curhouseholders.List<HouseHolder>()会失效
注:
-
创建的映射文件,属性要改成嵌入资源,否则会报错
-
Nhibernate为了延迟加载,要求数据库产生映射的类中所有的公开成员(public/protected)都是virtual
3.<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="DemoEntity" assembly="DemoEntity">
这里namespace 实体所在命名空间、assembly 实体所在程序集,填写好了之后,class元素的name属性就可以直接填写类名了
关联关系
关联关系是实体类与实体类之间的结构关系,分别为
一对一:<one-to-one>、多对一:<many-to-one>、多对多:<many-to-many>
以多对一关系为例:多个狗子对应一个主人,表结构:
T_Persons表:Id,Name; T_Dogs表:Id,Name,PersonId
inverse="true" 代表反转,把控制权交给另一方
Person实例
public class Person
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<Dog> DogList { get; set; }
}
Person.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="DemoEntity" assembly="DemoEntity">
<class name="Person" table="T_Persons" lazy="false">
<id name="Id" column="Id" type="Int32">
<generator class="native" />
</id>
<property name="Name" access="property" type="String">
<column name="Name" />
<bag name="DogList" inverse="true" generic="true" table="T_Dogs" lazy="true">
<key column="Id" />
<one-to-many class="Dog" not-found="ignore"/>
</bag>
</class>
</hibernate-mapping>
Dog实例
public class Dog
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual int? PersonId { get; set; }
public virtual Person CurrentPerson { get; set; }
}
Dog.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" >
<class name="DemoEntity.Dog,DemoEntity" table="T_Dogs" lazy="false">
<id name="Id" column="Id">
<generator class="identity"/>
</id>
<property name="Name" access="property" type="String">
<column name="Name" />
</property>
<many-to-one name="CurrentPerson" column="PersonId" class="DemoEntity.Person,DemoEntity" not-found="ignore"></many-to-one>
</class>
</hibernate-mapping>
代码实现,这时添加狗子的同时,将主人的Id也写进去:
public ActionResult Add(Dog dmodel)
{
using (isession = NHibernateHelper.GetCurrentSession())
{
using (var trans = isession.BeginTransaction())
{
var ownner = new Person() { Name = dmodel.Name + "主人", Age = 1, Sex = 1 ,CreateDateTime=DateTime.Now};
dmodel.CurrentPerson = ownner;
isession.Save(ownner);
isession.Save(dmodel);
trans.Commit();
}
}
return Json(new AjaxResult { Status = "ok" });
}
多对多关系:学生和家长是多对多关系,一个学生可以对应多个家长,一个家长也可以对应多个学生,表格:
Student表:Id,Name;Householder:Id,Name;StudentHouseholder:Id,StudentId,HouseholderId
代码如下:
Student实体
public class Student
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<HouseHolder> HouseHolders { get; set; }
}
Student.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="DemoEntity" assembly="DemoEntity">
<class name="Student" table="Student" lazy="true">
<id name="Id" column="Id" type="Int32">
<generator class="native" />
</id>
<property name="Name" access="property" type="String">
<column name="Name" />
</property>
<bag name="HouseHolders" table="StudentHouseHolder" cascade="save-update" lazy="false">
<key column="StudentId" />
<many-to-many class="HouseHolder" column="HouseHolderId" not-found="ignore"/>
</bag>
</class>
</hibernate-mapping>
HouseHolder实体
public class HouseHolder
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<Student> Students { get; set; }
}
HouseHolder.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="DemoEntity" assembly="DemoEntity">
<class name="HouseHolder" table="HouseHolder" lazy="true">
<id name="Id" column="Id" type="Int32">
<generator class="native" />
</id>
<property name="Name" access="property" type="String">
<column name="Name" />
</property>
<bag name="Students" table="StudentHouseHolder" lazy="true" inverse="true">
<key column="HouseHolderId" />
<many-to-many class="Student" column="StudentId" not-found="ignore"/>
</bag>
</class>
</hibernate-mapping>
cascade="save-update" :表示级联,只在保存或更新的时候执行,这样就在添加学生的时候可以自动添加学生家长,并将他们的联系添加到中间表
还有他的的几个值:all|none|save-update|delete;如果设置:all,则在你删除的时候,会帮你把你爸妈也删了
学生表设置cascade的同事家长表也要设置inverse="true",代表控制权交由对方
如果不设置cascade,在保存学生的同时也要保存家长,否则会提示在设置cascade之前,保存临时实例
代码如下:
public ActionResult Add(Student pmodel)
{
using (isession = NHibernateHelper.GetCurrentSession())
{
using (var trans = isession.BeginTransaction())
{
var householder = new HouseHolder { Name = pmodel.Name + "爸" };
var curhouseholder1 = new HouseHolder { Name = pmodel.Name + "妈" };
pmodel.HouseHolders = new List<HouseHolder> { householder, curhouseholder1 };
//isession.Save(householder); 设置级联,家长信息自动保存
isession.Save(curhouseholder1);
isession.Save(pmodel);
trans.Commit();
}
}
return Json(new AjaxResult { Status = "ok" });
}
多对多关系还可以拆解为两个多对一关系,中间表与学生表,中间表与家长表都是多对一的关系