How to do Pagination with filter - java

I want to make paginable service with filter but its fail to resultset
Here is my controller
#GetMapping(path = "/PageFilter")
public DataResponsePagination<HistoryBankWrapper, HistoryBank> pageFilter(
#RequestParam("filter") String keyfilter ,
#RequestParam("sortField") String field ,
#RequestParam("sortOrder") String order ,
#RequestParam("page") int page,
#RequestParam("size") int size) {
return new DataResponsePagination<HistoryBankWrapper, HistoryBank>(historyBankService.findByFilter(keyfilter,field,order, page, size));
}
Here is my service
public PaginationList<HistoryBankWrapper, HistoryBank> findByFilter(String keyfilter,String sortField,String sortOrder, int page, int size) {
Pageable paging = PageRequest.of(page, size);
Page<HistoryBank> historyPage = historyBankRepository.findAllFilter(keyfilter, sortField, sortOrder, paging);
List<HistoryBank> historyList = historyPage.getContent();
List<HistoryBankWrapper> historyWrapperList = toWrapperList(historyList);
return new PaginationList<HistoryBankWrapper, HistoryBank>(historyWrapperList, historyPage);
}
here is my Repository
#Query(value = "SELECT * FROM HISTORY_BANK WHERE :sortField LIKE '%' || :keyFilter || '%' ORDER BY :sortField :sortOrder",
countQuery = "SELECT count(*) FROM HISTORY_BANK",
nativeQuery = true)
Page<HistoryBank> findAllFilter(#Param("keyFilter") String keyfilter, #Param("sortOrder") String sortOrder,#Param("sortField") String sortField, #Param("paging") Pageable paging);

Ditch your own method and use the framework. Use specifications to create a dynamic query.
Your repository should extend the JpaSpecificationExecutor and then you should invoke the findAll(Specification, Pageable) from your service. Your service should prepare the Specification it needs to build the query.
public interface YourRepository extends JpaRepository<HistoryBank, Long>,
JpaSpecificationExecutor<HistoryBank> {}
Your service can then use the aforementioned findAll method.
public PaginationList<HistoryBankWrapper, HistoryBank> findByFilter(String keyfilter,String sortField,String sortOrder, int page, int size) {
Sort sort = Sort.by(Sort.Direction.fromString(sortOrder), sortField);
Pageable paging = PageRequest.of(page, size);
Specification<HistoryBank> spec = (r, c, b) -> b.like(r.get(sortField), "%" + keyFilter + "%"));
Page<HistoryBank> historyPage = historyBankRepository.findAllFilter(spec, paging);
List<HistoryBank> historyList = historyPage.getContent();
List<HistoryBankWrapper> historyWrapperList = toWrapperList(historyList);
return new PaginationList<HistoryBankWrapper, HistoryBank>(historyWrapperList, historyPage);
}
You can even make this easier by modifying your controller to directly bind to a Pageable object, that saves you creating it.
public DataResponsePagination<HistoryBankWrapper, HistoryBank> pageFilter(
#RequestParam("filter") String keyfilter ,
Pageable page) {
{
Now you only need to create the Specification and can just pass along the Pageable.

In the Jpa repository you use the symbol of OR (||) try with a keyword or
and modified your query for filtering you have to use and instead of or

Related

How to query same table with same parameter but with different where conditions and get combined result at last

I have a global search I will search with one keyword but need to get results with all the matching columns of a table.
Page<A> a = null;
a = zRepo.getResultByNameSearch(searchText)
a = zRepo.getResultByNumberSeach(searchText)
a = zRepo.getRsultByProjectSearch(searchText)
#Query("select * from a_table x where x.name like :searchText")
Page<A> getResultByNameSearch(#Param("searchText") String searchText, Pageable pageable);
#Query("select * from a_table where x.number like :searchText")
Page<A> getResultByNumberSearch(#Param("searchText") String searchText, Pageable pageable);
#Query("select * from a_table where x.project like :searchText")
Page<A> getResultByProjectSearch(#Param("searchText") String searchText, Pageable pageable);
So each repository call queries and fetches the same table but according to the searchText.
Let's assume name = "Company910", number = "XX910", project = "910".
Now I'm searching for "910" and want to get results with all the 3 values. Page<a> will be having all the columns of a_table with the list of results as per "Company910", "XX910", "910".
How to implement this or is there any other way where I can maintain a single query instead of three different for name, number and project?
For the first part of how to get the combined result. I will do something like this.
Create repository interface as you wrote but using spring data jpa intead of raw query
user CompletableFuture with each method call
combine the result into a dto
return this combined result
#Repository
public interface SampleDocumentRepository extends JpaRepository<SampleDocument, Integer> {
Page<SampleDocument> findByNameContains(String name, Pageable pageable);
Page<SampleDocument> findByNumberContains(String number, Pageable pageable);
Page<SampleDocument> findByProjectContains(String project, Pageable pageable);
}
Service class where result are combined together
#Service
#AllArgsConstructor
public class SampleDocumentService {
private final SampleDocumentRepository repository;
#Transactional(readOnly = true)
public CompletableFuture<ResultDto> search(String query) {
PageRequest page = PageRequest.of(0, 20);
CompletableFuture<Page<SampleDocument>> nameSearch = CompletableFuture.supplyAsync(() -> repository.findByNameContains(query, page));
CompletableFuture<Page<SampleDocument>> numberSearch = CompletableFuture.supplyAsync(() -> repository.findByNumberContains(query, page));
CompletableFuture<Page<SampleDocument>> projectSearch = CompletableFuture.supplyAsync(() -> repository.findByProjectContains(query, page));
return CompletableFuture.allOf(nameSearch, numberSearch, projectSearch)
.thenApply(unused -> new ResultDto(nameSearch.join(), numberSearch.join(), projectSearch.join()));
}
}
Then the call from the service
#GetMapping("/search")
public CompletableFuture<ResultDto> search(#RequestParam("query") String query) {
return service.search(query);
}
call using your query argument
http://localhost:8080/sample/search?query=hello
To answer the second part, if you want to check if the query is present in any of the columns you can write JPA query combining the Or operator like this.
Page<SampleDocument> findByNameContainsOrNumberContainsOrProjectContains(String name, String number, String project, Pageable pageable);
Caller would be something like this
#Transactional(readOnly = true)
public CompletableFuture<ResultDto> searchAll(String query) {
PageRequest page = PageRequest.of(0, 20);
CompletableFuture<Page<SampleDocument>> nameSearch = CompletableFuture.supplyAsync(() ->
repository.findByNameContainsOrNumberContainsOrProjectContains(query, query, query, page));
return CompletableFuture.supplyAsync(() -> new ResultDto(nameSearch.join(), null, null));
}
If you want to use Async with spring data and completable please follow this link

Implementing Page with Filtering

i have this repository, service and controller, and i have no ideea how to solve the error that i m getting in service, any suggestions please? (Type mismatch: cannot convert from List<UtenteDto> to Page<UtenteDto>)
String FILTER_UTENTE_NOME_AND_COGNOME_QUERY = "select b from Utente b where UPPER(b.nome) like CONCAT('%',UPPER(?1),'%') and UPPER(b.cognome) like CONCAT('%',UPPER(?2),'%')";
#Query(FILTER_UTENTE_NOME_AND_COGNOME_QUERY)
List<Utente> findByFirstNameLikeAndLastNameLike(String nomeFilter, String cognomeFilter,Pageable pageable);
public Page<UtenteDto> findByFirstNameLikeAndLastNameLike(String nomeFilter, String cognomeFilter, int page, int size){
Pageable pageable= PageRequest.of(page, size);
return utenteRepository.findByFirstNameLikeAndLastNameLike(nomeFilter,cognomeFilter,pageable)
.stream()
.map(e->mapper.map(e,UtenteDto.class))
.collect(Collectors.toList());
}
#GetMapping("/utenti")
#ResponseStatus(value = HttpStatus.OK)
public Page<UtenteDto> getAllFilteredByNomeAndCognome(#RequestParam(defaultValue = "") String nomeFilter,
#RequestParam(defaultValue = "") String cognomeFilter,
#RequestParam(defaultValue = "0") int page,
#RequestParam(defaultValue = "30") int size){
AccessTokenAccountRepository repository;
return utenteService.findByFirstNameLikeAndLastNameLike(nomeFilter,cognomeFilter,page,size);
}
Your method is declared to return Page, but you are actually returning List.
Work with a page instead, best would be to change repository method to return Page and then use map() to change entities to dtos.
Page<Utente> page = get from repo;
Page<UtenteDto> dtoPage = page.map(e -> mapper.map(e,UtenteDto.class));

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

Either use #Param on all parameters except Pageable and Sort typed once Spring boot

I saw some questions about this error but I could not find a solution for my case.
I'm implementing a paging in the spring boot application.
I Have this method in my controller
#RequestMapping(method = RequestMethod.GET, value = "/distrito", params = { "page", "size" })
public ResponseEntity<Page<Distritos>> buscarTodosDistritos(HttpServletRequest request, #RequestParam("page") int page, #RequestParam("size") int size) throws ServletException {
Map<String, String>informacaoUsuario = uService.getInformacoesUsuario(request);
Page<Distritos> distritosBuscados = distritosService.buscarFiltro(Long.parseLong(informacaoUsuario.get("idEntidadeSelecionada")), page, size);
return new ResponseEntity<>(distritosBuscados, HttpStatus.OK);
}
and my service
public Page<Distritos> buscarFiltro(Long usuarioEntidade ,int size, int page){
return distritosRepository.encontrar(usuarioEntidade, size, page);
}
my repository
#Query( nativeQuery=true, value="SELECT dist.nome, dist.id_distrito, dist.id_entidade, dist.id_municipio, dist.id_uf, dist.codigo_dne, dist.flag_ativo, enti.nome Entidade, muni.nome Municipio, unfe.nome UF FROM glb.distritos dist, glb.entidades enti, glb.municipios muni, glb.ufs unfe WHERE dist.id_entidade = enti.id_entidade AND dist.id_municipio = muni.id_municipio AND muni.id_uf = unfe.id_uf and enti.id_entidade = :parametroId order by nome ")
public Page<Distritos> encontrar(#Param ("parametroId")Long usuarioEntidade, int size, int page);
and i got this error
Caused by: java.lang.IllegalArgumentException: Either use #Param on all parameters except Pageable and Sort typed once, or none at all!
at org.springframework.util.Assert.isTrue(Assert.java:92) ~[spring-core-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.data.repository.query.Parameters.assertEitherAllParamAnnotatedOrNone(Parameters.java:297) ~[spring-data-commons-1.13.4.RELEASE.jar:na]
at org.springframework.data.repository.query.Parameters.<init>(Parameters.java:91) ~[spring-data-commons-1.13.4.RELEASE.jar:na]
how can i solve that ???
You have to pass a Pageable Object and not the size and page like you do :
public Page<Distritos> encontrar(#Param ("parametroId") Long usuarioEntidade,
Pageable pageable);
and call your method like this
return distritosRepository.encontrar(usuarioEntidade, new PageRequest(size, page));
You can either create a Pageable Object which hold a sorted attribute, so instead of using order by nome in your query you can use :
Sort sort = new Sort(new Sort.Order(Direction.ASC, "nome"));
Pageable pageable = new PageRequest(size, page, sort);
return distritosRepository.encontrar(usuarioEntidade, pageable);
Check org.springframework.data.domain.Pageable class which is provided by Spring for pagination. Controller will extract your parameters and build it automatically.
Pass the Pageable object instead:
public Page<Distritos> buscarFiltro(Long usuarioEntidade ,int size, int page){
Pageable pageable = new PageRequest(page, size);
return distritosRepository.encontrar(usuarioEntidade, pageable);
}
and repo:
public Page<Distritos> encontrar(#Param ("parametroId")Long usuarioEntidade
, Pageable pageable);

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