ADO.NET与ORM的比较(2):NHibernate实现CRUD

简介:
说明:个人感觉在Java领域大型开发都离不了ORM的身影,所谓的SSH就是Spring+Struts+Hibernate,除了在学习基础知识的时候被告知可以使用JDBC操作数据库之外,大量的书籍中都是讲述使用Hibernate这个ORM工具来操作数据。在.NET中操作数据库的方式有多种,除了最直接的方式就是使用ADO.NET之外,还可以使用NHibernate这个Hibernate在.NET中的实现ORM,如果你对第三方的ORM持怀疑态度,你还可以使用来自微软的实现、根正苗红的Linq或者EntityFramework。
大部分从早期就开始使用.NET开发的程序员可能对ADO.NET有种迷恋,使用ADO.NET可以充分将我们早期的SQL知识发挥得淋漓尽致,并且出于对性能的考虑,有些人对.NET中的ORM还保持一种观望态度,包括我自己也是这种态度。不过即使在实际开发中不用,并不代表我们不能去了解和比较这些技术,任何事物的出现和消亡总有其原因的,我们可以了解它们的优点和长处。所以本人抽出了几个周末的时间分别用ADO.NET、NHibernate、Linq和EntityFramework来实现对数据库单表数据的创建、读取、更新和删除操作,也就是所谓的CRUD(C:Create/R:Read/U:Update/D:Delete)。
通过实现相同功能的比较,大家自己判断那种方式更适合自己。需要说明的是,如果在VS2008中使用EntityFramework就需要安装VS2008SP1。
在本篇周公将讲述如何利用NHibernate实现CRUD功能,为了便于对比,在本次中采用与上次同样的数据库和表,并且也实现同样的功能。NHibernate是一个ORM框架,所谓ORM就是Object Relational Mapping,是一种将关系型数据库中的数据与面向对象语言中对象建立映射关联的技术,我们可以想操作对象一样操作数据,NHibernate将我们对对象的变更保存到数据库中去,并且还负责以对象的方式从数据库中查询数据,这样就可以使开发人员从处理SQL和ADO.NET上节省一些时间和精力用于处理业务逻辑。
1、准备
首先我们需要从网上下载NHibernate框架,这个可以到它的官方网站 www.nhibernate.org上去下载,在周公写这篇文章的时候从网上能下载到的最新版本为2.1.2.GA。如果下载的是压缩包,请将压缩包解压,将会看到四分文件夹,它们的名称和作用分别如下:
Configuration_Templates:存放NHibernate连接数据库的配置文件的示例,在这个文件夹下分别有连接Firebird、SQL Server、MySQL、Oracle、PostgreSQL和SQLite数据库的配置示例,可以根据实际所使用的数据库选择示例中的代码并更改其中的数据源即可。
Required_Bins:存放NHibernate运行过程中所需要的类库文件,其中还包含了第三方的开源日志框架Log4Net,在不确定到底会使用那些类库的条件下建议将这个文件夹下所有的dll文件拷贝到项目的bin文件夹下。
Required_For_LazyLoading:存放延时加载特性支持所需的框架文件,在这个文件夹下提供了三种实现,选择一种将其下所有的dll文件拷贝到项目所在的bin文件夹下。在本示例中选择了Castle。
Tests:存放测试用文件,在此文件夹下还有一个名为ABC.hbm.xml的文件,这是一个数据表对应的配置文件的示例,在这个文件夹下的文件我们都用不着,不过在以后我们会用到.hbm.xml文件。
2、创建项目
我们创建一个项目,项目类型可以根据情况选择,因为我今后要使用NUnit来对项目进行单元测试,所以创建的类库项目。
创建项目成功之后将如下文件拷贝到项目的bin目录下:
Antlr3.Runtime.dll
Castle.Core.dll
Castle.DynamicProxy2.dll
Iesi.Collections.dll
log4net.dll
NHibernate.ByteCode.Castle.dll
NHibernate.dll
然后在项目中添加对这些dll的引用,如下图所示:
3、编写代码
3.1创建NHibernate配置文件
NHibernate的配置文件有相对比较固定的格式,这个可以从下载到框架文件压缩包解压得到,位于Configuration_Templates文件夹下。
向当前项目添加一个名为hibernate.cfg.xml的xml文件,在周公的机器上这个文件的格式内容如下:
<? 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 ="connection.connection_string" >     
     Data Source=zhou;Initial Catalog=AspNetStudy;User ID=sa;Password=jerry;        
     </ property >     
     < property  name ="adonet.batch_size" >10 </ property >     
     < property  name ="show_sql" >true </ property >     
     < property  name ="dialect" >NHibernate.Dialect.MsSql2005Dialect </ property >     
     < property  name ="use_outer_join" >true </ property >     
     < property  name ="command_timeout" >10 </ property >     
     < property  name ="query.substitutions" >true 1, false 0, yes 'Y', no 'N' </ property >     
     < property  name ="proxyfactory.factory_class" >     
     NHibernate.ByteCode.Castle.ProxyFactoryFactory,        
     NHibernate.ByteCode.Castle        
     </ property >     
     < mapping  assembly ="NHibernateDemo" />     
