Using Spring Data to Page - java

I have a class that returns a List<UserObject> containing 184 items to my repository. This isn't hitting a database but the List contains what I expect.
List<UserObject> objectList = UserObjectData.getObjectArray();
In my repository, I have a Pageable object that should return 2 items per page, starting at page 0. It has these attributes:
System.out.println(pageable.getPageSize()); // returns '2'
System.out.println(pageable.getOffset()); // returns '0'
System.out.println(pageable.getPageNumber()); // returns '0'
Then I create a Page<UserObject> like this:
Page<UserObject> theObjects = new PageImpl<UserObject>(objectList, pageable, objectList.size());
... and this is what comes back:
System.out.println(theObjects.getSize()); // returns '2'
System.out.println(theObjects.getNumber()); // returns '0'
System.out.println(theObjects.getNumberOfElements()); // returns '184'
System.out.println(theObjects.getTotalElements()); // returns '184'
System.out.println(theObjects.getTotalPages()); // returns '92'
I pass Page<UserObject> back to a controller that returns it to a REST client but that response contains all 184 items, not 2 like I expect.
Does the above code look correct; especially the new PageImpl<UserObject>? If so, my problems might be in how I'm handling the Page<UserObject>
in the controller and I'll focus on figuring out what I'm doing wrong there.
EDIT: following is the code in full:
#RestController
public class UserController {
private UserObjectService service;
NewUserRepository newUserRepository;
#RequestMapping(value = "getObjects", method = RequestMethod.GET)
public Page<UserObject> getObjects(#RequestParam int page, #RequestParam int size) {
Pageable pageable = new PageRequest(page,size);
newUserRepository = new NewUserRepository();
Page<UserObject> newPageResult = newUserRepository.findAll(pageable);
return newPageResult;
}
public class NewUserRepository implements PagingAndSortingRepository<UserObject, Long> {
public Page<UserObject> findAll(Pageable pageable) {
List<UserObject> objectList = UserObjectData.getObjectArray(); // I can verify this contains 184 items
Page<UserObject> theObjects = new PageImpl<UserObject>(objectList, pageable, objectList.size());
return theObjects;
}

Change page object creation to this:
Page<UserObject> theObjects = new PageImpl<UserObject>(objectList, pageable, (pageable.getOffset() + pageable.getPageSize()));

Related

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));
}

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);

Passing pageable (spring data) in post request

