日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
淺析NHibernate一對一映射的延遲加載

在使用NHibernate的過程中也遇到了許多麻煩,不過也得到了不少體會。例如NH的不足之處,我理想中的ORM框架是怎么樣的,等等這些,以后有機(jī)會也可以慢慢和各位進(jìn)行討論。

創(chuàng)新互聯(lián)建站專注于梅江企業(yè)網(wǎng)站建設(shè),響應(yīng)式網(wǎng)站,商城開發(fā)。梅江網(wǎng)站建設(shè)公司,為梅江等地區(qū)提供建站服務(wù)。全流程按需開發(fā),專業(yè)設(shè)計,全程項目跟蹤,創(chuàng)新互聯(lián)建站專業(yè)和態(tài)度為您提供的服務(wù)

不過這篇文章談?wù)摰钠鋵嵵皇且粋€小技巧,一個workaround,而且甚至于這個是由于我對NHibernate不夠了解而造成的。因此,如果您有更好的做法也請不吝指出。這個問題也就是“如何實現(xiàn)NHibernate一對一映射的延遲加載”。

NHibernate一對一映射問題描述

之前對于問題的描述,其實還有很多額外的要求沒有講清楚,而需要“workaround”的現(xiàn)狀,也是這些要求共同形成的。經(jīng)過嘗試,如果放棄其中任何一個(如把主表ID的生成策略從identity改為native),則可能就會有更直接的做法了。這些條件是:

NHibernate一對一映射

主鍵關(guān)聯(lián)

主表的ID為自增字段

所有字段NOT NULL。

主表和子表設(shè)置級聯(lián)刪除

現(xiàn)在的問題,就是在這些條件下,如何實現(xiàn)“獲取主表對象時,并不加載其對應(yīng)的子表數(shù)據(jù)”,也就是所謂的“延遲加載”。當(dāng)然,除了能夠“延遲加載”以外,還必須可以插入,更新和刪除——我也嘗試過使用某些特殊的映射方式,可以實現(xiàn)延遲加載,但是卻無法插入,這自然也無法滿足要求。

為了便于理解和實驗,我在這里也將其“具體化”。首先是Model,User和UserDetail,它們是典型的一對一關(guān)系:

 
 
 
  1. public class User
  2. {
  3.     public virtual int UserID { get; set; }
  4.     public virtual string Name { get; set; }
  5.     public virtual UserDetail Detail { get; set; }
  6. }
  7. public class UserDetail
  8. {
  9.     public virtual int UserID { get; set; }
  10.     public virtual int Age { get; set; }
  11.     public virtual User User { get; set; }
  12. }

而數(shù)據(jù)庫方面則是一個User表和一個UserDetail表:

 
 
 
  1. CREATE TABLE [dbo].[User](
  2.     [UserID] [int] IDENTITY(1,1) NOT NULL,
  3.     [Name] [nvarchar](50) NOT NULL,
  4.  CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED 
  5. (
  6.     [UserID] ASC
  7. ))
  8. GO
  9. CREATE TABLE [dbo].[UserDetail](
  10.     [UserID] [int] NOT NULL,
  11.     [Age] [int] NOT NULL,
  12.  CONSTRAINT [PK_UserDetail] PRIMARY KEY CLUSTERED 
  13. (
  14.     [UserID] ASC
  15. ))
  16. GO
  17. ALTER TABLE [dbo].[UserDetail]  WITH CHECK ADD
  18. CONSTRAINT [FK_UserDetail_User] FOREIGN KEY([UserID])
  19. REFERENCES [dbo].[User] ([UserID])
  20. ON DELETE CASCADE
  21. GO
  22. ALTER TABLE [dbo].[UserDetail] CHECK CONSTRAINT [FK_UserDetail_User]
  23. GO

User表為主表,主鍵為UserID,自增。UserDetail為副表,主鍵為UserID,同時作為外鍵與User表產(chǎn)生關(guān)聯(lián)。同時,外鍵上設(shè)置了級聯(lián)刪除,也就是在刪除User表的紀(jì)錄時,會自動刪除UserDetail的紀(jì)錄。

對于環(huán)境的描述就到這里,如果您想要自己實驗的話,可以直接使用這些代碼。值得強(qiáng)調(diào)一下的是,有些朋友可能會使用NHibernate自動生成數(shù)據(jù)表,那么請注意嚴(yán)格調(diào)整NHibernate的配置,使其與這個環(huán)境完全相同。

傳統(tǒng)一對一映射

