«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();
В приведенном выше фрагменте кода мы вручную инициируем и фиксируем транзакцию. Хотя в среде Spring Boot мы можем добиться этого, используя аннотацию @Transactional.
4. Поддержка транзакций в Spring
Если нам нужен более детальный контроль, мы можем использовать 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.