I have a rest API server which has the following API.
I have some other APIs, where i get pageable from GET requests. Here, I need to make a post request for passing the queryDto. So, I cannot pass the page=0?size=20 etc as url parameters.
I would like to know how to pass pageable as JSON object to a POST request
#RequestMapping(value = "/internal/search", method = RequestMethod.POST)
#ResponseStatus(HttpStatus.OK)
#ResponseBody
public ResponseList<Object> findObjects(#RequestBody QueryDto queryDto, Pageable pageable) {
if (queryDto.isEmpty()) {
throw new BadRequestException();
}
return someService.findObjectsByQuery(queryDto, pageable);
}
I Think that is not possible, at least not already provided by the framework.
The Spring has a HandlerMethodArgumentResolver interface with an implementation called PageableHandlerMethodArgumentResolver that retrieves the request param value calling something like HttpServletRequest.getParameter. So, you can bind the Pageable instance passing the parameters "page" and "size" for GET and POST. So, the following code works:
#RequestMapping(value="/test",method = RequestMethod.POST)
#ResponseBody
public String bindPage(Pageable page){
return page.toString();
}
$ curl -X POST --data "page=10&size=50" http://localhost:8080/test
Return:
Page request [number: 10, size 50, sort: null]
But, if you pass an json nothing happens:
$ curl -X POST --data "{page:10&size:50}" http://localhost:8080/test
Return:
Page request [number: 0, size 20, sort: null]
Spring Post method
#RequestMapping(value = "/quickSearchAction", method = RequestMethod.POST)
public #ResponseBody SearchList quickSearchAction(#RequestParam(value="userId") Long userId,
Pageable pageable) throws Exception {
return searchService.quickSearchAction(userId, pageable);
}
Postman Example:
http://localhost:8080/api/actionSearch/quickSearchAction?
userId=4451&number=0&size=20&sort=titleListId,DESC
In above POST Pageable is used for Sorting and Pagination in Spring RESTful service. Use below syntax at URL.
number 0, size 20, Sort by field titleListId and direction DESC
All passing parameter internally recognizes by Pageable as Sorting / Pagination parameters as below
number - Page number
size - Page Size
sort - sort by(Order by)
direction - ASC / DESC
Updated:
Angular Example:
CustomerComponent.ts file
let resultDesignations = null;
let fieldName = "designationId";
this.customerService.getDesignations(fieldName, "DESC").subscribe(
(data) => {
resultDesignations = data;
},
(err) => {
this.error(err.error);
},
() => {
this.designations = resultDesignations;
}
);//END getDesignations`
CustomerService.ts
getDesignations(fieldName: string, sortOrder: string): Observable<any> {
return this.httpClient.get("http://localhost:9876/api/getDesignations", {
params: {
sort: fieldName,sortOrder
}
});
}
It seems to work just fine for me if you continue to provide them as query parameters on the URL, and still post data in.
POST http://localhost:8080/xyz?page=2&size=50
Content-Type: application/json
{
"filterABC": "data"
}
Spring seems to translate the page, size, sort etc. into the Pageable provided to the method on the way in.
create a class that has the Pageable and QueryDto objects as members. Then pass JSON in the post body of this new object.
for example,
public class PageableQueryDto
{
private Pageable pageable;
private QueryDto queryDto;
... getters and setters.
}
Edit
As noted in the comment below, you may need to implement the Pageable interface.
The result could be something like this:
public class PageableQueryDto implements Pageable
{
private Pageable pageable;
private QueryDto queryDto;
... getters and setters.
... Implement the Pageable interface. proxy all calls to the
... contained Pageable object.
... for example
public void blam()
{
pageable.blam();
}
... or maybe
public void blam()
{
if (pageable != null)
{
pageable.blam();
}
else
{
... do something.
}
}
sample example
#RequestMapping(path = "/employees",method = RequestMethod.POST,consumes = "application/json",produces = "application/json")
ResponseEntity<Object> getEmployeesByPage(#RequestBody PageDTO page){
//creating a pagable object with pagenumber and size of the page
Pageable pageable= PageRequest.of(page.getPage(),page.getSize());
return ResponseEntity.status(HttpStatus.ACCEPTED).body(employeeRegistryService.getEmployeesByPage(pageable));
}
In your case try to add pagination variables in QueryDTO
create a Pageable object and pass it to service
I think that will solve :)

Convert Page<Entity> to PageDTO<EntityDTO>

