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

时间:2022-09-05 19:04:34

【转载】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的引用,如下图所示:
【转载】ADO.NET与ORM的比较(2):NHibernate实现CRUD 
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的属性更改为复制属性改为始终复制,如下图所示:
【转载】ADO.NET与ORM的比较(2):NHibernate实现CRUD 
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的类文件,这个类的代码如下:

using System;
using System.Collections.Generic;
using System.Text; namespace NHibernateDemo
{
/// <summary>
/// 数据库中UserInfo表的持久化类
/// 作者:周公
/// 博客地址:http://blog.csdn.net/zhoufoxcn
/// 日期:2010-03-19
/// </summary>
public class UserInfo
{
public virtual int UserId { get; set; }
public virtual string UserName { get; set; }
public virtual string RealName { get; set; }
public virtual int Age { get; set; }
public virtual bool Sex { get; set; }
public virtual string Mobile { get; set; }
public virtual string Phone { get; set; }
public virtual string Email { get; set; }
}
}

注意:NHibernate通过使用属性的getter和setter操作来实现对象的持久化,并且要求类不能为sealed,方法和属性必须为virtual。
3.4编写辅助类
通过上面的文件NHibernate就可以实现关系映射了,这里编写相关的辅助类代码,如下:

using System;
using System.Collections.Generic;
using System.Text;
using Iesi.Collections;
using NHibernate;
using NHibernate.Cfg; namespace NHibernateDemo
{
/// <summary>
/// 说明:这个类是为了演示NHibernate中的CRUD的用法
/// 作者:周公(周金桥)
/// 日期:2010-03-07
/// </summary>
public class NHibernateCRUD
{
private ISessionFactory sessionFactory;
public NHibernateCRUD()
{
sessionFactory = new Configuration().Configure().BuildSessionFactory();
} public ISession GetSession()
{
return sessionFactory.OpenSession();
}
/// <summary>
/// 统计用户总数
/// </summary>
/// <returns></returns>
public int Count()
{
#region 方法一
//ISession session = GetSession();
//ISQLQuery query = session.CreateSQLQuery("select count(1) from UserInfo");
//int count = query.List<int>()[0];
//session.Close();
//return count;
#endregion #region 方法二
ISession session = GetSession();
IQuery query = session.CreateQuery("select count(c.UserId) from UserInfo c");
//注意:不能对于count函数不能使用query.List<int>(),因为默认返回的数值类型是long
//否则会抛出GenericADOException,异常描述是:Could not execute query[SQL: SQL not available]
//InnerException: System.ArgumentException,InnerException描述是:"值“*”不是“System.Int32”类型,不能在此泛型集合中使用。/r/n参数名: value
int count =(int)(query.List<long>()[]);
session.Close();
return count;
#endregion
}
/// <summary>
/// 创建用户
/// </summary>
/// <param name="info">用户实体</param>
/// <returns></returns>
public int Create(UserInfo info)
{
ISession session = GetSession();
int newId=(int)(session.Save(info));
session.Flush();
session.Close();
return newId;
}
/// <summary>
/// 读取用户信息
/// </summary>
/// <param name="userId">用户编号</param>
/// <returns></returns>
public UserInfo Read(int userId)
{
ISession session = GetSession();
UserInfo info=session.Get<UserInfo>(userId);
session.Close();
return info;
}
/// <summary>
/// 更新用户信息
/// </summary>
/// <param name="info">用户实体</param>
/// <returns></returns>
public void Update(UserInfo info)
{
ISession session = GetSession();
session.Update(info);
session.Flush();
session.Close();
}
/// <summary>
/// 删除用户
/// </summary>
/// <param name="userId">用户编号</param>
/// <returns></returns>
public void Delete(int userId)
{
ISession session = GetSession();
//在NHibernate中支持直接使用参数值、":"+参数名、?(类似于在Access中使用参数化SQL语句的方式,给参数赋值要按照参数出现的顺序来)等几种方式
IQuery query = session.CreateQuery("delete from UserInfo where UserId=:UserId");
//如果采用":"+参数名方式使用参数,那么给参数赋值时不需要在参数名前加":"号,如query.SetInt32(":UserId", userId);就会报错
query.SetInt32("UserId", userId);
int affectedRows = query.ExecuteUpdate();
session.Close();
//return affectedRows;
} /// <summary>
/// 删除用户
/// </summary>
/// <param name="userId">用户实体</param>
/// <returns></returns>
public void Delete(UserInfo info)
{
ISession session = GetSession();
session.Delete(info);
session.Flush();
session.Close();
} /// <summary>
/// 获取用户表中编号最大的用户
/// </summary>
/// <returns></returns>
public int GetMaxUserId()
{
ISession session = GetSession();
IQuery query=session.CreateQuery("select max(UserId) from UserInfo");
int userId=query.List<int>()[];
session.Close();
return userId;
}
}
}

