Какой тип блокировки используется в native query с select for update в Java?
Что проверяют
Понимаете ли вы, что блокировку ставит база, а не Java или Hibernate, и сколько она живёт. Следом почти всегда спрашивают: что будет без @Transactional, чем это отличается от @Version и что делать с дедлоками.
Как отвечать
SELECT ... FOR UPDATE берёт эксклюзивную блокировку на уровне строк, которые вернул запрос, и держит её до конца транзакции, то есть до COMMIT или ROLLBACK. В терминах JPA это пессимистичная блокировка на запись, LockModeType.PESSIMISTIC_WRITE. В нативном запросе режим блокировки через JPA не задаётся: всё определяет текст SQL, Hibernate о блокировке не знает.
Что блокируется, а что нет:
- ждут: другие
UPDATE,DELETE,SELECT ... FOR UPDATEиFOR SHAREпо тем же строкам; - не ждут: обычные
SELECTв PostgreSQL, MySQL InnoDB и Oracle. Они читают версию строки через MVCC и видят данные на момент своего снимка.
Варианты, которые стоит назвать:
NOWAIT: не ждать, а сразу получить ошибку, если строка занята;SKIP LOCKED: пропустить занятые строки. Основа очереди задач, когда несколько воркеров разбирают одну таблицу;FOR NO KEY UPDATE: более слабый режим PostgreSQL, не мешает вставкам в дочерние таблицы с внешним ключом на эту строку.
В Spring метод с таким запросом обязан работать внутри транзакции. В режиме автокоммита блокировка снимается сразу после выполнения запроса и ни от чего не защищает. Время ожидания ограничивают настройкой базы: SET LOCAL lock_timeout в PostgreSQL, innodb_lock_wait_timeout в MySQL.
Где спотыкаются
Говорят, что FOR UPDATE запрещает другим транзакциям читать строку. В MVCC-базах обычное чтение не блокируется, ждут только те, кто тоже хочет изменить или заблокировать.
Забывают про транзакцию: вызывают запрос из метода без @Transactional или из того же бина через this, где прокси не срабатывает, и блокировка не держится.
Не учитывают индексы в MySQL. В InnoDB на уровне REPEATABLE READ условие без подходящего индекса блокирует все просмотренные записи и промежутки между ними, и вместо одной строки встаёт половина таблицы. Отсюда же дедлоки; лечатся они захватом строк в одном порядке, например по возрастанию id.