2010-11-05 56 views
1

我正在開票系統,具有用於多個subsidaries其中每個都有自己的一套發票編號的支持,因此我有(附屬的主鍵的表,InvoiceNo )替代最大值(ID)在複雜的主鍵

我不能使用MySQL自動遞增場,因爲那將是不斷遞增的所有subsidaries相同的計數。

我不想做單獨的表制定的附屬公司會有新的subsidaries添加需要的話......

我目前使用的「選擇MAX(ID)凡附屬= X」,從我的表格,並根據此添加發票。

我使用NHibernate和發票插入,來了InvoiceItem插入之前,因此,如果發票插入失敗,InvoiceItem將無法進行。但是,我會抓住例外,重新檢索最大(ID),然後再試一次。

這種方法有什麼問題?如果有的話,什麼是替代方案?

的要求振振有辭是因爲我讀了這個問題的答案之一:Nhibernate Criteria: 'select max(id)'

回答

1

正如你所說的,這種方法的問題是多個會話可能會嘗試插入相同的發票編號。你得到一個唯一的約束違規,必須再試一次,那可能會失敗,等等。

我解決了創造新的發票時鎖定subsiduary這樣的問題。但是,不要鎖定表,(a)如果使用的是InnoDB,則存在lock table命令默認爲commit the transaction的問題。 (b)沒有理由不同時爲兩個不同的補貼發票開具發票,因爲它們有不同的獨立發票號碼。

我會做你的情況是:

  • 開放的交易,並確保您的表是InnoDB的。
  • 使用SELECT .. FOR UPDATE命令鎖定該補貼。這可以在NHibernate中使用LockMode.UPGRADE完成。
  • 查找使用MAX(..)函數最大的id和做插入
  • 提交事務

這串行化所有發票插入一個subsiduary(即只有一個會話能做到這樣一個刀片上的一次,任何第二次嘗試都會等到第一次完成或已經回滾)但這就是你想要的。您不希望在發票編號中出現漏洞(例如,如果您插入發票編號3485然後失敗,則會有發票3484和3486但沒有3485)。

+0

這看起來像我正在尋找的東西,你能否清理鎖定過程多一點。我需要從發票中選擇哪個子公司= x進行更新,然後在同一個會話上進行另一個查詢以獲取最大ID? – 2010-11-05 11:37:32

+1

你需要一張桌子'補貼',你每個補貼有一行。這將是你鎖定的那一行,以表明專職操作正在進行。 'START TRANSACTION',然後'SELECT id FROM subsuary where''' FOR UPDATE',然後'SELECT MAX(id)FROM invoice ...','INSERT ...','COMMIT'。 – 2010-11-05 13:29:56

+0

所以,當你在子公司表鎖排輔助它不會讓任何新的記錄被插入具有此行作爲一個外鍵約束?那是對的嗎? – 2010-11-05 13:48:19

3

這是一個非常糟糕的主意生成主鍵時使用。我的建議如下:

  • 不要給主鍵一個商業含義(合成鍵);

  • 使用用於產生髮票號的二次機制。

這會讓你的生活變得更輕鬆。然後可以例如生成發票號碼的機制請看下面的表格:

  • 子公司;
  • NextInvoiceNumber。

這將獨立於數據庫是如何工作的內部編號。

有了這樣的機制,你就可以再次使用的自動遞增字段,甚至更好,GUID的。

與閱讀材料的一些鏈接:

http://fabiomaulo.blogspot.com/2008/12/identity-never-ending-story.html http://nhforge.org/blogs/nhibernate/archive/2009/02/09/nh2-1-0-new-generators.aspx

+0

我仍然有鎖定問題,在該表上。儘管我將來可能會實施PreLoadEvent安全檢查。 – 2010-11-05 12:07:48

+0

您的鎖定問題將被嚴重緩解,因爲您只鎖定了一條記錄。爲了有一個一致的MAX(ID),則理論上必須鎖定整個表,因爲要鎖定什麼是你想要的'SELECT MAX(ID)凡附屬=?'變化。有一個重大的區別。另外,通過鎖定輔助表,您正在做一些「奇怪的事情」,因爲輔助表與發票表無關。這只是一種鎖定策略,但您對數據沒有做任何事情。 – 2010-11-05 12:49:38

+0

+1很好的解決方案:) – 2010-11-08 15:55:54