Получить доступ

Как в JDBC выполнить несколько операций в одной транзакции вручную?

MiddleJDBCSQL
Ещё спрашивают так:
  • Как управлять транзакцией в JDBC?
  • Что такое autocommit?
Коротко

conn.setAutoCommit(false) → операции → commit(); при исключении rollback(); в finally вернуть autocommit и закрыть соединение.

  • По умолчанию autocommit включён — каждый запрос отдельная транзакция
  • Соединение из пула нужно вернуть в исходном состоянии: autocommit, уровень изоляции, read-only
  • try-with-resources закрывает Statement и ResultSet

А ещё В Spring это делает @Transactional, руками — редко

Так подсказку видит пользователь sotto на живом собеседовании — за пару секунд, пока вы думаете вслух.

Подробно

Autocommit. По умолчанию JDBC-соединение в режиме autocommit: каждая команда сразу фиксируется. Для нескольких связанных изменений (перевод денег) нужна одна транзакция.

Ручное управление:

  1. conn.setAutoCommit(false) — начать транзакцию;
  2. выполнить операции на этом соединении;
  3. conn.commit() — зафиксировать;
  4. при любом исключении — conn.rollback();
  5. в 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.
  • Оставить незавершённую транзакцию на соединении из пула.

Проверьте себя

Один верный вариант. Подумайте, потом откройте ответ.

Показать варианты и ответ
  • ✓setAutoCommit(false), выполнить операции, commit; при ошибке — rollback
  • ✗Просто выполнить операции — JDBC объединит их сам
  • ✗Вызывать commit() перед каждой операцией
  • ✗Установить setAutoCommit(true) и вызвать flush()

Что спросят дальше

  • Какие isolation levels есть в JDBC?
  • Зачем нужен connection pool?

Разбор темы

Связанные вопросы

Закрытая бета

Подсказки на собеседовании — в реальном времени

sotto слышит вопрос интервьюера и показывает короткий ответ и план, как в блоке «Коротко». Оставьте контакт — пришлём доступ и промокод.

  • Промокод на 2 часа — сверх пробных 30 минут
  • Программа для Windows — скачать сразу, войти — через Яндекс ID или VK ID
  • Прямая связь с разработчиком: что улучшить — сделаем