次のコミットまで、作業中の行に Lock
を設定しようとしています:
entityManager.createQuery("SELECT value from Table where id=:id")
.setParameter("id", "123")
.setLockMode(LockModeType.PESSIMISTIC_WRITE)
.setHint("javax.persistence.lock.timeout", 10000)
.getSingleResult();
2つのスレッドが同時にデータベースに書き込もうとすると、一方のスレッドが他方より先に更新操作に到達し、2番目のスレッドが10秒待ってから PessimisticLockException
。
ただし、代わりに、タイムアウトの設定に関係なく、スレッドは他のスレッドが終了するまでハングします。
この例を見てください:
database.createTransaction(transaction -> {
// Execute the first request to the db, and lock the table
requestAndLock(transaction);
// open another transaction, and execute the second request in
// a different transaction
database.createTransaction(secondTransaction -> {
requestAndLock(secondTransaction);
});
transaction.commit();
});
2番目のリクエストでは、トランザクションはタイムアウトが設定されるまで待機してから PessimisticLockException
をスローすると予想しましたが、代わりに永久にデッドロックします。
Hibernateはこのようにして、データベースへのリクエストを生成します。
SELECT value from Table where id=123 FOR UPDATE
これで answerPostgres
はタイムアウトを0に設定するSELECT FOR UPDATE NO WAIT
のみを許可することがわかりましたが、そのようにタイムアウトを設定することはできません。
Hibernate / JPA
で使用できる他の方法はありますか?たぶん この方法 はどういうわけか推奨されますか?
Hibernateは クエリヒントの束 をサポートします。使用しているものは、悲観的ロックではなく、クエリのタイムアウトを設定します。クエリとロックは互いに独立しているため、以下に示すヒントを使用する必要があります。
ただし、その前に、Hibernateがタイムアウト自体を処理しないことに注意してください。それをデータベースに送信するだけであり、適用するかどうか、また適用する方法はデータベースに依存します。
悲観的ロックのタイムアウトを設定するには、代わりにjavax.persistence.lock.timeout
ヒントを使用する必要があります。次に例を示します。
entityManager.createQuery("SELECT value from Table where id=:id")
.setParameter("id", "123")
.setLockMode(LockModeType.PESSIMISTIC_WRITE)
.setHint("javax.persistence.lock.timeout", 10000)
.getSingleResult();
PostgresQLのロックタイムアウトのヒントはPostgreSQL 9.6では機能しません(.setHint("javax.persistence.lock.timeout", 10000
)
私が見つけた唯一の解決策は、postgresql.confのlock_timeoutプロパティのコメントを外すことです:
lock_timeout = 10000 # in milliseconds, 0 is disabled
私はあなたが試すことができると思います
SET LOCAL lock_timeout = '10s';
SELECT ....;
Hibernateがこの追加設定をサポートしていないと思います。あなたはそれを価値があるかどうかわからない、それを拡張する方法を見つけることを試みることができます。 Postgesデータベース(mvcc)でロックを使用するのは最も賢明なオプションではないためです。
NO WAIT
およびコードから数回遅延再試行します。
まさにあなたが望んでいることをするlock_timeout
パラメータがあります。
postgresql.conf
で、またはALTER ROLE
またはALTER DATABASE
を使用して、ユーザーごとまたはデータベースごとに設定できます。