【问题标题】:Sort child rows of each group in the desired order and take the desired number of top rows in each group in JPA按所需顺序对每个组的子行进行排序,并在 JPA 中获取每个组中所需数量的顶行
【发布时间】:2014-04-26 17:08:05
【问题描述】:

是否可以按所需顺序对每个组(每个父行的子行)的子行进行排序,并在 JPA 中获取每个组中所需数量的顶部行?

例如,我在 MySQL 数据库中有三个表。

  • 类别
  • 子类别
  • 产品

这些表之间的关系很直观——按照它们出现的顺序是一对多的。

我正在sub_category 表上执行以下条件查询。

CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder();
CriteriaQuery<SubCategory>criteriaQuery=criteriaBuilder.createQuery(SubCategory.class);
EntityType<SubCategory> entityType = entityManager.getMetamodel().entity(SubCategory.class);
Root<SubCategory> root = criteriaQuery.from(entityType);
criteriaQuery.distinct(true);

Join<SubCategory, Category> catJoin = root.join(SubCategory_.catId, JoinType.INNER);
SetJoin<SubCategory, Product> prodJoin = root.join(SubCategory_.productSet, JoinType.INNER);

List<Predicate>predicates=new ArrayList<Predicate>();
predicates.add(criteriaBuilder.isTrue(root.get(SubCategory_.visible)));
predicates.add(criteriaBuilder.isTrue(catJoin.get(Category_.visible)));
predicates.add(criteriaBuilder.isTrue(prodJoin.get(Product_.visible)));

criteriaQuery.where(predicates.toArray(new Predicate[0]));
criteriaQuery.orderBy(criteriaBuilder.desc(root.get(SubCategory_.subCatId)));               
List<SubCategory> list = entityManager.createQuery(criteriaQuery).getResultList();

此查询恰好获取所有这些表中visible(MySQL 中的TINYINT 映射到相应 JPA 实体中的 Boolean 属性)为 true 的所有子类别。

执行此查询后,每个子类别的一组产品将以Set&lt;Product&gt; 的形式提供。

每个子类别的一组产品进行排序后,我想获取每个子类别的一组前五名产品(不是每个子类别的所有产品)按降序排列。

有可能吗?

我使用的是 EclipseLink 2.3.2 提供的 JPA 2.0。

【问题讨论】:

    标签: mysql jpa eclipselink criteria greatest-n-per-group


    【解决方案1】:

    我不熟悉在 JPA 中限制连接或子查询的方法。如果你找到了一些优雅的方法,请不要忘记在这里发布。

    据我所知,您可以做一些不太有效的事情:

    1. 删除“加入”产品 - 仅检索类别和子类别。
    2. 使用单独的查询检索每个子类别的产品,并为每个“产品”查询指定一个 setMaxResults(5)。

    另一种可能性是使用本机查询。这会更有效,但填充实体 bean 会很乏味。下面是一个示例,说明您可以采取哪些措施来获得每个子类别的前 5 名产品:

    select * from category c
    inner join sub_category cn on cn.category_id = c.id
    inner join product p1 on p1.sub_category_id = cn.id and p1.id in
    (select tmp.id from (select p.id, row_number() over (partition by p.sub_category order by p.id) as rn from product p where p.visible = 1) tmp
    where tmp.rn < 6);
    

    (显然查询需要一些调整才能使其正常工作)

    【讨论】:

    • 我只能想到 2 但它会产生很高的成本,因为我们需要执行一个查询来获取每个子类别的产品列表,获取产品列表的查询进入内部子类循环。
    • 对,您必须分别查询每个产品列表。如果您想很好地控制查询,请使用本机,但您将使用对象数组而不是 JPA 对象。您始终可以只获取整个产品列表并过滤掉除前五名之外的产品,但我想您已经考虑到了这种可能性(如果您这样做,请确保您的对象已分离)。
    • 产品联接仅用于在 SQL 中强制执行条件检查,WHERE visible=1product 表中。
    • 你想要的结果是什么?我知道您想要:类别列表 -> 持有子类别列表 -> 持有前 5 名可见产品的列表?我错过了什么吗?
    • 你猜对了。除此之外,所有类别都已显示在菜单中。因此,它们不需要被列出(尽管它们应该是可见的)。实际上,我需要在&lt;p:dataGrid&gt;中显示一个子类别的产品列表,在对每组产品进行降序排序后,在&lt;p:dataGrid&gt;的每一行中水平显示每个子类别的前5个产品。
    【解决方案2】:

    不幸的是,AFAIK,限制查询结果的唯一方法是使用Query#setMaxResults(),这仅适用于 Query 对象,不适用于子查询。

    我建议使用一种变通方法,例如仅选择包含查询的子类别。由于延迟加载,在返回结果时,JPA 不应该为每个结果获取所有产品(您可以通过一些日志记录检查这一点),从而避免不必要的数据库加载。

    稍后,当您需要每个子类别的前 5 个产品时,而不是使用

    SubCategory sc;
    List<Product> list = sc.getProducts();
    

    只需为每个子类别运行一个查询:

    SubCategory sc;
    List<Product> list = subCategoryService.getTopProducts(sc);
    

    如果每次显示的子类别数量合理(如最多 30 个,至于可以在分页数据列表中显示的结果),则性能不会明显恶化。

    【讨论】:

    • 我正在尝试运行第二个 sn-p (List&lt;Product&gt; list = subCategoryService.getTopProducts(sc);) here,这会引起那里提到的另一个问题 :)
    猜你喜欢
    • 2021-12-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-20
    • 2021-05-07
    • 1970-01-01
    相关资源
    最近更新 更多