【问题标题】:JPA Create Criteria with GROUP COUNT and HAVING on nested objectsJPA 在嵌套对象上使用 GROUP COUNT 和 HAVING 创建条件
【发布时间】:2017-12-11 20:32:13
【问题描述】:

给定这个 SQL 查询

SELECT
    ug.lookup_key, 
    count(ug.id) as count
FROM user u
INNER JOIN user_group ug on ug.id = u.id
WHERE 
    u.age >= 11 AND 
    u.age <= 20 AND
    ug.lookup_key in('12345')
GROUP BY ug.lookup_key
HAVING count(ug.id) < 7

我已经写好了

CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Object[]> query = criteriaBuilder.createQuery(Object[].class);
Root<UserGroup> d = query.from(UserGroup.class);
Join<UserGroup, User> join = d.join("users");

Predicate pred1 = criteriaBuilder.between(join.get("age"), ageFrom, ageTo);
Expression<String> exp = d.get("lookupKey");
Predicate pred2 = exp.in(lookupKeys);

query.where(pred1, pred2);

query.multiselect(d.get("lookupKey"), criteriaBuilder.count(d.get("id"))).groupBy(d.get("lookupKey"));

List<Object[]> results = entityManager.createQuery(query).getResultList();

for(Object[] object : results){
    System.out.println(object[0] + " " + object[1]);
}

SQL 返回 {"12345",4} 而代码返回 {"12345", 37}。 SQL 是正确的结果。数据库中有 37 个用户用于具有该查找键的组,因此我了解数字的来源,但我不明白如何使用 CreateCriteria 查询执行 JOIN、GROUP BY、HAVING 以获得结果。我不想使用 JPQL。

实体...

@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    private String name;

    private int age;

    private double salary;

    @ManyToOne(optional=false,cascade=CascadeType.ALL, targetEntity=UserGroup.class)
    @JsonBackReference
    private UserGroup group;

    // Getters and Setters //

}

@Entity
public class UserGroup {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    private String name;

    private String lookupKey;

    @OneToMany(mappedBy="group",targetEntity=User.class, fetch=FetchType.EAGER)
    @JsonManagedReference
    private Collection users;

    // Getters and Setters //

}

还有,这是实现它的方法

public void summarizeGroupsByLookupKey(long ageFrom, long ageTo, List<String> lookupKeys, long numUsers){
        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
        CriteriaQuery<Object[]> query = criteriaBuilder.createQuery(Object[].class);
        Root<UserGroup> d = query.from(UserGroup.class);
        Join<UserGroup, User> join = d.join("users");

        Predicate pred1 = criteriaBuilder.between(join.get("age"), ageFrom, ageTo);
        Expression<String> exp = d.get("lookupKey");
        Predicate pred2 = exp.in(lookupKeys);

        query.where(pred1, pred2);
        query.multiselect(d.get("lookupKey"), criteriaBuilder.count(d.get("id")));
        query.groupBy(d.get("lookupKey"));
        query.having(criteriaBuilder.<Long>lessThan(criteriaBuilder.count(d.get("id")), numUsers));

        List<Object[]> results = entityManager.createQuery(query).getResultList();

        for(Object[] object : results){
            System.out.println(object[0] + " " + object[1]);
        }
    }

通过信息...使用 Spring Boot 1.5.1 和所有默认的 JPA、Hibernate 等。

JPA 专家可以提供一些帮助吗?谢谢!

【问题讨论】:

  • 你能分享UserUserGroup实体吗?
  • 编辑答案以包含它们

标签: jpa join group-by having


【解决方案1】:

将多选部分更改为使用countDistinct(..)

query.multiselect(d.get("lookupKey")
      ,criteriaBuilder.countDistinct(d.get("id")));

还有having(..)

query.having(criteriaBuilder.<Long>lessThan(
                   criteriaBuilder.countDistinct(d.get("id")), numUsers)
                );

原始查询返回每个匹配用户的行,然后将行 userGroup.id 相乘。

【讨论】:

  • 是的 - 目的是使查询具有包容性。我更新了 SQL。我也实现了你提到的have子句,但是当它应该返回{“12345”,4}时它仍然返回{“12345”,37}。这与 join() 的设置方式有关 - 我只是不知道它是什么。
  • @jmorhardt 好吧​​,我能说什么...您的代码对我来说很好用。 have-从句当然与你得到的计数无关。您是否使用硬编码属性对此进行了测试/记录了您传递的属性?我确实从映射中删除了targetEntity=...,因为不需要但应该完全相同。
  • 我一直在 IntelliJ IDEA 中调试它。绝对确认参数符合预期。你有你的代码示例吗?也许我能看出区别。稍后我将添加整个方法。
  • 除提及外,您的代码完全相同。现在无法粘贴,但明天会尽快回到办公室。
  • @jmorhardt 复制粘贴了您的summarizeGroupsByLookupKey(..),它通过了我的测试。但我没有你的数据。唯一的补充是 - 因为使用 Postgres 保留字 USER - 将 @Table(name="\"user\"") 添加到 User 但这并不重要。也许我完全误解了你的问题。也许您应该将 37 行用户粘贴到问题中,告诉您想查看哪些行以及哪些 JPA 提供程序和数据库使用?
猜你喜欢
  • 2016-10-11
  • 2016-07-23
  • 1970-01-01
  • 1970-01-01
  • 2015-09-15
  • 2021-01-28
  • 2021-01-19
  • 2022-01-10
  • 1970-01-01
相关资源
最近更新 更多