Вызов Transactional методов Spring + Hibernate

У класса есть два метода, оба помеченные аннотацией 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 из контроллера, то есть это будет единственная транзакция на все сохранения, что является ошибкой.

Правильное решение

  • Убираем лишний транзакционный метод сохранения списка из сервиса
  • Выносим проход по списку из сервиса в контроллер
  • Теперь контроллер будет перебирать объекты из списка и вызывать транзакционный метод сервиса для сохранения
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
  }
}

No comments:

Post a Comment

Why BQ28Z610 function Current() returns 0 mA

Fixing 0 mA Current Readings on the BQ28Z610 Device Custom driver for the BQ28Z610 device was connected directly via I2C. It is p...