如何在读写操作之间锁定 table
How to lock table between read and write operations
我的情况是每条新记录都应该包含一个唯一且可读的值。
该值对用户具有业务意义,将在我们的数据库中作为自然 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;
}
}
服务层将调用工厂并保存新的发票:
@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 将成功,第二个将由于唯一约束异常而失败。
因此,在读取最新的业务值时,我需要锁定发票 table。我认为在将新的 Invoice 实体保存到 DB 之前,此锁应该处于活动状态。
我应该如何在 Hibernate 中执行此操作?
序列是一组按需按顺序生成的整数 1、2、3 ...。序列在数据库中经常使用,因为许多应用程序要求 table 中的每一行都包含一个唯一值,而序列提供了一种生成它们的简单方法。
一种方法
class BusinessFactory {
public String createUniqueValue() {
String valueNotUsed = (String) getSession().createSQLQuery("select nextval('hibernate_sequence')");
// generate new value
return newValue;
}}
不使用冲突检测并发控制机制,例如依赖唯一约束或optimistic locking,您可以使用悲观锁定。
您需要:
一个 InvoiceSequence
table 有一个映射到它的实体
这个table只有一行,存储最新的发票序列值
您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);
独占锁也会阻止并发读写。
我的情况是每条新记录都应该包含一个唯一且可读的值。 该值对用户具有业务意义,将在我们的数据库中作为自然 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;
}
}
服务层将调用工厂并保存新的发票:
@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 将成功,第二个将由于唯一约束异常而失败。
因此,在读取最新的业务值时,我需要锁定发票 table。我认为在将新的 Invoice 实体保存到 DB 之前,此锁应该处于活动状态。
我应该如何在 Hibernate 中执行此操作?
序列是一组按需按顺序生成的整数 1、2、3 ...。序列在数据库中经常使用,因为许多应用程序要求 table 中的每一行都包含一个唯一值,而序列提供了一种生成它们的简单方法。
一种方法
class BusinessFactory {
public String createUniqueValue() {
String valueNotUsed = (String) getSession().createSQLQuery("select nextval('hibernate_sequence')");
// generate new value
return newValue;
}}
不使用冲突检测并发控制机制,例如依赖唯一约束或optimistic locking,您可以使用悲观锁定。
您需要:
一个
InvoiceSequence
table 有一个映射到它的实体这个table只有一行,存储最新的发票序列值
您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);
独占锁也会阻止并发读写。