[Java] @Transactional в Spring под капотом

Автор Сообщение
news_bot ®

Стаж: 6 лет 9 месяцев
Сообщений: 27286

Создавать темы news_bot ® написал(а)
28-Дек-2020 13:30

В этой статье рассматривается как работает аннотация @Transactional в Spring под капотом. При этом в основном рассматривается НЕ реактивный стек. Статья рассматривает код Spring 5.3 и Spring Boot 2.4Оглавление
Где лежит @Transactional и как его добавить в проект?Аннотация @Transactional находится в пакете org.springframework.transaction.annotation, т.е. ее полное имя org.springframework.transaction.annotation.Transactional и является частью Spring Framework с версии 1.2. Не путайте с javax.transaction.Transactional. Собственная аннотация Spring предоставляет более расширенные возможности настройки, которые рассматриваются дальше. Аннотацию javax.transaction.Transactional Spring также поддерживает, но лучше их не смешивать и, если создаете приложение на Spring, использовать родные аннотации.Для того, чтобы добавить в проект пакет, требуется прописать зависимость:
  • для maven
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
  • для gradle
compile group: 'org.springframework', name: 'spring-tx', version: '5.3.2'
Но с приходом Spring Boot так делать уже не требуется - этот пакет добавлен во все требуемые starter-ы как транзитивная зависимость и, когда вы добавляете, например, spring-boot-starter-data-jpa, то spring-tx подтягивается автоматически через spring-data-jpaКто создает инфраструктуру для обработки @Transactional?В те темные времена, когда еще не было Spring Boot, чтобы включить поддержку @Transactional мы добавляли над конфигурацией аннотацию @EnableTransactionManager, которая является также частью пакета org.springframework.transaction.annotation.Эта аннотация имеет следующие настройки:
  • proxyTargetClass(по умолчанию, false) - будет ли прокси создаваться через CGLIB (true) или через interface-based proxies (false). Обратите внимание, что, если поставить true, ВСЕ объекты Spring (beans) будут создаваться через CGLIB, не только те, что помечены @Transactional
  • mode (по умолчанию AdviceMode.PROXY) - как будут применены Advise. Возможные варианты - AdviceMode.PROXY или AdviceMode.ASPECTJ. Если выбрать AspectJ и корректно его настроить, то при компиляции будет сгенерирован код так, что тело метода будет уже обернуто кодом, управляющим транзакцией. Если выбран AdviceMode.PROXY , то будет использован стандартный механизм создания proxy объектов. (пример с AspectJ)
  • order - указывает, когда будет применен advice. По умолчанию, LOWEST_PRECEDENCE - т.е. он будет вызван последним в цепочке advice. Это может быть важно, когда вы добавляете собственные advice, в которых есть работа с базой данных
Что же эта аннотация делает? Посмотрим на нее:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {...}
Она содержит селектор TransactionManagementConfigurationSelector, работа которого будет рассмотрена дальше.С приходом Spring Boot необходимость в аннотации@EnableTransactionManager отпала. Теперь это перешло в ответственность Spring Boot. Как же он это делает?Когда вы добавляете в проект зависимость spring-boot-starter-что-то, то подтягивается транзитивная зависимость - spring-boot-autoconfigure, который содержит в файле spring.factories список авто-конфигураций. Как это работает - можно посмотреть SpingBoot-потрошитель от Борисова. В этом файле есть упоминание конфигурации
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration
которая будет загружена при подъеме контекста. Полный текст для удобства приведен ниже.TransactionAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(PlatformTransactionManager.class)
@AutoConfigureAfter({JtaAutoConfiguration.class, HibernateJpaAutoConfiguration.class,
    DataSourceTransactionManagerAutoConfiguration.class, Neo4jDataAutoConfiguration.class})
