Hibernate 6自定义用户类型JdbcType错误致查询函数执行异常
问题背景
使用Quarkus 3.1.2.Final(集成Hibernate Core 6.2.4.Final)开发项目,为自定义类MyStringWrapper实现了MyStringUserType自定义用户类型,实体中配置如下:
@Type(MyStringUserType.class) public MyStringWrapper field;
当通过Criteria API执行包含upper这类字符串函数的查询时,触发类型不匹配异常,相同逻辑在Hibernate 5中可正常运行。
复现代码
查询方法:
@Transactional List<MyEntity> getByQuery() { CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery<MyEntity> query = cb.createQuery(MyEntity.class); Root<MyEntity> root = query.from(MyEntity.class); query.where( cb.equal(cb.upper(root.get("field")), new MyStringWrapper("SOME VALUE")) ); TypedQuery<MyEntity> result = em.createQuery(query); return result.getResultList(); }
异常信息
[ERROR] org.acme.UserTypeTest.testQuery Time elapsed: 0.129 s <<< ERROR! org.hibernate.QueryException: Parameter 1 of function upper() has type STRING, but argument is of type org.acme.MyStringWrapper ...
调试发现
org.hibernate.query.sqm.produce.function.ArgumentTypesValidator#getJdbcType方法返回VarbinaryJdbcType,导致校验时因非字符类型触发错误,但自定义用户类型的getSqlType明明返回java.sql.Types.VARCHAR,预期应使用VarcharJdbcType。尝试添加@JdbcType(VarcharJdbcType.class)和@JdbcTypeCode(Types.VARCHAR)注解未能解决问题。
解决方案
Hibernate 6对自定义用户类型的类型推导逻辑做了调整,要让字符串函数正确识别自定义类型,需按以下方式修改:
1. 让自定义UserType实现StringRepresentableJdbcType接口
显式告知Hibernate该类型属于字符串可表示类型,这样upper/substring等函数能正确识别其字符属性:
public class MyStringUserType implements UserType<MyStringWrapper>, StringRepresentableJdbcType<MyStringWrapper> { // 原有UserType的实现逻辑(比如nullSafeGet、nullSafeSet等) @Override public String toString(MyStringWrapper value) { return value != null ? value.getValue() : null; } @Override public MyStringWrapper fromString(CharSequence string) { return string != null ? new MyStringWrapper(string.toString()) : null; } }
2. 补充字段注解
在实体字段上明确指定JDBC类型代码:
@Type(MyStringUserType.class) @JdbcTypeCode(Types.VARCHAR) public MyStringWrapper field;
3. 备选方案:改用CompositeUserType
如果自定义类型需要更复杂的映射逻辑,Hibernate 6推荐使用CompositeUserType,它在类型推导和函数支持上有更好的兼容性。
经过上述调整后,Hibernate能正确识别自定义类型对应的JDBC类型为VARCHAR,upper等字符串函数即可正常处理该字段。
内容的提问来源于stack exchange,提问作者Timz