</ session-factory >     
</ hibernate-configuration >
如果你也是使用SQL Server2005作为数据库的话,那么需要更改connection.connection_string中的数据库连接信息为你本机的连接信息,并且实际情况更改assembly的值为你当前项目编译后的程序集名称。
在这里还需要注意的是需要将hibernate.cfg.xml的属性更改为复制属性改为始终复制,如下图所示:
3.2创建映射文件
映射文件包含了对象/关系所需要的元数据,其中包含了所使用的持久化类和到数据库的映射。NHibernate就是通过映射文件知道怎样加载和存储持久化对象。
在项目中增加一个名为UserInfo.hbm.xml的xml文件,这个xml文件的内容如下:
<? xml  version ="1.0"  encoding ="utf-8"  ?>     
< hibernate-mapping  xmlns ="urn:nhibernate-mapping-2.2"  assembly ="NHibernateDemo"  namespace ="NHibernateDemo" >     
< class  name ="UserInfo" >     
     < id  name ="UserId"  column ="UserId" >     
      < generator  class ="native" />     
     </ id >     
     < property  name ="UserName" />     
     < property  name ="RealName" />     
     < property  name ="Age" />     
     < property  name ="Sex" />     
     < property  name ="Mobile" />     
     < property  name ="Phone" />     
     < property  name ="Email" />     
</ class >     
</ hibernate-mapping >
注意:如果想要获得在编辑hibernate.cfg.xml或者UserInfo.hbm.xml时的智能提示,可以将下载NHibernate压缩包中的nhibernate-configuration.xsd和nhibernate-mapping.xsd文件拷贝到VS的一个特殊文件夹中,在本机上周公在C盘安装了VS2008,那么需要将上面提到的文件拷贝到C:\Program Files\Microsoft Visual Studio 9.0\Xml\Schemas目录下,你需要根据你的实际情况来拷贝。
需要更改这个文件的属性,改为“嵌入的资源”,如下图所示:
 
