Клас має два методи, обидва позначені анотацією Transactional, що відбувається коли один з цих методів викликає другий?
public class MyServiceImpl {
@Transactional
public void method1() {
//do something
method2();
}
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void method2() {
//do something
}
}
«В связи с тем, что для поддержки транзакций через аннотации используется Spring AOP, в момент вызова method1() на самом деле вызывается метод прокси объекта. Создается новая транзакция и далее происходит вызов method1() класса y ServiceImpl. А когда из method1() вызовем method2(), обращения к прокси нет, вызывается уже сразу метод нашего класса и, соответственно, никаких новых транзакций создаваться не будет».
Джерело: https://habr.com/ru/post/347752/
Розглянемо живий приклад, коли може виникнути подібна помилка
- Є контролер, який отримує з деякого джерела список об'єктів, які потрібно зберегти до БД.
- Є сервіс, який за допомогою DAO зберігає об'єкти.
- Задача: збереження кожного окремого об'єкта повинно відбуватися в окремій транзакції.
Неправильне рішення
public class EntityService {
//...
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void persistEntity(Entity entity) {
entityDao.persist(entity);
}
@Transactional
public void persistEntityList(List<Entity> entityList) {
for(Entity enity : entityList) persistEntity(enity);
}
}
public class EntityController() {
public void persistAll() {
//...
entityService.persistEntityList(entityList);
}
}
В даному випадку, буде створена лише одна транзакція — під час виклику метода сервісу persistEntityList з контролера, тобто це буде єдина транзакція на усі збереження, ще не відповідає умовам задачі.
Правильне рішення
- Прибираємо з сервісу зайвий transactional-метод збереження списку.
- Виносимо прохід за списком з сервісу в контролер.
- Тепер контролер буде перебирати об'єкти зі списку та викликати Transactional-метод сервиса для збереження кожного об'єкта.
public class EntityService {
//...
@Transactional
public void persistEntity(Entity entity) {
entityDao.persist(entity);
}
}
public class EntityController() {
public void persistAll() {
//...
for(Entity enity : entityList) entityService.persistEntity(enity);
// or: entityList.forEach(entityService::persist); // Java 8 style
}
}