替代复杂主键中的Max(ID)

时间:2010-11-05 10:44:22

标签: c# database nhibernate primary-key

我正在建立一个发票系统,支持多个补贴,每个补贴都有自己的发票号码,因此我有一个主键为(子公司,InvoiceNo)的表

我不能使用MySQL自动增量字段,因为它会不断增加所有补贴的相同数量。

我不想为每个子公司制作单独的表格,因为根据需要会增加新的补贴......

我目前正在使用“从我的表中选择Max(ID)Where Subsidiary = X”,并根据此添加发票。

我正在使用nHibernate,并且Invoice插入位于InvoiceItem插入之前,因此如果Invoice插入失败,则不会执行InvoiceItem。但相反,我将捕获异常,重新检索Max(ID)并再试一次。

这种方法有什么问题?如果有的话,还有什么选择呢?

要求的共鸣是因为我阅读了这个问题的一个答案:Nhibernate Criteria: 'select max(id)'

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:1)

正如您所说,此方法的问题是多个会话可能会尝试插入相同的发票ID。您会遇到唯一的约束违规,必须重试,也可能会失败,等等。

我通过在创建新发票期间锁定补贴来解决这些问题。但是,不要锁定表,(a)如果您使用的是InnoDB,则默认情况下lock table命令会commit the transaction。 (b)没有理由不同时为两个不同的补贴发票添加不同的独立发票编号。

在你的情况下我会做的是:

  • 打开一个事务并确保您的表是InnoDB。
  • 使用SELECT .. FOR UPDATE命令锁定补贴。这可以使用NHibernate中的LockMode.UPGRADE来完成。
  • 使用max(..)函数查找最大ID并执行插入
  • 提交交易

这序列化了一个补贴的所有发票插入(即,只有一个会话可以立即执行此类插入,任何第二次尝试将等到第一次完成或已经回滚)但这就是您想要的。您不希望发票编号中有漏洞(例如,如果您插入发票ID 3485然后失败,则会有发票3484和3486但没有3485)。