Sonali
Sonali

Reputation: 525

Implementing Pagination using entity manager in spring

How can I implement pagination in Spring + hibernate project ? Following is the code. I will get PageRequest object and I want to return Page of item

 @Repository
    public class ItemRepository {

      @PersistenceContext
      EntityManager entityManager;

      public Page<Item> findItems(PageRequest pageRequest) {
        // TODO: Implement me
        return new Page<>(new ArrayList<>(), 0, 0);
      }
    }


    public class PageRequest {

      private final int count;
      private final int pageNumber;

      public PageRequest(int pageNumber, int count) {
        assert pageNumber >= 0;
        assert count > 0;

        this.pageNumber = pageNumber;
        this.count = count;
      }

      public int getCount() {
        return count;
      }

      public int getPageNumber() {
        return pageNumber;
      }
    }

Upvotes: 11

Views: 23153

Answers (3)

Sonali
Sonali

Reputation: 525

I found the solution

public Page<Item> findItems(PageRequest pageRequest) {

    Query query = entityManager.createQuery("From Item");
    int pageNumber =pageRequest.getPageNumber();
    int pageSize = pageRequest.getCount();
    query.setFirstResult((pageNumber) * pageSize);
    query.setMaxResults(pageSize);
    List <Item> fooList = query.getResultList();

    Query queryTotal = entityManager.createQuery
            ("Select count(f.id) From Item f");
    long countResult = (long)queryTotal.getSingleResult();
    int i=(int)countResult;
   return new PageImpl<>(fooList, pageRequest.getPageNumber(), i);
  }

I had to query two times to DB once to get records and then to get all the count.

Upvotes: 24

bezbos.
bezbos.

Reputation: 2354

In a project that uses Spring Data JPA, you can implement pagination with the EntityManager quite efficiently, because it allows you to directly map the result set values into DTOs, skipping redundant entity to DTO mapping:

@Service
public class ItemService {

    @PersistenceContext
    private EntityManager entityManager;

    @Transactional(readOnly = true)
    public Page<ItemDTO> getItemsPage(Pageable page) {
        var itemList = entityManager
                .unwrap(Session.class)
                .createNamedQuery("Item.availableItems", Object[].class)
                .setFirstResult(page.getPageNumber() * page.getPageSize())
                .setMaxResults(page.getPageSize())
                .setTupleTransformer((tuples, aliases) -> {
                    var item = new ItemDTO();
                    item.setId((Long) tuples[0]);
                    item.setName((String) tuples[1]);
                    //...
                    return item;
                })
                .getResultList();

        long totalCount = itemList.size() < page.getPageSize()
                ? page.getPageSize()
                : entityManager
                    .createQuery("SELECT count(i) FROM Item i", Long.class)
                    .getSingleResult();

        return new PageImpl<>(itemList, page, totalCount);
    }
}

In this example, the query is defined in the Item entity class:

@NamedQuery(
        name = "Items.availableItems",
        query = "SELECT i.id, i.name FROM Item i"
)
@Entity
@Table(name = "items")
public class Item {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
    // ...
}

With this you can easily return the paginated result:

public ResponseEntity<List<ItemDTO>> getAvailableItems(
        @Min(0) @Valid @RequestParam(value = "pageNumber", defaultValue = "0") Integer pageNumber,
        @Min(1) @Valid @RequestParam(value = "pageSize",  defaultValue = "10") Integer pageSize) {
    Page<ItemDTO> items = itemService.getAvailableItemsPage(PageRequest.of(pageNumber, pageSize));
    return ResponseEntity
            .ok()
            .header("x-total-count", String.valueOf(items.getTotalElements()))
            .header("x-page-number", String.valueOf(pageNumber))
            .header("x-page-size", String.valueOf(pageSize))
            .body(items.toList());
}

Upvotes: 2

Petar Bivolarski
Petar Bivolarski

Reputation: 1757

One way to do it is to add logic in your PageRequest class to "slice" an incoming list depending on its Pageable method parameter and return it as org.springframework.data.domain.PageImpl.

Here is a static method that you can use in your PageRequest class:

public static <E> Page<E> returnPagedList(Pageable pageable, List<E> listOfEntities) {
    List<E> listToReturn = listOfEntities;
    if (pageable.isPaged()) {
        int pageSize = pageable.getPageSize();
        int currentPage = pageable.getPageNumber();
        int startItem = currentPage * pageSize;
        if (listOfEntities.size() < startItem) {
            listToReturn = Collections.emptyList();
        } else {
            int toIndex = Math.min(startItem + pageSize, listOfEntities.size());
            listToReturn = listOfEntities.subList(startItem, toIndex);
        }
    }
    return new PageImpl<>(listToReturn, pageable, listOfEntities.size());
}

Then in your repository you can do this (notice you receive a Pageable object):

  public Page<Item> findItems(Pageable pageable) {
   EntityManager em = getEntityManager();
   List<Item> list = ... // get list of all Items
   return PageRequest.pagedList(pageable, list);
    }
  }

Upvotes: 0

Related Questions