ADO.NET與ORM的比較:NHibernate實現(xiàn)CRUD(轉(zhuǎn))
原文地址
http://blog.csdn.net/zhoufoxcn/article/details/5402511
說明:個人感覺在Java領(lǐng)域大型開發(fā)都離不了ORM的身影,所謂的SSH就是Spring+Struts+Hibernate,除了在學習基礎知識的時候被告知可以使用JDBC操作數(shù)據(jù)庫之外,大量的書籍中都是講述使用Hibernate這個ORM工具來操作數(shù)據(jù)。在.NET中操作數(shù)據(jù)庫的方式有多種,除了最直接的方式就是使用ADO.NET之外,還可以使用NHibernate這個Hibernate在.NET中的實現(xiàn)ORM,如果你對第三方的ORM持懷疑態(tài)度,你還可以使用來自微軟的實現(xiàn)、根正苗紅的Linq或者EntityFramework。
大部分從早期就開始使用.NET開發(fā)的程序員可能對ADO.NET有種迷戀,使用ADO.NET可以充分將我們早期的SQL知識發(fā)揮得淋漓盡致,并且出于對性能的考慮,有些人對.NET中的ORM還保持一種觀望態(tài)度,包括我自己也是這種態(tài)度。不過即使在實際開發(fā)中不用,并不代表我們不能去了解和比較這些技術(shù),任何事物的出現(xiàn)和消亡總有其原因的,我們可以了解它們的優(yōu)點和長處。所以本人抽出了幾個周末的時間分別用ADO.NET、NHibernate、Linq和EntityFramework來實現(xiàn)對數(shù)據(jù)庫單表數(shù)據(jù)的創(chuàng)建、讀取、更新和刪除操作,也就是所謂的CRUD(C:Create/R:Read/U:Update/D:Delete)。
通過實現(xiàn)相同功能的比較,大家自己判斷那種方式更適合自己。需要說明的是,如果在VS2008中使用EntityFramework就需要安裝VS2008SP1。
在本篇周公將講述如何利用NHibernate實現(xiàn)CRUD功能,為了便于對比,在本次中采用與上次同樣的數(shù)據(jù)庫和表,并且也實現(xiàn)同樣的功能。NHibernate是一個ORM框架,所謂ORM就是Object Relational Mapping,是一種將關(guān)系型數(shù)據(jù)庫中的數(shù)據(jù)與面向?qū)ο笳Z言中對象建立映射關(guān)聯(lián)的技術(shù),我們可以想操作對象一樣操作數(shù)據(jù),NHibernate將我們對對象的變更保存到數(shù)據(jù)庫中去,并且還負責以對象的方式從數(shù)據(jù)庫中查詢數(shù)據(jù),這樣就可以使開發(fā)人員從處理SQL和ADO.NET上節(jié)省一些時間和精力用于處理業(yè)務邏輯。
1、準備
首先我們需要從網(wǎng)上下載NHibernate框架,這個可以到它的官方網(wǎng)站www.nhibernate.org上去下載,在周公寫這篇文章的時候從網(wǎng)上能下載到的最新版本為2.1.2.GA。如果下載的是壓縮包,請將壓縮包解壓,將會看到四分文件夾,它們的名稱和作用分別如下:
Configuration_Templates:存放NHibernate連接數(shù)據(jù)庫的配置文件的示例,在這個文件夾下分別有連接Firebird、SQL Server、MySQL、Oracle、PostgreSQL和SQLite數(shù)據(jù)庫的配置示例,可以根據(jù)實際所使用的數(shù)據(jù)庫選擇示例中的代碼并更改其中的數(shù)據(jù)源即可。
Required_Bins:存放NHibernate運行過程中所需要的類庫文件,其中還包含了第三方的開源日志框架Log4Net,在不確定到底會使用那些類庫的條件下建議將這個文件夾下所有的dll文件拷貝到項目的bin文件夾下。
Required_For_LazyLoading:存放延時加載特性支持所需的框架文件,在這個文件夾下提供了三種實現(xiàn),選擇一種將其下所有的dll文件拷貝到項目所在的bin文件夾下。在本示例中選擇了Castle。
Tests:存放測試用文件,在此文件夾下還有一個名為ABC.hbm.xml的文件,這是一個數(shù)據(jù)表對應的配置文件的示例,在這個文件夾下的文件我們都用不著,不過在以后我們會用到.hbm.xml文件。
2、創(chuàng)建項目
我們創(chuàng)建一個項目,項目類型可以根據(jù)情況選擇,因為我今后要使用NUnit來對項目進行單元測試,所以創(chuàng)建的類庫項目。
創(chuàng)建項目成功之后將如下文件拷貝到項目的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創(chuàng)建NHibernate配置文件
NHibernate的配置文件有相對比較固定的格式,這個可以從下載到框架文件壓縮包解壓得到,位于Configuration_Templates文件夾下。
向當前項目添加一個名為hibernate.cfg.xml的xml文件,在周公的機器上這個文件的格式內(nèi)容如下:
- <?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作為數(shù)據(jù)庫的話,那么需要更改connection.connection_string中的數(shù)據(jù)庫連接信息為你本機的連接信息,并且實際情況更改assembly的值為你當前項目編譯后的程序集名稱。
在這里還需要注意的是需要將hibernate.cfg.xml的屬性更改為復制屬性改為始終復制,如下圖所示:
3.2創(chuàng)建映射文件
映射文件包含了對象/關(guān)系所需要的元數(shù)據(jù),其中包含了所使用的持久化類和到數(shù)據(jù)庫的映射。NHibernate就是通過映射文件知道怎樣加載和存儲持久化對象。
在項目中增加一個名為UserInfo.hbm.xml的xml文件,這個xml文件的內(nèi)容如下:
- <?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目錄下,你需要根據(jù)你的實際情況來拷貝。
需要更改這個文件的屬性,改為“嵌入的資源”,如下圖所示:
3.3創(chuàng)建持久化類
在當前項目中創(chuàng)建一個名為UserInfo.cs的類文件,這個類的代碼如下:
- using System;
- using System.Collections.Generic;
- using System.Text;
- namespace NHibernateDemo
- {
- /// <summary>
- /// 數(shù)據(jù)庫中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操作來實現(xiàn)對象的持久化,并且要求類不能為sealed,方法和屬性必須為virtual。
3.4編寫輔助類
通過上面的文件NHibernate就可以實現(xiàn)關(guān)系映射了,這里編寫相關(guān)的輔助類代碼,如下:
- 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>
- /// 統(tǒng)計用戶總數(shù)
- /// </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函數(shù)不能使用query.List<int>(),因為默認返回的數(shù)值類型是long
- //否則會拋出GenericADOException,異常描述是:Could not execute query[SQL: SQL not available]
- //InnerException: System.ArgumentException,InnerException描述是:"值“*”不是“System.Int32”類型,不能在此泛型集合中使用。/r/n參數(shù)名: value
- int count =(int)(query.List<long>()[0]);
- session.Close();
- return count;
- #endregion
- }
- /// <summary>
- /// 創(chuàng)建用戶
- /// </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中支持直接使用參數(shù)值、":"+參數(shù)名、?(類似于在Access中使用參數(shù)化SQL語句的方式,給參數(shù)賦值要按照參數(shù)出現(xiàn)的順序來)等幾種方式
- IQuery query = session.CreateQuery("delete from UserInfo where UserId=:UserId");
- //如果采用":"+參數(shù)名方式使用參數(shù),那么給參數(shù)賦值時不需要在參數(shù)名前加":"號,如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>()[0];
- 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>
- /// 統(tǒng)計用戶總數(shù)
- /// </summary>
- /// <returns></returns>
- public void Count()
- {
- Assert.Greater(instance.Count(), 0);
- }
- [Test]
- /// <summary>
- /// 創(chuàng)建用戶
- /// </summary>
- /// <param name="info">用戶實體</param>
- /// <returns></returns>
- public void Create()
- {
- UserInfo info = new UserInfo()
- {
- Age = 12,
- Email = "zzz@ccav.com",
- Mobile = "13812345678",
- Phone = "01012345678",
- 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(1);
- Assert.NotNull(info);
- }
- [Test]
- /// <summary>
- /// 更新用戶信息
- /// </summary>
- /// <param name="info">用戶實體</param>
- /// <returns></returns>
- public void Update()
- {
- UserInfo info = instance.Read(1);
- 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的很大的一個好處就是很少或者根本不用編寫數(shù)據(jù)庫記錄映射到對象的代碼,并且在大部分情況下也不用編寫SQL代碼(取而代之的是HSQL)。在使用ADO.NET時代我經(jīng)常有這樣的體會,因為數(shù)據(jù)庫的變動可能需要更改從顯示到數(shù)據(jù)庫訪問層的相關(guān)代碼,并且還需要更改自己編寫的從數(shù)據(jù)庫記錄轉(zhuǎn)換為對象的代碼,而現(xiàn)在僅僅需要更改映射文件和持久化類的代碼就可以了,除此之外我們還可以使用面向?qū)ο蟮姆绞綄?shù)據(jù)進行操作,大家可以看到在NHibernateCRUD中對數(shù)據(jù)庫進行CRUD的代碼比直接使用ADO.NET進行CRUD操的代碼要簡潔許多,這就是ORM的魅力。
當然使用ORM也會存在一些缺點,畢竟ORM在底層使用的是ADO.NET,對于一個有經(jīng)驗的開發(fā)人員來說,可能直接使用ADO.NET的性能比使用NHibernate的效率要高,通過NHibernate的緩存機制多少能在某種程度上緩解這個問題。不過當數(shù)據(jù)量在千萬級左右時這個問題就顯得比較突出了,在一些數(shù)據(jù)量在百萬級以下的情況中,合理使用NHibernate是沒有什么問題的。
浙公網(wǎng)安備 33010602011771號