@EnableConfigurationProperties(TransactionProperties.class)
public class TransactionAutoConfiguration {
  @Bean
  @ConditionalOnMissingBean
  public TransactionManagerCustomizers platformTransactionManagerCustomizers(
      ObjectProvider<PlatformTransactionManagerCustomizer<?>> customizers) {
    return new TransactionManagerCustomizers(customizers.orderedStream().collect(Collectors.toList()));
  }
  @Bean
  @ConditionalOnMissingBean
  @ConditionalOnSingleCandidate(ReactiveTransactionManager.class)
  public TransactionalOperator transactionalOperator(ReactiveTransactionManager transactionManager) {
    return TransactionalOperator.create(transactionManager);
  }
  @Configuration(proxyBeanMethods = false)
  @ConditionalOnSingleCandidate(PlatformTransactionManager.class)
  public static class TransactionTemplateConfiguration {
    @Bean
    @ConditionalOnMissingBean(TransactionOperations.class)
    public TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager) {
      return new TransactionTemplate(transactionManager);
    }
  }
  @Configuration(proxyBeanMethods = false)
  @ConditionalOnBean(TransactionManager.class)
  @ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class)
  public static class EnableTransactionManagementConfiguration {
    @Configuration(proxyBeanMethods = false)
    @EnableTransactionManagement(proxyTargetClass = false)
    @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",
        matchIfMissing = false)
    public static class JdkDynamicAutoProxyConfiguration {
    }
    @Configuration(proxyBeanMethods = false)
    @EnableTransactionManagement(proxyTargetClass = true)
    @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
        matchIfMissing = true)
    public static class CglibAutoProxyConfiguration {
    }
  }
}
Как работают авто-конфигурации хорошо объяснено в "SpringBoot-потрошитель", здесь упомяну только наиболее интересные моменты.Данная авто-конфигурация будет работать только, если в classpath есть класс PlatformTransactionManager, о работе которого будет написано дальше. Здесь же создается TransactionalOperator, используемый в реактивном стеке. Наиболее важной частью является статический класс EnableTransactionManagementConfigurationОн содержит два подкласса, отличаются они только настройкой
spring.aop.proxy-target-class
Если spring.aop.proxy-target-class = true, то применяется аннотация @EnableTransactionManagement(proxyTargetClass = true)Если spring.aop.proxy-target-class = false, то применяется аннотация EnableTransactionManagement(proxyTargetClass = false)Обратите внимание, что по умолчанию для всех современных spring-boot проектов
spring.aop.proxy-target-class=true
Поэтому будет использован механизм CGLIB для создания proxy. (Советую посмотреть интервью, где Борисов рассказывает про это изменение поведения Spring Boot).Кто обрабатывает @Transactional?В этой части рассмотрим, какая инфраструктура создается для обработки .В прошлой части мы закончили на
@Import(TransactionManagementConfigurationSelector.class)
Посмотрим, что происходит дальше.Здесь используется аннотация @Import. Она обычно используется для обработки следующих трех типов компонентов (component): @Configuration, ImportSelector, ImportBeanDefinitionRegistrar. TransactionManagementConfigurationSelector , который здесь загружается, принадлежит к ImportSelector. При загрузке контекста он, основываясь на настройках EnableTransactionManagement, определяет какие классы будут подгружаться дальше. Ниже приведен часть кода TransactionManagementConfigurationSelectorTransactionManagementConfigurationSelector.
public class TransactionManagementConfigurationSelector
    extends AdviceModeImportSelector<EnableTransactionManagement> {
  @Override
  protected String[] selectImports(AdviceMode adviceMode) {
    switch (adviceMode) {
      case PROXY:
        return new String[]{AutoProxyRegistrar.class.getName(),
            ProxyTransactionManagementConfiguration.class.getName()};
      case ASPECTJ:
        return new String[]{determineTransactionAspectClass()};
      default:
        return null;
    }
  }
.....
Здесь используются следующие классы для конфигураций:AutoProxyRegistrar - класс для регистрации средств создания бинов, ProxyTransactionManagementConfiguration - класс для настройки ProxyTransactionManagement. Посмотрим, что внутри ProxyTransactionManagementConfiguration. (Для удобство код приведен ниже. Хотя форматирование его сильно побило)ProxyTransactionManagementConfiguration
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyTransactionManagementConfiguration
    extends AbstractTransactionManagementConfiguration {
  @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
  @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
  public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
      TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {
    BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
    advisor.setTransactionAttributeSource(transactionAttributeSource);
    advisor.setAdvice(transactionInterceptor);
    if (this.enableTx != null) {
      advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
    }
    return advisor;
  }
  @Bean
  @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
  public TransactionAttributeSource transactionAttributeSource() {
    return new AnnotationTransactionAttributeSource();
  }
  @Bean
  @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
  public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
    TransactionInterceptor interceptor = new TransactionInterceptor();
    interceptor.setTransactionAttributeSource(transactionAttributeSource);
    if (this.txManager != null) {
      interceptor.setTransactionManager(this.txManager);
    }
    return interceptor;
  }
}
Первым создается bean transactionAttributeSource из класса AnnotationTransactionAttributeSource. Он реализует интерфейс TransactionAttributeSource.
public interface TransactionAttributeSource {
  default boolean isCandidateClass(Class<?> targetClass) {
    return true;
  }
  @Nullable
  TransactionAttribute getTransactionAttribute(Method method,
                                               @Nullable Class<?> targetClass);
}
Этот bean применяется для проверки: используется ли где-то @Transactional и для получения метаданных (например, propagation) для аннотированных методов или классов.Дальше создается TransactionInterceptor, внутри которого и будет происходить магия транзакции, которая рассматривается дальше.И наконец BeanFactoryTransactionAttributeSourceAdvisor, внутри которого помещается TransactionInterceptor. BeanFactoryTransactionAttributeSourceAdvisor - это обычный PointcutAdvisor (который объединил в одну сущность advisor and pointcut. Пример использования можно посмотреть здесь или здесь, или в видео "Spring patterns для взрослых".Если вкратце описать принцип их действия, то pointcut определяет, какие классы и методы в них будут проксированы, а advice - какой код будет выполняться.В нашем случае в конце концов в качестве pointcut будет использован TransactionAttributeSource, а в качестве advice - TransactionInterceptor.Возникает вопрос - а где всеми любимый BeanPostProcessor, про которые нам так часто все говорили?Здесь на себя всю работу берет InfrastructureAdvisorAutoProxyCreator, который как раз и является BeanPostProcessor.
Иерархия наследования для класса InfrastructureAdvisorAutoProxyCreatorОбработка @Transactional выполняется по обычным правилам Spring и никакой особой магии здесь нет. Примерная схема работы (взята из официальной документации)
Примерная схема работы @TransactionalКратко о том как работает proxy или самый популярный вопрос на собеседованииВо многих фирмах, где проходил собеседование задавали следующий вопрос:Дан сервис
@Service
public class ServiceTest{
  @Transactional
  public void test1{
      test2();
  }
  @Transactional(propagation = Propagation.REQUIRES_NEW)
  public void test2(){
  }
}
Будет ли при вызове test2 из метода test1 создана новая транзакция?Самый грамотный и полный ответ, который я встречал, был уже приведен в этой статье на habr. В этой же статье рассмотрим только классический и самый простой случай для лучшего понимания, что происходит дальше.Под капотом наш bean будет иметь примерно следующий вид
public class Proxy{
  private ServiceTest targetService;
  public void test1{
    //код начала транзакции
    // ...
      targetService.test2();
    //код конца транзакции
    // ...
  }
  public void test2(){
    //код начала транзакции
    // ...
      targetService.test2();
    //код конца транзакции
    // ...
  }
}
Это прекрасно иллюстрирует этот рисунок
взято с https://www.javainuse.com/spring/spring-boot-aopИ отсюда следует, что при вызове метода test1 код, управляющий транзакциями, вызван не будет и новая транзакция не откроется. Аналогичное поведение - когда вызывается метод родительского класса (реальный случай в одном из проектов).Как обрабатываются @TransactionalПрежде чем перейти к рассмотрению порядка обработки, давайте посмотрим, какие настройки предоставляет нам эта аннотация. Здесь рассмотрена только часть из них. Остальные можно посмотреть в этой статье на habr или в документацииНаиболее интересные настройка @Transactional
  • propagation - способ "распространения" транзакций
Выделяется следующие способы:MANDATORY - если есть текущая активная транзакция - выполняется в ней, иначе выбрасывается исключениеNESTED - выполняется внутри вложенной транзакции, если есть активная, если нет активной - то аналогично REQUIREDNEVER - выполняется вне транзакции, если есть активная - выбрасывается исключениеNOT_SUPPORTED - выполняется вне транзакции - если есть активная, она приостанавливаетсяREQUIRED - (значение по умолчанию) - если есть активная, то выполняется в ней, если нет, то создается новая транзакцияREQUIRES_NEW- всегда создается новая транзакция, если есть активная - то она приостанавливаетсяSUPPORTS - если есть активная - то выполняется в ней, если нет - то выполняется не транзакционно
  • Правила управления откатом
noRollbackFor и noRollbackForClassName - определяет исключения, при которых транзакция НЕ будет откатанаrollbackFor и rollbackForClassName - определяет исключения, при которых транзакция БУДЕТ откатанаПеред тем как перейти к TransactionInterceptor, давайте вспомним, как мы работали с транзакциями до Spring.Код взят из статьи
Connection connection = DriverManager.getConnection(...);
try {
  connection.setAutoCommit(false);
  PreparedStatement firstStatement = connection.prepareStatement(...);
  firstStatement.executeUpdate();
  PreparedStatement secondStatement = connection.prepareStatement(...);
  secondStatement.executeUpdate();
  connection.commit();
} catch (Exception e) {
  connection.rollback();
}
Порядок работы такой:
  • создаем соединение - DriverManager.getConnection(...)
  • выполняем необходимые запросы
  • если не было ошибок - выполняем commit (connection.commit())
  • Если все-таки была ошибка - откатываем изменения
Вернемся к TransactionInterceptor. Основным методом является - invoke, который делегирует работу родительскому методу invokeWithinTransaction класса TransactionalAspectSupportСокращенный код приведен ниже
protected Object invokeWithinTransaction(Method method,
                                         Class<?> targetClass,
                                         final InvocationCallback invocation)
  // получаем TransactionManager tm и TransactionAttribute txAttr
  // ...
  if (this.reactiveAdapterRegistry != null &&
      tm instanceof ReactiveTransactionManager) {
    //код для работы с реактивным стэком
    // ...
  }
PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
final String joinpointIdentification =
  methodIdentification(method, targetClass, txAttr);
if (txAttr == null ||
    !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
  // начинаем транзакцию, если нужно
  TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr,
                                                        joinpointIdentification);
  Object retVal;
  try {
    // выполняем работу внутри транзакции
    retVal = invocation.proceedWithInvocation();
  } catch (Throwable ex) {
    // откатываемся, если нужно
    completeTransactionAfterThrowing(txInfo, ex);
    throw ex;
  } finally {
    // чистим ThreadLocal переменные
    cleanupTransactionInfo(txInfo);
  }
  if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
    //код для библиотеки vavr
    // ...
  }
  // выполняем commit, если не было ошибок
  commitTransactionAfterReturning(txInfo);
  return retVal;
} else {
  // код для WebSphere
  // ...
}
}
Если посмотреть внимательно, то этот код повторяет предыдущую логику. Ничего нового:
  • получаем соединение/транзакцию - createTransactionIfNecessary
  • выполняем необходимые запросы - invocation.proceedWithInvocation
  • если не было ошибок - выполняем commitTransactionAfterReturning
  • Если все-таки была ошибка - откатываем изменения - completeTransactionAfterThrowing
