說明:個人感覺在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的類文件,這個類的代碼如下:
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>()[0]);
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>()[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>
/// 統計用戶總數
/// </summary>
/// <returns></returns>
public void Count()
{
Assert.Greater (instance.Count(), 0);
}
[Test]
/// <summary>
/// 創建用戶
/// </summary>
/// <param name="info">用戶 實體</param>
/// <returns></returns>
public void Create()
{
UserInfo info = new UserInfo()
{
Age = 12,
Email = "[email protected]",
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的很大的一個好處就是很少或者根本不用編寫數據庫記錄映射到對象 的代碼,並且在大部分情況下也不用編寫SQL代碼(取而代之的是HSQL)。在使 用ADO.NET時代我經常有這樣的體會,因為數據庫的變動可能需要更改從顯示到 數據庫訪問層的相關代碼,並且還需要更改自己編寫的從數據庫記錄轉換為對象 的代碼,而現在僅僅需要更改映射文件和持久化類的代碼就可以了,除此之外我 們還可以使用面向對象的方式對數據進行操作,大家可以看到在NHibernateCRUD 中對數據庫進行CRUD 的代碼比直接使用ADO.NET進行CRUD操的代碼要簡潔許多, 這就是ORM的魅力。
當然使用ORM也會存在一些缺點,畢竟ORM在底層使用的是ADO.NET,對於一個 有經驗的開發人員來說,可能直接使用ADO.NET的性能比使用NHibernate的效率 要高,通過NHibernate的緩存機制多少能在某種程度上緩解這個問題。不過當數 據量在千萬級左右時這個問題就顯得比較突出了,在一些數據量在百萬級以下的 情況中,合理使用 NHibernate是沒有什麼問題的。
出處http://zhoufoxcn.blog.51cto.com/792419/286570