NHibernate從入門到精通系列

http://www.cnblogs.com/GoodHelper/archive/2011/02/17/1948744.htmlhtml

 

NHibernate從入門到精通系列(4)——持久對象的生命週期(上)sql

 

  

  內容摘要數據庫

    持久對象的狀態的概念緩存

    持久對象的狀態Demosession

 

  1、持久對象的狀態的概念  app

  在NHibernate中有三種狀態,對它的深刻理解,才能更好的理解NHibernate的運行機理,剛開始不太注意這些概念,後來發現它是重要的。對於NHibernate和SQL的關係有更好的理解;對於理解須要持久化的.NET對象,在它的生命週期中三種狀態之間的互相轉化有很大幫助。如圖1.1所示ide

圖1.1post

  • 臨時態(Transient):用new建立的對象,它沒有持久化,沒有歸入Session中,隨時能夠被垃圾回收,處於此狀態的對象叫臨時對象。特色:數據庫中沒有與之對應的記錄;
  • 持久態(Persistent):已經持久化,加入到了Session緩存中。經過NHibernate保存的對象或經過Get/Load等方法獲取出的對象,其對象沒有脫離Session的管理,處於此狀態的對象叫持久對象;
  • 遊離態(Detached):持久化對象脫離了Session的對象。如Session緩存被清空的對象。特色:已經持久化,但不在Session緩存中。處於此狀態的對象叫遊離對象;

 

  2、持久對象的狀態Demo性能

  2.1 準備工做單元測試

  (1)創建名爲「NHibernateTest」的項目

  (2)引用相應的程序集並引入上節課的「Domain」項目。

  (3)複製上節課的「hibernate.cfg.xml」配置模板

 

hibernate.cfg.xml
<?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=.\SQLEXPRESS;database=NHibernateDemo;uid=sa;pwd=;     </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">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>

 

 

  (4)引用「log4net.dll」並配置App.config,用於輸出日誌 

  

App.config
<?xml version="1.0"?> <configuration>   <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />   </configSections>
  <!--log4net配置-->   <log4net debug="true">     <appender name="LogFileAppender" type="log4net.Appender.FileAppender">       <param name="File" value="Logs\Log.log" />       <param name="datePattern" value="MM-dd HH:mm" />       <param name="AppendToFile" value="true" />       <layout type="log4net.Layout.PatternLayout">         <param name="ConversionPattern" value="%d [%t] %-5p %c [%x] - %m%n" />       </layout>     </appender>     <appender name="HttpTraceAppender" type="log4net.Appender.ASPNetTraceAppender">       <layout type="log4net.Layout.PatternLayout">         <param name="ConversionPattern" value="%d [%t] %-5p %c [%x] - %m%n" />       </layout>     </appender>     <appender name="EventLogAppender" type="log4net.Appender.EventLogAppender">       <layout type="log4net.Layout.PatternLayout">         <param name="ConversionPattern" value="%d [%t] %-5p %c [%x] - %m%n" />       </layout>     </appender>     <appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">       <param name="File" value="Logs/Log.log" />       <param name="AppendToFile" value="true" />       <param name="MaxSizeRollBackups" value="10" />       <param name="MaximumFileSize" value="100K" />       <param name="RollingStyle" value="Size" />       <param name="StaticLogFileName" value="true" />       <layout type="log4net.Layout.PatternLayout">         <param name="ConversionPattern" value="%d [%t] %-5p %c [%x] - %m%n" />       </layout>     </appender>     <root>       <level value="ALL" />       <appender-ref ref="RollingLogFileAppender" />     </root>   </log4net>
  <startup>     <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>   </startup> </configuration>

 

 

 

  (5)複製「LinFu.DynamicProxy.dll」和「NHibernate.ByteCode.LinFu.dll」文件,粘貼到項目中。

 

  (6)增長用於單元測試的類文件「LifecycleTest.cs」

 

LifecycleTest.cs
    [TestFixture]     public class LifecycleTest     {         private ISessionFactory sessionFactory;
        public LifecycleTest()         {             log4net.Config.XmlConfigurator.Configure();         }
        [SetUp]         public void Init()         {             var cfg = new NHibernate.Cfg.Configuration().Configure("Config/hibernate.cfg.xml");             sessionFactory = cfg.BuildSessionFactory();         }      }

 

 

 

  如圖2.1.1所示,準備完成後,即可以開始咱們的演示。

圖2.1.1

 

 

  2.2 臨時態(Transient)到持久態(Persistent)

  先new一個對象,該對象的狀態爲Transient,而後調用Save()方法將該對象持久化到數據庫中,該對象的狀態變爲Persistent。在未關閉Session前,修改該對象的屬性,最後提交事務。

