У меня есть проект SpringBoot 2, и я использую jpa данных Spring с гибернацией с MySQL5.7
У меня проблемы со следующим вариантом использования: у меня есть метод службы, который вызывает метод другой службы. Если второй метод службы генерирует исключение во время выполнения, первый метод также помечается как откат, и я больше не могу фиксировать что-либо. Я хотел бы откатить только второй метод и все же что-то зафиксировать в первом.
Я попытался использовать пропаганду.NESTED, но вложенные транзакции не разрешены в спящем режиме (даже если jpaTransactionManager поддерживает их, а MySQL поддерживает точки сохранения).
Как я могу решить эту проблему? Могу ли я каким-то образом настроить вложенность?
Помните, что мне нужен второй метод, чтобы увидеть изменения, зафиксированные первым, поэтому я не могу пометить второй метод как распространение.
Вот пример кода, чтобы прояснить мою проблему:
FirstServiceImpl.java
@Service
public class FirstServiceImpl implements FirstService
@Autowired
SecondService secondService;
@Autowired
FirstServiceRepository firstServiceRepository;
@Transactional
public void firstServiceMethod() {
//do something
...
FirstEntity firstEntity = firstServiceRepository.findByXXX();
firstEntity.setStatus(0);
firstServiceRepository.saveAndFlush(firstEntity);
...
boolean runtimeExceptionHappened = secondService.secondServiceMethod();
if (runtimeExceptionHappened) {
firstEntity.setStatus(1);
firstServiceRepository.save();
} else {
firstEntity.setStatus(2);
firstServiceRepository.save();
}
}
SecondServiceImpl.java
@Service
public class SecondServiceImpl implements SecondService
@Transactional
public boolean secondServiceMethod() {
boolean runtimeExceptionHappened = false;
try {
//do something that saves to db but that may throw a runtime exception
...
} catch (Exception ex) {
runtimeExceptionHappened = true;
}
return runtimeExceptionHappened;
}
Итак, проблема в том, что когда secondServiceMethod () вызывает исключение времени выполнения, он откатывает свои операции (и это нормально), а затем устанавливает для своей возвращаемой переменной runtimeExceptionHappened значение false, но тогда firstServiceMethod помечается только как откат, а затем
firstEntity.setStatus(1);
firstServiceRepository.save();
не совершено.
Поскольку я не могу использовать NESTED-размножение, как я могу достичь своей цели?
@WilderValera, как я писал: «... Пожалуйста, помните, мне нужен второй метод, чтобы увидеть изменения, зафиксированные первым, поэтому я не могу пометить второй метод как распространение.REQUIRES_NEW ...». REQUIRES_NEW на secondServiceMethod не увидит изменений firstServiceMethod (я отредактировал образец кода, чтобы подчеркнуть это)
Использование REQUERIED_NEW в secondService должно работать. Я посмотрел на ваш код, и кажется, что вы больше не можете фиксировать из-за saveAndFlush (firstEntity), это может пометить ваш первый tx как совершенный.
REQUIRES_NEW запускает новую транзакцию, поэтому, если первая фиксация не видна во второй, поскольку первая еще не завершена




Я бы посоветовал вам разбить их на две отдельные транзакции.
В первой транзакции выполните всю работу в firstServiceMethod, которую вы знаете, что хотите зафиксировать. (например, через saveAndFlush). Теперь, когда вы выходите из этого метода, изменения фиксируются, поэтому они будут доступны для последующих вызовов.
Затем пусть то, что называется firstServiceMethod, вызовет новый метод транзакции setFirstEntityStatus(), который вызывает secondServiceMethod и устанавливает соответствующий статус объекта.
По сути, вместо того, чтобы пытаться выполнить NEST транзакции, разделите их на две полностью отдельные транзакции и используйте упорядочение, чтобы гарантировать, что результат 1-й будет доступен для 2-й.
Хорошо, но это звучит как обходной путь и заставляет меня переместить второй вызов службы на контроллере, даже если контроллер может не знать подробных реализаций службы.
Это обходной путь ... обходной путь для того факта, что вложенные транзакции не поддерживаются :-)
Я надеялся, что есть способ получше, но похоже, что это единственно возможное решение. Я приму ваш ответ, даже если мне не нравится извлекать логику из службы.
Используйте REQUIRED_NEW