ORM篇——使用NHibernate配置对象实体的一些小问题

问题记录,前段时间公司有新的项目所以我想在新的项目里使用NHibernate这类ORM框架来代替原有的代码生成的拼SQL,不过可惜的是最终使用NHibernate还是没成功,最终还是继续使用已有的代码生成方式。

NHibernate在去年的这个时候也研究过,可后来也搁浅了,今年的这个时候研究了结果有搁浅了,不知道是不是犯冲?不过还是把遇到的问题记录下来吧,以备后患。

配置实体的一些小问题

下面就开始记录下我碰到的问题吧,其实都是自己没看资料而导致的问题,稍微熟悉NHibernate的朋友都应该知道的。

1、hbm文件的格式检查

这个问题是最弱智的,由于我在手写映射文件的时候没有将NHibernate的XSD文件放到VS相关目录下(X:\Program Files\Microsoft Visual Studio X.0\Xml\Schemas),而直接根据数据库关系手写了相应的映射文件,结果导致程序一再运行报错(由于NHibernate在运行的时候会将所有的映射文件格式是否正确进行检查),导致不得不反工。

2、延迟加载与直接加载引发的问题

在实际配置映射文件的过程中,我采用的配置方式与Linq2Sql类似,主要的规则就是:

1)如果此表为主键表,其他表中与此表,则在映射文件中添加<bag>集合,同时在实体代码中添加集合对象

配置代码如下:


1

2

3

4

<bag name="StudentList" table="Student">

  <key column="ID" />

  <one-to-many class="Model.Student, Model"/>

</bag>

实体代码如下:


1

2

3

4

5

6

7

8

9

10

private IList<Student> _studentList = new List<Student>();

/// <summary>

/// 子集:ID

/// </summary>

public virtual IList<Student> StudentList

{

    get { return _studentList; }

    set { _studentList = value; }

}

2)如果此表存在外键关联,则在映射文件中添加<many-to-one>多对一映射,同时在实体代码中添加实体属性

配置代码如下:


1

2

3

<many-to-one name="Class" class="Model.ClassInfo, Model">

  <column name="ClassId" length="4" sql-type="int" not-null="false" />

</many-to-one>

实体代码如下:


1

2

3

4

5

6

7

8

9

10

private ClassInfo _class;

/// <summary>

/// 外键:ClassId

/// </summary>

public virtual ClassInfo Class

{

    get { return _class; }

    set { _class = value; }    

}

具体数据库关系图如下:

如果是正常访问类本身属性是没什么问题,但是如果访问上面配置外键Class属性或者StudentList集合属性就会出现下面的异常:

其原因就是延迟加载导致,由于默认情况下NHibernate对于外键这类数据是采用延迟加载的,而我的查询方式是采用如下代码:


1

2

3

4

5

6

7

8

9

public virtual IList<T> GetList()

{

    using (_session = NHibernateHelper.GetCurrentSession())

    {

        IList<T> list = (from item in _session.Query<T>()

                         select item).ToList<T>();

        return list;

    }

}

可以看到代码中,我采用using方式将_session在读取完数据后释放了,而此时外键数据,这个时候并没有加载上去,所以,当我需要访问外键数据的时候,NHibernate会再次查询数据库,而此时的_session已经被关闭了,所以就导致了上面的异常。

如果想解决这个问题有2个办法:

1)才用直接加载,在配置的时候为<bag>或<many-to-one>配置上增加lazy=”false”属性,表示不采用延迟加载

2)修改查询代码,不将_session给关闭掉,如下代码:


1

2

3

4

5

6

7

8

9

public virtual IList<T> GetList()

{

    _session = NHibernateHelper.GetCurrentSession();

    

    IList<T> list = (from item in _session.Query<T>()

                        select item).ToList<T>();

    return list;

    

}

其实这个问题在李哥(李永京)的NHibernate系列中已经写明了:初探延迟加载机制初探立即加载机制

3、复合主键的问题

在实际的数据库表设计中经常会出现复合主键的设计,比如:通过一张关联表来实现2张表之间的多对多关系,如下图:

