Рубрики
Без рубрики

Сделки с Spring и JPA

Как лучше всего настроить транзакции в Spring – конфигурация, распространение транзакций и какой уровень должен иметь транзакционную семантику.

Автор оригинала: Eugen Paraschiv.

1. Обзор

В этом уроке мы обсудим правильный способ настройки транзакций Spring , как использовать аннотацию @Transactional и общие подводные камни.

Для более подробного обсуждения конфигурации сохранения ядра ознакомьтесь с учебником Spring with JPA .

В принципе, существует два различных способа настройки транзакций: аннотации и AOP, каждый из которых имеет свои преимущества. Здесь мы обсудим более распространенную конфигурацию аннотаций.

Дальнейшее чтение:

Настройка отдельного источника данных Spring для тестов

Краткое руководство по загрузке исходных данных с помощью Spring Boot

Показать инструкции Hibernate/JPA SQL из Spring Boot

2. Настройка транзакций

Spring 3.1 вводит @EnableTransactionManagement аннотацию|/, которую мы можем использовать в классе @Configuration для включения поддержки транзакций:

@Configuration
@EnableTransactionManagement
public class PersistenceJPAConfig{

   @Bean
   public LocalContainerEntityManagerFactoryBean
     entityManagerFactoryBean(){
      //...
   }

   @Bean
   public PlatformTransactionManager transactionManager(){
      JpaTransactionManager transactionManager
        = new JpaTransactionManager();
      transactionManager.setEntityManagerFactory(
        entityManagerFactoryBean().getObject() );
      return transactionManager;
   }
}

Однако если мы используем проект Spring Boot и имеем зависимости spring-data-* или spring-tx от пути к классу, то управление транзакциями будет включено по умолчанию .

3. Настройка Транзакций С Помощью XML

Для версий до 3.1 или если Java не является опцией, вот конфигурация XML с использованием аннотаций и поддержка пространства имен:


   

4. Аннотация @Transactional

С настроенными транзакциями теперь мы можем аннотировать компонент с помощью @Transactional либо на уровне класса, либо на уровне метода:

@Service
@Transactional
public class FooService {
    //...
}

Аннотация также поддерживает дальнейшую конфигурацию :

  • тип Распространения транзакции
  • Уровень изоляции транзакции
  • a Тайм-аут для операции, обернутой транзакцией
  • флаг Только для чтения – подсказка поставщику персистентности о том, что транзакция должна быть доступна только для чтения
  • правила Отката для транзакции

Обратите внимание, что по умолчанию откат происходит только для непроверенных исключений во время выполнения. Проверенное исключение не вызывает отката транзакции. Конечно, мы можем настроить это поведение с помощью параметров rollbackFor и noRollbackFor аннотации.

5. Потенциальные подводные камни

5.1. Транзакции и Прокси-серверы

На высоком уровне Spring создает прокси-серверы для всех классов, аннотированных с помощью @Transactional , либо в классе, либо в любом из методов. Прокси-сервер позволяет фреймворку вводить логику транзакций до и после запущенного метода, в основном для запуска и фиксации транзакции.

Важно иметь в виду, что, если транзакционный компонент реализует интерфейс, по умолчанию прокси-сервер будет динамическим прокси-сервером Java. Это означает, что будут перехвачены только внешние вызовы методов, поступающие через прокси-сервер. Любые вызовы самопроизвольного вызова не запускают транзакцию, даже если метод имеет аннотацию @Transactional .

Еще одно предостережение при использовании прокси-серверов заключается в том, что только общедоступные методы должны быть аннотированы с помощью @Transactional. Методы любой другой видимости будут просто игнорировать аннотацию молча, поскольку они не проксируются.

В этой статье подробно рассматриваются дальнейшие подводные камни проксирования здесь.

5.2. Изменение уровня изоляции

courseDao.createWithRuntimeException(course);

Мы также можем изменить уровень изоляции транзакций:

@Transactional(isolation = Isolation.SERIALIZABLE)

Обратите внимание, что это на самом деле было введено весной 4.1; если мы запустим приведенный выше пример до весны 4.1, это приведет к:

org.springframework.транзакция.InvalidIsolationLevelException : Стандартный JPA не поддерживает пользовательские уровни изоляции – используйте специальный JpaDialect для реализации JPA

5.3. Транзакции Только для чтения

Флаг Только для чтения обычно создает путаницу, особенно при работе с JPA. Из Javadoc:

Это просто служит подсказкой для реальной подсистемы транзакций; это не обязательно приведет к сбою попыток доступа на запись. Менеджер транзакций, который не может интерпретировать подсказку только для чтения, не выдаст исключение при запросе транзакции только для чтения.

Дело в том, что мы не можем быть уверены, что вставка или обновление не произойдет, когда установлен флаг только для чтения . Это поведение зависит от поставщика, в то время как JPA не зависит от поставщика.

Также важно понимать, что флаг только для чтения релевантен только внутри транзакции. Если операция происходит вне контекста транзакции, флаг просто игнорируется. Простым примером этого может быть вызов метода с аннотацией:

@Transactional( propagation = Propagation.SUPPORTS,readOnly = true )

В нетранзакционном контексте транзакция не будет создана, и флаг Только для чтения будет проигнорирован.

5.4. Ведение журнала транзакций

Полезным методом для понимания проблем, связанных с транзакциями, является точная настройка ведения журнала в транзакционных пакетах. Соответствующий пакет в Spring – это ” org.springframework.transaction” , который должен быть настроен с уровнем ведения журнала TRACE.

5.5. Откат транзакции

Аннотация @Transactional – это метаданные, которые определяют семантику транзакций в методе. У нас есть два способа отката транзакции: декларативный и программный.

В декларативном подходе |/мы аннотируем методы с помощью @ Транзакционной аннотации . Аннотация @Transactional использует атрибуты rollbackFor или rollbackForClassName для отката транзакций, а атрибуты noRollbackFor или noRollbackForClassName для предотвращения отката при перечисленных исключениях.

Поведение отката по умолчанию в декларативном подходе будет откатываться при исключениях среды выполнения.

Давайте рассмотрим простой пример использования декларативного подхода для отката транзакции для исключений или ошибок во время выполнения:

@Transactional
public void createCourseDeclarativeWithRuntimeException(Course course) {
    courseDao.create(course);
    throw new DataIntegrityViolationException("Throwing exception for demoing Rollback!!!");
}

Далее мы будем использовать декларативный подход для отката транзакции для перечисленных проверенных исключений. | Откат в нашем примере есть on SQLException :

@Transactional(rollbackFor = { SQLException.class })
public void createCourseDeclarativeWithCheckedException(Course course) throws SQLException {
    courseDao.create(course);
    throw new SQLException("Throwing exception for demoing rollback");
}

Давайте рассмотрим простое использование атрибута noRollbackFor в декларативном подходе для предотвращения отката транзакции для указанного исключения:

@Transactional(noRollbackFor = { SQLException.class })
public void createCourseDeclarativeWithNoRollBack(Course course) throws SQLException {
    courseDao.create(course);
    throw new SQLException("Throwing exception for demoing rollback");
}

В программном подходе мы откатываем транзакции с помощью TransactionAspectSupport :

public void createCourseDefaultRatingProgramatic(Course course) {
    try {
       courseDao.create(course);
    } catch (Exception e) {
       TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}

6. Заключение

В этой статье мы рассмотрели базовую конфигурацию транзакционной семантики с использованием как Java, так и XML. Мы также узнали, как использовать @Transactional, и лучшие практики транзакционной стратегии.

Как всегда, код, представленный в этой статье, доступен на Github .