Gymterview
middle

Что такое паттерн Unit of Work?

Unit of Work (Единица работы) – паттерн, который отслеживает все изменения доменных объектов в рамках одной бизнес-операции и выполняет их в БД как единую атомарную транзакцию. Как корзина покупок в магазине: вы складываете товары, убираете ненужные, а на кассе всё оформляется одной операцией.

Пример
    ┌────────────────────────────────────────────┐
    │             Unit of Work                   │
    │                                            │
    │  Новые:     [Payment A]                    │
    │  Изменённые: [Account X, Account Y]        │
    │  Удалённые:  []                            │
    │                                            │
    │  commit() → INSERT Payment A               │
    │             UPDATE Account X               │
    │             UPDATE Account Y               │
    │             COMMIT                         │
    └────────────────────────────────────────────┘

Зачем нужен

  • Группировка нескольких операций в одну транзакцию.
  • Минимизация обращений к БД (batch insert/update).
  • Гарантия целостности данных – либо все изменения применяются, либо ни одно.
  • Отслеживание изменений (dirty checking).

Реализация в Java/Spring через JPA/Hibernate

В Java-экосистеме Unit of Work реализуется неявно через JPA/Hibernate. Persistence Context в JPA – это и есть реализация паттерна Unit of Work:

Пример кода
@Service
public class TransferService {

    @Transactional  // Spring начинает Unit of Work
    public void transfer(Long fromId, Long toId, BigDecimal amount) {
        Account from = accountRepository.findById(fromId).orElseThrow();
        Account to = accountRepository.findById(toId).orElseThrow();

        from.withdraw(amount);   // Hibernate отслеживает изменение (dirty)
        to.deposit(amount);      // Hibernate отслеживает изменение (dirty)

        // НЕ нужно вызывать save() -- Hibernate при commit()
        // автоматически выполнит UPDATE для всех "грязных" сущностей
    }
    // По завершении метода Spring коммитит транзакцию
    // Hibernate выполняет flush() -- записывает все изменения в БД
}

Как работает в Hibernate

  1. @Transactional начинает транзакцию и создаёт Persistence Context.
  2. Загруженные сущности отслеживаются (managed state).
  3. Изменения сущностей запоминаются в Persistence Context.
  4. При коммите (flush()) Hibernate сравнивает текущее состояние с исходным (dirty checking) и генерирует SQL.
  5. Все SQL выполняются в рамках одной транзакции.

Итог

Persistence Context в JPA – это реализация Unit of Work. Разработчику на Spring Boot редко приходится реализовывать этот паттерн вручную, но понимание его механики помогает избежать типичных ошибок: например, модификация detached-сущности не будет автоматически сохранена, потому что она находится вне Unit of Work.

На собеседовании: Интервьюер ожидает связь Unit of Work с Hibernate Persistence Context и @Transactional. Частая ошибка – не знать, что dirty checking происходит автоматически, и вызывать save() для каждой managed-сущности.