Hibernate Envers AuditReader查询报错:smallint与bytea类型不匹配
从你描述的问题和提供的日志来看,核心问题是Hibernate Envers在执行审计查询时,错误地将字符串类型的RevisionType枚举名称(比如'DEL')绑定到了数据库中smallint类型的REVTYPE字段上,导致PostgreSQL无法找到匹配的运算符,触发了smallint <> bytea的异常。
问题细节回顾
你的代码逻辑本身没问题:
AuditReader auditReader = AuditReaderFactory.get(entityManager); List<Number> revisionNumbers = auditReader.getRevisions(Queue.class, queue.getId()); for (Number rev : revisionNumbers) { Queue auditedQueue = auditReader.find(Queue.class, queue.getId(), rev); audQueues.add(auditedQueue); }
但SQL日志显示,查询中的queue_aud0_.REVTYPE<>?参数被绑定成了字符串'DEL',而数据库表的REVTYPE字段是Hibernate自动生成的smallint类型,两者类型不匹配,手动执行查询也会触发同样的异常。
问题根源分析
Hibernate Envers默认使用org.hibernate.envers.RevisionType枚举来标识审计操作类型,这个枚举默认是以**序号(ORDINAL)**映射到数据库的:
ADD对应0,MOD对应1,DEL对应2
正常情况下,Envers应该绑定整数2而不是字符串'DEL'到参数中。出现这个问题的常见原因有以下几种:
Hibernate核心与Envers版本不一致
你使用的是hibernate-envers:4.2.0.Final,但如果你的项目中hibernate-core的版本和这个不一致(比如是4.3.x或其他版本),就会导致内部类型映射逻辑冲突,出现枚举类型绑定错误。自定义RevisionEntity时映射错误
如果你自定义了审计修订实体(继承DefaultRevisionEntity),并且显式声明了RevisionType字段,却错误地使用了@Enumerated(EnumType.STRING)而不是@Enumerated(EnumType.ORDINAL),就会导致Envers将枚举名称字符串写入数据库,但自动生成的表字段是smallint,查询时自然会类型不匹配。老旧版本的已知Bug
Hibernate Envers 4.2.0.Final是2013年发布的非常老旧的版本,这个版本存在一些已修复的类型映射Bug,比如在某些场景下错误地将RevisionType枚举按字符串处理。
解决方案
针对上述原因,你可以按以下步骤排查修复:
检查并统一Hibernate版本
确保hibernate-core和hibernate-envers的版本完全一致,比如都使用4.2.0.Final,或者升级到4.2.x系列的最新补丁版本(比如4.2.21.Final),避免版本冲突。修正自定义RevisionEntity的映射
如果你有自定义的修订实体,确保RevisionType字段的枚举映射是序号类型:
@Entity @RevisionEntity(MyRevisionListener.class) public class CustomRevisionEntity extends DefaultRevisionEntity { // 其他自定义字段... @Enumerated(EnumType.ORDINAL) private RevisionType revtype; // getter/setter }
升级Envers到稳定版本
考虑升级到更高版本的Hibernate Envers(比如5.6.x系列,这是兼容JPA 2.2的稳定版本),老旧版本的Bug在后续版本中已经被修复,能避免这类类型不匹配的问题。临时验证方案
你可以手动将SQL中的'DEL'替换为整数2执行查询,应该能正常返回结果,这也能验证我们的分析是正确的。
内容的提问来源于stack exchange,提问作者Gobanit

