Testcontainers测试含Polygon字段实体时PostgreSQL类型缓存查找失败
问题描述
我使用Quarkus + Hibernate对接PostgreSQL/PostGIS,定义了包含Polygon字段的Geofence实体。用Cucumber编写测试时遇到问题:测试场景的第一个Example执行正常,但第二个及后续Example执行时,调用persistAndRefresh方法的persist步骤会抛出错误:
javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: could not execute statement ... Caused by: org.postgresql.util.PSQLException: ERROR: cache lookup failed for type 18049
测试环境细节:
- 用Cucumber编写测试,每个测试包含多个Examples
- 通过Testcontainers启动单个PostgreSQL/PostGIS容器(镜像
postgis/postgis:16-3.4),每个测试后通过dump恢复数据库 - 每个测试后会清理测试上下文、恢复数据库快照、清除时间上下文
临时解决方案:在persist前调用查询全表的方法(如getAll()),错误就不会出现。
错误成因
这个错误的核心是Hibernate缓存的PostGIS几何类型OID与数据库恢复后的实际OID不匹配:
- PostgreSQL中自定义类型(比如PostGIS的
geometry/polygon)有唯一的OID标识,Hibernate启动时会查询数据库获取这些类型的OID并缓存到元数据中,用于后续JDBC交互时的类型映射。 - 复用同一个Testcontainers容器时,每次测试后用dump恢复数据库,若恢复的dump与首次测试时的数据库状态存在差异,会导致恢复后的数据库中PostGIS类型的OID与Hibernate缓存的旧OID(比如18049)不一致,数据库无法找到该OID对应的类型,从而触发错误。
- Cucumber的多Example场景会复用Quarkus的测试上下文(包括Hibernate的EntityManagerFactory),导致Hibernate不会自动重新加载类型元数据,一直沿用首次测试时缓存的无效OID。
修复方案
针对这个问题,可以从以下几个方向修复:
- 重置Hibernate元数据缓存:
在每个测试场景执行后,强制Hibernate清除并重新加载类型元数据。在Quarkus中,可以注入EntityManagerFactory,调用相关方法刷新元数据,或者利用Quarkus测试扩展的上下文重置机制,确保每次测试都重新初始化Hibernate的元数据。 - 重置数据库连接池:
配置测试环境的数据库连接池,在每个测试后关闭所有连接并重新初始化连接池,让新连接使用恢复后数据库的最新类型元数据。 - 生成dump时保留类型OID:
使用pg_dump的--oids参数生成包含类型OID的数据库dump,这样恢复后的数据库中PostGIS类型的OID会与首次测试时一致,避免Hibernate缓存的OID失效。 - 启动新容器(备选):
如果上述方案无法生效,可以放弃复用Testcontainers容器,改为每个测试场景启动新的PostGIS容器。虽然会增加测试耗时,但能彻底规避缓存不一致问题。
为什么查询全表能规避错误
调用getAll()全表查询时,Hibernate会执行完整的数据库交互,这个过程会触发Hibernate自动重新加载数据库的最新类型元数据,覆盖之前缓存的旧OID。后续执行persist操作时,Hibernate就会使用恢复后数据库的正确类型OID,自然不会再触发类型查找失败的错误。
内容的提问来源于stack exchange,提问作者Самир Шахмурадлы
相关产品推荐
相关产品推荐

