← Назад к вопросам

Для чего нужен rollback?

2.0 Middle🔥 191 комментариев
#Базы данных и SQL

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Rollback в базах данных

Определение

Rollback — это операция отката транзакции, которая отменяет все изменения, сделанные в рамках этой транзакции, и возвращает базу данных в состояние, которое было до начала транзакции.

Зачем нужен Rollback

1. Обеспечение консистентности данных

Rollback гарантирует ACID свойства транзакций, особенно Atomicity (атомарность). Если во время выполнения транзакции произойдет ошибка, все изменения отменяются полностью, а не частично.

Connection conn = dataSource.getConnection();
try {
    conn.setAutoCommit(false);
    
    // Операция 1: Снимаем деньги со счета
    PreparedStatement stmt1 = conn.prepareStatement("UPDATE accounts SET balance = balance - ? WHERE id = ?");
    stmt1.setDouble(1, 100);
    stmt1.setInt(2, 1);
    stmt1.executeUpdate();
    
    // Операция 2: Пополняем счет получателя
    PreparedStatement stmt2 = conn.prepareStatement("UPDATE accounts SET balance = balance + ? WHERE id = ?");
    stmt2.setDouble(1, 100);
    stmt2.setInt(2, 2);
    stmt2.executeUpdate();
    
    conn.commit();
} catch (SQLException e) {
    conn.rollback(); // Откатываем обе операции
    throw e;
} finally {
    conn.close();
}

2. Обработка ошибок

Если вторая операция в транзакции завершится с ошибкой (например, счет получателя не существует), первая операция не должна остаться в базе. Rollback отменяет все изменения.

3. Предотвращение некорректных состояний

Без rollback можно получить несогласованное состояние данных. Например, при переводе денег: деньги сняты, но не зачислены получателю.

4. Работа с блокировками

Rollback освобождает все блокировки, удерживаемые транзакцией, позволяя другим транзакциям получить доступ к данным.

Практические примеры

Пример с Spring и JPA

@Service
public class TransferService {
    @Transactional
    public void transferMoney(Long fromAccountId, Long toAccountId, BigDecimal amount) {
        Account from = accountRepository.findById(fromAccountId).orElseThrow();
        Account to = accountRepository.findById(toAccountId).orElseThrow();
        
        if (from.getBalance().compareTo(amount) < 0) {
            throw new InsufficientFundsException(); // Автоматический rollback
        }
        
        from.setBalance(from.getBalance().subtract(amount));
        to.setBalance(to.getBalance().add(amount));
        
        accountRepository.save(from);
        accountRepository.save(to);
    }
}

Если выброшено исключение, аннотация @Transactional автоматически выполнит rollback.

Пример с заметным откатом

public void createUserWithProfile(User user, UserProfile profile) throws Exception {
    try {
        userRepository.save(user);
        
        // Если это выбросит exception
        profileRepository.save(profile);
        
        // И будет выполнен rollback, то пользователь не будет создан
    } catch (Exception e) {
        // Обе операции откачены
        logger.error("Ошибка при создании пользователя", e);
        throw e;
    }
}

Когда срабатывает Rollback

  1. При выбросе исключения (в управляемых транзакциях Spring)
  2. При вызове conn.rollback() (при ручном управлении)
  3. При close() соединения если commit() не был вызван
  4. При timeout транзакции
  5. При конфликте блокировок (deadlock)

Важные замечания

  • Rollback — это дорогая операция, особенно для больших транзакций
  • Для оптимизации следует минимизировать время транзакции
  • Savepoint позволяет откатить только часть транзакции
  • Не все типы ошибок вызывают rollback автоматически (checked exceptions в Spring)