如何在读写操作之间锁定表

use*_*927 3 java hibernate jpa locking transactions

我遇到的情况是,每条新记录都应包含唯一且可读的值。该值对用户具有业务意义,并将在我们的数据库中作为自然ID(主键旁边)进行处理。

让您大致了解价值的结构:

 - record 1 has business value 'INVOICE_AAA_001'
 - record 2 has business value 'INVOICE_AAA_002'
   ...
 - record 999  has business value 'INVOICE_AAA_999'
 - record 1000 has business value 'INVOICE_BAA_001'
 - record 1001 has business value 'INVOICE_BAA_002'
   ...
Run Code Online (Sandbox Code Playgroud)

此业务价值是由工厂创造的:

class BusinessFactory {

    ...    

    public String createUniqueValue() {
        String lastValue = (String) getSession().createQuery("select businessValue " + 
                                                             "from Invoice as invoice " + 
                                                             "order by businessValue")
                                                .setMaxResults(1)
                                                .setReadOnly(true)
                                                .uniqueResult();    

       // generate new value 

       return newValue;
    }

}
Run Code Online (Sandbox Code Playgroud)

服务层将致电工厂并保存新的发票:

  @Transactional
  public void saveNewInvoice() {
      final String newValue = businessFactory.createUniqueValue();        
      Invoice invoice = new Invoice(newValue);
      invoiceRepository.save(invoice);
  }
Run Code Online (Sandbox Code Playgroud)

这里的问题是trx1和trx2读取业务值'INVOICE_BAA_002'的情况可能存在。接下来发生的是2个trx使用相同的值。第一次提交的trx将成功,第二次提交将由于唯一的约束异常而失败。

因此,在读取最新的业务价值时,我需要在发票表上加一个锁。我认为在新的发票实体保存到数据库之前,该锁定应该处于活动状态。

我应该如何在Hibernate中做到这一点?

Vla*_*cea 6

不必使用冲突检测并发控制机制(例如依赖于唯一约束或乐观锁定),而可以使用悲观锁定。

您需要具备:

  1. InvoiceSequence映射了实体的表
  2. 该表只有一行,存储最新的发票序列值
  3. 获得记录的排他锁

    InvoiceSequence invoiceSequence = em.find(InvoiceSequence.class, 1L, LockModeType.PESSIMISTIC_WRITE)
    
    Run Code Online (Sandbox Code Playgroud)
  4. 您可以使用业务逻辑来增加序列,并修改实体以存储最新值:

    String currentSequence = invoiceSequence.getValue();
    String nextSequence = sequenceGenerator.nextValue(currentSequence);
    invoiceSequence.setValue(nextSequence);
    
    Run Code Online (Sandbox Code Playgroud)

排他锁也将阻止同时进行的读写操作。我的《高性能Java持久性》一书非常详尽地介绍了“事务”一章,您可能会发现它也很有用。