ПРОКСИФИКАЦИЯ
В вашем сервисе вы создали 2 метода, оба @Transactional
. Когда вы создаете ваш bean-компонент, Spring создаст прокси-сервер, который добавит вам поведение во время выполнения для транзакционного метода. Давайте погрузимся глубже: Этот проксификатор иллюстрируется изображением. Любой звонящий из внешнего мира будет напрямую разговаривать не с вами , а с вашим доверенным лицом. И тогда прокси-сервер позвонит вам, чтобы выполнить код вашего сервиса.
Теперь, это " Любой абонент из внешнего мира не будет напрямую разговаривать с вами " очень важно. Если вы делаете внутренний вызов, как вы делаете в persistEmployee
, который вызывает persistLineManager
, то вы не проходите через прокси. Вы вызываете напрямую свой метод, НЕ ПРОКСИ. Поэтому аннотации в верхней части вашего persistLineManager
метода не читаются.
Итак, когда persistLineManager
выдает RuntimeException
, исключение перехватывается непосредственно вашим вызывающим persistEmployee
, вы go прямо в вашем улове. Поскольку прокси-сервер отсутствует, отката нет, поскольку транзакционный прокси-сервер не перехватил исключение.
Если вы сделаете только это, у вас произойдет откат:
@Transactional(propagation = Propagation.REQUIRED)
public void persistEmployee() {
Employee employee = new Employee("Peter", "Washington DC");
entityManager.persist(employee);
persistLineManager();
// Don't catch and the exception will be catched by the transaction proxy, which will rollback
}
public void persistLineManager() {
Employee lineManager = new Employee("John", "NYC");
entityManager.persist(lineManager);
if(lineManager != null) // intentionally! To trigger rollback
throw new RuntimeException("Rollback!");
}
По умолчанию @Transactional
откат для RuntimeException
ШАГ СДЕЛКИ
Предположим, что вы все еще хотите, чтобы оба метода были транзакционными независимо, и вы можете использовать TransactionTemplate
. Вот пример:
class MyService {
// Have a field of type TransactionTemplate
private TransactionTemplate template;
// In the constructor, Spring will inject the correct bean
public MyService(PlatformTransactionManager transactionManager) {
template = new TransactionTemplate(transactionManager);
// Set this here if you always want this behaviour for your programmatic transaction
template.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
}
// Here you start your first transaction when arriving from the outside
@Transactional(propagation = Propagation.REQUIRED)
public void persistEmployee() {
Employee employee = new Employee("Peter", "Washington DC");
entityManager.persist(employee);
// Inner call
try {
persistLineManager();
} catch (RuntimeException e) {
// Do what you want
}
}
public void persistLineManager() {
// Here, ask to your transactionTemplate to execute your code.
template.execute(status -> {
Employee lineManager = new Employee("John", "NYC");
entityManager.persist(lineManager);
if(lineManager != null) // intentionally! To trigger rollback
throw new RuntimeException("Rollback!");
return null;
});
}
}
Я не все протестировал, вы можете столкнуться с некоторыми ошибками, но я надеюсь, что вы поняли идею.
PROPAGATION
Позвольте мне добавить последнюю часть о разнице между PROPAGATION_REQUIRED и PROPAGATION_REQUIRES_NEW:
PROPAGATION_REQUIRED:
- Либо у меня нет транзакции, затем я создаю одну
- Или выполняется транзакция, и я присоединяюсь к ней.
PROPAGATION_REQUIRES:
- В любых ситуациях, независимо от того, выполняется транзакция или нет, я создаю новую.
Пример:
- Клиент входя в мой транзакционный метод, с PROPAGATION_REQUIRED. Он создает имя транзакции "TA".
- Этот транзакционный метод вызывает метод, который также является транзакционным, но PROPAGATION_REQUIRES_NEW. Он создает вторую транзакцию с именем "TB"
- . Имеет now: "client" -> "TA" -> "TB"
- Но второй метод вызывает откат. В этом случае будет выполнен откат только «TB», так как «TA» и «TB» - это две разные транзакции.
- Итак, в БД я буду сохранять все операции, которые были выполнены в «TA», но не в "ТБ".
Надеюсь, это поможет