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

Spring 2.7迁移至3.0.6:Specification子查询In语句报错求助

问题描述

使用QueryBuilder构建复杂Specification查询,通过repository.findAll(Specification<...>)实现结果过滤。该查询在Spring 2.7中运行正常,但迁移至Spring 3.0.6后失效。注释掉builder.in(equip.get("id")).value(subqueryEquip),这一行后查询可正常执行,但该语句是必要的过滤条件。

控制台抛出的错误信息:

class org.hibernate.metamodel.mapping.internal.BasicAttributeMapping
cannot be cast to class
org.hibernate.metamodel.mapping.EntityValuedModelPart
(org.hibernate.metamodel.mapping.internal.BasicAttributeMapping and
org.hibernate.metamodel.mapping.EntityValuedModelPart are in unnamed
module of loader 'app')

推测是Subquery结合In语句的写法在新版本中出现问题,相关代码如下:

Specification生成函数

public static Specification<Usuari> filterByLaboratorisAndEquipsAssignatsToUser(UsuariFilterParams usuariFilterParams, UsuariLogat usuariLogat) {
        return (root, query, builder) -> {
            List<Predicate> predicates = buildListOfPredicates(root, builder, usuariFilterParams);

            query.select(root.get("id")).distinct(true);

            Subquery<Equip> subqueryEquip = buildSubqueryEquip(query, builder, usuariLogat.getId());

            Join<Usuari, Equip> equip = root.join("equips", JoinType.LEFT);
            Join<Usuari, Rol> rol = root.join("rol");

            predicates.add(builder.and(
                    builder.notEqual(root.get("id"), usuariLogat.getId()),
                    builder.or(
                            builder.in(equip.get("id")).value(subqueryEquip),
                            builder.equal(rol.get("id"), (RolsEnum.ROLE_USUARI_LABORATORI.getId()))
                    ),
                    builder.greaterThan(rol.get("id"), usuariLogat.getFirstRole().getId())));

            query.where(builder.and(predicates.toArray(new Predicate[0])));
            return query.getRestriction();
        };
    }

子查询构建函数

private static Subquery<Equip> buildSubqueryEquip(CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder, long usuariLogatId) {
        Subquery<Equip> subqueryEquip = query.subquery(Equip.class);
        Root<Equip> subEquip = subqueryEquip.from(Equip.class);

        Join<Equip, Usuari> subUsuariEquip = subEquip.join("usuaris");
        return subqueryEquip.select(subEquip).where(criteriaBuilder.equal(subUsuariEquip.get("id"), usuariLogatId));
    }
解决方案

问题根源在于子查询返回的是Equip实体对象,但in语句中对比的是equip.get("id")(基础数据类型,如Long),类型不匹配导致Hibernate抛出类型转换错误。Spring 3.x配套的Hibernate 6.x对Criteria API的类型校验更为严格,而Spring 2.7使用的Hibernate 5.x允许宽松的类型匹配,这就是迁移后报错的原因。

修改步骤:

  1. 修改子查询,使其返回Equip的ID而非整个实体:
private static Subquery<Long> buildSubqueryEquip(CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder, long usuariLogatId) {
        Subquery<Long> subqueryEquip = query.subquery(Long.class);
        Root<Equip> subEquip = subqueryEquip.from(Equip.class);

        Join<Equip, Usuari> subUsuariEquip = subEquip.join("usuaris");
        // 选择Equip的id字段
        return subqueryEquip.select(subEquip.get("id")).where(criteriaBuilder.equal(subUsuariEquip.get("id"), usuariLogatId));
    }
  1. 调整Specification中的子查询类型声明:
// 将子查询类型改为Long,与返回值匹配
Subquery<Long> subqueryEquip = buildSubqueryEquip(query, builder, usuariLogat.getId());

这样修改后,in语句对比的是同类型的ID值,即可解决类型转换错误,让查询正常执行。

内容的提问来源于stack exchange,提问作者Albert Lazaro de Lara

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 18:14:59