使用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?
我使用以下提示和灵感解决了这个难题:
- 使用Koitoer的@Query anotations 限制结果集
- 如何通过MicSim的JPA中的count()订购
- 我自己进行了详尽的实验
关于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