關(guān)于一對一映射是否可以延遲加載的問題,我在互聯(lián)網(wǎng)上找了許多資料。有NHibernate的資料,也有沒N的資料。有的資料上說不支持,有的資料卻又說可以實現(xiàn)。不過根據(jù)那些說“可以”的資料進(jìn)行配置,卻還是無法做到延遲加載。而把這個問題發(fā)到NHibernate的用戶郵件列表中也沒有得到答復(fù)。不管怎么樣,我把普通的配置也發(fā)布在這里吧。

 
 
 
  1.  version="1.0" encoding="utf-8" ?>
  2.  xmlns="urn:nhibernate-mapping-2.2" assembly="NHTest" namespace="NHTest">
  3.    name="User" table="`User`">
  4.      name="UserID" type="Int32" column="UserID">
  5.        class="identity" />
  6.     
  7.      name="Detail" class="UserDetail" cascade="save-update" lazy="proxy" />
  8.      name="Name" type="String" />
  9.   
  10.    name="UserDetail" table="`UserDetail`" lazy="true">
  11.      name="UserID" type="Int32" column="UserID">
  12.        class="foreign">
  13.          name="property">User
  14.       
  15.     
  16.      name="User" class="User" constrained="true" />
  17.      name="Age" type="Int32" />
  18.   

按照某些資料的說法,我們把one-to-one的lazy設(shè)為proxy,并且把UserDetail節(jié)點的lazy設(shè)為true,便可以實現(xiàn)延遲加載。也就是說,在執(zhí)行以下代碼時,并不會去獲取UserDetail的內(nèi)容:

var user = session.Get(1);
可是現(xiàn)在,NHibernate告訴我們現(xiàn)在使用的SQL是這樣子的(您也可以使用SQL Profiler進(jìn)行觀察):

 
 
 
  1. SELECT
  2.     user0_.UserID as UserID0_1_,
  3.     user0_.Name as Name0_1_,
  4.     userdetail1_.UserID as UserID1_0_,
  5.     userdetail1_.Age as Age1_0_
  6. FROM
  7.     [User] user0_
  8. left outer join
  9.     [UserDetail] userdetail1_
  10.         on user0_.UserID=userdetail1_.UserID
  11. WHERE
  12.     user0_.UserID=@p0;
  13. @p0 = 1

很明顯,它仍然把UserDetail一并獲取出來了。如果您覺得這里哪里錯了,請告訴我。

開始繞彎路

從現(xiàn)在開始,我們就要走“彎路”了。雖然我們無法在一對一映射的情況下實現(xiàn)延遲加載,但是我們可以輕易做到“一對多”映射時,延遲加載“集合”中的子對象。我們這個workaround的關(guān)鍵,便是利用了“一對多”情況下的延遲加載,把“一對一”作為“一對多”的特殊情況進(jìn)行處理。不過這里就需要我們修改User的Model了:

 
 
 
  1. public class User
  2. {
  3.     public virtual int UserID { get; set; }
  4.     public virtual string Name { get; set; }
  5.     private ISet m_detailLazyProxySet;
  6.     private ISet DetailLazyProxySet
  7.     {
  8.         get
  9.         {
  10.             if (this.m_detailLazyProxySet == null)
  11.             {
  12.                 this.m_detailLazyProxySet = new HashedSet();
  13.             }
  14.             return this.m_detailLazyProxySet;
  15.         }
  16.         set
  17.         {
  18.             this.m_detailLazyProxySet = value;
  19.         }
  20.     }
  21.     public virtual UserDetail Detail
  22.     {
  23.         get
  24.         {
  25.             return this.DetailLazyProxySet.Count <= 0 ? null :
  26.                 this.DetailLazyProxySet.Single();
  27.         }
  28.         set
  29.         {
  30.             this.DetailLazyProxySet.Clear();
  31.             this.DetailLazyProxySet.Add(value);
  32.         }
  33.     }
  34. }

也多虧NHibernate支持對private屬性的讀寫,我們可以把DetailLazyProxySet設(shè)為私有屬性,對外部保持“純潔”——但是,很明顯我們還是污染了Model。因此,這無論如何也只是一個workaround。

