Перейти к основному содержимому

Транзакции с Spring и JPA

· 5 мин. чтения

1. Обзор

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

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

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

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 с использованием аннотаций и поддержки пространства имен:

<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="myEmf" />
</bean>
<tx:annotation-driven transaction-manager="txManager" />

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

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

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

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

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

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

5. Возможные ловушки

5.1. Транзакции и прокси

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

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

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

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

courseDao.createWithRuntimeException(course);

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

@Transactional(isolation = Isolation.SERIALIZABLE)

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

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

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

Флаг readOnly обычно вызывает путаницу, особенно при работе с JPA. Из Javadoc:

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

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

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

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

Из нетранзакционного контекста транзакция не будет создана и флаг readOnly будет проигнорирован.

5.4. Регистрация транзакций

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

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

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

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

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

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

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

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