根据我上面列出的规则,我的配置如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">

  <class name="Model.SubjectAndStudent, Model" table="SubjectAndStudent">

    <composite-id>

      <key-property name="SubjectId" type="int">

        <column name="SubjectId" length="4" sql-type="int" not-null="false" />

      </key-property>

      <key-property name="StudentId" type="int">

        <column name="StudentId" length="4" sql-type="int" not-null="false"/>

      </key-property>

    </composite-id>

    <many-to-one name="Subject"  class="Model.Subject, Model">

      <column name="SubjectId" length="4" sql-type="int" not-null="false" />

    </many-to-one>

    <many-to-one name="Student"  class="Model.Student, Model">

      <column name="StudentId" length="4" sql-type="int" not-null="false" />

    </many-to-one>

  </class>

</hibernate-mapping>

实体代码如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

public class SubjectAndStudent

{

    public virtual int SubjectId { get; set; }

    public virtual int StudentId { get; set; }

    public virtual Subject Subject { get; set; }

    public virtual Student Student { get; set; }

    #region override

    public override bool Equals(object obj)

    {

        return base.Equals(obj);

    }

    public override int GetHashCode()

    {

        return base.GetHashCode();

    }

    #endregion

}

这边需要注意的是,采用复合主键映射,在编写实体类的时候,需要重载Equals和GetHashCode方法,否则会报错。

正常来看,这个映射和实体代码的编写没什么问题,但是在实际的新增运行过程中出现了这个问题:

仔细查看了下映射文件才发现问题所在,就是在具体的配置映射关系的时候将SubjectId和StudentId配置了2次,而实际在表中,这2个字段都只有一个,找了下资料,解决办法还是有,想了解的朋友可以继续查看李哥(李永京)的联合主键(composite-id)这篇文章,不过在李哥的文章中给出的解决方案我觉得有点繁琐,需要将复合主键单独建立一个类,然后重新编写映射关系和实体类。

后来在纠结了很久的情况下,无意中在VS中按空格查看<many-to-one>配置节下可用的属性的时候发现了2个属性insert和update,查了下资料后知道这2个属性是用于控制在新增和更新状态下是否映射,将其都置为false后,代码果然正常执行了,配置如下:


1

2

3

4

5

6

<many-to-one name="Subject"  class="Model.Subject, Model" insert="false" update="false">

  <column name="SubjectId" length="4" sql-type="int" not-null="false" />

</many-to-one>

<many-to-one name="Student"  class="Model.Student, Model" insert="false" update="false">

  <column name="StudentId" length="4" sql-type="int" not-null="false" />

</many-to-one>

话说,各位看过的朋友如果觉得本文对您还有点用,或者觉得本文还有价值的话,麻烦将鼠标移到【推荐】上,帮我点击下,非常非常的感谢!

时间: 2025-01-14 02:56:01

ORM篇——使用NHibernate配置对象实体的一些小问题的相关文章

ORM篇——有关NHibernate查询封装

前篇文章记录了我在配置NHibernate实体所遇到的一些问题,今天这篇则主要介绍的是对NHibernate的查询封装相关的问题. NHibernate到目前的3.0版本已经有了几种查询方式,比如:Get.Query.HQL.Criteria,实际使用的过程中可以根据需求选择其中1种或多种方式进行查询,对于许多项目开发过程中总会对项目进行一些抽象封装,采用NHibernate进行开发的话,大部分应该都会对Nhibernate进行一些简单的封装,如下图: 基本的增删改我就不说了,今天主要想说的是对

ORM利器:NHibernate(三)五部曲+简单对象CRUD+HQL

前面的两篇文章中,我们对NHibernate已经做了大致了解 <ORM利器:NHibernate(一)简介>Nhibernate的作用:解决了对象和数据库的转化问题 <ORM利器:NHibernate(二)使用CodeSmith快速生成映射文件和映射类>利用CodeSmith由表导出映射类(就是通常所说的Entity)和映射文件(告诉你表和对象之间是如何建立一一对应的关系的). 接下来将会对NHibernate的使用做Demo解析,分为五部曲: 创建表.若要把对象转换为数据库中的表

