Spring Boot 3.0.2迁移时JPA自定义查询出现ClassCastException问题
解决Spring Boot 3.0.2迁移后JPA自定义查询的ClassCastException问题
问题根源
Spring Boot 3.x默认使用Hibernate 6.x,相比旧版Hibernate 5.x,JPQL参数处理逻辑更严格:
- 列表类型参数(如
List<Enum>、List<String>)不能直接用=判断,必须用IN - 列表参数的
null判断逻辑变更,(:param) is null不再适用于集合类型,会触发类型转换错误 - 字符串LIKE条件与列表IN条件混合时,参数绑定的优先级或处理逻辑变化,引发冲突
具体修复步骤
1. 修正JPQL中列表参数的条件表达式
把所有列表参数的null检查改为空列表检查,并将=替换为符合规范的IN:
原错误条件:
AND ((:type) is null OR me.type = :type) AND ((:created) is null OR me.createdBy IN (:created)) AND ((:models) is null OR me.model IN (:models))
修改后:
AND ((:type) is empty OR me.type IN (:type)) AND ((:created) is empty OR me.createdBy IN (:created)) AND ((:models) is empty OR me.model IN (:models))
2. Java代码统一处理null列表参数
设置参数前,将所有null的列表参数转为空列表,避免Hibernate处理null集合时的异常:
// 处理列表参数,null转为空列表 List<Enum> safeType = type != null ? type : Collections.emptyList(); List<String> safeCreated = creators != null ? creators : Collections.emptyList(); List<String> safeModels = models != null ? models : Collections.emptyList(); // 构建查询并设置参数 entityManager.createQuery( "SELECT distinct me FROM myEntity me " + "WHERE me.deleted = false " + "AND me.parent is null AND me.id IN (:ids) " + "AND ((:type) is empty OR me.type IN (:type)) " + "AND ((lower(me.label) LIKE lower(:name)) OR (lower(me.description) LIKE lower(:name))) " + "AND ((:created) is empty OR me.createdBy IN (:created)) " + "AND ((:models) is empty OR me.model IN (:models))") .setParameter("name", "%" + name + "%") .setParameter("ids", ids) .setParameter("created", safeCreated) .setParameter("type", safeType) .setParameter("models", safeModels) .getResultList();
3. 优化LIKE条件参数处理(可选)
提前在Java代码中处理字符串大小写,减少JPQL函数调用,避免潜在的参数处理冲突:
String likeName = "%" + (name != null ? name.toLowerCase() : "") + "%"; // JPQL中简化为: // AND ((me.label LIKE :name) OR (me.description LIKE :name))
4. 改用Criteria API构建动态查询(推荐)
手动拼接JPQL易出错,Criteria API可更安全地处理动态条件,彻底规避参数绑定问题:
CriteriaBuilder cb = entityManager.getCriteriaBuilder(); CriteriaQuery<MyEntity> cq = cb.createQuery(MyEntity.class); Root<MyEntity> root = cq.from(MyEntity.class); // 基础条件集合 List<Predicate> predicates = new ArrayList<>(); predicates.add(cb.isFalse(root.get("deleted"))); predicates.add(cb.isNull(root.get("parent"))); predicates.add(root.get("id").in(ids)); // 处理名称模糊匹配 String likeName = "%" + name.toLowerCase() + "%"; Predicate namePredicate = cb.or( cb.like(cb.lower(root.get("label")), likeName), cb.like(cb.lower(root.get("description")), likeName) ); predicates.add(namePredicate); // 按需添加列表参数条件 if (type != null && !type.isEmpty()) { predicates.add(root.get("type").in(type)); } if (creators != null && !creators.isEmpty()) { predicates.add(root.get("createdBy").in(creators)); } if (models != null && !models.isEmpty()) { predicates.add(root.get("model").in(models)); } // 组装并执行查询 cq.select(root).distinct(true).where(predicates.toArray(new Predicate[0])); List<MyEntity> result = entityManager.createQuery(cq).getResultList();
修复逻辑说明
- Hibernate 6.x的
is empty是专门针对集合类型的判断,避免了null转换时的ClassCastException - 使用
IN符合JPQL规范,Hibernate无需再隐式转换列表参数类型 - 提前将null列表转为空列表,统一参数类型,避免内部绑定冲突
- Criteria API通过代码控制条件添加,彻底避免手动拼接JPQL的语法和参数错误
内容的提问来源于stack exchange,提问作者Pantelisy
相关产品推荐
相关产品推荐

