java - 插入新行,并通过简单计算增加最大值(并发问题)

标签 java spring oracle hibernate

在我的 java/spring/hibernate 应用程序中,我需要生成格式为 yyxxxxxxxx 的发票号码(yy - 当前年份,xxxxxxxx - 每年从 1 开始的编号序列,总共 10 位数字。)

例如,明年的第一个数字将为 1800000001。

我使用Oracle 11g Express版。该应用程序同时在 2 个 Tomcat 服务器上运行。

我不确定如何处理可能的重复项 - 或者更确切地说,违反唯一约束,因为 InvoiceNumber 列存在唯一约束。 (GenerateInvoiceNumbers 表中有 3 列 - ID(PK,自动递增)、InvoiceNumberCreatedOn。)

到目前为止,应用程序检索具有 max(ID) 的行,生成新的发票编号并插入新行。由于多个线程/服务器可能选择相同的 max(ID) 并因此生成/插入相同的发票编号,因此应用程序捕获 Spring 抛出的 DataIntegrityViolationException ,增加发票编号并保持尝试插入它直到成功(设置了一些max_attempts_limit)。它有效,但似乎不是一个干净的解决方案。

我考虑将发票编号生成逻辑放入存储过程中 - 应该可以对其加锁并一次只允许执行一次。但随后我将不得不处理一个不同的异常,这将使我达到现在的水平。

有没有更好的方法来解决这个问题?

编辑:虽然本质上我并不真正关心每个生成的数字。我只需要知道每年的最大发票数量即可生成下一个。我可以制作这样的结构:

ID, Year, LastNumber

如果当前年份行尚不存在,则执行INSERT,否则使用递增的LastNumberUPDATE - 这在并发环境中似乎更容易执行。

最佳答案

不要使用 max(id),创建 oracle 序列并从序列中获取下一个值 - select nextval。这是最可靠且不易出错的方法。即使您有 1 个应用程序,当多个线程尝试从数据库获取 max(id) 时,您也会发生竞争

或者,您可以对 1 个节点使用如下所示的内容:

synchonized (lock) {
   long id = selectMaxFromDatabase(id);
   id ++;
   if (id % 2 != 0) {
      id ++;
   }  
}

对于第二个节点:

synchonized (lock) {
   long id = selectMaxFromDatabase(id);
   id ++;
   if (id % 2 == 0) {
      id ++;
   }  
}

一个应用程序将插入奇数 ID,第二个应用程序将插入偶数 ID。

关于java - 插入新行,并通过简单计算增加最大值(并发问题),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41483993/

相关文章:

java - 如何通过扩展cmis创建新类型:folder type in alfresco

java - 如何获取访问 apache tomcat 服务器上托管的 java web 应用程序的计算机和用户信息?

java - 调用gdb调试java程序

spring - 415 Spring中不支持的媒体类型JAXB

sql - 在分层数据中计算有约束的行数

sql - PLSQL CASE WHEN 条件

java - influxdb失败导致spring应用程序运行失败

spring - Oauth2 Spring 中的更改响应

java - 将属性文件中的字符串值注入(inject)到集合中

oracle - 事务上下文中 Oracle 存储过程/函数的语义