我正在建立一个发票系统,支持多个补贴,每个补贴都有自己的发票号码,因此我有一个主键为(子公司,InvoiceNo)的表
我不能使用MySQL自动增量字段,因为它会不断增加所有补贴的相同数量。
我不想为每个子公司制作单独的表格,因为根据需要会增加新的补贴......
我目前正在使用“从我的表中选择Max(ID)Where Subsidiary = X”,并根据此添加发票。
我正在使用nHibernate,并且Invoice插入位于InvoiceItem插入之前,因此如果Invoice插入失败,则不会执行InvoiceItem。但相反,我将捕获异常,重新检索Max(ID)并再试一次。
这种方法有什么问题?如果有的话,还有什么选择呢?
要求的共鸣是因为我阅读了这个问题的一个答案:Nhibernate Criteria: 'select max(id)'
答案 0 :(得分:3)
在生成主键时使用这是一个非常糟糕的主意。我的建议如下:
不要给主键带来商业意义(合成键);
使用辅助机制生成发票编号。
这将使您的生活更轻松。然后,例如,生成发票号码的机制可以是是一个看起来像这样的表:
这会将内部编号与数据库的工作方式分开。
使用这种机制,您将能够再次使用自动增量字段,甚至更好地使用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)没有理由不同时为两个不同的补贴发票添加不同的独立发票编号。
在你的情况下我会做的是:
SELECT .. FOR UPDATE
命令锁定补贴。这可以使用NHibernate中的LockMode.UPGRADE
来完成。这序列化了一个补贴的所有发票插入(即,只有一个会话可以立即执行此类插入,任何第二次尝试将等到第一次完成或已经回滚)但这就是您想要的。您不希望发票编号中有漏洞(例如,如果您插入发票ID 3485然后失败,则会有发票3484和3486但没有3485)。