Рассмотрим внимательно каждую часть. Получение транзакции
protected TransactionInfo createTransactionIfNecessary
  (PlatformTransactionManager tm, TransactionAttribute txAttr,
   final String joinpointIdentification) {
  if (txAttr != null && txAttr.getName() == null) {
    txAttr = new DelegatingTransactionAttribute(txAttr) {
      @Override
      public String getName() {
        return joinpointIdentification;
      }
    };
  }
  TransactionStatus status = null;
  if (txAttr != null) {
    if (tm != null) {
      status = tm.getTransaction(txAttr);
    }
  }
  return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
Из важных частей здесь можно отметить только вызов transactionalManager.getTransaction(...)(работа с TransactionalManager будет описана позже) и вызов prepareTransactionInfo , в котором выполняется txInfo.bindToThread(); этот метод устанавливает TransactionInfo в ThreadLocal переменную transactionInfoHolder, из которой теперь всегда можно получить статус транзакции через статический метод currentTransactionStatusУправление откатом изменений
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
  if (txInfo != null && txInfo.getTransactionStatus() != null) {
    if (txInfo.transactionAttribute != null &&
        txInfo.transactionAttribute.rollbackOn(ex)) {
      try {
        txInfo.getTransactionManager()
          .rollback(txInfo.getTransactionStatus());
      } catch (Exception ex) {
        // ...
      }
    } else {
      try {
        txInfo.getTransactionManager()
          .commit(txInfo.getTransactionStatus());
      } catch (Exception ex) {
        // ...
      }
    }
  }
}
Если транзакция активна, то проверяем, надо ли ее откатывать при этой ошибке, если не надо, то выполняем фиксацию транзакции commit(txInfo.getTransactionStatus()) Проверка исключений выполняется в txInfo.transactionAttribute.rollbackOn(ex) . Выигрывает наиболее близкое по иерархии исключений требование. Фиксация транзакции
protected void commitTransactionAfterReturning(TransactionInfo txInfo) {
  if (txInfo != null &&
      txInfo.getTransactionStatus() != null) {
    txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
  }
}
Просто выполняется фиксация, если транзакция активна.TransactionManager - что это такое?На данный момент TransactionManager, маркировочный интерфейс, не содержащий никаких методов. Его наследуют ReactiveTransactionManager и PlatformTransactionManagerРассмотрим внимательней последний.
public interface PlatformTransactionManager extends TransactionManager {
  TransactionStatus getTransaction(TransactionDefinition definition)
    throws TransactionException;
  void commit(TransactionStatus status) throws TransactionException;
  void rollback(TransactionStatus status) throws TransactionException;
}
Интерфейс содержит всего 3 метода - создание транзакции, commit и rollback.Spring предоставляет абстрактный класс AbstractPlatformTransactionManager, который реализовывает требования по propagation. Наследники этого класса по разному реализуют его абстрактные методы, так как это довольно сильно зависит от используемой технологии. Стоит отметить, что в своей работе AbstractPlatformTransactionManager и его подклассы активно используют org.springframework.transaction.support.TransactionSynchronizationManage для синхронизации и хранения метаинформации, включая connection. Хранение информации осуществляется в наборе статических ThreadLocal переменных.
private static final ThreadLocal<Map<Object, Object>> resources =
  new NamedThreadLocal<>("Transactional resources");
