新しい各レコードに一意で読み取り可能な値が含まれている必要がある状況があります。この値はユーザーにとってビジネス上の意味があり、データベースでは(主キーの横にある)自然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は成功し、2番目は一意の制約例外のために失敗します。
したがって、最新のビジネス値を読み取るときに、請求書テーブルをロックする必要があります。このロックは、新しい請求書エンティティがDBに保存されるまでアクティブである必要があると思います。
Hibernateでこれをどのように行う必要がありますか?
一意の制約や楽観的ロックに依存するなど、競合検出の同時実行制御メカニズムを使用する代わりに、悲観的ロックを使用できます。
あなたが持っている必要があります:
InvoiceSequence
エンティティがマップされたテーブルInvoiceSequence invoiceSequence = em.find(InvoiceSequence.class, 1L, LockModeType.PESSIMISTIC_WRITE)
ビジネスロジックを使用してシーケンスをインクリメントし、エンティティを変更して最新の値を格納します。
String currentSequence = invoiceSequence.getValue();
String nextSequence = sequenceGenerator.nextValue(currentSequence);
invoiceSequence.setValue(nextSequence);
排他ロックは、同時読み取りと書き込みの両方を防ぎます。私のHigh-PerformanceJava Persistenceの本には、トランザクションに関する非常に徹底的な章があり、それも役立つかもしれません。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加