Транзакция на чистом JDBC
Вопрос
Как выполнить перевод денег между счетами на чистом JDBC так, чтобы он был атомарным и корректным при параллельных переводах?
Попробуйте сами
Ответьте вслух за 2 минуты, как на собесе. Потом сверьтесь с тезисами: что прозвучало, а что нет.
Показать ответ и разбор
Коротко
Что должно прозвучать в ответе
Одно соединение, setAutoCommit(false), commit и rollback в catch
try-with-resources для Connection и PreparedStatement
Блокировка строк: SELECT ... FOR UPDATE или атомарный UPDATE с проверкой баланса
Порядок блокировки строк по id, чтобы избежать дедлоков
Уровень изоляции и повтор при ошибке сериализации
Как это делает Spring @Transactional
Что могут спросить следом
- Что будет, если упасть между двумя UPDATE?
- Чем оптимистическая блокировка отличается от пессимистической?
Разбор
Беру одно соединение из пула, выключаю autoCommit, выполняю оба UPDATE через PreparedStatement и вызываю commit, а в catch rollback и пробрасываю ошибку. Соединение закрываю в try-with-resources, чтобы оно вернулось в пул. Атомарности мало, нужна корректность при параллельных переводах: списание делаю условным UPDATE accounts SET balance = balance - ? WHERE id = ? AND balance >= ? и проверяю число изменённых строк, либо сначала беру строки через SELECT ... FOR UPDATE. Чтобы два встречных перевода не устроили дедлок, блокирую счета в порядке возрастания id. При SERIALIZABLE обрабатываю ошибку сериализации повтором. В Spring всё это прячет @Transactional: он берёт соединение, выключает autocommit, привязывает соединение к потоку и коммитит или откатывает по результату метода.
Ответьте сами, а не прочитайте
В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.