使用Spring Data JpaRepository按计数排序

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

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

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

我必须按bes.size()排序

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

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

  1. 使用Koitoer的@Query anotations 限制结果集
  2. 如何通过MicSim的JPA中的count()订购
  3. 我自己进行了详尽的实验

关于spring-data ,我没有意识到的第一个也是最重要的事情是,即使使用@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 { @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 findAllWithBCount(Pageable pageable); } 

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

 public interface ARepository extends JpaRepository { @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 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 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 findAllWithBCountOrderByCountDesc(Pageable pageable); } 

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

一个选项比原始解决方案简单得多,并且还有其他好处,是创建聚合数据的数据库视图,并通过@SecondaryTable@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

 @Entity @Table @SecondaryTable(name = "a_summary_view", pkJoinColumns = {@PrimaryKeyJoinColumn(name = "id", referencedColumnName= "id")}) public class A{ @Column(table = "a_summary_view") private Integer bCount; @Column(table = "a_summary_view") private BigDecimal bTotal; @Column(table = "a_summary_view") private Date lastBDate; } 

您现在可以纯粹参考实体A对sort,filer,query等进行排序。

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

我对Spring Data知之甚少,但对于JPQL,要按关联集合的大小对对象进行排序,可以使用查询

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