• 一步步学习NHibernate(4)——多对一,一对多,懒加载(1)


    请注明转载地址:http://www.cnblogs.com/arhat

    通过上一章的学习,我们学会如何使用NHibernate对数据的简单查询,删除,更新和插入,那么如果说仅仅是这样的话,那么NHibenrate的优势有在哪里呢?那么今天就要和大家一起来分享一下NHibernate的优势——懒加载(初探)。

    我们知道,在关系数据库中,表和表之间是有联系的,那么通常情况下,我们通过连接查询能够把相关的数据查询处理,只是连接语句似乎大概写起来似乎是有些繁琐的,那么NHibenrate为我们提供了一种变相的操作,可以使这种连接查询变得简单多了。

    现在呢,我们更改一下数据库,在数据库中我们新建一个表Clazz(班级表),并插入相关的数据。

    wps_clip_image-560

    同时,我们得更改一下Student表,在Student表中加入一个外键Cid,我们知道,学生和班级之间是存在者关系的。我们从两个方面来说:

    对于Clazz:一个班级对应多个属性就是一对多的关系(one-to-many),是one的一方

    对于Student:多个学生对应一个班级就是多对一的关系(mand-to-one),是many的一方

    所以,我们需要对Student表中建立一个和Clazz对应的外键

    wps_clip_image-4236

    好,现在我们清空一下Student中数据。

    下面就是一个重点了,我们需要改写一下Student实体类和添加一个Clazz实体类。由于Student是Clazz的外键表,所以,我们应该这样改写Student实体类,在Student实体类中添加一个属性为Clazz,用来获得这个学生对应的班级,代码如下:

    public class Student
    
        {
    
    public virtual int SId { get; set; }
    
    public virtual string SName { get; set; }
    
    public virtual string SSex { get;set; }
    
    public virtual DateTime SBirthday{get;set;}
    
    public virtual Clazz Clazz { get; set; }
    
    }

    然后添加Clazz类,代码如下

    public class Clazz
     {
    
    public virtual int CId { get; set; }
    
    public virtual string CName { get; set; }
    
    public virtual ISet<Model.Student> Students { get; set; }
    
      }

    由于,Clazz和Student是多对一的关系,所以在Clazz中需要定义一个集合属性,用来获得这个班级中的学生。这里使用的集合是Iesi.Collections.Generic.ISet类型。那么需要在Model项目中添加Iesi.Collections.dll的引用。

    但是我们虽然更改了实体类的属性,但是NHibernate却不知道他们之间的关系,所以我们需要更改Student.hbm.xml和Clazz.hbm.xml映射文件,使NHibernate能够知道他们之间的关系。

    Student.hbm.xml内容如下:

    <?xml version="1.0" encoding="utf-8" ?>
    
    <hibernate-mapping assembly="Model" namespace="Model" xmlns="urn:nhibernate-mapping-2.2">
    
        <class name="Student" table="Student">
    
            <id name="SId" type="int">
    
                <column name="sid"></column>
    
                <generator class="native"></generator>
    
            </id>
    
            <property name="SName" type="string">
    
                <column name="sname"/>
    
            </property>
    
            <property name="SSex" type="string">
    
                <column name="ssex"/>
    
            </property>
    
            <property name="SBirthday" type="DateTime">
    
                <column name="sbirthday"/>
    
            </property>
    
            <many-to-one name="Clazz" column="CId" class="Model.Clazz"></many-to-one>
    
        </class>
    
    </hibernate-mapping>

    在Student.hbm.xml中,我们添加了一个<many-to-one>的节点,这个节点是用来说明在Student中Clazz属性是一个外键。其中name是Student中Clazz属性的属性名,column是Student表中的外键字段名,class是用来设置和Student关联的对象的完整名字(命名空间+类名)。

    然后,我们看一下Clazz.hbm.xml的文件内容。

    <?xml version="1.0" encoding="utf-8" ?>
    
    <hibernate-mapping assembly="Model" namespace="Model" xmlns="urn:nhibernate-mapping-2.2">
    
        <class name="Clazz" table="clazz" lazy="true">
    
            <id name="CId" type="int">
    
                <column name="cid"></column>
    
                <generator class="native"></generator>
    
            </id>
    
            <property name="CName" type="string">
    
                <column name="cname"></column>
    
            </property>
    
            <set name="Students" table="student">
    
                <key column="Cid"></key>
    
                <one-to-many class="Model.Student"/>
    
            </set>
    
        </class>
    
    </hibernate-mapping>

    大家可以看到,由于在Clazz实体类中我们定义了一个集合属性,那么也就是表示了Clazz和Sutdent之间的多对一关系。那么自在Clazz.hbm.xml映射文件中,需要通过<set>节点来声明这个属性。其中<Set>节点中的name是Clazz中的集合属性名,table是指对应着数据库的那个表。其中<set>节点中有两个子节点<key>是用来说明Student表中的外键字段,<ono-to-many>是用来说明班级和学生之间的一对多关系,class属性是指和Clazz关联的Student的全类名。

    一旦这两个映射文件通过<many-to-one>和<one-to-many>的设置,那么NHibenrate就知道了Student和Clazz之间的关系了。

    由于在Student表中没有数据,我们现在插入几条数据来做测试。

    wps_clip_image-21467

    然后,我们得做两个测试,才能说明NHibernate给我提供的懒加载机制以及测试中的问题。现在我们更改一下D_User.cs的代码

    public Model.Student GetUser(int id) 
    {
    
    //using(ISession session = NHibernateHelper.OpenSession())
    
    //{
    
    //    return session.Get<Model.Student>(id);
    
    //}
    
    //}
    
    ISession session = NHibernateHelper.OpenSession();
    
                {
    
    return session.Get<Model.Student>(id);
    
                }
    
     }

    现在老魏有一个要求,就是要查找一下id=1学生的姓名和所在的班级名称。那么如果在SQL中,我们得使用一个连接语句,但是在NHibernate中一切将会变得简单多了。

    然后,我们在主程序中更改一下代码:

    DAL.D_User dal = new DAL.D_User();
    
     Model.Student student = dal.GetUser(1);
    
    Console.WriteLine("学生:"+student.SName+",所在的班级是:"+student.Clazz.CName);

    运行一下,看看结果如何

    wps_clip_image-14777

    我们发现,的确查询出了正确的结果。那么在执行的时候,NHibernate发出了两条SQL语句,那么大家可以看出,一个是查询Student的语句,一个是查询班级的SQL语句,那么现在我们的问题就来了,为什么NHibernate会发出两条语句呢。我们来测试一下,我们把:

    Console.WriteLine("学生:"+student.SName+",所在的班级是:"+student.Clazz.CName);

    给更改一下,只输出学生的姓名。我们会发现NHibernate只发出了一条SQL语句。

    wps_clip_image-18415

    那么这是为什么呢?原来是NHibernate在做查询的时候,只是把数据Student表的数据查询出来了,反而和它关联的Clazz数据并没有查询出来。但是如果我们把注释去掉,在运行的时候,会发现NHibernate发出两条语句,而第二条语句就是用来查询和Student关联的Clazz属性的。这是为什么呢?这个原因就是在NHibernate默认情况下是启用懒加载机制的,那么什么是懒加载呢?

    懒加载,我们认为是在需要的时候才开始执行,不需要的时候就不执行。那上面我们的测试结果已经说明这个问题了,当我们只是查询Student的基本信息时,它就只查询Student信息,但是现在由于我们不紧要显示Student的基本信息,还要显示班级信息,那么在显示完Student信息之后,发现有一句话:student.Clazz.CName。那么NHibenrate就会知道:”哦,现在你需要你的班级信息了,那好吧,我把班级信息查询出来,并班Clazz的信息创建一个对象,把这个对象赋值给Student的Clazz属性吧”。此时,NHibenrate就会向数据库发送一条SQL语句来查询Clazz的信息。

    从上面我么可以看出,NHinberate的最大优点就是拥有的懒加载,使我们的查询变得简单起来了。

    下面我们得做第二实验,D_User代码如下:

    public Model.Student GetUser(int id) 
     {
    
    using(ISession session = NHibernateHelper.OpenSession())
    
                {
    
    return session.Get<Model.Student>(id);
    
                }
    
                }
    
    //ISession session = NHibernateHelper.OpenSession();
    
    //{
    
    //    return session.Get<Model.Student>(id);
    
    //}
    
    }

    这回呢,我们使用using语句来释放ISession资源。主程序代码不变,我们来看看运行的结果:

    wps_clip_image-12360

    出错了,报了一个异常”no Session”。这是为什么呢?因为我们使用using语句来强制释放ISession的资源,而这个时候只能查询出Student的基本信息,反而查询不到了和它关联的Clazz西信息,原因很简单,就是懒加载的执行需要ISession在没有释放的前提下才能够执行的,所以我们一旦释放了ISession的资源,则懒加载是不起作用的。那么看到这里,大家可以想到,这是懒加载的问题,那么如果我们不使用懒加载不就可以了吗?我们来做一下实验。把Student.hbm.xml和Clazz.hbm.xml的<class>节点中取消懒加载,代码如下:

    <class name="Clazz" table="clazz" lazy="false">
    
    <class name="Student" table="Student" lazy="false">

    然后我们运行一下程序,看看结果如何:

    wps_clip_image-1790

    的确查询出来了,但是我么看看SQL语句,此时的SQL语句是一个连接查询,当然从性能上看基本上没有什么影响,因为这是从many这一段发起的。如果从one那一短发起,那就大大不同了,至于为什么,我们在下一章中来讨论。

    我们会发现,如果我们取消了懒加载,那么结果是正确的,这就叫“立即执行”。但是,如果没有了懒加载的话,那么我们在写程序的时候会非常的难受,因为我们感受到懒加载给我们带来的好处,一般情况下,我们在使用完ISession之后要释放资源的。所以这里就出现一个矛盾,我们既要释放资源,也要使用懒加载,那么该怎么办呢?请看下章!

  • 相关阅读:
    认识hammer.js
    Mac使用Charles进行HTTPS抓包
    CentOS7下安装Python3及Pip3并保留Python2
    CenOS7.4内核升级修复系统漏洞
    PHP连接不上MySQL解决方案总结
    linux安装redis
    图片上传的两种实现方式
    python笔记6 模块与包 程序开发规范 包 re sys time os模块
    python Image 模块处理图片
    python笔记5 接口类抽象类 封装 反射 设计模式 模块 :random随机数 josn shelve持久化存储
  • 原文地址:https://www.cnblogs.com/arhat/p/3570303.html
Copyright © 2020-2023  润新知