首页 > 解决方案 > 尝试将重复记录插入数据库时​​出现 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 锁吗?

标签: javamysqldatabasespring-boottransactions

解决方案


推荐阅读