如何避免使用MySQL和JPA重复键

时间:2012-09-11 15:21:12

标签: java mysql jpa

我正在使用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;

然后在两个单独的窗口 X Y 中执行以下命令,每次执行一个命令,首先在 X 然后在的ý

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 中的每个语句都会立即被接受并且没有错误,两者中的语句#3都返回零行,直到在 Y

中的命令#4之后发生错误
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

实际上这并不意外......对我来说意外的是 Y 在执行命令#3 时不会阻塞。一旦两个线程都查询了'Jones'并且没有看到任何内容,我的想法显然是注定的,因为它们不可能都是正确的。

我认为SELECT ... FOR UPDATE的重点是它在记录上设置了一个独占锁定,这会导致 Y 阻止声明#3。

问题

我错过了什么?甚至可以在JPA甚至MySQL中做我想做的事情吗?

注意:

  • MySQL版本为5.5.23-log MySQL Community Server (GPL)
  • 更改为SERIALIZABLE没有任何帮助。

0 个答案:

没有答案