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

Является ли транзакцией операция UPDATE?

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

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

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

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

Является ли транзакцией операция UPDATE?

Нет, операция UPDATE сама по себе не является полной транзакцией. UPDATE — это одна операция/команда в SQL, которая может быть частью транзакции, но не обязательно. Важно понимать разницу между операцией и транзакцией.

Различие: операция vs транзакция

Операция (Statement) — это одна команда:

UPDATE users SET age = 30 WHERE id = 1;

Это одна операция UPDATE. Она выполняется, но это ещё не транзакция.

Транзакция (Transaction) — это группа операций, выполняемых как единое целое:

BEGIN TRANSACTION;  -- Начало транзакции
UPDATE users SET age = 30 WHERE id = 1;      -- Операция 1
UPDATE accounts SET balance = balance - 100 WHERE user_id = 1;  -- Операция 2
INSERT INTO audit_log VALUES ('Updated user 1');  -- Операция 3
COMMIT;  -- Конец транзакции, все операции фиксируются как одно целое

Как UPDATE работает в контексте транзакций

1. Автокоммит — UPDATE как неявная транзакция

В большинстве БД по умолчанию включен режим автокоммита. Это означает, что каждая операция становится отдельной транзакцией:

// С включённым автокоммитом (по умолчанию):
statement.executeUpdate("UPDATE users SET age = 30 WHERE id = 1");
// Эквивалентно:
// BEGIN; UPDATE ...; COMMIT;  -- Неявная транзакция

2. Явная транзакция с несколькими операциями

@Transactional  // Spring управляет транзакцией
public void transferMoney(Long fromUserId, Long toUserId, BigDecimal amount) {
    // Операция 1: UPDATE счёта отправителя
    userRepository.decreaseBalance(fromUserId, amount);
    
    // Операция 2: UPDATE счёта получателя
    userRepository.increaseBalance(toUserId, amount);
    
    // Операция 3: INSERT в лог
    auditLogRepository.save(new AuditLog("Transfer executed"));
    
    // Если всё прошло успешно → COMMIT
    // Если ошибка → ROLLBACK (откатит все три операции)
}

ACID свойства — почему транзакция важнее, чем одна операция

1. Atomicity (Атомарность)

Одна UPDATE операция обычно атомарная сама по себе, но несколько операций — требуют транзакции:

// Проблема БЕЗ транзакции:
// UPDATE account1 SET balance = balance - 100;  // Успешно
// [СБОЙ СЕРВЕРА]
// UPDATE account2 SET balance = balance + 100;  // Не выполнится
// Результат: деньги потеряны!

// С транзакцией:
BEGIN;
UPDATE account1 SET balance = balance - 100;
UPDATE account2 SET balance = balance + 100;
COMMIT;  // Либо обе операции выполнены, либо обе откачены

2. Consistency (Консистентность)

Транзакция гарантирует, что БД переходит из одного консистентного состояния в другое:

-- Инвариант: сумма всех счетов должна быть постоянна
-- Начальное состояние: account1=1000, account2=1000, итого=2000

BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;   -- account1=900
UPDATE accounts SET balance = balance + 100 WHERE id = 2;   -- account2=1100
COMMIT;  -- Итого всё ещё 2000 ✓ Консистентное состояние

-- Если бы была ошибка в середине:
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;   -- account1=900
[ОШИБКА]
-- ROLLBACK  -- account1 вернётся к 1000, account2 остаётся 1000
-- Итого 2000 ✓ Консистентное состояние

3. Isolation (Изоляция)

Транзакции изолируют друг друга, одна UPDATE не видит незаписанных изменений другой:

// Транзакция 1 (Пользователь A):
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
// Читает текущий баланс
LONG newBalance = SELECT balance FROM accounts WHERE id = 1;
COMMIT;

// Транзакция 2 (Пользователь B) в то же время:
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
UPDATE accounts SET balance = balance - 50 WHERE id = 1;
COMMIT;

// Без изоляции: оба видели одно значение, обновили его → потеря данных
// С SERIALIZABLE: одна ждёт другую, результат консистентен

4. Durability (Надёжность)

После коммита транзакции данные сохранены, даже при сбое:

statement.executeUpdate("UPDATE users SET age = 30 WHERE id = 1");
connection.commit();  // Данные физически записаны на диск
// Даже если сервер упадёт, данные не потеряются

Как это работает в Java с Spring

