java - 如何让两个相同的读+写并发事务相互等待?

标签 java database transactions spring-transactions isolation-level

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

@Transactional
public int getUserIdCreateIfNotExistent(String email) throws Exception {
  try {
    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);
  }
}

当同时调用此方法两次时,将导致数据库中有两个不同的用户使用相同的电子邮件地址(对唯一电子邮件地址没有限制,这仅用于说明目的)。

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

编辑: 在有人建议使用 synchronized 或类似方法在 java 中锁定解决方案之前,了解此方法是 Web 应用程序的一部分并在命中特定端点时调用是很重要的。 Web 应用程序在几台不同的机器上运行并且是负载平衡的,当同时调用端点两次时会出现问题。这意味着代码可能会在两台不同的机器上并行执行,并与另一台机器上的同一个数据库对话

最佳答案

通过咨询锁解决:

@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);
  }
}

关于java - 如何让两个相同的读+写并发事务相互等待?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38905122/

相关文章:

java - 添加 admob jar 文件和类未找到错误

php - 我可以使用 Zend Framework 将对象添加到数据库的当前方式是什么?

mysql - 在mysql中的3个表的内部连接中使用where

java - 读取paradox DBF文件并导入H2数据库

php - 多个服务层和数据库事务

email - 如何修复 mandrill/mailchimp 事务性电子邮件问题收件人域不匹配

java - 如何在字符串列上使用 HIbernate 标准进行比较,将其视为长列

java - 在 Java 中使用子字符串从字符串中获取整数和字符?

Java 计算器 - 在字符串输入中查找整数

Django - 在引发错误后在 transaction.atomic block 内对数据库进行操作