private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
  new NamedThreadLocal<>("Transaction synchronizations");
private static final ThreadLocal<String> currentTransactionName =
  new NamedThreadLocal<>("Current transaction name");
private static final ThreadLocal<Boolean> currentTransactionReadOnly =
  new NamedThreadLocal<>("Current transaction read-only status");
private static final ThreadLocal<Integer> currentTransactionIsolationLevel =
  new NamedThreadLocal<>("Current transaction isolation level");
private static final ThreadLocal<Boolean> actualTransactionActive =
  new NamedThreadLocal<>("Actual transaction active");
Как выводить логи транзакций?Spring выполняет логирование практически всех действий с транзакциями: для этого надо включить уровень логирования DEBUG для пакета org.springframework.transaction , и если используете hibernate, - org.hibernate.transaction
logging:
   level:
      org.springframework.orm.jpa: DEBUG
      org.springframework.transaction: DEBUG
Также можно узнать, что происходит с транзакциями программно, обращаясь к ThreadLocal переменным, которые мы рассмотрели, например,
TransactionSynchronizationManager.isActualTransactionActive();
TransactionAspectSupport.currentTransactionStatus();
Императивная работа с транзакциями (через TransactionTemplate)Кроме управления транзакциями через аннотации, Spring предоставляет возможность императивного управления транзакциями. Для этого Spring, как и во многих других случаях, использует шаблон "Template" (JdbcTemplate, RestTemplate). Используется класс TransactionTemplate. Bean продекларирован в TransactionAutoConfiguration. Основным методом является метод execute
public <T> T execute(TransactionCallback<T> action) throws TransactionException {
  if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
    return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
  } else {
    TransactionStatus status = this.transactionManager.getTransaction(this);
    T result;
    try {
      result = action.doInTransaction(status);
    } catch (RuntimeException | Error ex) {
      rollbackOnException(status, ex);
      throw ex;
    } catch (Throwable ex) {
      rollbackOnException(status, ex);
      throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
    }
    this.transactionManager.commit(status);
    return result;
  }
}
который повторяет стандартный механизм rollback/commit:
  • получаем транзакцию (getTransaction)
  • выполняем действие (doInTransaction)
  • если была ошибка, откатываемся (rollbackOnException)
  • если все хорошо, то фиксируем транзакцию (commit)