@Service
public class BankingService {
    
    @Autowired
    private AccountRepository accountRepository;
    
    // БЕЗ @Transactional — каждая операция = отдельная транзакция
    public void transferMoneyUnsafe(Long from, Long to, BigDecimal amount) {
        Account fromAcc = accountRepository.findById(from).orElseThrow();
        fromAcc.setBalance(fromAcc.getBalance().subtract(amount));
        accountRepository.save(fromAcc);  // COMMIT произошёл
        
        // [ЕСЛИ ЗДЕСЬ ОШИБКА, то первый UPDATE уже зафиксирован]
        
        Account toAcc = accountRepository.findById(to).orElseThrow();
        toAcc.setBalance(toAcc.getBalance().add(amount));
        accountRepository.save(toAcc);  // COMMIT произошёл
        // Если была ошибка выше, тут может быть исключение
    }
    
    // С @Transactional — все операции в одной транзакции
    @Transactional
    public void transferMoneySafe(Long from, Long to, BigDecimal amount) {
        Account fromAcc = accountRepository.findById(from).orElseThrow();
        fromAcc.setBalance(fromAcc.getBalance().subtract(amount));
        accountRepository.save(fromAcc);  // В памяти, не зафиксирован
        
        // [ЕСЛИ ОШИБКА ЗДЕСЬ]
        if (amount.compareTo(BigDecimal.ZERO) < 0) {
            throw new IllegalArgumentException("Negative amount");
        }
        // Все изменения откатятся, включая первый UPDATE
        
        Account toAcc = accountRepository.findById(to).orElseThrow();
        toAcc.setBalance(toAcc.getBalance().add(amount));
        accountRepository.save(toAcc);  // В памяти, не зафиксирован
        // В конце метода: COMMIT фиксирует ВСЕ операции атомарно
    }
}

UPDATE в разных контекстах

1. Сырой SQL: UPDATE — это одна команда

UPDATE users SET age = 30 WHERE id = 1;  -- Одна операция

2. В транзакции: UPDATE — часть группы операций

BEGIN;
UPDATE users SET age = 30 WHERE id = 1;
UPDATE audit_log SET last_update = NOW() WHERE user_id = 1;
COMMIT;  -- Транзакция из двух операций

3. В Hibernate/JPA: UPDATE отслеживается в контексте

@Transactional
public void updateUser(Long id, String newName) {
    User user = entityManager.find(User.class, id);
    user.setName(newName);  // Dirty checking
    // entityManager.persist() не нужен!
    // При выходе из метода → flush → UPDATE отправляется в БД
    // После → COMMIT фиксирует все изменения
}

Уровни изоляции транзакций (важно для UPDATE)

УровеньПроблемаUPDATE видитПример
READ UNCOMMITTEDГрязное чтениеНезафиксированные UPDATEОпасно
READ COMMITTEDПотерянные обновленияТолько фиксированныеСтандартный
REPEATABLE READPhantom readsФиксированные + своиХороший
SERIALIZABLEНетКак будто последовательноБезопасный, медленный

Практический пример проблемы

// Сценарий: двое пользователей обновляют одно поле

// Пользователь A:
User user = userRepository.findById(1);
user.setViews(user.getViews() + 1);  // views: 100 → 101
userRepository.save(user);
// UPDATE users SET views = 101 WHERE id = 1;

// Пользователь B (в то же время):
User user = userRepository.findById(1);  // Читает views = 100
user.setViews(user.getViews() + 1);  // views: 100 → 101
userRepository.save(user);
// UPDATE users SET views = 101 WHERE id = 1;

// Результат: views = 101, но должно быть 102!
// Одно обновление потеряло (lost update)

// Решение — Optimistic Locking с @Version:
@Entity
public class User {
    @Version
    private Long version;  // Автоматически увеличивается
    // При UPDATE с неправильной версией → исключение
}

Заключение

UPDATE — это операция, а не транзакция:

  • Одна UPDATE = одна команда SQL, может быть обёрнута в неявную транзакцию (автокоммит)
  • Несколько UPDATE/INSERT/DELETE = должны быть обёрнуты в явную транзакцию
  • @Transactional в Spring = управляет транзакцией, группирует все операции метода
  • ACID гарантии = даёт транзакция, а не одна операция
  • Всегда используй @Transactional для операций, которые должны быть атомарными
Является ли транзакцией операция UPDATE? | PrepBro