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

Ошибка TransactionRequiredException

Узнайте о причине ошибки TransactionRequiredException и о том, как ее устранить.

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

1. Обзор

В этом уроке мы рассмотрим причину ошибки TransactionRequiredException и способы ее устранения.

2. Исключение TransactionRequiredException

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

Например, попытка обновить запись без транзакции:

Query updateQuery
  = session.createQuery("UPDATE Post p SET p.title = ?1, p.body = ?2 WHERE p.id = ?3");
updateQuery.setParameter(1, title);
updateQuery.setParameter(2, body);
updateQuery.setParameter(3, id);
updateQuery.executeUpdate();

Вызовет исключение с сообщением следующего содержания:

...
javax.persistence.TransactionRequiredException: Executing an update/delete query
  at org.hibernate.query.internal.AbstractProducedQuery.executeUpdate(AbstractProducedQuery.java:1586)
...

3. Предоставление транзакции

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

Transaction txn = session.beginTransaction();
Query updateQuery
  = session.createQuery("UPDATE Post p SET p.title = ?1, p.body = ?2 WHERE p.id = ?3");
updateQuery.setParameter(1, title);
updateQuery.setParameter(2, body);
updateQuery.setParameter(3, id);
updateQuery.executeUpdate();
txn.commit();

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

4. Поддержка транзакций весной

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

Например, предположим, что мы хотим обновить сообщение перед отправкой уведомления по электронной почте:

public void update() {
    entityManager.createQuery("UPDATE Post p SET p.title = ?2, p.body = ?3 WHERE p.id = ?1")
      // parameters
      .executeUpdate();
    sendEmail();
}

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

Таким образом, обновление записи в TransactionTemplate предотвратит этот сценарий, поскольку он немедленно зафиксирует операцию:

public void update() {
    transactionTemplate.execute(transactionStatus -> {
        entityManager.createQuery("UPDATE Post p SET p.title = ?2, p.body = ?3 WHERE p.id = ?1")
          // parameters
          .executeUpdate();
        transactionStatus.flush();
        return null;
    });
    sendEmail();
}

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

В заключение, как правило, рекомендуется заключать операции с базой данных в транзакцию. Это помогает предотвратить повреждение данных. Полный исходный код доступен на Github .