Карта / Spring Data / Границы транзакций

Границы транзакций

Граница транзакции — где она начинается и где заканчивается. На практике это значит: на каком именно методе ставить @Transactional — слишком “узко” и слишком “широко” одинаково плохи.

@Service
class OrderService {

    @Transactional                  // граница здесь — на уровне бизнес-операции
    void placeOrder(Order order) {
        validate(order);             // часть транзакции
        save(order);                  // часть транзакции
        notifyWarehouse(order);       // часть транзакции
    }
}

Правильная граница — обычно сервисный метод, представляющий целую бизнес-операцию (“оформить заказ”), а не отдельный шаг внутри неё.

Копнуть глубже

Propagation (распространение транзакции) — что делать, если @Transactional-метод вызывает другой @Transactional-метод:

@Transactional(propagation = Propagation.REQUIRED)   // по умолчанию: использовать текущую транзакцию, если есть
@Transactional(propagation = Propagation.REQUIRES_NEW)  // всегда новая, независимая транзакция

REQUIRES_NEW полезен, когда нужно, чтобы часть операции (например, логирование) сохранилась даже если основная транзакция откатится — например, залогировать попытку платежа независимо от того, прошла она или нет.

Слишком широкая граница — проблема. Если обернуть @Transactional весь метод контроллера, включая медленный вызов внешнего API внутри, соединение с базой держится открытым всё это время — впустую занимает ресурс пула соединений (см. тему «Пулы (Hikari)») и блокирует строки в базе дольше необходимого.

Слишком узкая граница — другая проблема. Если разбить одну логическую операцию на несколько отдельных мелких транзакций, теряется атомарность — часть может пройти успешно, а часть — нет, и данные окажутся в противоречивом состоянии.

🎤 Закрыл тему, если можешь объяснить:
• как правильно выбрать границу транзакции (уровень бизнес-операции);
• что такое propagation и чем плохи слишком широкие/узкие границы (если дошёл до 2-го слоя).