Что такое TransactionTemplate в Spring и в каких случаях его применяют?

Ответ

TransactionTemplate — это класс в Spring, предоставляющий программный (declarative) способ управления транзакциями. Он является альтернативой декларативному управлению через @Transactional и даёт больше контроля над границами транзакции.

Когда использовать?

  • Когда нужен полный программный контроль над началом и завершением транзакции.
  • Для выполнения нескольких операций в одной транзакции внутри одного метода.
  • Когда логика коммита или отката зависит от условий, которые сложно выразить через @Transactional.

Пример использования:

@Service
public class TransferService {
    private final TransactionTemplate transactionTemplate;

    public TransferService(PlatformTransactionManager transactionManager) {
        this.transactionTemplate = new TransactionTemplate(transactionManager);
        // Настройка параметров транзакции
        this.transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        this.transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
    }

    public void transferMoney(Long fromAccId, Long toAccId, BigDecimal amount) {
        // Код внутри execute() выполняется в транзакции
        transactionTemplate.execute(status -> {
            Account from = accountRepository.findById(fromAccId).orElseThrow();
            Account to = accountRepository.findById(toAccId).orElseThrow();

            from.debit(amount);
            to.credit(amount);

            accountRepository.save(from);
            accountRepository.save(to);

            // Принудительный откат по условию
            if (someCondition) {
                status.setRollbackOnly();
            }

            return "Transfer completed"; // Возвращаемое значение
        }); // Автоматический commit/rollback
    }
}

Преимущества перед @Transactional:

  • Точный контроль: Границы транзакции чётко видны в коде.
  • Гибкость: Возможность динамически менять параметры (изоляция, распространение).
  • Условный rollback: Легко вызвать status.setRollbackOnly() в любом месте.

Недостаток: Код становится более многословным по сравнению с аннотацией @Transactional.

Ответ 18+ 🔞

Так, слушай сюда, про этот ваш TransactionTemplate. Это ж, блядь, как если бы вместо того, чтобы сказать «эй, Спринг, сделай мне тут всё в транзакции» (это @Transactional), ты сам берёшь руль в руки и говоришь: «Отъебись, я сам всё сделаю, от и до».

А когда это, блядь, нужно? Представь, ты не просто хочешь обернуть метод в транзакцию, а тебе надо внутри одного метода, сука, нагородить кучу операций, да ещё и решать на ходу — коммитить это всё в БД или откатывать с грохотом. Или параметры транзакции у тебя меняются, как перчатки. Вот тогда ты и хватаешься за TransactionTemplate, как за спасательный круг.

Смотри, как это выглядит в деле:

@Service
public class TransferService {
    private final TransactionTemplate transactionTemplate;

    public TransferService(PlatformTransactionManager transactionManager) {
        this.transactionTemplate = new TransactionTemplate(transactionManager);
        // Тут ты настраиваешь свою боевую машину
        this.transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        this.transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
    }

    public void transferMoney(Long fromAccId, Long toAccId, BigDecimal amount) {
        // Всё, что внутри execute() — это священная земля транзакции
        transactionTemplate.execute(status -> {
            Account from = accountRepository.findById(fromAccId).orElseThrow();
            Account to = accountRepository.findById(toAccId).orElseThrow();

            from.debit(amount);
            to.credit(amount);

            accountRepository.save(from);
            accountRepository.save(to);

            // А вот тут магия! Решаешь на ходу — пиздец транзакции или нет
            if (someCondition) {
                status.setRollbackOnly(); // Всё, поезд ушёл, откат, сука!
            }

            return "Transfer completed"; // Вернуть можешь что угодно
        }); // А тут автоматом — либо коммит, либо откат в пизду
    }
}

Чем он, блядь, крут?

  • Контроль, как у Цербера: Ты сам видишь, где транзакция начинается и где кончается. Никаких сюрпризов из-за проксирования.
  • Гибкость до охуения: Захотел другую изоляцию или propagation — хуяк, поменял настройку. Динамически, мать его!
  • Условный пиздец: Захотел откатить — setRollbackOnly(), и всё, приехали. Никаких сложных исключений выдумывать.

А минус-то в чём? Да в том, что код становится длиннее, чем список грехов на исповеди. Писать @Transactional над методом — это раз плюнуть. А тут — целый ритуал с настройками и лямбдой. Но за контроль, блядь, всегда платишь многословностью, это как закон природы.