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

Как внутренняя транзакция выглядит в консоли БД

2.3 Middle🔥 111 комментариев
#ORM и Hibernate#Базы данных и SQL

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

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

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

# Как выглядит транзакция в консоли БД

Что видно в логах БД

Внутренние транзакции выглядят в консоли БД последовательностью SQL команд в рамках одной сессии. Покажу конкретные примеры для PostgreSQL.

PostgreSQL: Явное управление транзакциями

Пример 1: Простая транзакция

-- Начало транзакции (явное)
BEGIN;

-- Операция 1: INSERT
INSERT INTO users (id, username, email, created_at) 
VALUES (1, 'john_doe', 'john@example.com', now());

-- Операция 2: UPDATE
UPDATE accounts SET balance = balance - 100 
WHERE user_id = 1;

-- Операция 3: INSERT
INSERT INTO transactions (user_id, amount, type) 
VALUES (1, 100, 'withdrawal');

-- Завершение: фиксируем все изменения
COMMIT;
-- или откатываем (если ошибка)
ROLLBACK;

Как это выглядит в логе PostgreSQL

2024-03-22 14:35:22.123 [1234] LOG:  statement: BEGIN;
2024-03-22 14:35:22.124 [1234] LOG:  statement: INSERT INTO users (id, username, email, created_at) VALUES (1, 'john_doe', 'john@example.com', now());
2024-03-22 14:35:22.125 [1234] LOG:  statement: UPDATE accounts SET balance = balance - 100 WHERE user_id = 1;
2024-03-22 14:35:22.126 [1234] LOG:  statement: INSERT INTO transactions (user_id, amount, type) VALUES (1, 100, 'withdrawal');
2024-03-22 14:35:22.127 [1234] LOG:  statement: COMMIT;
2024-03-22 14:35:22.128 [1234] LOG:  statement: SELECT 1;  -- next query

При ошибке (ROLLBACK)

2024-03-22 14:35:22.123 [1234] LOG:  statement: BEGIN;
2024-03-22 14:35:22.124 [1234] LOG:  statement: INSERT INTO users (id, username, email, created_at) VALUES (1, 'john_doe', 'john@example.com', now());
2024-03-22 14:35:22.125 [1234] ERROR:  duplicate key value violates unique constraint "users_email_key"
2024-03-22 14:35:22.126 [1234] LOG:  statement: ROLLBACK;  -- всё откатывается

Spring Boot + Spring Data JPA: Как это работает

Java код

@Service
public class OrderService {
    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private PaymentRepository paymentRepository;
    
    @Transactional  // Аннотация для управления транзакциями
    public void createOrder(Order order, Payment payment) {
        // 1. Сохраняем заказ
        orderRepository.save(order);
        
        // 2. Обрабатываем платёж
        paymentRepository.save(payment);
        
        // 3. Обновляем статус
        order.setStatus("CONFIRMED");
        orderRepository.save(order);
        
        // В конце автоматически вызывается COMMIT
    }
    
    @Transactional
    public void failingOrder(Order order) {
        orderRepository.save(order);
        // Если здесь выбросится исключение — всё откатится
        throw new RuntimeException("Payment failed");
        // ROLLBACK произойдёт автоматически
    }
}

В логах БД появится

2024-03-22 14:35:22.100 [conn-5234] LOG:  statement: BEGIN;
2024-03-22 14:35:22.101 [conn-5234] LOG:  statement: INSERT INTO orders (id, user_id, total, status, created_at) VALUES (100, 42, 299.99, 'PENDING', now());
2024-03-22 14:35:22.102 [conn-5234] LOG:  statement: INSERT INTO payments (id, order_id, amount, method) VALUES (1, 100, 299.99, 'CREDIT_CARD');
2024-03-22 14:35:22.103 [conn-5234] LOG:  statement: UPDATE orders SET status = 'CONFIRMED' WHERE id = 100;
2024-03-22 14:35:22.104 [conn-5234] LOG:  statement: COMMIT;

Пример: Nested Transactions

Java код с вложенными транзакциями

@Service
public class ComplexOrderService {
    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private InventoryService inventoryService;
    
