У меня есть транзакция 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
, но все, что он делает, это сделать транзакцию, которая совершает последний откат.
EDIT: Прежде чем кто предлагает блокировки решений в Java с помощью synchronized
или что-то подобное, это важно понимать, что этот метод является частью веб-приложения и вызывается, когда конкретная конечная точка удара. Веб-приложение работает на нескольких разных машинах и сбалансировано по нагрузке, и проблема возникает, когда конечная точка вызывается дважды одновременно. Это означает, что код может выполняться параллельно на двух разных машинах, разговаривающих с одной и той же БД на другой машине.
Уровни изоляции не поможет, так как вы вставляя новые строки. если вы не столкнетесь с уникальными ограничениями. –