私は、JPA/Hibernate と MySQL を使用して Java アプリケーションで作業しており、簡単なように思われることを行う正しい方法を理解しようとしています。
目標
目標は、並行して実行されている 2 つのスレッド (もちろん、それぞれ独自のトランザクションを使用) がクエリを実行し、見つからない場合はデータベースに一意の値を挿入できるようにすることです。例外がスローされる可能性はありません。 MySQLによって。
したがって、各スレッドは最初にクエリを実行してレコードが存在するかどうかを確認し、存在しない場合はそのレコードを挿入して終了します。シンプルに聞こえます。
法則
LockModeType.PESSIMISTIC_WRITE
これは、最初のスレッドのクエリがレコードに排他的な書き込みロックを設定することを意味するため、クエリを設定することで例外の可能性を排除できると考えましたSELECT .. FOR UPDATE
。したがって、最初のスレッドがコミットされるまで、2 番目のスレッドのクエリはブロックされます。これにより、2 番目のスレッドが起動し、新しいレコードを確認できるようになります。
MySQL で直接遊んだ後、2 番目のスレッドが実際にはクエリ中にブロックされていないため、欠けている基本的なものがあることに気付きました。
練習
次のようなテーブルを作成しました。
CREATE TABLE `test`.`A` (
`id` BIGINT( 20 ) NOT NULL ,
`name` VARCHAR( 255 ) NOT NULL ,
PRIMARY KEY ( `id` ) ,
UNIQUE (`name`)
) ENGINE = InnoDB;
次に、2 つの別々のウィンドウXとYで次のコマンドを実行し、各コマンドを一度に 1 つずつ実行します。最初はXで、次にYで実行します。
1 SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
2 BEGIN;
3 SELECT * FROM A WHERE `name`='Jones' FOR UPDATE;
4 INSERT INTO A (`name`) VALUES ('Jones');
5 COMMIT;
このテストを実行すると、各ステートメントはすぐに受け入れられ、XとYの両方でエラーが発生しなくなります。Yのコマンド #4 の後にエラーが発生するまで、両方のステートメント #3 によってゼロ行が返されます。
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
実際、これは予期しないことではありません... (私にとって)予期しないことは、コマンド #3を実行してもYがブロックされないことです。両方のスレッドがクエリを実行しても何も表示されない場合、両方が正しいとは限らないため、私の考えは明らかに失敗に終わります。'Jones'
要点はSELECT ... FOR UPDATE
、レコードに排他ロックを設定することであり、これによりYがステートメント #3 でブロックされるようになるはずです。
質問
私は何が欠けていますか?JPAでもMySQLでも、私がやりたいことをすることさえ可能ですか?
ノート:
- MySQLのバージョンは
5.5.23-log MySQL Community Server (GPL)
- に変更しても何の役に
SERIALIZABLE
も立ちません。