    @Transactional
    public void processOrder(Order order) {
        // Основная транзакция начинается
        orderRepository.save(order);
        
        // Вызываем метод с @Transactional(propagation = REQUIRES_NEW)
        try {
            inventoryService.deductInventory(order.getItems());
        } catch (InsufficientInventoryException e) {
            // Вложенная транзакция откатилась
            // Но основная может продолжить
            order.setStatus("BACKORDER");
            orderRepository.save(order);
        }
        
        // Основная транзакция коммитится
    }
}

@Service
public class InventoryService {
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void deductInventory(List<OrderItem> items) {
        // Новая, отдельная транзакция
        // COMMIT или ROLLBACK независимо от основной
    }
}

В логах БД

2024-03-22 14:35:22.100 [conn-5234] LOG:  statement: BEGIN;  -- основная транзакция
2024-03-22 14:35:22.101 [conn-5234] LOG:  statement: INSERT INTO orders ...;
2024-03-22 14:35:22.102 [conn-5234] LOG:  statement: SAVEPOINT sp_1;  -- SavePoint для вложения
2024-03-22 14:35:22.103 [conn-5234] LOG:  statement: UPDATE inventory SET quantity = quantity - 5 WHERE product_id = 1;
2024-03-22 14:35:22.104 [conn-5234] LOG:  statement: ROLLBACK TO sp_1;  -- откатываем только вложенную часть
2024-03-22 14:35:22.105 [conn-5234] LOG:  statement: UPDATE orders SET status = 'BACKORDER' ...;
2024-03-22 14:35:22.106 [conn-5234] LOG:  statement: COMMIT;  -- основная транзакция коммитится

MySQL: Аналогичный пример

-- Начало транзакции
START TRANSACTION;

-- Операции
INSERT INTO users ...
UPDATE accounts ...
DELETE FROM temp_data ...

-- Фиксируем
COMMIT;

Как включить логирование транзакций в Spring Boot

application.properties

# Логирование SQL
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true

# Для PostgreSQL в логах
logging.level.org.postgresql.core=DEBUG
logging.level.org.postgresql.jdbc.PgConnection=DEBUG

# Для Hibernate
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE

Вывод в консоль

Hibernate: BEGIN
Hibernate: INSERT INTO orders (id, total, status) VALUES (?, ?, ?)
Hibernate: INSERT INTO payments (id, order_id, amount) VALUES (?, ?, ?)
Hibernate: COMMIT

Isolation Levels и их отражение

Установка уровня изоляции

@Transactional(isolation = Isolation.READ_COMMITTED)
public void saveWithReadCommitted(Order order) {
    // В логах БД появится:
    // SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
    orderRepository.save(order);
}

В консоли БД

2024-03-22 14:35:22.100 LOG: SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
2024-03-22 14:35:22.101 LOG: BEGIN;
2024-03-22 14:35:22.102 LOG: INSERT INTO orders ...;
2024-03-22 14:35:22.103 LOG: COMMIT;

Практический пример: Откат при ошибке

Java

@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 InsufficientBalanceException();
        }
        
        from.setBalance(from.getBalance().subtract(amount));
        to.setBalance(to.getBalance().add(amount));
        
        accountRepository.save(from);
        accountRepository.save(to);
    }
}

В логах БД (успех)

BEGIN;
UPDATE accounts SET balance = 900 WHERE id = 1;
UPDATE accounts SET balance = 1100 WHERE id = 2;
COMMIT;

В логах БД (ошибка и откат)

BEGIN;
UPDATE accounts SET balance = 900 WHERE id = 1;
-- Исключение в Java: InsufficientBalanceException
ROLLBACK;
-- Балансы остаются неизменными!

Итоговая схема

Spring @Transactional
        ↓
Spring Data JPA
        ↓
Hibernate ORM
        ↓
Database Driver (JDBC)
        ↓
Датабаза (PostgreSQL/MySQL)
        ↓
В логах БД видны: BEGIN, SQL операции, COMMIT/ROLLBACK

Ключевые моменты

  1. Группировка операций — всё между BEGIN и COMMIT выполняется атомарно
  2. Откат при ошибке — автоматический ROLLBACK при исключении
  3. Изоляция — транзакции не видят грязные данные друг друга
  4. Дурабельность — после COMMIT данные сохранены окончательно
  5. Вложенность — SAVEPOINT для вложенных транзакций