註:本文中使用的是NH1.0.1.0版本,其他版本配置可能略有不同。
範例程式碼下載。
資料庫指令碼下載
一、體繫結構
首先我們來看看NHibernate的體繫結構:
NHibernate是資料庫和應用程式之間的持久化層,應用程式和NHibernate通過持久化(Persistent)對象聯絡在一起。NHibernate使用app.config或者web.config和對應檔進行設定。
下面來看看怎麼使用NHibernate API的一個最小子集。
下面是圖中一些對象的定義:
Configuration(NHibernate.Cfg.Configuration)
-
設定物件,用來指定用來出建立ISessionFactory的屬性和對應檔。(圖中沒有此對象)
-
SessionFactory (NHibernate.ISessionFactory)
-
Session的工廠。
-
會話,Session (NHibernate.ISession)
-
代表應用程式和持久化層之間的一次對話。封裝了一個ADO.NET串連。也是Transaction的工廠。儲存有必需的(第一級)持久化對象的緩衝,用於遍曆對象圖,或者通過標識符尋找對象。
-
事務Transaction (NHibernate.ITransaction)
-
(可選) 單線程,生命期短促的對象,應用程式用它來表示一批工作的原子操作。是底層的ADO.NET事務的抽象。一個Session某些情況下可能跨越多個Transaction 事務。
二、範例程式碼
我們看看他們是如何協同工作的,請看下面的代碼。
程式碼範例(未改變資料庫中的資料,查詢操作):
//配置ConfigurationConfiguration cfg = new Configuration().Configure();//建立ISessionFactoryISessionFactory factory = cfg.BuildSessionFactory();//開啟ISessionISession session = factory.OpenSession();session=factory.OpenSession();try{在這裡添加操作}finally{session.Close();}
Configuration的Configure方法,將使用應用程式的設定檔或者hibernate.cfg.xml檔案的<hibernate-configuration>節點進行配置,傳回值是一個配置好了的Configuration對象的執行個體。
然後這個Configuration通過BuildSessionFactory方法建立ISessionFactory的執行個體。
ISessionFactory的執行個體通過OpenSession開啟一個ISession的執行個體,用這個執行個體完成資料庫的操作,然後關閉它。
如果你的代碼中需要事務,只需要把代碼稍微修改一下。
程式碼範例(改變了資料庫中的資料,資料的增刪改):
//配置ConfigurationConfiguration cfg = new Configuration().Configure();//建立ISessionFactoryISessionFactory factory = cfg.BuildSessionFactory();//定義事務ITransaction tx = null;//開啟ISessionISession session = factory.OpenSession();try{//開始事務tx = session.BeginTransaction();在這裡添加操作tx.Commit();}catch(HibernateException ex){if (tx!=null) tx.Rollback();throw ex;}finally{//關閉ISessionsession.Close();}
我一般在不影響資料的方法(例如:查詢)中不包含事務,而影響資料的方法(例如:增刪改)使用事務。
三、設定檔
前面提到Configuration的Configure方法需要用到應用程式的設定檔或者hibernate.cfg.xml檔案,這裡我使用hibernate.cfg.xml設定檔,在下一章我們再探討使用應用程式的設定檔和其他的配置方法。
這是一個典型的設定檔:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.0">
<session-factory name="DDLLY.MyDoc.NHibernate.QuickStart">
<!-- 屬性 -->
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="connection.connection_string">server=.;database=user;uid=sa;pwd=sa;</property>
<property name="show_sql">false</property>
<property name="dialect">NHibernate.Dialect.MsSql2000Dialect</property>
<property name="use_outer_join">true</property>
<property name="query.substitutions">true 1, false 0, yes 'Y', no 'N'</property>
<!-- 對應檔 -->
<mapping assembly="DDLLY.MyDoc.NHibernate.QuickStart" />
</session-factory>
</hibernate-configuration>
屬性connection.provider設定了資料庫連接提供者(一般不需要更改,除非你決定自己實現提供者)。
connection.driver_class設定了資料庫的的驅動類(這裡設定了SqlServer的驅動類)。
connection.connection_string是資料庫的連接字串。
show_sql設定是否在運行時是否在控制台顯示正在執行的SQL語句(這個屬性在偵錯工具時很有用)。
dialect是NHibernate方言, 可以讓NHibernate使用某些特定的資料庫平台的特性。
use_outer_join在後面的章節會有介紹。
query.substitutions,把NHibernate查詢中的一些短語替換為SQL短語(比如說短語可能是函數或者字元)。
mapping節點設定了將使用到的對應檔,現在的設定方法是使用"DDLLY.MyDoc.NHibernate.QuickStart“中所有的對應檔。
四、對應檔
ISession怎麼知道和資料庫的哪張表哪個欄位打交道,以及怎麼把表和實體物件聯絡起來呢?靠的就是對應檔。
假設我們有個很簡單的例子,我們有如下的一張表需要進行資料操作(欄位和表含義一看就知道,我就不羅嗦了):
CREATE TABLE [dbo].[users] ([Id] [int] IDENTITY (1, 1) NOT NULL ,[UserName] [nvarchar] (64) NOT NULL ,[Password] [varchar] (32) NOT NULL ,[Email] [varchar] (64) NULL )
我們設定它的對應檔和相應的代碼檔案:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0">
<class name="DDLLY.MyDoc.NHibernate.QuickStart.User, DDLLY.MyDoc.NHibernate.QuickStart" table="users">
<id name="Id" type="Int32">
<generator class="identity" />
</id>
<property name="UserName" type="String"/>
<property name="Password" type="String"/>
<property name="Email" type="String"/>
</class>
</hibernate-mapping>
public class User{public User(){}private int m_Id;private string m_UserName;private string m_Password;private string m_Email;public int Id {get { return m_Id; }set { m_Id = value; }}public string UserName {get { return m_UserName; }set { m_UserName = value; }}public string Password {get { return m_Password; }set { m_Password = value; }}public string Email {get { return m_Email; }set { m_Email = value; }}}
<class name="DDLLY.MyDoc.NHibernate.QuickStart.User, DDLLY.MyDoc.NHibernate.QuickStart" table="users"> 表示對應檔對應的類檔案是DDLLY.MyDoc.NHibernate.QuickStart程式集中的 DDLLY.MyDoc.NHibernate.QuickStart.User類,逗號後面是表示程式集。對應的資料庫表為users。
<id name="Id" type="Int32">
<generator class="identity" />
</id>
表示Id是主鍵欄位,generator是產生器,這裡是使用SqlServer中內建識別欄位來產生。
property定義了其他的欄位。
五、完成代碼
好了,下面我們可以開始對資料進行操作了。
我們來看看ISession為我們提供的常用方法。
Load:讀取實體物件
Find:通過HQL尋找實體物件
Save:儲存實體物件
Delete:刪除實體物件
用這些方法我們便可以完成對資料庫的增刪改查的操作了。下面是我完成的代碼(使用NUnit進行測試)。
using System;using System.Collections;using System.Data;using System.Data.SqlClient;using NHibernate;using NHibernate.Cfg;using NUnit.Framework;namespace DDLLY.MyDoc.NHibernate.QuickStart{/// <summary>/// User測試類別/// </summary>[TestFixture]public class UserFixture{#region 常量//連接字串const string CONNECTIONSTRING="server=.;database=user;uid=sa;pwd=sa;";#endregion#region SetUp和TearDown[SetUp]public void SetUp() {//添加名為LLY,密碼為123456的使用者,因為這是資料庫的第一條記錄,所以資料庫標識為1string SetUpSql="INSERT INTO users([UserName],[Password]) VALUES('LLY','123456')";SqlExecuteNonQuery(SetUpSql);}[TearDown]public void TearDown() {//清空users表,並把標識(identity)清零string TearDownSql="TRUNCATE TABLE users";SqlExecuteNonQuery(TearDownSql);}#endregion#region 私人方法/// <summary>/// 執行SQL語句/// </summary>/// <param name="sql">SQL語句</param>private void SqlExecuteNonQuery(string sql){SqlConnection con=new SqlConnection(CONNECTIONSTRING);con.Open();SqlCommand cmd=new SqlCommand();cmd.Connection=con;cmd.CommandText=sql;cmd.ExecuteNonQuery();con.Close();}#endregion#region 測試方法/// <summary>/// 測試讀取使用者/// </summary>[Test]public void TestLoadUser(){User user=null;//配置ConfigurationConfiguration cfg = new Configuration().Configure();//建立ISessionFactoryISessionFactory factory = cfg.BuildSessionFactory();//開啟ISessionISession session = factory.OpenSession();session=factory.OpenSession();try{user=session.Load(typeof(User),1) as User; }finally{session.Close();}//************************資料檢查*****************************//檢查Assert.IsNotNull(user,"沒有找到ID為1的使用者!");Assert.AreEqual("LLY",user.UserName,"沒有找到名字為LLY的使用者!");}/// <summary>/// 測試尋找使用者/// </summary>[Test]public void TestFindUser(){IList userlist=null;//配置ConfigurationConfiguration cfg = new Configuration().Configure();//建立ISessionFactoryISessionFactory factory = cfg.BuildSessionFactory();//開啟ISessionISession session = factory.OpenSession();string hql="from User as u where u.UserName='LLY'";session=factory.OpenSession();try{userlist=session.Find(hql);}finally{session.Close();}//************************資料檢查*****************************//檢查Assert.AreEqual(1,userlist.Count,"名字為LLY的使用者個數不為1!");}/// <summary>/// 測試添加使用者/// </summary>[Test]public void TestAddUser() {User newUser = new User();//配置ConfigurationConfiguration cfg = new Configuration().Configure();//建立ISessionFactoryISessionFactory factory = cfg.BuildSessionFactory();//定義事務ITransaction tx = null;//開啟ISessionISession session = factory.OpenSession();try{//開始事務tx = session.BeginTransaction();newUser.UserName = "DDL";newUser.Password = "123456";newUser.Email = "DDLLY@tom.com";// 儲存新使用者session.Save(newUser);tx.Commit();}catch(HibernateException ex){if (tx!=null) tx.Rollback();throw ex;}finally{//關閉ISessionsession.Close();}//************************資料檢查*****************************IList userlist=null;string hql="from User as u where u.UserName='DDL'";session=factory.OpenSession();try{userlist=session.Find(hql);}finally{session.Close();}//檢查是否有且僅有一個名稱為DDL的使用者Assert.AreEqual(1,userlist.Count,"名字為DDL的使用者個數不為1!");}/// <summary>/// 測試刪除使用者/// </summary>[Test]public void TestDeleteUser(){IList userlist=null;User user=null;//配置ConfigurationConfiguration cfg = new Configuration().Configure();//建立ISessionFactoryISessionFactory factory = cfg.BuildSessionFactory();//定義事務ITransaction tx = null;//開啟ISessionISession session = factory.OpenSession();try{//開始事務tx = session.BeginTransaction();user=session.Load(typeof(User),1) as User;session.Delete(user);tx.Commit();}catch(HibernateException ex){if (tx!=null) tx.Rollback();throw ex;}finally{session.Close();}//************************資料檢查*****************************string hql="from User as u where u.UserName='LLY'";session=factory.OpenSession();try{userlist=session.Find(hql);}finally{session.Close();}//檢查Assert.AreEqual(0,userlist.Count,"名字為LLY的使用者沒有被刪除!");}/// <summary>/// 測試更新使用者/// </summary>[Test]public void TestUpdateUser(){User user=null;//配置ConfigurationConfiguration cfg = new Configuration().Configure();//建立ISessionFactoryISessionFactory factory = cfg.BuildSessionFactory();//定義事務ITransaction tx = null;//開啟ISessionISession session = factory.OpenSession();try{//開始事務tx = session.BeginTransaction();user=session.Load(typeof(User),1) as User;user.Password="123";session.Update(user);tx.Commit();}catch(HibernateException ex){if (tx!=null) tx.Rollback();throw ex;}finally{session.Close();}//************************資料檢查*****************************session=factory.OpenSession();try{user=session.Load(typeof(User),1) as User;}finally{session.Close();}//檢查Assert.IsNotNull(user,"沒有找到名字為LLY的使用者!");Assert.AreEqual("123",user.Password,"沒有找到名字為LLY的使用者!");}#endregion}}