Modify Spring data Page<> class projection response in service layer - java

I am using projection, and I need to format the date variable in list of objects, but Page<> type response is read only, so I can not iterate and modify the objects. What Should I do?
#Override
public Page<OrderDto> findAll(Pageable pageable) {
return OrderDao.findByIsEnabledTrue(pageable, OrderDto.class);
}
I tried like this:
#Override
public Page<OrderDto> findAll(Pageable pageable) {
Page<OrderDto> page = orderDao.findByIsEnabledTrue(pageable, OrderDto.class);
List<OrderDto> orderDtos = page.getContent();
orderDtos.stream().forEach(it ->{
it.setFormattedCreatedAt(new SimpleDateFormat("dd-M-yyyy").format(it.getCreatedAt()));
});
page.getContent().clear();
page.getContent().addAll(orderDtos);
return page;
}

page.getContent() returns an unmodifiable list. The best approach is to use map method provided by page:
#Override
public Page<OrderDto> findAll(Pageable pageable) {
Page<OrderDto> page = orderDao.findByIsEnabledTrue(pageable, OrderDto.class);
page = page.map(this :: transformOrderDto);
return page;
}
private OrderDto transformOrderDto(final OrderDto order) {
order. setFormattedCreatedAt(new SimpleDateFormat("dd-M-yyyy").format(order.getCreatedAt()));
return order;
}

Since 1.10, Page has supported a map method that is specifically meant to let you transform the objects contained in it.

Related

Spring Boot Pagination - PagingAndSortingRepository not returning any results

I've created a PagingAndSortingRepository in Spring Boot, but it does not return any results (thought it should).
The repository:
#Repository
public interface DoctorPagingRepository extends PagingAndSortingRepository<Doctor, Integer> {
Page<Doctor> findByName(String name, Pageable pageable);
Page<Doctor> findAllByUsername(String username, Pageable pageable);
Page<Doctor> findAllByName(String name, Pageable pageable);
Page<Doctor> findAllByNameContaining(String name, Pageable pageable);
}
I have other repositories which work, when not using Pageable methods i.e.
#Repository
public interface UserPagingRepository extends PagingAndSortingRepository<User, Integer> {
User findByUsername(String username);
User findByEmail(String email);
}
But anytime I attempt to user pageable repository methods it does work:
#GetMapping("get-doctor/name/{name}/{pageNum}")
public JsonResponse findDoctorByFirstname(#PathVariable String name, #PathVariable String pageNum) {
//set page number and return up to 10 elements
Pageable page = PageRequest.of(Integer.parseInt(pageNum), 10);
//get list of users from that page
Page<Doctor> doctorPage = userServices.getDoctorPaging().findAllByNameContaining(name, page);
//set response object with users
DoctorListResponse res = new DoctorListResponse();
try {
doctorPage.getContent().forEach( u -> {
logger.trace(u.toString());
res.getDoctorJsons().add(new DoctorJson(u));
res.setSuccess(true);
});
} catch (Exception e) {
res.setSuccess(false);
}
res.setTotalPages(doctorPage.getTotalPages());
return res;
}
Can anyone see where this might be going wrong?
It turned out that the problem was to do with PageRequest.of() method's first parameter page is 'zero based page index', which I didn't realise. So it was returning the second page of results when pageNum was set to 1. The second page was empty as only 5 results in the database could have matched my query would be on the first page (as each page was set to hold ten results).
The solution was just to -1 from the pageNum:
Pageable page = PageRequest.of(Integer.parseInt(pageNum)-1, 10);

How to sorting repository entity with pagination in spring boot

