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. Поддержка транзакций весной
Если нам нужен более детальный контроль, мы можем использовать TransactionTemplate
Spring . Потому что это позволяет программисту запускать персистентность объекта непосредственно перед тем, как приступить к выполнению кода метода.
Например, предположим, что мы хотим обновить сообщение перед отправкой уведомления по электронной почте:
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 .