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

Что такое Trunk-based?

1.0 Junior🔥 141 комментариев
#Soft Skills и карьера

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

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

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

# @Transactional аннотация в Spring

@Transactional - одна из самых важных аннотаций в Spring для управления транзакциями. Это критично для обеспечения ACID свойств при работе с БД.

Как работает @Transactional

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

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    
    @Transactional
    public User createUser(UserRequest request) {
        User user = new User(request.getName(), request.getEmail());
        return userRepository.save(user);
        // Транзакция коммитится автоматически здесь
    }
}

Spring оборачивает метод в try-catch, и если все успешно - коммитит, если ошибка - откатывает.

Аннотирование

На уровне класса

@Service
@Transactional
public class UserService {
    // Все методы будут @Transactional
    public void method1() { }
    public void method2() { }
}

На уровне метода

@Service
public class UserService {
    @Transactional
    public void save(User user) { }
    
    // Этот метод НЕ будет transactional
    public void delete(User user) { }
}

Основные параметры

1. readOnly

// Для операций чтения
@Transactional(readOnly = true)
public List<User> getAllUsers() {
    return userRepository.findAll();
}

// Для операций записи
@Transactional(readOnly = false) // или просто @Transactional
public User saveUser(User user) {
    return userRepository.save(user);
}
readOnly = true оптимизирует транзакцию для чтения (БД может не открывать транзакцию).

2. propagation

Определяет, как транзакция распространяется на вложенные методы:

@Service
public class UserService {
    @Transactional(propagation = Propagation.REQUIRED)
    public void method1() {
        method2(); // Использует ту же транзакцию
    }
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void method2() {
        // Создает НОВУЮ транзакцию
        // Даже если method1 откатится, method2 будет закоммичена
    }
}

Типы propagation:

  • REQUIRED (default): использует существующую или создает новую
  • REQUIRES_NEW: всегда создает новую
  • NESTED: создает savepoint
  • SUPPORTS: использует если есть, иначе выполняет без транзакции
  • MANDATORY: ошибка если нет транзакции
  • NOT_SUPPORTED: выполняет БЕЗ транзакции
  • NEVER: ошибка если есть транзакция

3. isolation

Уровни изоляции транзакций:

@Transactional(isolation = Isolation.READ_COMMITTED)
public User updateUser(User user) {
    return userRepository.save(user);
}

Уровни:

  • DEFAULT: используется default БД
  • READ_UNCOMMITTED: грязные чтения
  • READ_COMMITTED: только коммиченные данные
  • REPEATABLE_READ: снимок данных в начале
  • SERIALIZABLE: полная изоляция

4. timeout

@Transactional(timeout = 5) // 5 секунд
public void longOperation() {
    // Если превысит 5 сек, откатится
    expensiveOperation();
}

5. rollbackFor / noRollbackFor

// Откатит транзакцию при исключении
@Transactional(rollbackFor = CustomException.class)
public void process() {
    throw new CustomException(); // откатит
}

// Не откатит при RuntimeException
@Transactional(noRollbackFor = IgnorableException.class)
public void flexibleProcess() {
    throw new IgnorableException(); // коммитит несмотря на ошибку
}

По умолчанию откатывает только на RuntimeException и Error.

Практические примеры

Пример 1: Простая операция

@Service
public class OrderService {
    @Autowired
    private OrderRepository orderRepository;
    
    @Transactional
    public Order createOrder(OrderRequest request) {
        Order order = new Order(request);
        return orderRepository.save(order);
    }
}

Пример 2: Несколько операций

@Service
public class PaymentService {
    @Autowired
    private AccountRepository accountRepository;
    @Autowired
    private TransactionRepository transactionRepository;
    
    @Transactional
    public void transfer(Long fromId, Long toId, BigDecimal amount) {
        Account from = accountRepository.findById(fromId);
        Account to = accountRepository.findById(toId);
        
        from.debit(amount);
        to.credit(amount);
        
        accountRepository.save(from);
        accountRepository.save(to);
        
        Transaction transaction = new Transaction(fromId, toId, amount);
        transactionRepository.save(transaction);
        
        // Если любая операция fail, все откатится
    }
}

Пример 3: Вложенные транзакции

@Service
public class ComplexService {
    @Transactional
    public void mainOperation() {
        // основная логика
        saveData();
    }
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveData() {
        // Эта операция в отдельной транзакции
        // Даже если mainOperation откатится, saveData остается
    }
}

Важные моменты

1. Self-invocation проблема

@Service
public class UserService {
    @Transactional
    public void method1() {
        method2(); // НЕ будет transactional!
    }
    
    @Transactional
    public void method2() {
        // Проблема: вызов через this игнорирует @Transactional
    }
}

Решение: вызвать через bean:

@Service
public class UserService {
    @Autowired
    private UserService self;
    
    public void method1() {
        self.method2(); // Теперь будет transactional
    }
}

2. Checked exceptions

По умолчанию не откатывает на checked exceptions:

@Transactional(rollbackFor = Exception.class)
public void process() throws Exception {
    // Теперь откатит и на checked exceptions
}

3. Lazy loading проблема

@Transactional
public User getUser(Long id) {
    return userRepository.findById(id); // lazy collections инициализируются
}

// БЕЗ @Transactional может быть LazyInitializationException
public void useUser() {
    User user = getUser(1);
    user.getOrders().size(); // Может быть ошибка
}

Лучшие практики

  1. Используй @Transactional только где нужно (не на контроллере)
  2. Размещай на сервисном слое
  3. Помни о performance при длительных операциях
  4. Используй readOnly = true для читающих операций
  5. Определяй timeout для длительных операций
  6. Избегай вложенных @Transactional методов без необходимости
  7. Помни о self-invocation проблеме

@Transactional - мощный инструмент, но нужно понимать, как он работает под капотом.

Что такое Trunk-based? | PrepBro