You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 14:30:42