
本文详解 wildfly 25 环境下 `@transactional` 注解未触发 jta 事务、导致 `wflyjpa0060: transaction is required` 异常的根本原因,重点揭示被忽略的验证异常(如 `validationexception`)如何静默污染事务状态,并提供可落地的诊断与修复方案。
在 WildFly 等 Java EE/Jakarta EE 应用服务器中,@Transactional 是声明式事务管理的核心机制——它依赖容器自动开启、提交或回滚 JTA 事务。然而,一个常见却极易被忽视的现象是:方法明明标注了 @Transactional,EntityManager 却抛出 WFLYJPA0060: Transaction is required 异常,提示“当前无活跃事务”。这并非配置错误或注解遗漏,而往往源于事务生命周期中未被捕获的“静默失败”。
根本原因在于:事务一旦进入 MarkedRollbackOnly 状态,后续所有需事务的操作(如 em.persist()、em.flush())均会立即失败,且该状态不可逆。而触发该状态的,不一定是显式的 throw new RuntimeException(),更可能是被吞掉的 javax.validation.ValidationException。
在本案例中,问题代码的关键线索藏于实体类字段:
public class DocumentImportLog {
@NotNull // ← 问题根源在此!
private BARCODE_TYPE barcode; // 枚举类型,但输入值不匹配预定义项
// ...
}当 importLogRepo.forcePersist(entry) 执行时,Hibernate 在 flush 阶段执行 Bean Validation,发现 barcode 为 null 或非法值,抛出 ValidationException。若该异常未被上层捕获并处理(例如仅 catch (Exception e) 而未显式处理 ValidationException),容器将默认标记当前事务为 ROLLBACK_ONLY。此后,即使后续方法仍在同一事务上下文中调用,任何 EntityManager 操作都会因事务已失效而报错。
✅ 正确做法不是移除校验,而是显式处理验证异常并控制事务边界:
@Transactional
public void readInputFolder(Path inputFolder) throws IOException {
try {
// ...业务逻辑...
importLogRepo.forcePersist(entry);
} catch (ValidationException ve) {
// 记录警告,跳过非法条目,不破坏事务
log.warn("Skip invalid entry due to validation failure: {}", ve.getMessage());
return; // 继续处理下一个文件,事务保持活跃
}
}⚠️ 注意事项:
- 不要全局 catch (Exception e) 后静默吞掉 ValidationException:这是最隐蔽的事务杀手;
- @NotNull 等 Bean Validation 注解在 flush()/persist() 时由 JPA 提供者(如 Hibernate)触发,属于事务内核行为;
- WildFly 的 JTA 实现对事务状态极其严格:一旦标记 ROLLBACK_ONLY,同一线程内所有后续 JPA 操作均拒绝执行;
- 若需“容忍性导入”,应将校验逻辑前置(如 Validator.validate())并在事务外处理异常,而非依赖事务内抛出后捕获;
- 确保 persistence.xml 中
正确指向 JTA 数据源(本例已满足),且 EntityManager 为容器注入的 JTA 类型(即 @PersistenceContext,非 @PersistenceUnit)。
总结:@Transactional 失效 rarely 源于配置,而多源于事务内部未受控的异常流。排查时请始终检查日志中是否出现 ValidationException、ConstraintViolationException 等非业务异常;启用 WildFly 的 JTA 日志(logging.category."org.jboss.jts".level = DEBUG)可直观观察事务状态变迁。真正的事务健壮性,始于对每一处异常路径的明确契约设计。