NHibernate3剖析:Query篇之NHibernate.Linq增强查询

系列引入 NHibernate3.0剖析系列分别从Configuration篇.Mapping篇.Query篇.Session策略篇.应用篇等方面全面揭示NHibernate3.0新特性和应用及其各种应用程序的集成,基于NHibernte3.0版本.如果你还不熟悉NHibernate,可以快速阅读NHibernate之旅系列文章导航系列入门,如果你已经在用NHibernate了,那么请跟上NHibernate3.0剖析系列吧. NHibernate专题:http://kb.cnblogs.com

ORM利器:NHibernate(二)使用CodeSmith快速生成映射文件和映射类

    在上一篇文章<ORM利器:NHibernate(一)简介>,我们对NHibernate做了简要介绍,接下来的系列文章将以Demo的形式和大家分享使用NHibernate的初步使用. 一 CodeSmith简介 本文以表自动生成NHibernate的映射文件和映射类的实例来说明一下本软件的使用方法.     CodeSmith是一种基于模板的代码生成工具,其使用类似于ASP.NET的语法来生成任意类型的代码和文件.使用 CodeSmith,可以生成包括简单的强类型集合和完整应用程序在内的

ORM利器:NHibernate(一)简介

简介: ORM(Object Relational Mapping)对象关系映射,是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术 . 为什么出现ORM?     面向对象的特征:我们通常使用的开发语言Java,.Net都是面向对象的.面向封装了对象,对象内部有属性和方法.     关系型数据库的特点:以表的形式组织我们的数据.以列的形式表述了表的特征.表中的一条记录=一个对象,表中的列代表了对象中的属性.表之间是建立了一定的联系.     如何表中的一条记录提取出来封装成一个对象?

EXT心得--并非所有的items配置对象都属于EXT的内置类

之前我对EXT的items中未指明xtype的配置对象有一个错误的认识--即虽然某个items未指明它下面的某个组件的xtype,但这个组件肯定属性EXT的某个类.然而今天在查看actioncolumn的配置items时发现这个思想是错误的认识. 看以下代码 },{ header : "删除", xtype:'actioncolumn', //3.4.0/#!/api/Ext.grid.ActionColumn 未在当前版本找到该类.--ext4api Ext.grid.column.

atitit. orm mapping cfg 映射配置(3)-------hbnt one2maney cfg

atitit. orm mapping cfg  映射配置(3)-------hbnt one2maney  cfg 1. 建立list 1 2. 配置xml 1 3. Hibernate中Set和List的配置 1 4. Bag(结合了List与Set), 2 1. 建立list /** * 集合属性只能以接口声明.例如在下面的代码中,schools的类型只能是List,不能是ArrayList,但该集合属性必须使用实现类完成初始化. */ public List list=new Array

ORM进阶之Hibernate中对象的三大状态解析

ORM进阶之 ORM简单介绍 ORM进阶之Hibernate简单介绍及框架搭 ORM进阶之Hibernate的三大对象 ORM进阶之Hibernate中对象的三大状态解析 在Hibernatea中每一个对象都有三种状态,他们在这三种状态下,Hibernate会他们进行不同的处理.下边我们通过一张图来看一下这三种状态以及他们之间的互相转换! 能够看到对象可能会有这三种状态.暂时状态(transient),持久化状态(persistent).游离状态(detached).下边我们来分别来解释一下这三

配置对象方法传参

我们见到的一般常规的函数传参是下面这样的: function fn ( a,b ) { console.log( a,b ); } //常规传参方式 fn( 1,2 ); //12 但是当函数需要很多个参数,而且参数的位置会影响到结果的时候常规的函数传参方式就显得捉襟见肘,例如 function fn ( a,b,c,d,e,f,g ){ console.log( a,b,c,d,e,f,g ); } 此时如果再使用原来那种传参方式出错的概率会大大增加 此时我们可以尝试 配置对象 传参,可以把上