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 .