3.5单元测试代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using NHibernateDemo; namespace NUnitTest
{
[TestFixture]
public class NHibernateTest
{
private NHibernateCRUD instance = null;
[SetUp]
public void Initialize()
{
instance = new NHibernateCRUD();
}
[Test]
/// <summary>
/// 统计用户总数
/// </summary>
/// <returns></returns>
public void Count()
{
Assert.Greater(instance.Count(), );
}
[Test]
/// <summary>
/// 创建用户
/// </summary>
/// <param name="info">用户实体</param>
/// <returns></returns>
public void Create()
{
UserInfo info = new UserInfo()
{
Age = ,
Email = "zzz@ccav.com",
Mobile = "",
Phone = "",
RealName = "测试" + DateTime.Now.Millisecond.ToString(),
Sex = true,
UserName = "zhoufoxcn" + DateTime.Now.Millisecond.ToString()
};
instance.Create(info);
}
[Test]
/// <summary>
/// 读取用户信息
/// </summary>
/// <param name="userId">用户编号</param>
/// <returns></returns>
public void Read()
{
UserInfo info = instance.Read();
Assert.NotNull(info);
}
[Test]
/// <summary>
/// 更新用户信息
/// </summary>
/// <param name="info">用户实体</param>
/// <returns></returns>
public void Update()
{
UserInfo info = instance.Read();
info.RealName = "测试" + DateTime.Now.Millisecond.ToString();
instance.Update(info);
}
[Test]
/// <summary>
/// 删除用户
/// </summary>
/// <param name="userId">用户编号</param>
/// <returns></returns>
public void DeleteByID()
{
int userId = instance.GetMaxUserId();
instance.Delete(userId);
} [Test]
/// <summary>
/// 删除用户
/// </summary>
/// <param name="userId">用户实体</param>
/// <returns></returns>
public void Delete()
{
int userId = instance.GetMaxUserId();
UserInfo info = instance.Read(userId);
Console.WriteLine("MaxUserId=" + userId);
instance.Delete(info);
}
}
}

4.点评
使用ORM的很大的一个好处就是很少或者根本不用编写数据库记录映射到对象的代码,并且在大部分情况下也不用编写SQL代码(取而代之的是HSQL)。在使用ADO.NET时代我经常有这样的体会,因为数据库的变动可能需要更改从显示到数据库访问层的相关代码,并且还需要更改自己编写的从数据库记录转换为对象的代码,而现在仅仅需要更改映射文件和持久化类的代码就可以了,除此之外我们还可以使用面向对象的方式对数据进行操作,大家可以看到在NHibernateCRUD中对数据库进行CRUD的代码比直接使用ADO.NET进行CRUD操的代码要简洁许多,这就是ORM的魅力。
当然使用ORM也会存在一些缺点,毕竟ORM在底层使用的是ADO.NET,对于一个有经验的开发人员来说,可能直接使用ADO.NET的性能比使用NHibernate的效率要高,通过NHibernate的缓存机制多少能在某种程度上缓解这个问题。不过当数据量在千万级左右时这个问题就显得比较突出了,在一些数据量在百万级以下的情况中,合理使用NHibernate是没有什么问题的。

