【问题标题】:How to lock table between read and write operations如何在读写操作之间锁定表
【发布时间】:2016-02-15 11:39:57
【问题描述】:

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

这个商业价值是由工厂创造的:

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 表。我认为在将新的 Invoice 实体保存到 DB 之前,此锁应该处于活动状态。

我应该如何在 Hibernate 中执行此操作?

【问题讨论】:

  • 一个序列可以帮助你。你可以创建一个吗?
  • 我不知道如何创建序列。你能给我更多关于这方面的信息吗?序列是否会阻止 2 个并发事务使用相同的值?

标签: java hibernate jpa transactions locking


【解决方案1】:

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

你需要:

  1. 一个 InvoiceSequence 表,其中映射了一个实体

  2. 此表只有一行,存储最新的发票序列值

  3. acquire an exclusive lock记录在案:

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

    String currentSequence = invoiceSequence.getValue();
    String nextSequence = sequenceGenerator.nextValue(currentSequence);
    invoiceSequence.setValue(nextSequence);
    

排他锁也会阻止并发读写。

【讨论】:

  • 感谢您的回答!我想我必须定义某种锁定超时以防止另一个 trx 无限期等待并确保锁定将被释放?也许我还应该添加一个方面,由于锁定超时而自动重试失败的 trx?
【解决方案2】:

序列是一组整数 1, 2, 3, ...,它们按需要按顺序生成。序列在数据库中经常使用,因为许多应用程序要求表中的每一行都包含一个唯一值,而序列提供了一种简单的方法来生成它们。

一种方法

class BusinessFactory {
public String createUniqueValue() {
    String valueNotUsed = (String) getSession().createSQLQuery("select nextval('hibernate_sequence')");    

   // generate new value 

   return newValue;
}}

【讨论】:

  • lastUniqueValue 的名称在这里具有误导性,因为您获得的值已经是未使用的值。 nextval 返回下一个 unused 值,因此您可以直接使用它。
  • 感谢您的回复。 nextval('hibernate_sequence') 是特定于 Hibernate 或数据库的吗?这个功能在所有关于防止方言的数据库上都可用吗?
  • 再次感谢您的回复。但我怀疑“nextval”是否对这种情况有用,因为我还必须管理字母数字位置(AAA、BAA、BBA ......)所以我认为我真的需要阅读最新插入记录的商业价值并应用锁。
  • 可以捕捉最后插入的字母来生成。并获取序列的新值并与字母合并
猜你喜欢
  • 2016-02-09
  • 2013-11-24
  • 2012-07-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-23
  • 2017-07-01
相关资源
最近更新 更多