jfluteです
こんにちは、フィードバックありがとうございます。
> ユーザー環境のネットが切れていたのか不明ですが、短時間(100ミリ秒満たない)にアクセスが来た際に
> 二つ目が一つ目のinsertの処理前に処理され重複して登録される事が発生しております。
状況を整理させてください。
A と B がほぼ同時に同じデータをリクエストして、
1. A が select してそのデータがDBにないことを確認
2. B が select してそのデータがDBにないことを確認
3. A が そのデータを insert してDBに登録
4. B が そのデータを insert してDBに登録
本来は、事前にselectしてそのデータがないかどうかを確認しているので、
B は 先に A が登録データを検知して insert を取りやめるはずだけど、
AとBの事前selectがほぼ同時なのでAもBもinsertされてしまう、
というようなことでしょうか?
もし、そうであれば、
通常は、PK制約違反、もしくはユニーク制約違反で落ちるかと思います。
例えば、事前selectのwhere句に列挙している条件にユニーク制約が貼られていれば、
そういう状況でも EntityAlreadyExistsException が発生して重複登録にはなりません。
もし、DB設計的に「ユニーク制約を貼れる状況なのに貼られてない」
というのであれば、いまからでもユニーク制約を貼ることをおススメします。
ぼくは、ユニーク制約貼れるところには必ず貼りますね。
ちなみに、その場合は事前selectも不要ですね。
insertしてその例外が発生したのであれば、すでに同じデータが存在していると言えるので。
もし、どうしてもユニーク制約を貼れない、もしくは、もっと複雑な業務条件での
重複チェックなのであれば、insert()を二回実行してしまうのをなんとかして防がないといけません。
トランザクション管理というキーワードが出ていましたが、
トランザクションは発行されていますでしょうか?
(selectとinsertは同じトランザクションで実行していますでしょうか?)
まず、selectとinsertを同じトランザクションにしないと防ぎようがないかなと。
ただもちろん、同じトランザクションにしただけではダメで、
トランザクション分離レベルが普通のReadCommittedだと、
select同士は待ちにならないので(通常はなったら困るし)、
select for update で更新ロックを取るなどしないといけないかなと。
ただ、存在しないレコードに対して select for update で待つかどうか、
ちょっとパッとどうだったかな!? って感じなので、
マルチスレッドのテストを書いて試してみるといいかと思います。
その辺、DBMSによって挙動が変わるかと思います。
こういうとき、DBFluteのバージョンや利用しているDBMSの種類とバージョン、
トランザクションの設定などの情報を一緒に載せて頂けると、
ML閲覧者の方もコメントしやすいかなと思います。
> --
> このメールは Google グループのグループ「DBFluteユーザの集い」に登録しているユーザーに送られています。
> このグループから退会し、グループからのメールの配信を停止するには
dbflute+u...@googlegroups.com
> にメールを送信してください。
> このグループに投稿するには
dbf...@googlegroups.com にメールを送信してください。
>
http://groups.google.com/group/dbflute からこのグループにアクセスしてください。
> その他のオプションについては
https://groups.google.com/d/optout にアクセスしてください。