3.3创建持久化类
在当前项目中创建一个名为UserInfo.cs的类文件,这个类的代码如下:
InBlock.gif using System;        
InBlock.gif using System.Collections.Generic;        
InBlock.gif using System.Text;        
InBlock.gif     
InBlock.gif namespace NHibernateDemo        
InBlock.gif{        
InBlock.gif         /// <summary>        
InBlock.gif         /// 数据库中UserInfo表的持久化类        
InBlock.gif         /// 作者:周公        
InBlock.gif         /// 博客地址:http://blog.csdn.net/zhoufoxcn        
InBlock.gif         /// 日期:2010-03-19        
InBlock.gif         /// </summary>        
InBlock.gif         public  class UserInfo        
InBlock.gif        {        
InBlock.gif                 public  virtual  int UserId { get; set; }        
InBlock.gif                 public  virtual  string UserName { get; set; }        
InBlock.gif                 public  virtual  string RealName { get; set; }        
InBlock.gif                 public  virtual  int Age { get; set; }        
InBlock.gif                 public  virtual  bool Sex { get; set; }        
InBlock.gif                 public  virtual  string Mobile { get; set; }        
InBlock.gif                 public  virtual  string Phone { get; set; }        
InBlock.gif                 public  virtual  string Email { get; set; }        
InBlock.gif        }        
InBlock.gif}
注意:NHibernate通过使用属性的getter和setter操作来实现对象的持久化,并且要求类不能为sealed,方法和属性必须为virtual。
3.4编写辅助类
通过上面的文件NHibernate就可以实现关系映射了,这里编写相关的辅助类代码,如下:
InBlock.gif using System;        
InBlock.gif using System.Collections.Generic;        
InBlock.gif using System.Text;        
InBlock.gif using Iesi.Collections;        
InBlock.gif using NHibernate;        
InBlock.gif using NHibernate.Cfg;        
InBlock.gif     
InBlock.gif namespace NHibernateDemo        
InBlock.gif{        
InBlock.gif         /// <summary>        
InBlock.gif         /// 说明:这个类是为了演示NHibernate中的CRUD的用法        
InBlock.gif         /// 作者:周公(周金桥)        
InBlock.gif         /// 日期:2010-03-07        
InBlock.gif         /// </summary>        
InBlock.gif         public  class NHibernateCRUD        
InBlock.gif        {        
InBlock.gif                 private ISessionFactory sessionFactory;        
InBlock.gif                 public NHibernateCRUD()        
InBlock.gif                {        
InBlock.gif                        sessionFactory =  new Configuration().Configure().BuildSessionFactory();        
InBlock.gif                }        
InBlock.gif     
InBlock.gif                 public ISession GetSession()        
InBlock.gif                {        
InBlock.gif                         return sessionFactory.OpenSession();        
InBlock.gif                }        
InBlock.gif                 /// <summary>        
InBlock.gif                 /// 统计用户总数        
InBlock.gif                 /// </summary>        
InBlock.gif                 /// <returns></returns>        
InBlock.gif                 public  int Count()        
InBlock.gif                {     
InBlock.gif方法一      #region 方法一        
InBlock.gif                         //ISession session = GetSession();        
InBlock.gif                         //ISQLQuery query = session.CreateSQLQuery("select count(1) from UserInfo");        
InBlock.gif                         //int count = query.List<int>()[0];        
InBlock.gif                         //session.Close();        
InBlock.gif                         //return count;     
InBlock.gif                        #endregion     
InBlock.gif    
InBlock.gif方法二      #region 方法二        
InBlock.gif                        ISession session = GetSession();        
InBlock.gif                        IQuery query = session.CreateQuery( "select count(c.UserId) from UserInfo c");        
InBlock.gif                         //注意:不能对于count函数不能使用query.List<int>(),因为默认返回的数值类型是long        
InBlock.gif                         //否则会抛出GenericADOException,异常描述是:Could not execute query[SQL: SQL not available]        
InBlock.gif                         //InnerException: System.ArgumentException,InnerException描述是:"值“*”不是“System.Int32”类型,不能在此泛型集合中使用。\r\n参数名: value        
InBlock.gif                         int count =( int)(query.List< long>()[0]);        
InBlock.gif                        session.Close();        
InBlock.gif                         return count;     
InBlock.gif                        #endregion        
InBlock.gif                }        
InBlock.gif                 /// <summary>        
InBlock.gif                 /// 创建用户        
InBlock.gif                 /// </summary>        
InBlock.gif                 /// <param name="info">用户实体</param>        
InBlock.gif                 /// <returns></returns>        
InBlock.gif                 public  int Create(UserInfo info)        
InBlock.gif                {        
InBlock.gif                        ISession session = GetSession();        
InBlock.gif                         int newId=( int)(session.Save(info));        
InBlock.gif                        session.Flush();        
InBlock.gif                        session.Close();        
InBlock.gif                         return newId;        
InBlock.gif                }        
InBlock.gif                 /// <summary>        
InBlock.gif                 /// 读取用户信息        
InBlock.gif                 /// </summary>        
InBlock.gif                 /// <param name="userId">用户编号</param>        
InBlock.gif                 /// <returns></returns>        
InBlock.gif                 public UserInfo Read( int userId)        
InBlock.gif                {        
InBlock.gif                        ISession session = GetSession();        
InBlock.gif                        UserInfo info=session.Get<UserInfo>(userId);        
InBlock.gif                        session.Close();        
InBlock.gif                         return info;        
InBlock.gif                }        
InBlock.gif                 /// <summary>        
InBlock.gif                 /// 更新用户信息        
InBlock.gif                 /// </summary>        
InBlock.gif                 /// <param name="info">用户实体</param>        
InBlock.gif                 /// <returns></returns>        
InBlock.gif                 public  void Update(UserInfo info)        
InBlock.gif                {        
InBlock.gif                        ISession session = GetSession();        
InBlock.gif                        session.Update(info);        
InBlock.gif                        session.Flush();        
InBlock.gif                        session.Close();        
InBlock.gif                }        
InBlock.gif                 /// <summary>        
InBlock.gif                 /// 删除用户        
InBlock.gif                 /// </summary>        
InBlock.gif                 /// <param name="userId">用户编号</param>        
InBlock.gif                 /// <returns></returns>        
InBlock.gif                 public  void Delete( int userId)        
InBlock.gif                {        
InBlock.gif                        ISession session = GetSession();        
InBlock.gif                         //在NHibernate中支持直接使用参数值、":"+参数名、?(类似于在Access中使用参数化SQL语句的方式,给参数赋值要按照参数出现的顺序来)等几种方式        
InBlock.gif                        IQuery query = session.CreateQuery( "delete from UserInfo where UserId=:UserId");        
InBlock.gif                         //如果采用":"+参数名方式使用参数,那么给参数赋值时不需要在参数名前加":"号,如query.SetInt32(":UserId", userId);就会报错        
InBlock.gif                        query.SetInt32( "UserId", userId);        
InBlock.gif                         int affectedRows = query.ExecuteUpdate();        
InBlock.gif                        session.Close();        
InBlock.gif                         //return affectedRows;        
InBlock.gif                }        
InBlock.gif     
InBlock.gif                 /// <summary>        
InBlock.gif                 /// 删除用户        
InBlock.gif                 /// </summary>        
InBlock.gif                 /// <param name="userId">用户实体</param>        
InBlock.gif                 /// <returns></returns>        
InBlock.gif                 public  void Delete(UserInfo info)        
InBlock.gif                {        
InBlock.gif                        ISession session = GetSession();        
InBlock.gif                        session.Delete(info);        
InBlock.gif                        session.Flush();        
InBlock.gif                        session.Close();        
InBlock.gif                }        
InBlock.gif     
InBlock.gif                 /// <summary>        
InBlock.gif                 /// 获取用户表中编号最大的用户        
InBlock.gif                 /// </summary>        
InBlock.gif                 /// <returns></returns>        
InBlock.gif                 public  int GetMaxUserId()        
InBlock.gif                {        
InBlock.gif                        ISession session = GetSession();        
InBlock.gif                        IQuery query=session.CreateQuery( "select max(UserId) from UserInfo");        
InBlock.gif                         int userId=query.List< int>()[0];        
InBlock.gif                        session.Close();        
InBlock.gif                         return userId;        
InBlock.gif                }        
InBlock.gif        }        
InBlock.gif}
3.5单元测试代码
InBlock.gif using System;        
InBlock.gif using System.Collections.Generic;        
InBlock.gif using System.Linq;        
InBlock.gif using System.Text;        
InBlock.gif using NUnit.Framework;        
InBlock.gif using NHibernateDemo;        
InBlock.gif     
InBlock.gif namespace NUnitTest        
InBlock.gif{        
InBlock.gif        [TestFixture]        
InBlock.gif         public  class NHibernateTest        
InBlock.gif        {        
InBlock.gif                 private NHibernateCRUD instance =  null;        
InBlock.gif                [SetUp]        
InBlock.gif                 public  void Initialize()        
InBlock.gif                {        
InBlock.gif                        instance =  new NHibernateCRUD();        
InBlock.gif                }        
InBlock.gif                [Test]        
InBlock.gif                 /// <summary>        
InBlock.gif                 /// 统计用户总数        
InBlock.gif                 /// </summary>        
InBlock.gif                 /// <returns></returns>        
InBlock.gif                 public  void Count()        
InBlock.gif                {        
InBlock.gif                        Assert.Greater(instance.Count(), 0);        
InBlock.gif                }        
InBlock.gif                [Test]        
InBlock.gif                 /// <summary>        
InBlock.gif                 /// 创建用户        
InBlock.gif                 /// </summary>        
InBlock.gif                 /// <param name="info">用户实体</param>        
InBlock.gif                 /// <returns></returns>        
InBlock.gif                 public  void Create()        
InBlock.gif                {        
InBlock.gif                        UserInfo info =  new UserInfo()        
InBlock.gif                        {        
InBlock.gif                                Age = 12,        
InBlock.gif                                Email =  "zzz@ccav.com",        
InBlock.gif                                Mobile =  "13812345678",        
InBlock.gif                                Phone =  "01012345678",        
InBlock.gif                                RealName =  "测试" + DateTime.Now.Millisecond.ToString(),        
InBlock.gif                                Sex =  true,        
InBlock.gif                                UserName =  "zhoufoxcn" + DateTime.Now.Millisecond.ToString()        
InBlock.gif                        };        
InBlock.gif                        instance.Create(info);        
InBlock.gif                }        
InBlock.gif                [Test]        
InBlock.gif                 /// <summary>        
InBlock.gif                 /// 读取用户信息        
InBlock.gif                 /// </summary>        
InBlock.gif                 /// <param name="userId">用户编号</param>        
InBlock.gif                 /// <returns></returns>        
InBlock.gif                 public  void Read()        
InBlock.gif                {        
InBlock.gif                        UserInfo info = instance.Read(1);        
InBlock.gif                        Assert.NotNull(info);        
InBlock.gif                }        
InBlock.gif                [Test]        
InBlock.gif                 /// <summary>        
InBlock.gif                 /// 更新用户信息        
InBlock.gif                 /// </summary>        
InBlock.gif                 /// <param name="info">用户实体</param>        
InBlock.gif                 /// <returns></returns>        
InBlock.gif                 public  void Update()        
InBlock.gif                {        
InBlock.gif                        UserInfo info = instance.Read(1);        
InBlock.gif                        info.RealName =  "测试" + DateTime.Now.Millisecond.ToString();        
InBlock.gif                        instance.Update(info);        
InBlock.gif                }        
InBlock.gif                [Test]        
InBlock.gif                 /// <summary>        
InBlock.gif                 /// 删除用户        
InBlock.gif                 /// </summary>        
InBlock.gif                 /// <param name="userId">用户编号</param>        
InBlock.gif                 /// <returns></returns>        
InBlock.gif                 public  void DeleteByID()        
InBlock.gif                {        
InBlock.gif                         int userId = instance.GetMaxUserId();        
InBlock.gif                        instance.Delete(userId);        
InBlock.gif                }        
InBlock.gif     
InBlock.gif                [Test]        
InBlock.gif                 /// <summary>        
InBlock.gif                 /// 删除用户        
InBlock.gif                 /// </summary>        
InBlock.gif                 /// <param name="userId">用户实体</param>        
InBlock.gif                 /// <returns></returns>        
InBlock.gif                 public  void Delete()        
InBlock.gif                {        
InBlock.gif                         int userId = instance.GetMaxUserId();        
InBlock.gif                        UserInfo info = instance.Read(userId);        
InBlock.gif                        Console.WriteLine( "MaxUserId=" + userId);        
InBlock.gif                        instance.Delete(info);        
InBlock.gif                }        
InBlock.gif        }        
InBlock.gif}    
4.点评
使用ORM的很大的一个好处就是很少或者根本不用编写数据库记录映射到对象的代码,并且在大部分情况下也不用编写SQL代码(取而代之的是HSQL)。在使用ADO.NET时代我经常有这样的体会,因为数据库的变动可能需要更改从显示到数据库访问层的相关代码,并且还需要更改自己编写的从数据库记录转换为对象的代码,而现在仅仅需要更改映射文件和持久化类的代码就可以了,除此之外我们还可以使用面向对象的方式对数据进行操作,大家可以看到在NHibernateCRUD中对数据库进行CRUD的代码比直接使用ADO.NET进行CRUD操的代码要简洁许多,这就是ORM的魅力。

