NHibernate 快速入門
這篇教程將透過你的第一個NHibernate項目,講述配置和映射所需要的基本步驟。
僅關注我們想要的,我們將從一個很簡單的例子開始。為了讓代碼量最小,我們也去掉了單元測試和接口的代碼,專一地關注持久化數據的代碼并闡述一些基本的CRUD(Change, Read, Update, Delete) 技術。
這篇教程和其他樣例中要用到的工具:
Microsoft SQL Server 2005 Express Edition - 存放我們的持久化數據。這篇教程在SQL Server 2008環境下也有效。在這篇教程中,我假設你有一個本地SQL Server的實例并且熟練地使用SQL Manger Studio。
NHibernate version 2.1.2 - 你可以點擊下方鏈接下載最先的 NHibernate: http://sourceforge.net/projects/nhibernate/files/NHibernate/
Microsoft Visual Studio 2008 - 我們的開發環境。
創建項目文件
在這個例子里,因為我們不關注用戶界面 ,所以我們將從創建一個新的控制臺應用程序項目開始:
根據這篇教程的目的,我們把我們的應用程序取名叫'HelloNHibernate'。我也推薦堅持這種命名的傳統,并遵循不同的樣例使用不同的名字,盡管在很多情形下,教程的其他部分也會使用這樣的命名。
添加對NHibernate的引用
我們下一步要添加一系列讓NHibernate發揮作用的必須添加的引用。當你下載并解壓你的NHibernate安裝包后,電腦上就會創建一些目錄,包括 'Required_bins'和'Required_for_LazyLoading。
首先,你要把 Required Bins目錄下所有的DLL包含進來 - 它們代表NHibernate使用的核心組件。
其次,我們把Required_for_LazyLoading的一個子目錄下所有的Dll包含進來。選擇哪個目錄的Dll來完成延遲加載,最終將取決于你。 在這篇教程和其他文章中,我使用的是Castle dynamic proxy。
配置NHibernate
建立好我們的引用之后,我們現在需要創建和更新我們的App.config (如果是網站,就修改Web.config)。在我們的項目中,我做以下幾個假設:
-
我們安裝了SQL Server 2005并創建好了數據庫.
</li> -
數據庫開啟了信任連接
</li> -
使用Castle proxy factory
</li> -
把TSQL語句輸出到控制臺(在你學習NHibernate的時候,我強烈推薦你這樣做,因為你可以看到后臺正在做什么).
</li> </ul>這是一個App.config配置的例子,它包含了這篇教程中所需要的部分。
<?xmlversion="1.0"encoding="utf-8" ?>
<configuration>
<configSections> <sectionname="hibernate-configuration"requirePermission="false"type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate" /> </configSections> <hibernate-configurationxmlns="urn:nhibernate-configuration-2.2"> <reflection-optimizeruse="false" /> <session-factory> <propertyname="connection.provider">NHibernate.Connection.DriverConnectionProvider</property> <propertyname="dialect">NHibernate.Dialect.MsSql2005Dialect</property> <propertyname="connection.driver_class">NHibernate.Driver.SqlClientDriver</property> <propertyname="connection.connection_string">Data Source=(local); Initial Catalog=quickstart; Trusted_Connection=true;</property> <propertyname='proxyfactory.factory_class'>NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</property> <propertyname="show_sql">true</property> </session-factory> </hibernate-configuration></configuration></pre>
在配置中,你將看到我們創建NHibernate來使用SQL Server和SQL 2005的語句。你也能看到連接字符串(可以根據需要修改),還有對Castle Proxy Factory的引用,也有一個告訴Nhibernate輸出TSQL語句到控制臺的標志(show_sql)。
創建我們的模型
NHibernate允許你直接使用Plain Old CLR Objects (POCOs),而不用通過存儲程序來直接和數據庫交互。使用POCOs的一個優勢在于我們不用綁定特定的持久化層。舉個例子,有些ORM解決方案需要特殊屬性,或者是基于你的模型對象,這些對象又是從特定的基類中繼承而來的。
在NHibernate中,不用特殊的修飾,就可以讓我們的對象和持久化層交互。在NHibernate中,我們唯一要意識到的是所有的我們希望持久化的屬性必須是虛擬的,并且要開啟延遲加載,所有類中的公共方法必須是虛擬的,哪怕它們并沒有包含到我們的映射文件中。
通常來講,最好把所有的屬性都設置為虛擬的,這樣你的基類就封裝了,不管你使用延遲加載與否。
另外,我們可以讓POCO里的屬性和方法不持久,并且可以讓多個POCOs映射到數據庫中相同的數據表。舉個例子,我們有一個POCO提供了產品的詳情,包括圖片和描述,其他的輕量版本只包括用來創建商品清單的名稱和數量。
在我們的教程中,我們將創建一個極端的簡單對象。在后期的教程中,我們會講述更加復雜的話題,比如父子關系,高級搜索技術等等。
創建在你的項目中創建一個類,命名為‘Produce’,并設置如下的屬性。注意Nhibernate是大小寫敏感的,所以我們的屬性名要準確地匹配后面要創建的Nhibernate映射。
namespace HelloNHibernate { public classProduct { public virtual int? ProductId { get; set; } public virtual string ProductName { get; set; } } }注意我們設置ProductId為可為null的值。這允許我們創建一個沒有ID的實例,反過來告訴NHibernate這是一個新對象而不是一個ID為'0'的對象。
創建NHibernate映射文件
Nhibernate使用XML映射文件來映射POCO到數據庫對象。雖然在很多案例中這可能是一對一關系,但這并不是必要的。你可以輕易地更改字段名稱并創建不同的映射來給數據庫對象創建可選的表示區域。
舉個例子,我們有一個復雜的'Product'對象,它包含了大量的文本信息,一個與訂單和復雜清單數據的容易。這對于我們正在編輯的單個產品的產品詳情區域來說,將是一個很好的映射,它將變得十分臃腫,以至于在產品的下拉列表中,它不能作為一個強類型列表的成員。在后面,要創建一個更簡單一點到只有產品ID和產品名稱的持久化區域的映射。
在我們的教程中,我們使用一個簡單的直接關系到我們上面創建的POCO的映射。來創建這樣的一個映射,我們要添加一個新的XML文件到上面展示的工程中。當創建XML文件時,要讓它以.hbm.xml命名方式結尾-這種傳統告訴NHibernate這是一個映射文件。我們的名稱叫做'Product.hbm.xml'。
你的XML文件應該包含以下信息。下面我們將一一講述每一個標簽:
<?xmlversion="1.0"encoding="utf-8" ?> <hibernate-mappingxmlns="urn:nhibernate-mapping-2.2" assembly="HelloNHibernate" namespace="HelloNHibernate"><class name="Product" table="Products"> <id name="ProductId"> <generatorclass="identity"/> </id> <property name="ProductName"length="50" /> </class></hibernate-mapping></pre>
-
在hibernate-maping標簽中,我們同時引用類集(POCOs)所屬的程序集合命名空間。當你的映射文件在單獨的命名空間或者程序集而不是模型中,這種方式是十分方便的。
</li> </ul>-
class元素表示到單個POCO的映射。name表示上面的程序集和命名空間中的類名,table屬性告訴NHibernate數據庫中的哪個表或者視圖將被映射。
</li> -
id元素告訴NHibernate哪個數據庫的字段和對應的對象作為一個唯一鍵來使用。在本例子中,我們使用ProductId這個字段。
</li> -
generator元素告訴NHibernate怎樣給新實體來創建唯一ID。因為我們使用SQL Server屬性中的ID屬性,所以我們指明generator類為'identity'。
</li> -
property標簽是你見得最多的標簽。它簡單地映射一個到數據表或者視圖中對應字段的映射。我已經把這個例子中最長的屬性包含進來了,因為我們想為映射創建一個架構。對于我們所需要的產品名稱,默認的255個字符已經足夠大了。
</li> </ul>一旦XML文件創建好了,你需要確保它被設置為嵌入式資源,否則NHibernate不會讀出你的XML文件,那么映射就不會生效了。完成這一步,你要在解決方案資源瀏覽器中點擊你新建的XML文件,更改XML的生成方式。
到這一步,你的解決方案應該和下圖類似:
使用 NHibernate連接數據庫
到了這一步,我們已經完成了安裝和配置,現在就準備使Nhibernate和我們的數據庫來交互。
透過這些例子,我推薦你單獨跟著做,并觀察對象是如何發生改變的,數據又是如何持久化到數據庫的。在你觀察持久化對象改變的時候,這有助于你更好地理解NHibernate是如何工作的,有助于你鞏固下面的樣例。
下面到了代碼了!在第一個樣例中,我們只關注使用NHibernate來完成持久化數據所需要的最小量的代碼,而不關注測試和其他更好的實踐,注意到這些事很重要的。當我們引申到更復雜的樣例中,我們要花費更多的時間為你的實際程序來創建運行更好的,更加健壯的架構。
在NHibernate中,我們的根對象將是一個實現ISessionFactory接口的對象。一個SessionFactory是一個很昂貴的對象,在你的程序中,你只有在需要一個就夠了。
要創建我們的SessionFactory,我們將使用NHibernate Configuration對象的一個方法來創建一個基于App.config配置中的會話工廠,并創建不同映射文件。
讓我們來看看一些代碼:
using NHibernate; using NHibernate.Cfg; using NHibernate.Tool.hbm2ddl;
namespace HelloNHibernate { class Program { static void Main(string[] args) {
Configuration config= new Configuration(); config.AddAssembly(typeof(Product).Assembly); ISessionFactory sessionFactory = config.BuildSessionFactory(); var schema = new SchemaExport(config); schema.Create(true, true); } }}</pre>
一一來講述前5行代碼.
第一行代碼基于app.config文件中的值創建了一個NHibernate COnfiguration對象的實例,這個對象在命名空間NHibernate.cfg中。
Configuration config= new Configuration();
接下來,我們要加載映射文件,它們用來把POCOs映射到持久化數據庫對象。加載映射文件有很多方法,但是所有的方法都需要傳遞一個程序集作為參數。NHibernate用這些程序集來查找所有的持久化映射數據。
在第一個樣例中,我們僅僅使用Product對象,所以我們可以用下面這行代碼。(另外,我們也可以用Assembly.GetExecutingAssembly(),因為Product hbm 文件和類對象都在主程序集中。)
config.AddAssembly(typeof(Product).Assembly);
然后我們使用Configuration對象的BuildSessionFactory()方法創建會話工廠。這個函數返回一個實現我們要使用的ISeesionFactory的對象。
ISessionFactorysessionFactory = config.BuildSessionFactory();
下面兩行代碼用來重構我們的架構。這些命令可以用來引導一個新應用程序的安裝,清除集合測試期間的數據庫,或者輸出DDL到一個文件用來給DBA提供一個腳本。
我們用這種方法來重置樣例程序執行中的數據庫,有了這樣的代碼,你不用擔心數據庫的清理工作。
var schema = newSchemaExport(config); schema.Create(true, true);
(你應該看看控制臺下的SQL語句。)
if exists (select * from dbo.sysobjects where id = object_id(N'Products') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table Products create table Products ( ProductId INT IDENTITY NOT NULL, ProductName NVARCHAR(50) null, primary key (ProductId) ) )使用NHibernate實現CRUD基本操作
現在我們可以通過一個有效的SessionFactory對象來建立我們的數據存儲,是時候存儲一些數據了。
第一個例子,我們要完成product表中基本的CRUD操作。我們將從下面的代碼片段開始(你可以在schema.Create之后立即添加這些代碼),然后分別詳細地講述每個操作。
ProductpNew = newProduct() {ProductName = "Canned Salmon"};using (var session = sessionFactory.OpenSession()) { //Create a Product using (var trans = session.BeginTransaction()) { session.Save(pNew); trans.Commit(); }
//Retrieve a product by ID ProductpGet = session.Get<Product>(pNew.ProductId); //Update a Product pGet.ProductName = "Canned Tuna"; using (var trans = session.BeginTransaction()) { session.Update(pGet); trans.Commit(); } //Delete a Product using (var trans = session.BeginTransaction()) { session.Delete(pNew); trans.Commit(); }}</pre>
從創建product ( Canned Salmon)開始。我們讓ProductId置空-這是因為這是一個ID字段,我們還沒有持久化這個對象。就像你所看到的,NHibernate不僅處理數據庫的持久化,還為我們設置這個屬性到新創建的ID值。(這是一個我們以往要用從存儲程序中返回的@@Identity變量來完成的一個任務。)
接下來,從SessionFactory中打開一個Session.所有的NHibernate持久化操作將會在Session的上下文中處理。
using (var session = sessionFactory.OpenSession())
完成會話創建后,我們可以來談談業務。在檢索數據的情況下,使用Session對象是不錯的選擇。但是在我們需要在數據庫中更改數據的情形下,我們要使用從當前Session對象創建的Transaction對象(當我們創建,更新,刪除product對象的時候,你會看到這一點。)
創建一個持久化對象
Wh不管我們有一個還是多個屬性,創建新的持久化對象遵循相同的模式,并能用幾行代碼就能完成。
- 為你要更新的數據創建一個轉化
- 在當前會話中執行Save方法,把你想要持久化的對象作為參數來傳遞
- 提交轉化 </ul>
//Create a Product using (var trans = session.BeginTransaction()) { session.Save(pNew); trans.Commit(); }(你應該在控制臺下看看下面的代碼)
NHibernate: INSERT INTO Products (ProductName) VALUES (@p0); select SCOPE_IDENTITY();@p0 = 'Canned Salmon'
如果你看看pNew的屬性,你會看到Nhibernate已經更新基于持久化數據庫實體的對象了(本例子中,將ProductId設為'1')。
通過ID檢索持久化對象
從數據庫中檢索對象時,NHibernate提供了一種簡單的方法,如果知道了檢索的實體的ID號。在這里,創建一個新對象(pGet)基于剛添加到數據庫(pNew)中的對象的ID。
//Retrieve a product by ID ProductpGet = session.Get<Product>(pNew.ProductId);
有一件重要的事情要注意,在會話中,pGet和pNew指向相同的持久化后的實體,所以改變任何一個對象(pGet或者pNew)都會改變對象中的反射。
你同樣要注意,控制臺中的輸出中沒有'Select'語句。這是因為我們能夠查找在會話中已經加載的對象(pNew)來連接pGet。
另一方面,如果我們想在不同的會話上下文中查找pGet,有兩種情況是不同的。
第一,pNew和pGet不再引用內存中的相同對象(盡管它們還是綁定到數據庫中相同的持久化對象)。第二,你將會在控制臺中看到Select語句。因為我們將會創建一個新會話,這個會話正是NHibernate用數據庫中值為'1'的ID檢索實體時所需要的。
NHibernate: SELECT product0_.ProductId as ProductId0_0_, product0_.ProductName as ProductN2_0_0_ FROM Products product0_ WHERE product0_.ProductId=@p0;@p0 = 1
更新一個持久化對象
同創建持久化對象一樣,我們更新對象是在轉化上下文中的一個對象之前執行一個方法,如下所示:
//Update a Product pGet.ProductName = "Canned Tuna"; using (var trans = session.BeginTransaction()) { session.Update(pGet); trans.Commit(); }(你在控制臺會看到以下代碼)
NHibernate: UPDATE Products SET ProductName = @p0 WHERE ProductId = @p1;@p0 = 'Canned Tuna', @p1 = 1
會話對象同樣有一個非常方便的'SaveOrUpdate'方法,它會自動地執行恰當的語句。比如,如果POCO的ID為空,會執行插入操作。如果不為空,會執行更新操作。
刪除一個持久化對象
Fo最后一個例子,我們將刪除一個product。同樣,因為這會影響到我們的持久化實體,我們將在轉化的作用域中執行刪除操作,并傳遞我們希望刪除的POCO最為參數。
//Delete a Product using (var trans = session.BeginTransaction()) { session.Delete(pNew); trans.Commit(); }(你在控制臺中會看到以下代碼)
NHibernate: DELETE FROM Products WHERE ProductId = @p0;@p0 = 1
總結
雖然這是一個非常簡單的例子,以下幾點要仔細思考。
-
我們沒有創建任何的DDL。大多數評價ORMs的開發者在尋找一個小程序運行方案時,寫了海量的TSQL語句。我們并沒有徹底地更改我們的POCOs-它們沒有屬性,接口或者基類。我們唯一的操作就是把屬性的值改為Virtual。有了這樣的變化,在沒有Nhibernate的情況下,我們可以輕易地使用相同的對象,這就提高了柔韌性和易測性。
</li> -
我們已經完成了要創建的TSQL的可視性。盡管某些數據庫(比如微軟的SQL Server)優化存儲程序的的能力還存在爭議,但在數據庫服務中,性能優勢最小量地分配到當前的處理能力。
</li> -
另一個有爭議的是,好的DBA可能會創建更加優化的代碼,比NHibernate創建的代碼還要好。
</li> </ul>
雖然謹慎的手動查詢比創建的代碼更好,但是大多數時候NHibernate創建的代碼遠比很多開發者寫的程序創建的代碼好得多。
舉個例子,一個一般的更新程序可能會帶來一百個參數,這可能會在數據庫記錄中潛在地發生改變。NHibernate,另一方面,將會智能地根據動態更新語句來改變實際變化的列,而表現得更好。在第一個教程中,我們闡述了使用NHibernate連接一個持久化數據資源的資本步驟,和如何實現簡單的CRUD操作。在下一系列的教程中,我們將會關注更加復雜的情況,并闡述更好的實踐來幫助你實現更好的可測試性和維持性的數據訪問層。
-
-