EntityManagerを使用してDBとのやりとりをしてる時に壁にぶち当たりました。
排他制御をしつつ更新を行わないといけないときロックどうやるん?ってとこです。
そもそもDB接続ですら既存お任せ状態なのでアクセス方法すら知らない。
なんて考えてると意外と簡単にロックかけられました。
EntityManagerで取得レコードにロックをかける
単純にロックをかけるだけの場合は以下のような感じでできます。
EntityManager em; private void test(){ // DBからSELECTする Query query = em.createQuery("SELECT a FROM UserMst a", UserMst.class); UserMst entity = em.getSingleResult(); // SELECTしたレコードをここでロックする em.lock(entity, PESSIMISTIC_WRITE); // PESSIMISTIC_WRITEは共有ロック // PESSIMISTIC_READは排他ロック らしい。 // この辺はDBの知識が入りそうなので調べてね。 }
このようにEntityManager.lockメソッドでロックをかけれます。
第一引数にロックをかけるレコード情報、第二引数にロックタイプを指定します。
EntityManagerのロックにタイムアウト時間を指定
で、今回やりたいのは単純なロックじゃないです。
前記の状態で、User①がレコードロック中にUser②が同じレコードをロック有りで取得しにいくと、User①のレコードロックが解除されるまでずーっと待ちます。今回やりたいのは、User②がレコードロックを取得できない場合、即時エラーにしたいのです。SQLで書くなら FOR UPDATE NO WAITを実現したい。ので、タ イムアウト設定するのがこちら
EntityManager em; private void test(){ // DBからSELECTする Query query = em.createQuery("SELECT a FROM UserMst a", UserMst.class); UserMst entity = em.getSingleResult(); // 設定用のマップオブジェクトを作成 Map<String,String> map = new HashMap<String,String>(); //"javax.presistance.lock.timeout"にタイ ムアウト時間を設定(0の場合はNOWAITの意味) map.put("javax.presistance.lock.timeout","0"); try{ // SELECTしたレコードをここでロックする。 em.lock(entity, PESSIMISTIC_WRITE, map); }catch(LockTimeoutException e){ // タイ ムアウトしたらLockTimeoutExceptionが飛んできます //例外処理はここに書いてね } }
タイムアウトとか細かな設定はMapで渡すことになってるようなので
設定用のMap変数を作成。
"javax.presistance.lock.timeout"というキーにタイムアウト時間を設定するので
タイムアウト時間を値に設定します。
その後、lockメソッドの第三引数に上記のMap変数を渡してあげればOK
ロックタイムアウトの時はLockTimeoutExceptionがスローされてくるので
エラー処理はキャッチして書きましょう。
EntityManagerなんて使わずにネイティブなSQL書かせてくれれば楽なのになぁ。