TransientToPersistentTest
        /// <summary>         /// 臨時態-->持久態         /// </summary>         [Test]         public void TransientToPersistentTest()         {             using (ISession session = this.sessionFactory.OpenSession())             {                 using (ITransaction tran = session.BeginTransaction())                 {                     //Transient                     var product = new Product                     {                         ID = Guid.NewGuid(),                         BuyPrice = 10M,                         Code = "ABC123",                         Name = "電腦",                         QuantityPerUnit = "20x1",                         SellPrice = 11M,                         Unit = ""                                             }; 
                    try                     {                         //Persistent                         session.Save(product);
                        //保存記錄後修改數據,觀察數據庫中數據的變化                         product.SellPrice = 12M;
                        tran.Commit();                     }                     catch (Exception ex)                     {                         tran.Rollback();                         throw ex;                     }                 }             }         }

 

 

  運行效果如圖2.2.1所示,首先生成了insert into語句,而後生成了update語句。

圖2.2.1

  一開始,Product的SellPrice屬性,我設置爲「11M」,而後調用「Save」方法持久化「Product」對象,接下來修改SellPrice屬性到「12M」。最後讓咱們打開數據庫,看一下里面的數據究竟是「11M」,仍是「12M」。如圖2.2.2所示,數據是「12M」。

圖2.2.2

 

  這時,咱們內心便產生了一個疑問:把Product的SellPrice屬性從「11M」修改成「12M」後,並無調用Save()或者Update()的方法,爲何數據庫中的數據會變呢?

  這是由於,當對象處於Persistent狀態,並無脫離Session管理時,其屬性發生改變後,數據庫相對應的記錄會與之同步

 

  2.3 持久態(Persistent)到遊離態(Detached),再到持久態(Persistent)

 

