Как управлять транзакциями в Java при работе с БД?

«Как управлять транзакциями в Java при работе с БД?» — вопрос из категории Базы данных, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Управление транзакциями в Java осуществляется через JDBC, JPA/Hibernate или Spring @Transactional. Ключевые принципы: атомарность, согласованность, изоляция, долговечность (ACID).

1. JDBC (низкоуровневый API)

Connection conn = dataSource.getConnection();
try {
    conn.setAutoCommit(false); // Начало транзакции

    PreparedStatement stmt = conn.prepareStatement(
        "UPDATE accounts SET balance = ? WHERE id = ?"
    );
    stmt.setBigDecimal(1, newBalance);
    stmt.setLong(2, accountId);
    stmt.executeUpdate();

    conn.commit(); // Фиксация изменений
} catch (SQLException e) {
    conn.rollback(); // Откат при ошибке
    throw e;
} finally {
    conn.setAutoCommit(true);
    conn.close();
}

2. JPA/Hibernate

EntityTransaction tx = entityManager.getTransaction();
try {
    tx.begin();
    Account account = entityManager.find(Account.class, accountId);
    account.setBalance(newBalance);
    tx.commit();
} catch (Exception e) {
    if (tx.isActive()) {
        tx.rollback(); // Явный откат
    }
    throw e;
}

3. Spring @Transactional (рекомендуемый подход)

@Service
public class AccountService {
    @Transactional(isolation = Isolation.READ_COMMITTED,
                   rollbackFor = {SQLException.class})
    public void transferMoney(Long fromId, Long toId, BigDecimal amount) {
        // Бизнес-логика
        // При исключении — автоматический rollback
    }
}

Ключевые аспекты:

  • Уровни изоляции: READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE.
  • Распространённые проблемы: грязное чтение, неповторяемое чтение, фантомное чтение.
  • Паттерны: оптимистичные/пессимистичные блокировки, компенсирующие транзакции (Saga).