【转载】ADO.NET与ORM的比较(2):NHibernate实现CRUD的更多相关文章

  1. 【转载】ADO&period;NET与ORM的比较&lpar;4&rpar;&colon;EntityFramework实现CRUD

    [转载]ADO.NET与ORM的比较(4):EntityFramework实现CRUD 说明:个人感觉在Java领域大型开发都离不了ORM的身影,所谓的SSH就是Spring+Struts+Hiber ...

  2. 【转载】ADO&period;NET与ORM的比较&lpar;3&rpar;&colon;Linq to SQL实现CRUD

    [转载]ADO.NET与ORM的比较(3):Linq to SQL实现CRUD 说明:个人感觉在Java领域大型开发都离不了ORM的身影,所谓的SSH就是Spring+Struts+Hibernate ...

  3. ADO&period;NET与ORM的比较&colon;NHibernate实现CRUD&lpar;转&rpar;

    原文地址 http://blog.csdn.net/zhoufoxcn/article/details/5402511 说明:个人感觉在Java领域大型开发都离不了ORM的身影,所谓的SSH就是Spr ...

  4. &lbrack;转载&rsqb;ADO&period;NET中的五个主要对象

    Connection:主要是开启程序和数据库之间的连接.没有利用连接对象将数据库打开,是无法从数据库中取得数据的.Close和Dispose的区别,Close以后还可以Open,Dispose以后则不 ...

  5. python下的orm基本操作&lpar;1&rpar;--Mysql下的CRUD简单操作&lpar;含源码DEMO&rpar;

    最近逐渐打算将工作的环境转移到ubuntu下,突然发现对于我来说,这ubuntu对于我这种上上网,收收邮件,写写博客,写写程序的时实在是太合适了,除了刚接触的时候会不怎么完全适应命令行及各种权限管理, ...

  6. ado&period;net与各种orm操作数据方式的比较

    ADO.NET与ORM的比较(1):ADO.NET实现CRUD http://zhoufoxcn.blog.51cto.com/792419/283952 ADO.NET与ORM的比较(2):NHib ...

  7. mybatis for &period;net

    MyBatis For .NET学习笔记:开篇 http://chenkai.blog.51cto.com/2023960/763806 MyBatis For .NET学习笔记[2]:配置环境 ht ...

  8. 转载:学习Entity Framework 中的Code First

    看完觉得不错,适合作为学习资料,就转载过来了 原文链接:http://www.cnblogs.com/Wayou/archive/2012/09/20/EF_CodeFirst.html 这是上周就写 ...

  9. ORM系列之Entity FrameWork详解

    一. 谈情怀 从第一次接触开发到现在(2018年),大约有六年时间了,最初阶段连接数据库,使用的是[SQL语句+ADO.NET],那时候,什么存储过程.什么事务 统统不理解,生硬的将SQL语句传入SQ ...

随机推荐

  1. 在公有云AZURE上部署私有云AZUREPACK以及WEBSITE CLOUD(三)

    (三) 搭建Windows Azure Pack环境 1安装SQL SERVER 2012 服务器 为简单起见,本例直接使用了Azure提供的具有SQLServer的Win2012 Server镜像来 ...

  2. nginx、php-fpm二三问

    php-cgi为什么没了? php-fpm子进程是干啥的?php-cgi是原来php自带的fastcgi进程管理器,有一些缺点,比如不能平滑重启,进程管理差.php-fpm可以看做升级版的php-fp ...

  3. DevExpress XtraGrid 数据导出导入Excel

    // <summary> /// 导出按钮 /// </summary> /// <param name="sender"></param ...

  4. Castle Windsor Fluent Registration API

    一对一注册 直接注册组件 container.Register( Component.For<MyServiceImpl>() ); 注册接口并提供组件 container.Registe ...

  5. JUnit使用Eclipse建立Test Case - 就是爱Java

    传统的测试方式,是以main(),作为代码的起点,而这次Mix将利用JUnit 进行测试,Eclipse有提供完整的整合环境,可以方便地使用JUnit,让Mix快速地进行单元测试,首先,开始撰写第1个 ...

  6. QList 和std&colon;&colon;list的比较

    QList QList<T> 是一个Qt通用容器类.它存储一序列的值,并且提供基于索引的数据访问方法和快速的插入和删除操作. QList<T>, QLinkedList< ...

  7. 解决java mail发送TXT附件被直接显示在正文中的问题

    这两天遇到一个问题,关于使用java mail发送邮件的问题. 详细是这样子的:我使用java mail发送异常报告邮件,邮件中有一个包含异常日志的附件,和关于设备信息的邮件正文.假设日志为log后缀 ...

  8. 在Amazon AWS RHEL 7上安装 配置PPTP VPN

    0 前言 0.1 为什么需要VPN? 国内的VPN不是必须,但是国外的VPN是很有用的.连接到国外的VPN服务器之后就可以访问Google,Facebook, Youtube等网站,没有Google的 ...

  9. python&plus;mysql&plus;flask创建一个微博应用(持续更新)

    微博应用的结构: 用户管理,包括登录管理,会话,用户角色,个人档案及用户头像. 数据库管理,包括数据库迁移处理. Web表单支持,包括字段检验和用于防止垃圾邮件的验证码功能. 大数据的分页功能. 全文 ...

  10. &lt&semi;转&gt&semi;年终盘点!2017年超有价值的Golang文章

    马上就要进入2018年了,作为年终的盘点,本文列出了一些2017年的关于Go编程的一些文章,并加上简短的介绍. 文章排名不分先后, 文章也不一定完全按照日期来排列.我按照文章的大致内容分了类,便于查找 ...