SQL исключения при попытке вставить дубликаты записей в базу данных - PullRequest
0 голосов
/ 03 октября 2019

У меня есть метод ниже, который проверяет, существует ли пользователь, и затем добавляет его в базу данных, если он не существует.

@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.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)

У меня сложилось впечатление, что обе транзакции будут выполнены 1 за другой?

Следующая ссылка

объяснение изоляции

Сериализуемый уровень изоляции является наиболее ограничивающим из всех уровней изоляции. Транзакции выполняются с блокировкой на всех уровнях (чтение, диапазон и блокировка записи), поэтому они выглядят так, как если бы они выполнялись сериализованным способом. Это приводит к сценарию, в котором не может возникнуть ни одна из упомянутых выше проблем, но другим способом мы не разрешаем параллелизм транзакций и, следовательно, вводим снижение производительности.

Однако оба потока все еще проверяют наличиеэлектронная почта пользователя, хотя 1-я транзакция еще не завершена? Как лучше всего справиться с этой проблемой? В этом случае допустимо использование блокировок Java?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...