java - 尝试将重复记录插入数据库时出现 sql 异常
问题描述
我有以下方法检查用户是否存在,如果不存在则将其添加到数据库中。
@Transactional(isolation = Isolation.READ_COMMITTED)
public Optional<User> create(User user) {
Optional<User> userOptional = userRepository.findByEmail(user.getEmail());
if (userOptional.isPresent()) {
return Optional.empty();
}
User savedUser = userRepository.save(user);
return Optional.of(savedUser);
}
两个线程 t1 和 t2 正在尝试使用相同的电子邮件(example@example.com)保存相同的用户。发生以下情况:
t1 找不到用户
t2 也找不到用户
t1 将用户添加到数据库
t2 尝试将用户添加到数据库,但由于记录已存在而失败并引发以下错误:
2019-10-02 18:28:43 WARN UKPC000029 --- [nio-8090-exec-2]
o.h.e.j.s.SqlExceptionHelper : SQL Error: 1062, SQLState: 23000
2019-10-02 18:28:43 ERROR UKPC000029 --- [nio-8090-exec-2]
o.h.e.j.s.SqlExceptionHelper : Duplicate entry 'example@example.com' for key 'UK_tcks72p02h4dp13cbhxne17ad'
2019-10-02 18:28:43 ERROR UKPC000029 --- [nio-8090-exec-2] o.h.i.ExceptionMapperStandardImpl : HHH000346: Error during managed flush [org.hibernate.exception.ConstraintViolationException: could not execute statement]
2019-10-02 18:28:43 DEBUG UKPC000029 --- [nio-8090-exec-2] o.s.o.j.JpaTransactionManager : Initiating transaction rollback after commit exception
org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [UK_tcks72p02h4dp13cbhxne17ad]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:296)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:253)
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:536)
为了解决这个错误,我尝试使用可序列化的隔离级别 @Transactional(isolation = Isolation.SERIALIZABLE)
但是在相同的情况下,我收到以下错误:
2019-10-02 18:52:56 WARN UKPC000029 --- [nio-8090-exec-2] o.h.e.j.s.SqlExceptionHelper : SQL Error: 1213, SQLState: 40001
2019-10-02 18:52:56 ERROR UKPC000029 --- [nio-8090-exec-2] o.h.e.j.s.SqlExceptionHelper : Deadlock found when trying to get lock; try restarting transaction
2019-10-02 18:52:56 ERROR UKPC000029 --- [nio-8090-exec-2] o.h.i.ExceptionMapperStandardImpl : HHH000346: Error during managed flush [org.hibernate.exception.LockAcquisitionException: could not execute statement]
2019-10-02 18:52:56 DEBUG UKPC000029 --- [nio-8090-exec-1] o.s.o.j.JpaTransactionManager : Not closing pre-bound JPA EntityManager after transaction
2019-10-02 18:52:56 DEBUG UKPC000029 --- [nio-8090-exec-2] o.s.o.j.JpaTransactionManager : Initiating transaction rollback after commit exception
org.springframework.dao.CannotAcquireLockException: could not execute statement; SQL [n/a]; nested exception is org.hibernate.exception.LockAcquisitionException: could not execute statement
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:287)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:253)
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:536)
我的印象是两笔交易都会一个接一个地完成?
以下链接
SERIALIZABLE 隔离级别是所有隔离级别中限制性最强的。事务在所有级别(读取、范围和写入锁定)上都使用锁定执行,因此它们看起来好像是以序列化方式执行的。这会导致可能不会出现上述任何问题的情况,但另一方面,我们不允许事务并发并因此引入性能损失。
然而,即使第一笔交易尚未完成,两个线程仍在检查用户电子邮件?处理这个问题的最佳方法是什么?在这种情况下可以接受使用 java 锁吗?
解决方案
推荐阅读
- string - 如何在飞镖颤动的文本中将我的字符串的第一个字母大写?
- ios - 有没有办法在 Cocoa Pods 的单个目标中使用静态和动态框架
- css - 如何将 innerClass 样式应用于 appBase 中的 searchBox?
- visual-studio-code - 每次单元运行都会重复 VS Code Jupyter 日志记录输出
- mysql - 错误 AS-->您的 SQL 语法有错误;检查与您的 MySQL 服务器版本相对应的手册,以获取在 ', 附近使用的正确语法
- python - 如何使用 uptplot python 在 pandas 数据框中查找交集
- postgresql - PostgreSQL plpgsql。如何扩展二维数组的第二维?
- osgearth - 如何使用OpenSenceGraph生成正射影像、
- sql - 在编写 Vertica 查询时需要帮助
- reactjs - 如何升级贝宝订阅?(获取状态:422 响应)