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)
...

简单来说:你想动数据库,但没跟 Spring 或 Hibernate 打声招呼说你要开事务。

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

比如你有一个业务场景:先更新一篇文章,然后发送邮件通知。如果直接用 @Transactional 包裹整个方法:

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. 总结

踩坑点 建议
忘记加事务就执行 update/delete 操作 ❌ 直接报错
使用 @Transactional 控制粒度不够 ⚠️ 可能导致副作用提前执行
手动控制事务太麻烦 ✅ 用 TransactionTemplate 更灵活

总的来说,任何涉及写操作的数据库访问都应该明确地包裹在事务中,这不仅是规范,更是防止数据不一致和脏写的最佳实践。


📌 本文适用于有一定经验的 Java 开发者,如果你对 JPA、Hibernate 和 Spring 的事务机制还不熟悉,建议先补补课再来看这篇文章。


原始标题:TransactionRequiredException Error | Baeldung