I am writing an REST API with Spring Boot.Here,I am trying to sort the list using the 'PagingAndSortingRepository'.This is what I have tried
Method in my Controller class
#GetMapping public ResponseEntity
<PagedModel<ComplainDTO>> getComplains(#RequestParam(defaultValue = "0",value = "page",required =false)int page, #RequestParam(value="sortBy" ,required = false,defaultValue = "complainId") String sortBy, PagedResourcesAssembler assembler) {
return ResponseEntity.status(HttpStatus.OK)
.body(assembler.toModel(complainService.getAllComplains(page,sortBy)));
}
Method on my service
#Override
public Page<ComplainDTO> getAllComplains(int page,String sortBy) {
Pageable pageable = PageRequest.of(page,20, Sort.by(sortBy));
Page<ComplainEntity> result =complainRepository.findAll(pageable);
return result.map(complainEntity -> toDTO(complainEntity));
}
I am taking input from the consumer to sort the list based on given attribute however the default behavior of Sort is Ascending .So,how can I implement this taking user params whether it being ascending or descending aside from using if-else.
The one option is asking the user to provide the sorting order using #RequestParam
#GetMapping public ResponseEntity
<PagedModel<ComplainDTO>> getComplains(#RequestParam(defaultValue = "0",value = "page",required =false)int page,
#RequestParam(value="sortBy" ,required = false,defaultValue = "complainId") String sortBy,
#RequestParam(value="orderBy" ,required = false,defaultValue = "ASC") String orderBy, PagedResourcesAssembler assembler) {
return ResponseEntity.status(HttpStatus.OK)
.body(assembler.toModel(complainService.getAllComplains(page,sortBy,orderBy)));
}
And then in the service pass the sorting order to Sort method
#Override
public Page<ComplainDTO> getAllComplains(int page,String sortBy, String orderBy) {
Pageable pageable = PageRequest.of(page,20, Sort.by(Sort.Direction.valueOf(orderBy),sortBy));
Page<ComplainEntity> result =complainRepository.findAll(pageable);
return result.map(complainEntity -> toDTO(complainEntity));
}

How to use Pageable as get-query parameter in spring-data-rest?

I have a simple query controller that takes query parameters (as Person dto) and a Pageable:
#RestController
public class PersonController {
#GetMapping("/persons")
public Object search(org.springframework.data.domain.Pageable pageable, Person form) {
repository.findAll(form, pageable);
}
}
The pageable can take both sort and page parameters as follows:
http://localhost:8080/persons?sort=age,desc&page=5
Problem: for the sort, I want to add order hints like NULLS_LAST or NULLS_FIRST.
Question: how can I achieve this using query params too?
You can modify Pageable object like this (method to put in your controller for example). This is an example with one sort field, but you can loop on all fields and modify nullsLast/nullsFirst in the same way. Please try :
private Pageable customSort(Pageable pageable) {
Sort sort = pageable.getSort();
Order order = sort.iterator().next();
List<Order> orders = new ArrayList<>();
// nullsFirst or nullsLast
orders.add(new Order(order.getDirection(),order.getProperty()).nullsFirst());
return PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(orders));
}
Try to do something like :
pageable.getSort().and(Sort.by(new Sort.Order(Sort.Direction.DESC, "age", Sort.NullHandling.NULLS_LAST)));

Using Pagination in Spring JPA

