使用Spring/Hibernate/Postgres时如何识别事务超时引发的异常
事务超时异常判定方案
现有版本(Spring 4.2.9 + Hibernate 4.3.11 + PG驱动9.4)的处理方式
你可以通过递归遍历异常根因匹配特征的方式实现判定,准确率满足生产要求:
- 首先捕获业务层抛出的
JpaSystemException,逐层向下获取异常的cause属性,直到找到根异常 - 校验根异常是否为
PSQLException,且异常信息包含canceling statement due to user request字符串 - 可选叠加校验逻辑:确认异常抛出的对应方法是否标注了
@Transactional且配置了大于0的timeout值,进一步降低误判可能
参考实现代码:
import org.springframework.orm.jpa.JpaSystemException; import org.postgresql.util.PSQLException; import org.springframework.transaction.annotation.Transactional; import java.lang.reflect.Method; /** * 判断异常是否为事务超时触发的查询取消异常 * @param e 捕获到的顶层异常 * @param targetMethod 异常抛出对应的业务方法 * @return 是否为事务超时异常 */ public boolean isTransactionTimeoutError(Throwable e, Method targetMethod) { // 定位根异常 while (e.getCause() != null) { e = e.getCause(); } // 匹配PG取消查询的异常特征 if (e instanceof PSQLException) { String errMsg = e.getMessage(); if (errMsg != null && errMsg.contains("canceling statement due to user request")) { // 可选校验:方法是否配置了事务超时 if (targetMethod.isAnnotationPresent(Transactional.class)) { int timeout = targetMethod.getAnnotation(Transactional.class).timeout(); return timeout > 0; } } } return false; }
该方案的误判概率极低,PostgreSQL仅在收到客户端主动发送的语句取消指令时才会返回该错误信息,和Spring事务超时触发的取消操作完全对应。
高版本依赖的优化处理方式
如果后续可以升级依赖组件,有更稳定且易维护的处理方案:
- Spring Framework 5.1及以上版本对事务超时场景做了专门封装,超时后会直接抛出
TransactionTimedOutException,直接捕获该异常即可识别,不需要遍历根因 - Hibernate 5.2及以上版本优化了JDBC异常转换逻辑,会将超时类异常统一转换为
JDBCConnectionTimeoutException、QueryTimeoutException等明确的超时类型,不需要解析数据库原生异常信息 - Postgres JDBC驱动42.0及以上版本为查询取消场景增加了标准化SQL状态码
57014,可以直接调用PSQLException.getSQLState()判断是否等于57014,不需要匹配异常字符串,稳定性更高
内容的提问来源于stack exchange,提问作者Simon Rees
相关产品推荐
相关产品推荐

