Pagination in Spring Data Rest for nested resource

2020-04-19 10:23发布

问题:

When the below URL is visited, I get paginations in response

/api/userPosts/

{
  "_links" : {
    "self" : {
      "href" : "/api/userPosts{?page,size,sort}",
      "templated" : true
    },
    "next" : {
      "href" : api/userPosts?page=1&size=20{&sort}",
      "templated" : true
    }
  },
  "_embedded" : {
    "userPosts" : [ {
     ...

However when visiting following URL, there is no pagination out of box by Spring Data REST -

/api/users/4/userPosts

{
  "_embedded" : {
    "userPosts" : [ {

Both UserRepository and UserPostRepository are JPARepository with pagination. As a result, the second URL is throwing GC overhead exceeded error since the row count for results returned is huge.

@RepositoryRestResource(excerptProjection = UserProjection.class)
public interface UserRepository extends BaseRepository<User, Integer>, UserRepositoryCustom {

}

public interface UserPostRepository extends BaseRepository<UserPost, Long> {

}


@NoRepositoryBean
public interface BaseRepository<T, N extends Serializable> extends JpaRepository<T, N>, QueryDslPredicateExecutor<T> {

}

Any way to have pagination with second URL as well ?

回答1:

Short painful answer: No. Absolutely not.

Long even more painful answer: Yes. By rewriting large sections of Spring Data, JPA and Hibernate. The core of the problem is that when you are requesting the the nested entity (collection or not) that nested entity is NOT queries from repository. But is is returned from the entity. There are no mechanics in Spring Data / JPA for paging

What /api/users/4/userPosts request in Spring REST does is basicly this:

User user = userRepository.findOne(4);
return user.userPosts;

So retrieving user.userPosts is Eager or Lazy reference to an nested entity and there is not way to page that.

Easiest and only solution to achieve this is : 1. create Spring Data search query: UserPostRepository.findByUserId(Long id, Pagination pa) 2. Create custom Spring MVC controller for mapping

   @Get("/api/users/{id}/userPosts")
   public Page<UserPost> getUserPostsByUserId(Long id, Pagination pagi) {
     return userPostRepository.findByUserId(id, pagi);
  1. Important! you must NOT! have user.userPosts annotated as nested in the User entity or request mapping will conflict.
  2. If you want the navigation hyperlinks for this path in User entity JSON then you need custom processing for User entity JSON creation. It is poorly documented and none of the examples cover this exact use case you you need to explore a bit.