I have a Spring Boot 1.5.2 project which up till now has been sending lists back to the view. I would like to use pagination instead of lists. I began to change the code in the service layer and repository, but it has not been a simple case of changing from List<T> to Page<T>.
In particular on the service layer I was returning different List<T> based on user role and then passing this list to a method that converts to Dto before sending it back to controller.
Spring Boot 1.5.2 appears to use Spring-data-jpa:1.11
Controller
#GetMapping("/dashboard/sale")
public String dashboard(#RequestParam(name = "p", defaultValue = "1") int pageNumber, Model model, HttpServletRequest request) {
List<SaleDashboard> listSaleDashboard = saleService.getPage(pageNumber);
model.addAttribute("listSaleDashboard", listSaleDashboard);
return "dashboard";
}
Service Layer
public List<SaleDashboard> getPage(int pageNumber) {
PageRequest request = new PageRequest(pageNumber - 1, PAGESIZE, Sort.Direction.ASC, "id");
List<Sale> listSale = new ArrayList<>();
if (roles.contains("ROLE_ADMIN")) {
listSale = saleRepository.findBySomeProperty(user.getUserDetails().getReportDept());
}
if (roles.contains("ROLE_USER")) {
listSale = saleRepository.findByListOfCreatingUser(userList);
}
List<SaleDashboard> listSaleDashboard = createSaleDashboard(listSale);
return listSaleDashboard;
}
public List<SaleDashboard> createSaleDashboard(List<Sale> sales) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
List<SaleDashboard> listSaleDashboard = new ArrayList<>();
for (Sale sale: sales) {
SaleDashboard saleDashboard = new SaleDashboard();
saleDashboard.setSaleId(sale.getId());
// ETC
listSaleDashboard.add(saleDashboard);
}
return listSaleDashboard;
}
In the service layer above I began to use PageRequest but thats as far as I got.
Repository
public interface SaleRepository extends JpaRepository<Sale, Long> , PagingAndSortingRepository<Sale, Long> {
#Query("SELECT e FROM Sale e WHERE e.creatingUser in (:userList)")
Page<Sale> findByListOfCreatingUser(#Param("userList") List<User> users, Pageable pageable);
}
How would I implement a similar service layer but use Page<T> instead of List<T>?

Conversion of List to Page in Spring

I am trying to convert list to page in spring. I have converted it using
new PageImpl(users, pageable, users.size());
But now I having problem with sorting and pagination itself. When I try passing size and page, the pagination doesn't work.
Here's the code I am using.
My Controller
public ResponseEntity<User> getUsersByProgramId(
#RequestParam(name = "programId", required = true) Integer programId Pageable pageable) {
List<User> users = userService.findAllByProgramId(programId);
Page<User> pages = new PageImpl<User>(users, pageable, users.size());
return new ResponseEntity<>(pages, HttpStatus.OK);
}
Here is my user Repo
public interface UserRepo extends JpaRepository<User, Integer>{
public List<User> findAllByProgramId(Integer programId);
Here is my service
public List<User> findAllByProgramId(Integer programId);
I had the same problem. I used subList:
final int start = (int)pageable.getOffset();
final int end = Math.min((start + pageable.getPageSize()), users.size());
final Page<User> page = new PageImpl<>(users.subList(start, end), pageable, users.size());
There is a Page implementation for that:
Page<Something> page = new PageImpl<>(yourList);
As indicated in the reference documentation, Spring Data repositories support pagination on query methods by simply declaring a parameter of type Pageable to make sure they're only reading the data necessary for the requested Page.
Page<User> page = findAllByProgramId(Integer programId, Pageable pageable);
That would return a Page object with the page size/settings defined in your Pageable object. No need to get a list and then try to create a page out of it.
You should do it like advised by the dubonzi's answer.
If you still want to use pagination for a given List use PagedListHolder:
List<String> list = // ...
// Creation
PagedListHolder page = new PagedListHolder(list);
page.setPageSize(10); // number of items per page
page.setPage(0); // set to first page
// Retrieval
page.getPageCount(); // number of pages
page.getPageList(); // a List which represents the current page
If you need sorting, use another PagedListHolder constructor with a MutableSortDefinition.
Try This:
public Page<Patient> searchPatientPage(SearchPatientDto patient, int page, int size){
List<Patient> patientsList = new ArrayList<Patient>();
Set<Patient> list=searchPatient(patient);
patientsList.addAll(list);
int start = new PageRequest(page, size).getOffset();
int end = (start + new PageRequest(page, size).getPageSize()) > patientsList.size() ? patientsList.size() : (start + new PageRequest(page, size).getPageSize());
return new PageImpl<Patient>(patientsList.subList(start, end), new PageRequest(page, size), patientsList.size());
}
This could be the solution. Sorting and pagination will work too this way:
Controller:
public ResponseEntity<User> getUsersByProgramId(
#RequestParam(name = "programId", required = true) Integer programId Pageable pageable) {
Page<User> usersPage = userService.findAllByProgramId(programId, pageable);
Page<User> pages = new PageImpl<User>(usersPage.getContent(), pageable, usersPage.getTotalElements());
return new ResponseEntity<>(pages, HttpStatus.OK);
}
Service:
Page<User> findAllByProgramId(Integer programId, Pageable pageable);
Repository:
public interface UserRepo extends JpaRepository<User, Integer>{
public Page<User> findAllByProgramId(Integer programId, Pageable pageable);
}
This way, we can also return different page of entity too.
In the JHipster framework there is an interface for such things PageUtil:
static <T> Page<T> createPageFromList(List<T> list, Pageable pageable) {
if (list == null) {
throw new IllegalArgumentException("To create a Page, the list mustn't be null!");
}
int startOfPage = pageable.getPageNumber() * pageable.getPageSize();
if (startOfPage > list.size()) {
return new PageImpl<>(new ArrayList<>(), pageable, 0);
}
int endOfPage = Math.min(startOfPage + pageable.getPageSize(), list.size());
return new PageImpl<>(list.subList(startOfPage, endOfPage), pageable, list.size());
}
You can use this generic function for converting List to page.
public static<T> Page<T> convertToPage(List<T> objectList, Pageable pageable){
int start = (int) pageable.getOffset();
int end = Math.min(start+pageable.getPageSize(),objectList.size());
List<T> subList = start>=end?new ArrayList<>():objectList.subList(start,end);
return new PageImpl<>(subList,pageable,objectList.size());
}
Implemented based on #shilaimuslm comment. In this case an exception will not be thrown if the start > end in subList.
List<User> users = // ...
Pageable paging = PageRequest.of(pagePagination, sizePagination);
int start = Math.min((int)paging.getOffset(), users.size());
int end = Math.min((start + paging.getPageSize()), users.size());
Page<User> page = new PageImpl<>(users.subList(start, end), paging, users.size());
//1) For a boot application create a paging repository interface
public interface PersonRepository extends PagingAndSortingRepository<Person,
String> {
// Common CURD method are automatically implemented
}
//2) create a service Interface
public interface PersonService {
Page<Person> listAllByPage(Pageable pageable); // Use common CURD findAll() method
Page<Object> listSpecByPage(Pageable pageable, String x);
}
//3) create a service Impl Class of service interface
#Service
public class PersonServiceImpl implements PersonService {
final PersonRepository personRepository;
#Autowired
PersonServiceImpl(PersonRepository personRepository){
this.personRepository = personRepository;
}
#Override
public Page<Person> listAllByPage(Pageable pageable) {
return personRepository.findAll(pageable);
}
#Override
public Page<Object> listSpecByPage(Pageable pageable, String path) {
List<Object> objectlist = new ArrayList<Object>();
// Do your process to get output in a list by using node.js run on a *js file defined in 'path' varriable
Page<Object> pages1 = new PageImpl<Object>(objectlist, pageable, objectlist.size());
return pages1;
}
}
//4) write your controller
public class PersonController {
final PersonService personService;
#Autowired
PersonController( PersonService personService ){
this.personService = personService;
}
#GetMapping("/get") // Use of findALL() function
Page<Person> listed( Pageable pageable){
Page<Person> persons = personService.listAllByPage(pageable);
return persons;
}
#GetMapping("/spec") // Use of defined function
Page<Object> listSpec( Pageable pageable, String path){
Page<Object> obj = personService.listSpecByPage(pageable, path);
return obj;
}
}
Thanks guys below code is working in my case
int start = pageble.getOffset();
int end = (start + pageble.getPageSize()) > vehicleModelsList.size() ? vehicleModelsList.size() : (start + pageble.getPageSize());
Have you tried extending your repository to PagingAndSortingRepository?
public interface UserRepo extends PagingAndSortingRepository<Ticket, Integer> {
Page<User> findAllByProgramId(Integer programId, Pageable pageable);
}
Service
Page<User> findAllByProgramId(Integer programId, Pageable pageable);
I assume you are using interface to the service:
Instead of returing complete array list take subblist as per your requirement.
You will get 'offset' and size from 'pageable' object in request body.
new PageImpl<User>(users.subList(start, end), pageable, users.size());
This is the correct answer to pagging a list
public ResponseEntity<User> getUsersByProgramId(
#RequestParam(name = "programId", required = true) Integer programId, Pageable pageable) {
List<User> users = userService.findAllByProgramId(programId);
final int toIndex = Math.min((pageable.getPageNumber() + 1) * pageable.getPageSize(),
bidList.size());
final int fromIndex = Math.max(toIndex - pageable.getPageSize(), 0);
Page<User> pages = new PageImpl<User>(users.subList(fromIndex, toIndex), pageable, users.size());
return new ResponseEntity<>(pages, HttpStatus.OK);
}
u didn't made paged result
new PageImpl<User>(users, pageable, users.size()); does not make paged result implicitly,
in this context, pageable argument just makes meta-data of Page object like page, offset, size... etc
So you have to use Repository method like
Page<User>findAllByProgramId(Integer programId, Pageable pageable);

Categories

Resources