当然使用ORM也会存在一些缺点,毕竟ORM在底层使用的是ADO.NET,对于一个有经验的开发人员来说,可能直接使用ADO.NET的性能比使用NHibernate的效率要高,通过NHibernate的缓存机制多少能在某种程度上缓解这个问题。不过当数据量在千万级左右时这个问题就显得比较突出了,在一些数据量在百万级以下的情况中,合理使用NHibernate是没有什么问题的。
















本文转自周金桥51CTO博客,原文链接: http://blog.51cto.com/zhoufoxcn/286570,如需转载请自行联系原作者



相关文章
|
11天前
|
安全 数据库 数据安全/隐私保护
七天.NET 8操作SQLite入门到实战 - 第五天引入SQLite-net ORM并封装常用方法
七天.NET 8操作SQLite入门到实战 - 第五天引入SQLite-net ORM并封装常用方法
|
4月前
|
SQL 开发框架 关系型数据库
16个值得推荐的.NET ORM框架
16个值得推荐的.NET ORM框架
133 1
|
4月前
1152人参与的.NET ORM框架使用情况统计投票结果
1152人参与的.NET ORM框架使用情况统计投票结果
|
SQL 开发框架 Oracle
值得推荐的.NET/.NET Core ORM框架汇总
值得推荐的.NET/.NET Core ORM框架汇总
897 0
值得推荐的.NET/.NET Core ORM框架汇总
|
SQL 关系型数据库 C#
.NET(C#)有哪些主流的ORM框架,SqlSugar,Dapper,EF还是...
前言 在以前的一篇文章中,为大家分享了《什么是ORM?为什么用ORM?浅析ORM的使用及利弊》。那么,在目前的.NET(C#)的世界里,有哪些主流的ORM,SqlSugar,Dapper,Entity Framework(EF)还是ServiceStack.OrmLite?或者是你还有更好的ORM推荐呢? 如果有的话,不防也一起分享给大家。
15072 0
|
JavaScript API 数据库
从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之七 || API项目整体搭建 6.2 轻量级ORM
代码已上传Github+Gitee,文末有地址   书接上文:《从壹开始前后端分离【 .NET Core2.0 Api + Vue 2.0 + AOP + 分布式】框架之六 || API项目整体搭建 6.1 仓储》,我们简单的对整体项目进行搭建,用到了项目中常见的仓储模式+面向接口编程,核心的一共是六层,当然你也可以根据自己的需求进行扩展,比如我在其他的项目中会用到Common层,当然我们这个项目接下来也会有,或者我还会添加Task层,主要是作为定时项目使用,我之前用的是Task Schedule,基本能满足需求。
1490 0
|
前端开发 JavaScript NoSQL
【nodejs】让nodejs像后端mvc框架(asp.net mvc)一orm篇【如EF般丝滑】typeorm介绍(8/8)
文章目录 前情概要 在使用nodejs开发过程中,刚好碰到需要做一个小工具,需要用到数据库存储功能。而我又比较懒,一个小功能不想搞一个nodejs项目,又搞一个后端项目。不如直接在nodejs里面把对数据库的操作也做掉。
1754 0
|
SQL 关系型数据库 测试技术
.NET轻量级ORM框架Dapper入门精通
一、课程介绍 本次分享课程包含两个部分《.NET轻量级ORM框架Dapper修炼手册》和《.NET轻量级ORM框架Dapper葵花宝典》,阿笨将带领大家一起领略轻量级ORM框架Dapper的魅力。
2987 0
|
SQL MySQL 关系型数据库
.NET轻量级ORM组件Dapper葵花宝典
一、摘要 为什么取名叫《葵花宝典》? 从行走江湖的世界角度来讲您可以理解为一本"武功秘籍",站在我们IT编程的世界角度应该叫"开发宝典"。 如果您在工作中主要接触的是操作MySQL数据库,但您又想学习和了解.NET轻量级ORM框架Dapper,那么就请跟着阿笨一起学习本次的分享课《.NET轻量级ORM框架Dapper葵花宝典》。
2181 0