如果您使用xml進(jìn)行配置,這自然沒有什么問題。不過我還是喜歡使用Fluent NHibernate,流暢,方便,還可以導(dǎo)出為xml。因此,我們這里提供Fluent NHibernate的代碼,相信您也可以輕易得出它所對應(yīng)的xml配置內(nèi)容:

 
 
 
  1. public class UserMap : ClassMap
  2. {
  3.     public UserMap()
  4.     {
  5.         Id(u => u.UserID).GeneratedBy.Identity();
  6.         Map(u => u.Name);
  7.         var paramExpr = Expression.Parameter(typeof(User));
  8.         var propertyExpr = Expression.Property(paramExpr, "DetailLazyProxySet");
  9.         var castExpr = Expression.Convert(propertyExpr, typeof(IEnumerable));
  10.         var lambdaExpr = Expression.Lambda, IEnumerable>>(castExpr, paramExpr);
  11.         HasMany(lambdaExpr)
  12.             .LazyLoad()
  13.             .AsSet()
  14.             .KeyColumnNames.Add("UserID")
  15.             .Cascade.All()
  16.             .Inverse();
  17.     }
  18. }
  19. public class UserDetailMap : ClassMap
  20. {
  21.     public UserDetailMap()
  22.     {
  23.         Id(d => d.UserID).GeneratedBy.Foreign("User");
  24.         Map(d => d.Age);
  25.         HasOne(d => d.User).Constrained();
  26.     }
  27. }

值得一提的是,由于DetailLazyProxySet是私有的,我們必須手動地構(gòu)造一個Lambda表達(dá)式傳遞給HasMany方法。在實際使用過程中,我們應(yīng)該提供額外的輔助方法(自然是為ClassMap新增一個擴(kuò)展方法),并配合約定(屬性名 + LazyProxySet)來進(jìn)行強(qiáng)類型的編碼定義。它可能是這樣的:

HasOneByProxySet(u => u.Detail)...
嗯,就是這么點代碼。

實驗

打開NHibernate的SQL輸出,并編寫如下代碼:

 
 
 
  1. var user = session.Get(1);
  2. Console.WriteLine("Name: {0}", user.Name);
  3. Console.WriteLine("Age: {0}", user.Detail.Age);

輸出如下:

 
 
 
  1. NHibernate:
  2.     SELECT
  3.         user0_.UserID as UserID1_0_,
  4.         user0_.Name as Name1_0_
  5.     FROM
  6.         [User] user0_
  7.     WHERE
  8.         user0_.UserID=@p0;
  9.     @p0 = 1
  10. ===> Name: Jeffrey Zhao
  11. NHibernate:
  12.     SELECT
  13.         detaillazy0_.UserID as UserID1_,
  14.         detaillazy0_.UserID as UserID0_0_,
  15.         detaillazy0_.Age as Age0_0_
  16.     FROM
  17.         [UserDetail] detaillazy0_
  18.     WHERE
  19.         detaillazy0_.UserID=@p0;
  20.     @p0 = 1
  21. ===> Age: 25

請注意兩條輸出(已標(biāo)紅)的位置,很明顯現(xiàn)在已經(jīng)實現(xiàn)了延遲加載。那么我們要“饑渴加載(Eager Load)”又當(dāng)如何?其實也很簡單:

 
 
 
  1. var user = session
  2.     .CreateCriteria()
  3.     .SetFetchMode("DetailLazyProxySet", FetchMode.Eager)
  4.     .Add(Expression.IdEq(8))
  5.     .UniqueResult();
  6. Console.WriteLine("===> Name: {0}", user.Name);
  7. Console.WriteLine("===> Age: {0}", user.Detail.Age);

同樣,“擴(kuò)展方法”配合“約定”,我們可以把SetFetchMode這行古怪的代碼改成:

.SetFetchMode(u => u.Detail)...

輸出如下:

 
 
 
  1. NHibernate:
  2.     SELECT
  3.         this_.UserID as UserID1_1_,
  4.         this_.Name as Name1_1_,
  5.         detaillazy2_.UserID as UserID3_,
  6.         detaillazy2_.UserID as UserID0_0_,
  7.         detaillazy2_.Age as Age0_0_
  8.     FROM
  9.         [User] this_
  10.     left outer join
  11.         [UserDetail] detaillazy2_
  12.             on this_.UserID=detaillazy2_.UserID
  13.     WHERE
  14.         this_.UserID = @p0;
  15.     @p0 = 8
  16. ===> Name: Jeffrey Zhao
  17. ===> Age: 25

我們的饑渴換來數(shù)據(jù)庫的級聯(lián),和諧而統(tǒng)一。

NHibernate一對一映射的延遲加載總結(jié)

至此,我們成功地實現(xiàn)了“一對一”的延遲加載,但是對NHibernate來說,一切都是個一對多的關(guān)系。我們獲得了表面的成功,付出了“Model被污染”的代價。

原文來自趙劼的博客園博文《NHibernate中一對一關(guān)聯(lián)的延遲加載》


網(wǎng)站標(biāo)題:淺析NHibernate一對一映射的延遲加載
網(wǎng)頁URL:http://www.dlmjj.cn/article/cdgcpdg.html