You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

@Transactional方法执行后出现PSQLException(语句已关闭)问题问询

PostgreSQL PSQLException: "This statement has been closed" 异常分析

问题场景

  • 抛出org.postgresql.util.PSQLException,提示内容:This statement has been closed.
  • 仅在APM监控中捕获到该异常,所有HTTP请求均返回200成功状态码
  • 异常发生在@Transactional方法执行后,方法内执行了foo.getBars().clear();和foo.getBars().addAll(bars);操作
  • Bars数据已成功插入数据库

实体映射配置

@Fetch(FetchMode.SUBSELECT)
@OneToMany(
    cascade = {CascadeType.ALL},
    fetch = FetchType.EAGER,
    orphanRemoval = true)
@JoinColumn(name = "foo_id", nullable = false)
private List<Bars> bars= new ArrayList<>();

排查信息

  • 被关闭的是Bars的insert语句
  • 异常已被框架正确处理,调试日志显示:Exception clearing maxRows/queryTimeout ...
  • 堆栈信息:
org.postgresql.jdbc.PgStatement.checkClosed(PgStatement.java:745)
org.postgresql.jdbc.PgStatement.getMaxRows(PgStatement.java:548)
com.zaxxer.hikari.pool.HikariProxyPreparedStatement.getMaxRows(HikariProxyPreparedStatement.java)
org.hibernate.resource.jdbc.internal.ResourceRegistryStandardImpl.close(ResourceRegistryStandardImpl.java:193)
org.hibernate.resource.jdbc.internal.ResourceRegistryStandardImpl.release(ResourceRegistryStandardImpl.java:108)
org.hibernate.engine.jdbc.mutation.internal.PreparedStatementDetailsStandard.releaseStatement(PreparedStatementDetailsStandard.java:69)
org.hibernate.engine.jdbc.mutation.internal.MutationExecutorPostInsertSingleTable.release(MutationExecutorPostInsertSingleTable.java:115)
org.hibernate.persister.entity.mutation.InsertCoordinator.doStaticInserts(InsertCoordinator.java:186)
org.hibernate.persister.entity.mutation.InsertCoordinator.coordinateInsert(InsertCoordinator.java:111)
org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2779)
org.hibernate.action.internal.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:81)
org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:676)
org.hibernate.engine.spi.ActionQueue.addResolvedEntityInsertAction(ActionQueue.java:291)
org.hibernate.engine.spi.ActionQueue.addInsertAction(ActionQueue.java:272)
org.hibernate.engine.spi.ActionQueue.addAction(ActionQueue.java:322)
org.hibernate.event.internal.AbstractSaveEventListener.addInsertAction(AbstractSaveEventListener.java:363)
org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:277)
org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:180)
org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:140)
org.hibernate.event.internal.DefaultMergeEventListener.saveTransientEntity(DefaultMergeEventListener.java:314)
org.hibernate.event.internal.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:233)
org.hibernate.event.internal.DefaultMergeEventListener.merge(DefaultMergeEventListener.java:152)
org.hibernate.event.internal.DefaultMergeEventListener.doMerge(DefaultMergeEventListener.java:142)
org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:126)
org.hibernate.internal.SessionImpl$$Lambda$.applyEventToListener
org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:138)
org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:869)
org.hibernate.internal.SessionImpl.merge(SessionImpl.java:840)
org.hibernate.engine.spi.CascadingActions$6.cascade(CascadingActions.java:253)
org.hibernate.engine.spi.CascadingActions$6.cascade(CascadingActions.java:243)
org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:513)
org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:434)
org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:220)
org.hibernate.engine.internal.Cascade.cascadeCollectionElements(Cascade.java:547)
org.hibernate.engine.internal.Cascade.cascadeCollection(Cascade.java:477)
org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:437)
org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:220)
org.hibernate.engine.internal.Cascade.cascade(Cascade.java:153)
org.hibernate.event.internal.DefaultMergeEventListener.cascadeOnMerge(DefaultMergeEventListener.java:571)
org.hibernate.event.internal.DefaultMergeEventListener.entityIsPersistent(DefaultMergeEventListener.java:212)
org.hibernate.event.internal.DefaultMergeEventListener.merge(DefaultMergeEventListener.java:155)
org.hibernate.event.internal.DefaultMergeEventListener.doMerge(DefaultMergeEventListener.java:142)

异常原因

这个异常是Hibernate、PostgreSQL JDBC驱动和HikariCP交互时的无害异常,本质是:

  • 事务完成后,Hibernate在清理JDBC PreparedStatement资源时,调用getMaxRows()检查状态
  • 但此时HikariCP已经提前关闭了这个Statement(可能是连接被回收、或池的空闲超时触发)
  • PostgreSQL驱动检测到Statement已关闭,抛出异常,但Hibernate的资源清理逻辑已经捕获并处理了这个异常,只会打一条调试日志,完全不影响业务

是否属于正常情况?

绝对正常。从当前情况来看:

  • Bars数据已成功插入,业务逻辑无问题
  • 所有请求均返回200,用户侧无感知
  • 仅APM能捕获到该异常,框架已完成异常处理(日志中的Exception clearing maxRows/queryTimeout ...就是处理证明)
    这属于框架内部的“噪音异常”,不影响业务正确性。

是否需要修改Bars的持久化方式?

目前不需要修改,除非碰到以下情况:

  • 异常频率过高,导致APM频繁告警或日志冗余
  • 出现实际业务数据不一致的问题

如果想消除该异常,可以尝试以下优化方案:

  1. 调整HikariCP配置:适当延长idleTimeout参数,避免连接在Hibernate完成资源清理前被回收
  2. 修改Fetch策略:将FetchType.EAGER改为FetchType.LAZY,减少EAGER加载带来的会话资源占用时间(需注意懒加载可能引发的N+1问题,可配合@Fetch(FetchMode.JOIN)或批量加载优化)
  3. 升级Hibernate版本:部分新版本Hibernate优化了资源清理逻辑,可能避免该异常抛出
  4. 升级PostgreSQL JDBC驱动:更新到最新稳定版,修复驱动层面的Statement状态检测逻辑

内容的提问来源于stack exchange,提问作者Hayi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 18:07:47