java插入新行,最大值通过简单计算递增(并发问题)
在我的java/spring/hibernate应用程序中,我需要以yyxxxxxxxx
格式生成发票编号(yy-当前年份,xxxxxxxx-每年从1开始的编号顺序,总共10位数字)
例如,明年的第一个数字将是1800000001
我使用Oracle 11g express edition。该应用程序在2台Tomcat服务器上同时运行
我不确定如何处理可能的重复,或者更确切地说是唯一的约束冲突,因为InvoiceNumber
列上有唯一的约束。(在GeneratedInvoiceNumbers
表中有3列-ID
(PK,自动递增),InvoiceNumber
,CreatedOn
。)
到目前为止,应用程序检索带有max(ID)
的行,生成一个新发票号并插入一个新行。由于多个线程/服务器可能会选择相同的max(ID)
,从而生成/插入相同的发票号,因此应用程序会捕获抛出的Spring DataIntegrityViolationException
,增加发票号,并一直尝试插入它,直到成功(设置了一些max_attempts_limit
)。它有效,但似乎不是一个干净的解决方案
我考虑将发票号生成逻辑放入存储过程中——应该可以对其进行锁定,并且一次只允许执行一次。但接下来我必须处理一个不同的例外,这会让我达到现在的状态
有没有更好的方法来解决这个问题
编辑:虽然本质上我并不关心每个生成的数字。我只需要知道每年的最大发票号就可以生成下一个。我可以把结构做成这样:
ID, Year, LastNumber
如果当前年份行还不存在,则执行INSERT
,否则执行UPDATE
增加LastNumber,这在并发环境中似乎更容易执行
# 1 楼答案
如果我正确理解了这个问题,应该是
这是一个并发问题,两个应用程序可能会试图同时在您的表上写入数据。因此,您可以在应用程序中从
0-5.0000.0000
开始写入,从10.0000.0000-5.000.0000
开始写入。此外,您不必担心它们写的是与00.0000.0001
相同的数字,因为它们是从不同的地方写的,这样您就不会因为并发问题而覆盖/丢失主键# 2 楼答案
正确的答案是使用序列,并让数据库处理它。易于实现,不易出错(如Anton所述)
# 3 楼答案
不要使用max(id),创建oracle序列并从序列中获取下一个值-
select nextval
。这是最可靠、最不容易出错的方法。即使有一个应用程序,当多个线程试图从数据库中获取max(id)时,也会有一场竞争或者,对于1个节点,您可以使用如下内容:
对于第二个节点:
一个应用程序将插入奇数ID,第二个应用程序将插入偶数ID