PersistentToTestDetached
/// <summary>         /// 持久態-->遊離態-->持久態         /// </summary>         [Test]         public void PersistentToTestDetached()         {             //Transient             var product = new Product             {                 ID = Guid.NewGuid(),                 BuyPrice = 10M,                 Code = "ABC123",                 Name = "電腦",                 QuantityPerUnit = "20x1",                 SellPrice = 11M,                 Unit = ""             };
            using (ISession session = this.sessionFactory.OpenSession())             {                 using (ITransaction tran = session.BeginTransaction())                 {                                    try                     {                         //Persistent                         session.Save(product);                         product.SellPrice = 12M;
                        tran.Commit();                     }                     catch (Exception ex)                     {                         tran.Rollback();                         throw ex;                     }                 }             }
            //Detached             product.SellPrice = 13M;
            using (ISession session = this.sessionFactory.OpenSession())             {                 using (ITransaction tran = session.BeginTransaction())                 {                     try                     {                         //Persistent                         session.Update(product);                         tran.Commit();                     }                     catch (Exception ex)                     {                         tran.Rollback();                         throw ex;                     }                 }             }         }

 

 

 

  運行效果如圖2.3.1所示。當對象處於遊離態(Detached)時,修改其屬性,是不會與數據庫發生同步的。調用Update()方法後,對象則變回持久態(Persistent)。

圖2.3.1

 

 

  2.4 Get方法獲得持久態(Persistent)

  經過Get()方法獲取持久態(Persistent)對象,而後修改其屬性,觀察是否與數據庫發生同步。運行效果如圖2.4.1所示,先生成insert into語句,而後生成select語句,最後生成update語句。

圖2.4.1

 

  咱們可以得出結論,經過Get()方法,是能夠獲得持久態(Persistent)對象的。

 

  2.5 Get和Load()方法的區別

  咱們模擬一個數據庫中不存在的對象,分別調用Get和Load()方法來測試產生的效果。

  

  Get方法的代碼以下:

 

Get
        /// <summary>         /// 查詢空記錄         /// </summary>         [Test]         public void GetNullTest()         {             Guid id = Guid.NewGuid(); 
            using (ISession session = this.sessionFactory.OpenSession())             {                 using (ITransaction tran = session.BeginTransaction())                 {                     try                     {                         //Persistent                         var product = session.Get<Product>(id);
                        Console.WriteLine("調用 Get()方法");
                        //斷言爲空                         Assert.Null(product);
                        tran.Commit();                     }                     catch (Exception ex)                     {                         tran.Rollback();                         throw ex;                     }                 }             }         }

 

 

  Get()方法的運行效果,如圖2.5.1所示。調用Get()方法後,數據庫中不存在的對象返回值爲null,而且一但調用Get()方法,就會生成SQL語句。

圖2.5.1

 

 

  Load()方法的代碼以下:

 

Load
      /// <summary>         /// 查詢空記錄         /// </summary>         [Test]         public void LoadTest()         {             Guid id = Guid.NewGuid();
            using (ISession session = this.sessionFactory.OpenSession())             {                 using (ITransaction tran = session.BeginTransaction())                 {                     try                     {                         //Persistent                         var product = session.Load<Product>(id);
                        Console.WriteLine("調用 Load()方法");
                        //斷言爲空                         Assert.NotNull(product);
                        //當查看其屬性時,則會生成SQL語句                         string name = product.Name;                         Assert.NotNull(name);  //斷言name不爲空                         tran.Commit();                     }                     catch (Exception ex)                     {                         tran.Rollback();                         throw ex;                     }                 }             }         }

 

 

 

  Load()方法的運行效果,如圖2.5.2所示。調用Load()方法查詢數據庫中不存在的對象,返回值不爲空;當調用Load()方法時,不馬上產生SQL語句,查看其屬性後才產生SQL語句,而且查看數據庫中不存在對象的屬性時會拋出異常。緣由是調用Load()方法會返回一個「代理類」,這是NHibernate的一個重要的特性——延遲加載。

 延遲加載(lazy load)也叫「懶加載」,是NHibernate關聯關係對象默認的加載方式,延遲加載機制是爲了不一些無謂的性能開銷而提出來的,所謂延遲加載就是當在真正須要數據的時候,才真正執行數據加載操做。能夠簡單理解爲,只有在使用的時候,纔會發出SQL語句進行查詢。 延遲加載的有效期是在Session打開的狀況下,當Session關閉後,會報異常。NHibernate的代理對象是由第三方組件「Antlr3.Runtime」提供的。

圖2.5.2

 

  2.6 Delete()方法

  先獲得一個持久態(Persistent)對象,而後調用Delete()方法刪除該對象,這時該對象變回臨時態(Transient)

  代碼以下:

 

Delete
        [Test]         public void DeleteTest()         {             using (ISession session = this.sessionFactory.OpenSession())             {                 using (ITransaction tran = session.BeginTransaction())                 {                     //Transient                     var product = new Product                     {                         ID = Guid.NewGuid(),                         BuyPrice = 10M,                         Code = "ABC123",                         Name = "電腦",                         QuantityPerUnit = "20x1",                         SellPrice = 11M,                         Unit = ""
                    };
                    try                     {                         //Persistent                         session.Save(product);
                        //Transient                         session.Delete(product);
                        tran.Commit();                     }                     catch (Exception ex)                     {                         tran.Rollback();                         throw ex;                     }                 }             }         }

 

 

  運行效果如圖2.6.1所示,先生成insert into語句,再生成delete語句。

圖2.6.1

 

 

  2.7 Update()方法

  先手動打造new一個數據庫中存在的遊離態(Detached)對象,而後直接調用Update()方法將對象的狀態設置爲持久態(Persistent)

  代碼以下:

 

Update
        [Test]         public void UpdateTest()         {             Guid id = Guid.NewGuid();
            using (ISession session = this.sessionFactory.OpenSession())             {                             using (ITransaction tran = session.BeginTransaction())                 {                     //Transient                     var product = new Product                     {                         ID = id,                         BuyPrice = 10M,                         Code = "ABC123",                         Name = "電腦",                         QuantityPerUnit = "20x1",                         SellPrice = 11M,                         Unit = ""
                    };
                    try                     {                         //Persistent                         session.Save(product);                         tran.Commit();                     }                     catch (Exception ex)                     {                         tran.Rollback();                         throw ex;                     }                 }             }
            using (ISession session = this.sessionFactory.OpenSession())             {                 using (ITransaction tran = session.BeginTransaction())                 {                     //Detached                     var product = new Product                     {                         ID = id,                         Code = "ABC456",                     };
                    try                     {                         //Persistent                         session.Update(product);
                        tran.Commit();                     }                     catch (Exception ex)                     {                         tran.Rollback();                         throw ex;                     }                 }             }         }

 

 

  運行效果如圖2.7.1所示,生成了update語句,並已經修改了對應的記錄。有的朋友就會問,爲何new的時候也能獲得遊離態(Detached)對象?由於判斷是否爲遊離態(Detached)對象,是根據數據庫中是否存在與之對應的記錄定奪的。

圖2.7.1

 

 

 

  代碼下載

  出處:http://www.cnblogs.com/GoodHelper/archive/2011/02/17/nhibernate_04.html

  歡迎轉載,但需保留版權。

相關文章
相關標籤/搜索