有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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

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

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

我使用Oracle 11g express edition。该应用程序在2台Tomcat服务器上同时运行

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

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

我考虑将发票号生成逻辑放入存储过程中——应该可以对其进行锁定,并且一次只允许执行一次。但接下来我必须处理一个不同的例外,这会让我达到现在的状态

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

编辑:虽然本质上我并不关心每个生成的数字。我只需要知道每年的最大发票号就可以生成下一个。我可以把结构做成这样:

ID, Year, LastNumber

如果当前年份行还不存在,则执行INSERT,否则执行UPDATE增加LastNumber,这在并发环境中似乎更容易执行


共 (3) 个答案

  1. # 1 楼答案

    如果我正确理解了这个问题,应该是

    这是一个并发问题,两个应用程序可能会试图同时在您的表上写入数据。因此,您可以在应用程序中从0-5.0000.0000开始写入,从10.0000.0000-5.000.0000开始写入。此外,您不必担心它们写的是与00.0000.0001相同的数字,因为它们是从不同的地方写的,这样您就不会因为并发问题而覆盖/丢失主键

  2. # 2 楼答案

    正确的答案是使用序列,并让数据库处理它。易于实现,不易出错(如Anton所述)

  3. # 3 楼答案

    不要使用max(id),创建oracle序列并从序列中获取下一个值-select nextval。这是最可靠、最不容易出错的方法。即使有一个应用程序,当多个线程试图从数据库中获取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