1

我正在使用 JPA/Hibernate 和 MySQL 使用 Java 应用程序,并试图理解正确的方法来做一些看起来应该很简单的事情。

目标

目标是:允许并行运行的两个线程(当然,每个线程都有自己的事务)查询,然后如果没有找到,则将唯一值插入到数据库中,并且这样做不会引发任何异常由 MySQL 提供。

所以每个线程首先查询记录是否存在,如果不存在,则插入该记录,然后完成。听起来很简单。

理论

我认为可以通过设置查询来消除异常的可能性LockModeType.PESSIMISTIC_WRITE,因为这将转换为SELECT .. FOR UPDATE,这意味着第一个线程的查询将在记录上设置一个独占写入锁,因此第二个线程的查询将阻塞,直到第一个线程提交,然后第二个线程将唤醒并能够看到新记录。

在直接使用 MySQL 之后,我意识到我缺少一些基本的东西,因为第二个线程实际上在其查询期间没有阻塞。

实践

我创建了一个这样的表:

CREATE TABLE `test`.`A` (
  `id` BIGINT( 20 ) NOT NULL ,
  `name` VARCHAR( 255 ) NOT NULL ,
  PRIMARY KEY ( `id` ) ,
  UNIQUE (`name`)
) ENGINE = InnoDB;

然后在两个单独的窗口XY中执行以下命令,一次执行一个命令,首先在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;

执行此测试时, XY中的每个语句都立即被接受且无错误,语句 #3 在两者中都返回零行,直到在Y中的命令 #4 之后发生错误:

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没有任何帮助。
4

0 回答 0