Java 如何使两个相同的读写并发事务互相等待?

Java 如何使两个相同的读写并发事务互相等待?,java,database,transactions,spring-transactions,isolation-level,Java,Database,Transactions,Spring Transactions,Isolation Level,我有一个Spring事务,它从数据库中读取数据,如果查找的内容不存在,它就会创建它。例如: @交易的 public int GetUserIDCreateIfNoteExistentString电子邮件引发异常{ 试一试{ 从email=?,Integer.class,email的用户返回jdbcTemplate.queryForObjectSELECT id; }捕获不正确的结果大小数据访问异常{ 线程10000; 将jdbcTemplate.queryForObjectINSERT返回到用户

我有一个Spring事务,它从数据库中读取数据,如果查找的内容不存在,它就会创建它。例如:

@交易的 public int GetUserIDCreateIfNoteExistentString电子邮件引发异常{ 试一试{ 从email=?,Integer.class,email的用户返回jdbcTemplate.queryForObjectSELECT id; }捕获不正确的结果大小数据访问异常{ 线程10000; 将jdbcTemplate.queryForObjectINSERT返回到用户电子邮件值中,返回id、Integer.class、email; } } 当同时调用此方法两次时,将导致两个不同的用户在数据库中使用相同的电子邮件。对于唯一的电子邮件没有限制,这只是为了说明

我希望第二个事务等待第一个事务,以便只创建一个用户。我已尝试将事务隔离级别更改为isolation.SERIALIZABLE,但它所做的只是使提交最后一次的事务回滚

编辑:
在任何人建议使用synchronized或类似的东西在java中锁定解决方案之前,重要的是要了解此方法是web应用程序的一部分,并且在特定端点被命中时调用。web应用程序在多台不同的机器上运行,并且是负载平衡的,当端点同时被调用两次时,问题就会发生。这意味着代码可能会在两台不同的机器上并行执行,并与另一台机器上的同一个DB通信。

您可以使用锁表。它将包含您需要锁定的所有内容,即表名(如用户)和行键(如电子邮件)


如果您需要更快的速度,那么可以使用memcached跨服务器进行复制。如果插入不多,则db解决方案可能很好。

使用建议锁解决:

@Transactional
public int getUserIdCreateIfNotExistent(String email) throws Exception {
  try {
    jdbcTemplate.queryForRowSet("SELECT pg_advisory_xact_lock(hashtext('users'), hashtext(?))", email);
    return jdbcTemplate.queryForObject("SELECT id FROM users WHERE email = ?", Integer.class, email);
  } catch (IncorrectResultSizeDataAccessException e) {
    Thread.sleep(10000);
    return jdbcTemplate.queryForObject("INSERT INTO users (email) values(?) RETURNING id", Integer.class, email);
  }
}

隔离级别没有帮助,因为您正在插入新行。除非遇到唯一的约束。