使用Spring Data JpaRepository按计数排序

时间:2014-04-17 14:50:58

标签: java jpa spring-data

我正在使用Spring Data JpaRepository,我发现它非常易于使用。我实际上需要所有这些功能 - 分页,排序,过滤。不幸的是,有一件令人讨厌的事情似乎迫使我回归使用普通的JPA。

我需要按相关集合的大小排序。比如我有:

@Entity
public class A{
    @Id
    private long id;
    @OneToMany
    private List<B> bes;
//boilerplate
}

我必须按bes.size()

排序

有没有办法以某种方式定制排序仍然利用分页,过滤和其他Spring Data的强大功能?

4 个答案:

答案 0 :(得分:16)

我使用以下提示和灵感解决了这个难题:

  1. Limiting resultset using @Query anotations by Koitoer
  2. How to order by count() in JPA来自MicSim
  3. 我自己的详尽实验
  4. 关于,我没有意识到的第一个也是最重要的事情是即使使用@Query自定义方法,也可以通过简单地传递Pageable对象来创建分页查询参数。这可以通过spring-data文档明确说明,因为它虽然非常强大,但它绝对不是很明显。

    很好,现在是第二个问题 - 我如何根据JPA中关联集合的大小对结果进行排序?我已经设法进入以下JPQL:

    select new package.AwithBCount(count(b.id) as bCount,c) from A a join a.bes b group by a
    

    其中 AwithBCount 是查询结果实际映射到的类:

    public class AwithBCount{
        private Long bCount;
        private A a;
    
        public AwithBCount(Long bCount, A a){
            this.bCount = bCount;
            this.a = a;
        }
        //getters
    }
    

    很兴奋我现在可以简单地定义我的存储库,如下面的那个

    public interface ARepository extends JpaRepository<A, Long> {
        @Query(
            value = "select new package.AwithBCount(count(b.id) as bCount,c) from A a join a.bes b group by a",
            countQuery = "select count(a) from A a"
        )
        Page<AwithBCount> findAllWithBCount(Pageable pageable);
    }
    

    我急忙尝试解决问题。完美 - 页面被返回,但当我尝试按bCount排序时,我感到很失望。原来,由于这是一个ARepository(不是AwithBCount存储库),spring-data将尝试在A中查找bCount属性而不是AwithBCount。最后我最终得到了三种自定义方法:

    public interface ARepository extends JpaRepository<A, Long> {
        @Query(
            value = "select new package.AwithBCount(count(b.id) as bCount,c) from A a join a.bes b group by a",
            countQuery = "select count(a) from A a"
        )
        Page<AwithBCount> findAllWithBCount(Pageable pageable);
    
        @Query(
            value = "select new package.AwithBCount(count(b.id) as bCount,c) from A a join a.bes b group by a order by bCount asc",
            countQuery = "select count(a) from A a"
        )
        Page<AwithBCount> findAllWithBCountOrderByCountAsc(Pageable pageable);
    
        @Query(
            value = "select new package.AwithBCount(count(b.id) as bCount,c) from A a join a.bes b group by a order by bCount desc",
            countQuery = "select count(a) from A a"
        )
        Page<AwithBCount> findAllWithBCountOrderByCountDesc(Pageable pageable);
    }
    

    ...以及服务级别的一些附加条件逻辑(可能可能使用抽象存储库实现进行封装)。因此,尽管不是非常优雅,但这样做了 - 这种方式(具有更复杂的实体)我可以按其他属性排序,进行过滤和分页。

答案 1 :(得分:4)

一个选项,比原始解决方案简单得多,还有其他好处,是创建聚合数据的数据库视图,并通过@SecondaryTable或{{1}将您的实体链接到此}。

例如:

@OneToOne

使用create view a_summary_view as select a_id as id, count(*) as b_count, sum(value) as b_total, max(some_date) as last_b_date from b

@SecondaryTable

现在,您可以纯粹参考实体A进行排序,提交,查询等。

作为额外优势,您在域模型数据中拥有可能在内存中计算成本高昂的数据,例如客户的所有订单的总价值,无需加载所有订单或恢复为单独的查询。

答案 2 :(得分:1)

谢谢@Alan Hay,这个解决方案对我来说很好。我只需要设置 @SecondaryTable 注释的外键属性,一切正常(否则 Spring Boot 尝试向 id 添加外键约束,这会引发 sql 视图错误)。

结果:

@SecondaryTable(name = "product_view",
            pkJoinColumns = {@PrimaryKeyJoinColumn(name = "id", referencedColumnName = "id")},
            foreignKey = @javax.persistence.ForeignKey(ConstraintMode.NO_CONSTRAINT))

答案 3 :(得分:0)

我不太了解Spring Data,但对于JPQL,要按关联集合的大小对对象进行排序,可以使用查询

Select a from A a order by a.bes.size desc