Интересна строчка начала транзакции
this.transactionManager.getTransaction(this);
Так как transactionManager является одновременно и TransactionDefinition (который мы настраиваем перед вызовом execute), то он передает самого себя в transactionManager, поэтому для транзакции используются те параметры, которые были переданы в transactionManager. Так же можно заметить, что в метод выполнения передается TrsansactionStatus, поэтому во время выполнения вы можете вручную указать, когда нужно откатывать, не пробрасывая исключение
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
  protected void doInTransactionWithoutResult(TransactionStatus status) {
    try {
      updateOperation1();
      updateOperation2();
    } catch (SomeBusinessExeption ex) {
      status.setRollbackOnly();
    }
  }
});
Обработка ошибок в HibernateTransactionManagerПри работе с hibernate, даже если вы поймали ошибку и обработали ее, вы не сможете уже зафиксировать транзакцию, так как она будет помечаться как rollbackOnly (в отличие от работы с JdbcTemplate, например). Как это работает и почему?Начнем с почему? - При работе с JPA вы не управляете последовательностью выполнения запросов и поэтому, если у вас произошла ошибка, hibernate не может восстановить правильный контекст и единственное, что ему остается, - пометить транзакцию как rollbackOnlyКак это работает? Если hibernate ловит ошибку, внутри себя он вызывает
TransactionDriverControl().markRollbackOnly()
который ставит флаг - rollbackOnly = true, даже если вы поймали эту ошибку и обработали. По логике Spring, если нет исключения, то вызывается PlatformTransactionManager.commitПри вызове PlatformTransactionManager.commit получаем статус транзакции, который внутри хранит флаг:
unexpectedRollback = status.isGlobalRollbackOnly(); //true
doCommit(status); //вызывается все равно
if (unexpectedRollback) { //кидаем исключение
  throw new UnexpectedRollbackException(
    "Transaction silently rolled back because it has been marked as rollback-only");
}
doCommit все равно вызывается и мы могли бы ожидать, что хоть что-то зафиксируется, но hibernate не имеет теперь консистентных данных, поэтому внутри hibernate есть такой код
@Override
public void commit() {
  try {
    //хоть метод и называется commit, но включает в себя логику отката
    if ( rollbackOnly ) {
      try {
        rollback();
        //...
        return;
      }
      catch (RollbackException e) {
        throw e;
      }
      catch (RuntimeException e) {
        throw e;
      }
    }
    JdbcResourceLocalTransactionCoordinatorImpl.this.beforeCompletionCallback();
    jdbcResourceTransaction.commit();
    // ...
То есть ответственность за откат здесь переходит на сторону Hibernate, а не Spring, хотя Spring и не вызывает PlatformTransactionManager.rollbackИспользованные материалыРаскрытие секретов о Spring Boot от БорисоваПример работы c AspectJSpringBoot-потрошительSpring referenceПример использования PointcutЕще один пример использования PointcutSpring patterns для взрослыхSpring AOP. Маленький вопросик с собеседованияЭффективное управление транзакциями в SpringСправка по @TransactionalIntroducation to Transactional in Java and Spring
===========
Источник:
habr.com
===========

Похожие новости: Теги для поиска: #_java, #_java, #_spring, #_transactions, #_java
Профиль  ЛС 
Показать сообщения:     

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы

Текущее время: 23-Ноя 01:49
Часовой пояс: UTC + 5