I am using spring data JPA.
my controller looks like following
#RequestMapping(value = "/pages/{pageNumber}", method = RequestMethod.GET, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Page<User>> paginatedUser(#PathVariable final Integer pageNumber)
{
final PageRequest request = new PageRequest(pageNumber - 1, DEFAULt_PAGE_SIZE, Sort.Direction.DESC, "startTime");
return new ResponseEntity<>(userRepository.findAll(request), HttpStatus.OK);
}
Now i decided to send instead of Page object, a PageDTO object to restrict things from sending.Is there any way i can convert Page to PageDTO using java 8.
I saw Page is derived from Iterable So i guess i can do something like following but not sure how to put it together with PageDTO and UserDTO.
StreamSupport.stream(userRepository.findAll(request).spliterator(),false)
is there any effecient java 8 way to do this.
I came up with this solution
#RequestMapping(value = "/pages/{pageNumber}", method = RequestMethod.GET, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<PageDTO> paginatedUser(#PathVariable final Integer pageNumber)
{
final PageRequest request = new PageRequest(pageNumber - 1, DEFAULt_PAGE_SIZE, Sort.Direction.DESC, "startTime");
final Page<User> page = userRepository.findAll(request);
return new ResponseEntity<>(new PageDTO(page, StreamSupport.stream(page.getContent().spliterator(), true).map(UserDTO::new)
.collect(Collectors.toList())), HttpStatus.OK);
}
public class PageDTO {
private int beginIndex;
private int currentIndex;
private int endIndex;
private List<?> entities;
public PageDTO(final Page<?> page, final List<?> entities) {
this.entities = entities;
this.currentIndex = page.getNumber() + 1;
this.beginIndex = Math.max(1, currentIndex - 5);
this.endIndex = Math.min(beginIndex + 10, page.getTotalPages());
}
Would like to know if there is another effecient way to do this?
I know this is an old question, but I ran into the same problem and I'd like to provide a possible solution to whomever may be interested. Here is what I found that helped me with my code:
https://github.com/pkainulainen/spring-data-jpa-examples/blob/master/query-methods/src/main/java/net/petrikainulainen/springdata/jpa/todo/TodoMapper.java
I also used JpaRepository which paginates the data, so that the DTO page will have the same parameters (page number, size etc). Here is my repository:
#Repository
public interface Repository extends JpaRepository<Entity, Integer> {
/**
* Name the method according to what query you need to execute
* e.g. findAll --> return all the rows that satisfy the following conditions,
* ByUsername --> username is a field in entity class,
* #param pageable: pagination is applied on the data.
* #return
*/
public Page<Entity> findAllByUsername(String username, Pageable pageable);
}
This is the method where I do the mapping:
public Page<EntityDTO> findByUsername(String username, Pageable pageable){
Page<Entity> entityPage = entityRepository.findAllByUsername(username, pageable);
List<EntityDTO> dtos = mapper.entityToEntityDTOs(entityPage.getContent());
return new PageImpl<>(dtos, pageable, entityPage.getTotalElements());
}
And my Mapstruct mapper:
import org.mapstruct.factory.Mappers;
/**
* Mapper for converting entity to DTO.
*/
#Mapper(componentModel = "spring", uses = {})
public interface Mapper {
/**
* The interface declares a member INSTANCE, providing clients access to the mapper implementation,
* which is the file target\generated-sources\com\company\springapp\dto\mappers\MapperImpl.java
* (automatically generated when compiling the project).
*/
AuditMapper INSTANCE = Mappers.getMapper( Mapper.class );
/**
* Convert entity to DTO.
* Mappings are used to 'bind' entity fields to DTO fields (for the mapper's implementation).
* #param entity
* #return
*/
#Mappings({
#Mapping(source = "id", target = "id"),
#Mapping(source = "username", target = "dtoUsername"),
#Mapping(source = "action", target = "dtoAction")
})
public EntityDTO entityToEntityDTO(Entity entity);
/**
* Convert entities' list to DTOs' list.
* #param entities
* #return
*/
public List<EntityDTO> entitiesToEntityDTOs(List<Entity> entities);
}
It's a too late answer, but this solution worked for me
Service
public Page<EntityDto> getAllEntities(Pageable pageable) {
return entityRepository.findAll(pageable).map(entityMapper::toEntityDto);
}
Mapper
import org.mapstruct.Mapper;
#Mapper(componentModel = "spring")
public interface EntityMapper {
EntityDto toEntityDto(Entity entity);
}
Controller
#GetMapping(path = "/entities")
public List<EntityDto> getAllEntities(Pageable pageable) {
Page<EntityDto> page = entityService.getAllEntities(pageable);
return page.getContent();
}
this will return a list of entities with size = 10 from the first page
http://localhost:port/api/entities?page=0&size=10
Thanks to JHipster!

FIlter data with Spring coming from request

I have some infromation coming from request such as:
http://localhost:9080/online/accounts/list/usersQuery?filter=uid&value=ab
And I have to treat this in Spring where the object is uid and the filter value is ab
So far I have the folowing code in Spring:
#RequestMapping(produces="application/json", value = "/usersQuery", method=RequestMethod.GET)
public #ResponseBody PagedResources<Resource<UserDetails>> listItemsSortQuery(#PageableDefault(size = 20, page = 0) Pageable pageable, PagedResourcesAssembler<UserDetails> assembler) {
Page<UserDetails> lstUserDetails = userDetailsRepository.findAll(pageable);
return assembler.toResource(lstUserDetails);
}
But it doesn't consider nothing about those two values.
What should I change in order to filter data according to the field uid and filter data ab ?
The uid is the user id in the user object and I need to pick all the users that have an id containing ab
Any help would be apreciated.
Try getting uid value with #RequestParam
#RequestMapping(produces="application/json", value = "/usersQuery", method=RequestMethod.GET)
public #ResponseBody PagedResources<Resource<UserDetails>> listItemsSortQuery(#PageableDefault(size = 20, page = 0) Pageable pageable, PagedResourcesAssembler<UserDetails> assembler,
#RequestParam("filter")String filter, #RequestParam("value")String value) {
Page<UserDetails> lstUserDetails = userDetailsRepository.findByFilter(pageable, filter, value);
return assembler.toResource(lstUserDetails);
}
EDITED:
In your repository you need a method to filter your data, i.e.
public interface UserDetailsRepository extends JpaRepository<UserDetails, Long> {
#Query("SELECT u FROM UserDetails u WHERE LOWER(?1) = LOWER(?2)")
Page<UserDetails> findByFilter(String filter, String value, Pageable pageable);
}

Categories

Resources