【问题标题】:Order result set based on the size of collection in an entity根据实体中集合的大小对结果集进行排序
【发布时间】:2021-03-11 03:16:21
【问题描述】:

考虑以下实体 -

class Team {
    private String name;

    @OneToMany
    private Set<Employee> employees;
}

class Employee {
    private String name;

    @OneToMany
    private Set<Skill> skills;
}

class Skill {
    private String name;
    private boolean active;
    private Date expiryDate;
}

我需要对 Teams 结果集进行排序,以使具有最大活跃和未过期技能的团队排在第一位。我正在使用 spring boot Specification & CriteriaQuery 来过滤不同领域的团队。到目前为止,我的以下代码无法按预期工作。

public class TeamSpecs implements Specification<Team> {

  @Override
  public Predicate toPredicate(Root<Team> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {

    Order o = cb.desc(cb.sum(cb.size(root.join("employees").get("skills")));
    cq.orderBy(o));

    return cb.like(cb.equal(root.get("name"), "%" + value + "%"));
  }
}

我在这里缺少什么吗?请建议

【问题讨论】:

  • 您面临的问题是什么?有什么例外吗?
  • @GovindaSakare - 我在尝试提取特定团队的总技能时收到以下错误原因:java.sql.SQLException:ORDER BY 的表达式 #1 包含聚合函数并适用于结果在 com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97) 在 com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:129) 在 com 的非聚合查询.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)

标签: java spring-boot spring-mvc spring-data-jpa criteriaquery


【解决方案1】:

为此,您首先必须加入表,然后过滤条目,将它们组合在一起,然后对它们进行排序。

所以你的 SQL 查询应该是这样的:

select team.*
from Team team
  inner join employee
  inner join skill
where skill.active = true and skill.expiryDate > today
group by team.name
order by count(skill.name) desc

旁注
在这种情况下使用Specification 不是您想要做的,因为它们不代表完整的查询,而是用于多个查询的语句或查询的一部分。

使用 JPA 标准查询:

    public List<Team> getTeamsWithActiveSkills() {
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<Team> cq = cb.createQuery(Team.class);
        Root<Team> root = cq.from(Team.class);
        Join<Team, Employee> employees = root.join("employees");
        Join<Team, Skill> skills = employees.join("skills");

        Predicate isActive = cb.isTrue(skills.get("active"));
        Predicate isNonExpired = cb.greaterThan(skills.get("expiryDate"), LocalDate.now());

        cq.where(isActive, isNonExpired).groupBy(root.get("name"));

        Order order = cb.desc(cb.count(skills));
        cq.orderBy(order);

        return em.createQuery(cq).getResultList();
    }

由于我个人认为条件查询难以阅读且不直观,您可以使用 Querydsl 作为替代方案。

使用 Querydsl:

public List<Team> getTeamsWithActiveSkills() {

        QTeam team = QTeam.team;
        QEmployee employee = QEmployee.employee;
        QSkill skill = QSkill.skill;
        JPQLQuery<Team> query = from(team).join(team.employees, employee).join(employee.skills, skill);

        query = teamJPQLQuery.where(skill.active.isTrue().and(skill.expiryDate.gt(LocalDate.now())));

        query = query .groupBy(team.name);
        query = query .orderBy(skill.name.count().desc());

        return query.fetch();
    }

【讨论】:

  • 这能回答你的问题吗?你还有什么问题吗?有更新吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-09-18
  • 2014-06-25
  • 1970-01-01
  • 2015-08-31
  • 1970-01-01
  • 2018-04-18
相关资源
最近更新 更多