最近框架项目需要,数据层想使用Nhibernate,代替传统的sql语句的写法,更加使用面向对象的思维来维护实体与数据库的这层关系映射(ORM),好在之前接触过Java时学习使用了Hibernate,先来了解ORM。
什么是ORM?
对象-关系映射(Object/Relation Mapping,简称ORM),是随着面向对象的软件开发方法发展而产生的。面向对象的开发方法是当今企业级应用开发环境中的主流开发方法,关系数据库是企业级应用环境中永久存放数据的主流数据存储系统。对象和关系数据是业务实体的两种表现形式,业务实体在内存中表现为对象,在数据库中表现为关系数据。内存中的对象之间存在关联和继承关系,而在数据库中,关系数据无法直接表达多对多关联和继承关系。因此,对象-关系映射(ORM)系统一般以中间件的形式存在,主要实现程序对象到关系数据库数据的映射。
字母O起源于"对象"(Object),而R则来自于"关系"(Relational)。几乎所有的程序里面,都存在对象和关系数据库。在业务逻辑层和用户界面层中,我们是面向对象的。当对象信息发生变化的时候,我们需要把对象的信息保存在关系数据库中。
Nhibernate是Hibernate在.NET环境中的实现。功能和目的上两者区别不大。在实现的机制方面,Nhibernate频繁地使用C#的新语法现象——lamda表达式,所以其使用方式看起来有点怪,没有Hibernate简明。
NHibernate是一个面向.NET环境的对象/关系数据库映射工具。对象/关系数据库映射(object/relational mapping,ORM)这个术语表示一种技术,用来把对象模型表示的对象映射到基于SQL的关系模型数据结构中去。
在今日的企业环境中,把面向对象的软件和关系数据库一起使用可能是相当麻烦和浪费时间的。而NHibernate不仅仅管理.NET类到数据库表的映射(包括.NET 数据类型到SQL数据类型的映射),还提供数据查询和获取数据的方法,可以大幅度减少开发时人工使用SQL和ADO.NET处理数据的时间。
NHibernate的目标主要是用于与数据持久化相关的编程任务,能够使开发人员从原来枯燥的SQL语句的编写中解放出来,解放出来的精力可以让开发人员投入到业务逻辑的实现上。对于以数据为中心的程序,开发人员往往是在数据库中使用存储过程来实现商业逻辑,这种情况下NHibernate可能不是最好的解决方案,但对于那些基于.NET,并且能够实现OO业务模型和商业逻辑的中间层应用,NHibernate是最有用的。NHibernate可以帮助用户消除或者包装那些针对特定厂商的SQL代码,并且帮用户把结果集从表格式的表示形式转换成一系列的对象。
如果打开你最近的程序(如,PetShop4.0),看看DAL(数据库访问层)代码,你肯定会看到很多近似的通用的模式。我们以保存对象的方法为例,你传入一个对象,为SqlCommand对象添加SqlParameter,把所有属性和对象对应,设置SqlCommand的CommandText属性为存储过程,然后运行SqlCommand。对于每个对象都要重复的写这些代码。除此之外,还有更好的办法吗?有,引入一个O/R Mapping。实质上,一个O/R Mapping会为你生成DAL。与其自己写DAL代码,不如用O/R Mapping。你用O/R Mapping保存,删除,读取对象,O/R Mapping负责生成SQL,你只需要关心对象就好。
步骤一:建实体对象Product
<span style="font-size:18px;">using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Domain { /// <summary> /// 商品 /// </summary> public class Product { /// <summary> /// ID /// </summary> /// public virtual Guid ID { get; set; } /// <summary> /// 编号 /// </summary> public virtual string Code { get; set; } /// <summary> /// 名称 /// </summary> public virtual string Name { get; set; } /// <summary> /// 规格 /// </summary> public virtual string QuantityPerUnit { get; set; } /// <summary> /// 单位 /// </summary> public virtual string Unit { get; set; } /// <summary> /// 售价 /// </summary> public virtual decimal SellPrice { get; set; } /// <summary> /// 进价 /// </summary> public virtual decimal BuyPrice { get; set; } /// <summary> /// 备注 /// </summary> public virtual string Remark { get; set; } } } </span>
<span style="font-size:18px;"><?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Domain" namespace="Domain"> <class name="Product" table="T_Product" lazy="true" > <id name="ID" column="ID" type="Guid" > <generator class="assigned" /> </id> <property name="Code" type="string"> <column name="Code" length="50"/> </property> <property name="Name" type="string"> <column name="Name" length="50"/> </property> <property name="QuantityPerUnit" type="string"> <column name="QuantityPerUnit" length="50"/> </property> <property name="Unit" type="string"> <column name="Unit" length="50"/> </property> <property name="SellPrice" type="decimal"> <column name="SellPrice" precision="14" scale="2"/> </property> <property name="BuyPrice" type="decimal"> <column name="BuyPrice" precision="14" scale="2"/> </property> <property name="Remark" type="string"> <column name="Remark" length="200"/> </property> </class> </hibernate-mapping> </span>
这里重点说一下 assembly 和 namespace属性。
assembly:集合的名称,如果在一个项目下有许多个实体对象,我们只需要指定这个属性,并在web.config中加入 如: “<mapping assembly="Domain"/>”就行了。
namespace:命名空间,如果设定了这个属性,class的 name只要指定与实体对象同名就行了。
还有要特别注意的是:XML文件的默认生成操作为“内容”,这里需要修改为“嵌入的资源”生成,因为NHibernate是通过查找程序集中的资源文件映射实体。这样在生成类库DLL文件时,这个XML文件才会打包进去,同时名称要自动修改为“Product.hbm.xml”。
3、web.config中进行配置
<span style="font-size:18px;"><?xml version="1.0" encoding="utf-8"?> <!-- This template was written to work with NHibernate.Test. Copy the template to your NHibernate.Test project folder and rename it in hibernate.cfg.xml and change it for your own use before compile tests in VisualStudio. --> <!-- This is the System.Data.dll provider for SQL Server --> <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2" > <session-factory name="NHibernateTest"> <property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property> <property name="connection.connection_string"> <!--数据库的用户名密码--> server=.;database=NHibernateDemo;uid=sa;pwd=123456; </property> <property name="adonet.batch_size">10</property> <!--是否显示sql语句--> <property name="show_sql">true</property> <!--数据库的方言--> <property name="dialect">NHibernate.Dialect.MsSql2008Dialect</property> <property name="use_outer_join">true</property> <property name="command_timeout">60</property> <!--数据库操作的方式--> <property name="hbm2ddl.auto">update</property> <property name="query.substitutions">true 1, false 0, yes 'Y', no 'N'</property> <property name="proxyfactory.factory_class">NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu</property> <!--找到对应的实体对象--> <mapping assembly="Domain"/> </session-factory> </hibernate-configuration></span>
4、新建IProductDao操作类
<span style="font-size:18px;">using System; using System.Collections.Generic; using System.Linq; using System.Text; using Domain; namespace Dao { public interface IProductDao { object Save(Product entity); void Update(Product entity); void Delete(Product entity); Product Get(object id); Product Load(object id); IList<Product> LoadAll(); } } </span>
<span style="font-size:18px;">using System; using System.Collections.Generic; using System.Linq; using System.Text; using NHibernate; using NHibernate.Linq; namespace Dao { public class ProductDao : IProductDao { private ISessionFactory sessionFactory; // 默认从 App.config,web.config或者hibernate.cfg.xml查找配置文件; //var cfg = new NHibernate.Cfg.Configuration().Configure(); //// 如果配置文件不是以上“App.config,web.config或者hibernate.cfg.xml”,是自定义的配置文件名称; //// 要注意使用MSTest进行单元测试时,请写配置文件的绝对路径,否则找不到; ////var cfg = new NHibernate.Cfg.Configuration().Configure("D:/ProjectStudyTest/NHibernate/NHibernateStudy/Lesson3.Dao/Config/hibernate.cfg.xml"); ////如果用户Nunit进行单元测试,写相对路径即可。 ////var cfg = new NHibernate.Cfg.Configuration().Configure("Config/hibernate.cfg.xml"); //sessionFactory = cfg.BuildSessionFactory(); public ProductDao() { //加载 var cfg = new NHibernate.Cfg.Configuration().Configure("Config/hibernate.cfg.xml"); sessionFactory = cfg.BuildSessionFactory(); } public object Save(Domain.Product entity) { using (ISession session = sessionFactory.OpenSession()) { var id = session.Save(entity); session.Flush(); return id; } } public void Update(Domain.Product entity) { using (ISession session = sessionFactory.OpenSession()) { session.Update(entity); session.Flush(); } } public void Delete(Domain.Product entity) { using (ISession session = sessionFactory.OpenSession()) { session.Delete(entity); session.Flush(); } } public Domain.Product Get(object id) { using (ISession session = sessionFactory.OpenSession()) { return session.Get<Domain.Product>(id); } } public Domain.Product Load(object id) { using (ISession session = sessionFactory.OpenSession()) { return session.Load<Domain.Product>(id); } } public IList<Domain.Product> LoadAll() { using (ISession session = sessionFactory.OpenSession()) { return session.Query<Domain.Product>().ToList(); } } } } </span>
7、ProductDaoTest测试类
<span style="font-size:18px;">using System; using System.Collections.Generic; using System.Linq; using System.Text; using NUnit.Framework; using Dao; namespace NHibernateTest { [TestFixture] public class ProductDaoTest { private IProductDao productDao; [SetUp] public void Init() { productDao = new ProductDao(); } [Test] public void SaveTest() { var product = new Domain.Product { ID = Guid.NewGuid(), BuyPrice = 10M, Code = "ABC123", Name = "电脑", QuantityPerUnit = "20x1", SellPrice = 11M, Unit = "台" }; var obj = this.productDao.Save(product); Assert.NotNull(obj); } [Test] public void UpdateTest() { var product = this.productDao.LoadAll().FirstOrDefault(); Assert.NotNull(product); product.SellPrice = 12M; Assert.AreEqual(12M, product.SellPrice); } [Test] public void DeleteTest() { var product = this.productDao.LoadAll().FirstOrDefault(); Assert.NotNull(product); var id = product.ID; this.productDao.Delete(product); Assert.Null(this.productDao.Get(id)); } [Test] public void GetTest() { var product = this.productDao.LoadAll().FirstOrDefault(); Assert.NotNull(product); var id = product.ID; Assert.NotNull(this.productDao.Get(id)); } [Test] public void LoadTest() { var product = this.productDao.LoadAll().FirstOrDefault(); Assert.NotNull(product); var id = product.ID; Assert.NotNull(this.productDao.Get(id)); } [Test] public void LoadAllTest() { var count = this.productDao.LoadAll().Count; Assert.True(count > 0); } } } </span>
到此开始使用NUnit进行的单元测试
数据库中的如下,已经帮我们自动创建完成:
优点
(1).面向对象:NHiberante的使用时只需要操纵对象,使开发更对象化,抛弃了数据库中心的思想,完全的面向对象思想。
(2).透明持久化:带有持久化状态的、具有业务功能的单线程对象,此对象生存期很短。这些对象可能是普通的POCO,这个对象没有实现第三方框架或者接口,唯一特殊的是他们正与(仅仅一个)Session相关联。一旦这个Session被关闭,这些对象就会脱离持久化状态,这样就可被应用程序的任何层自由使用。(例如,用作跟表示层打交道的数据传输对象。)
(3).它没有侵入性,即所谓的轻量级框架。正因为它具有透明持久化的优点,它才没有侵入性,才是一个轻量级框架。恒定一个框架为重量级、还是轻量级,是根据其侵入性而定夺的。而NHibernate就是一个轻量级ORM框架。
(4).较好的移植性:支持多种数据库,便于数据库的迁移。
(5).缓存机制:提供一、二级缓存和查询缓存。
(6).开发效率:众所周知,使用NHibernate可以简化程序开发,从而达到快速开发的目的。作为软件公司,项目管理的关键就是控制开发成本。正因为使用NHibernate后所写的代码量减少了,相对于原先使用“SqlHelper、DAL、BLL”开发程序的项目周期缩短了,成本就降低了。
以后NET开发模式,我们更应该尝试使用面向对象思维开发,这样来说,后期的维护成本大大的降低
(接下来会利用业余时间使用Net的一套搭建Nsprint+NHibernate+Nstructs+WCF+Windows+Silverlight+等中小型企业级系统开发平台)。