Как в JDBC выполнить несколько операций в одной транзакции вручную?
- Как управлять транзакцией в JDBC?
- Что такое autocommit?
conn.setAutoCommit(false) → операции → commit(); при исключении rollback(); в finally вернуть autocommit и закрыть соединение.
- По умолчанию autocommit включён — каждый запрос отдельная транзакция
- Соединение из пула нужно вернуть в исходном состоянии: autocommit, уровень изоляции, read-only
- try-with-resources закрывает Statement и ResultSet
А ещё В Spring это делает @Transactional, руками — редко
Подробно
Autocommit. По умолчанию JDBC-соединение в режиме autocommit: каждая команда сразу фиксируется. Для нескольких связанных изменений (перевод денег) нужна одна транзакция.
Ручное управление:
conn.setAutoCommit(false)— начать транзакцию;- выполнить операции на этом соединении;
conn.commit()— зафиксировать;- при любом исключении —
conn.rollback(); - в
finally— вернутьsetAutoCommit(true)и закрыть соединение (вернуть в пул).
Почему важно восстановить состояние: соединение из пула получит следующий запрос. Если оставить autocommit выключенным или незавершённую транзакцию, следующий код будет работать в чужой транзакции, держать блокировки (idle in transaction) и не видеть ошибок. Хорошие пулы (HikariCP) сбрасывают состояние при возврате, но полагаться на это не стоит.
Savepoint — откат части транзакции: conn.setSavepoint() и rollback(savepoint).
В Spring транзакцию открывает @Transactional (или TransactionTemplate): берёт соединение, выключает autocommit, привязывает соединение к потоку, коммитит или откатывает по исключению.
Пример
try (Connection conn = dataSource.getConnection()) {
conn.setAutoCommit(false);
try (PreparedStatement debit = conn.prepareStatement("UPDATE accounts SET balance = balance - ? WHERE id = ?");
PreparedStatement credit = conn.prepareStatement("UPDATE accounts SET balance = balance + ? WHERE id = ?")) {
debit.setLong(1, amount); debit.setLong(2, from); debit.executeUpdate();
credit.setLong(1, amount); credit.setLong(2, to); credit.executeUpdate();
conn.commit();
} catch (SQLException e) {
conn.rollback();
throw e;
} finally {
conn.setAutoCommit(true);
}
}Что хотят услышать
- Выключение autocommit, commit, rollback при ошибке.
- Восстановление состояния соединения перед возвратом в пул.
- try-with-resources.
- Как это делает Spring — плюс.
Частые ошибки
- Забыть rollback в catch.
- Оставить незавершённую транзакцию на соединении из пула.
Проверьте себя
Один верный вариант. Подумайте, потом откройте ответ.
Показать варианты и ответ
- ✗Просто выполнить операции — JDBC объединит их сам
- ✗setAutoCommit(true), выполнить операции, затем один commit() в конце — JDBC объединит их
- ✗Вызывать commit() перед каждой операцией
- ✓setAutoCommit(false), операции, commit(); при ошибке — rollback()
Что спросят дальше
- Какие isolation levels есть в JDBC?
- Зачем нужен connection pool?
Разбор темы
- JDBC и безопасные запросы
Connection/Statement/ResultSet, PreparedStatement против инъекций, ручные транзакции, ресурсы и пул соединений.
Связанные вопросы
Разбор собеседования с sotto
После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.