我有一种情况,每个新记录都应该包含一个唯一且可读的值。
该值对用户具有业务意义,并将在我们的数据库中作为自然 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'
   ...

这个商业值(value)是由工厂创造的:
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;
    }

}

Service层会调用工厂并保存一个新的Invoice:
  @Transactional
  public void saveNewInvoice() {
      final String newValue = businessFactory.createUniqueValue();
      Invoice invoice = new Invoice(newValue);
      invoiceRepository.save(invoice);
  }

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

因此,在读取最新的业务值时,我需要锁定 Invoice 表。我认为这个锁应该是 Activity 的,直到新的 Invoice 实体被保存到 DB。

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

最佳答案

您可以使用悲观锁定,而不是使用冲突检测并发控制机制,例如依赖唯一约束或 optimistic locking
你需要有:

  • 映射到实体的 InvoiceSequence
  • 这个表只有一行,存放最新的发票序列值
  • acquire an exclusive lock 上的记录:
    InvoiceSequence invoiceSequence = em.find(InvoiceSequence.class, 1L, LockModeType.PESSIMISTIC_WRITE)
    
  • 您使用业务逻辑增加序列并修改实体以存储最新值:
    String currentSequence = invoiceSequence.getValue();
    String nextSequence = sequenceGenerator.nextValue(currentSequence);
    invoiceSequence.setValue(nextSequence);
    

  • 排他锁也将阻止并发读取和写入。

    关于java - 如何在读写操作之间锁定表,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/33700390/

    10-11 21:32