2009-05-03 59 views
2

我在照片和標籤之間有多對多的關係:照片可以有多個標籤,多張照片可以共享相同的標籤。在NHibernate中與多對多關係使用哪種算法

我有一個循環掃描目錄中的照片,然後將它們添加到NHibernate。在該過程中,一些標籤被添加到照片中,例如, 2009年拍攝照片時的2009年標籤。

Tag類實現Equals和GetHashCode,並使用Name屬性作爲唯一的簽名屬性。照片和標籤都有替代鍵和版本。

我有類似下面的一些代碼:

public void Import() { 
    ... 
    foreach (var fileName in fileNames) { 
     var photo = new Photo { FileName = fileName }; 
     AddDefaultTags(_session, photo, fileName); 
     _session.Save(photo); 
    } 
    ... 
} 

private void AddDefaultTags(…) { 
    ... 
    var tag =_session.CreateCriteria(typeof(Tag)) 
        .Add(Restriction.Eq(「Name」, year.ToString())) 
        .UniqueResult<Tag>(); 

    if (tag != null) { 
     photo.AddTag(tag); 
    } else { 
     var tag = new Tag { Name = year.ToString()) }; 
     _session.Save(tag); 
     photo.AddTag(tag); 
    } 
} 

我的問題是當標籤不存在,例如新年的第一張照片。 AddDefaultTags方法檢查標籤是否存在於數據庫中,然後創建它並將其添加到NHibernate中。這在添加單張照片時效果很好,但是在新年和同一工作單元中導入多張照片時,它會失敗,因爲它仍然不存在於數據庫中,並且會再次添加。當它完成工作單元時,它會失敗,因爲它試圖在標籤表中添加兩個具有相同名稱的條目...

我的問題是如何確保NHibernate只嘗試在數據庫中創建單個標籤以上情況。我是否需要自己維護新添加的標籤列表,還是可以以這種方式設置映射?

回答

2

如果您的條件不應返回陳舊的數據,則需要運行_session.Flush()。 或者您應該可以通過將_session.FlushMode設置爲自動來正確執行此操作。

使用FlushMode.Auto,會話將在條件執行前自動刷新。

編輯:而且很重要!在閱讀你所展示的代碼時,它看起來並不像你在爲你的工作單位使用交易。我會建議在事務中包裝你的工作單元 - 如果你使用NH2.0 +,FlushMode.Auto需要工作。

進一步閱讀這裏:NHibernate ISession Flush: Where and when to use it, and why?

+0

對不起,我沒有很好地解釋那部分。我的FlushMode設置爲從不。我在一個工作單位內工作,在完成工作時我會做一個明確的刷新。哦,並且在交易中運行。 – HakonB 2009-05-04 07:31:17

0

如果您希望每次需要在保存後將其提交到該事務時檢查該新標記,數據庫中都會包含新標記。

另一種方法是在處理照片之前將標記讀取到集合中。 然後就像你說的那樣,你會搜索本地並根據需要添加新標籤。當您完成文件夾時,您可以提交會話。

你應該發佈你的映射,因爲我可能沒有正確解釋你的問題。

+0

這個方法的問題是我想運行導入作爲一個工作單元,並且在整個單元完成之前不向數據庫提交任何東西。理想情況下,運行在一個工作單元應該是透明的AddDefaultTags方法,但我想這是不可能的... 我想你明白我的問題就好。我不認爲映射本身是一個問題 - 它決定使用映射的正確模式。 – HakonB 2009-05-03 19:57:24

0

這是典型的「鎖定的東西,是不存在」的問題。我已經多次面對它,但仍然沒有一個簡單的解決方案。

這是我所知道的選項,直到如今:

  • 樂觀:對名稱的唯一約束,讓會議的一個扔在提交。然後你再試一次。當發生另一個錯誤時,您必須確保您不會以無限循環結束。
  • 悲觀:當您添加新標籤時,您使用TSQL鎖定整個標籤表。
  • .NET鎖定:您使用.NET鎖定同步這些線程。這僅適用於並行事務處於同一進程中的情況。
  • 創建使用自己的會話(參見下文)

實例標籤:

public static Tag CreateTag(string name) 
{ 
    try 
    { 
    using (ISession session = factors.CreateSession()) 
    { 
     session.BeginTransaction(); 
     Tag existingTag = session.CreateCriteria(typeof(Tag)) /* .... */ 
     if (existingtag != null) return existingTag; 
     { 
     session.Save(new Tag(name)); 
     } 
     session.Transaction.Commit(); 
    } 
    } 
    // catch the unique constraint exception you get 
    catch (WhatEverException ex) 
    { 
    // try again 
    return CreateTag(name); 
    } 
} 

這看起來很簡單,但也存在一些問題。你總是得到一個標籤,這個標籤既可以是現有的,也可以是創建的(並立即提交)但是你得到的標籤來自另一個會話,所以它被分離出來用於你的主會話。您需要使用級聯(您可能不想要)或更新將其附加到會話中。

創建標籤不再與您的主要交易耦合,這是目標,但也意味着回滾您的交易會將所有創建的標籤留在數據庫中。換句話說:創建標籤不再是您的交易的一部分。