I have an Angular based Application for Front End and Spring Boot for Back End.
My componaent had a variable that contains filters like that
search:any={
name:'',
surname:'',
address:'',
phone:'',
city:''
}
i pass it to my Api with Post Method like that:
getPeoplesByFilters(filter){
return this.http.post("http://localhost:8080/search", filter)
.map (resp => resp.json())
}
I receive this in my Spring Boot Controller by this method:
#RequestMapping(value="/search", method= RequestMethod.POST )
public Page<People> searchFilters(#RequestBody() People p,
#RequestParam(value="page", defaultValue="0") int page ,
#RequestParam(value="size", defaultValue="5") int size){
p.setName("%"+p.getName()+"%");
p.setSurname("%"+p.getSurname()+"%");
p.setAddress("%"+p.getAddress()+"%");
p.setPhone("%"+p.getPhone()+"%");
System.out.println(p.getName());
return poepleRepository.searchFilters( p , new PageRequest(page, size));
}
And in my Jpa Repository interface I have this query:
#Query("select p from poeple p where p.name like :#{#x.name} and p.surname like :#{#x.surname} and p.address like :#{#x.address} and p.phone like :#{#x.phone} and p.mobile like :#{#x.phone}")
public Page<People> searchFilters(#Param("x") People x, Pageable pageable);
But I don't have any result
I tried just with one field and i fill it in my front end App and it work
But when I fill just one field and let another empty i have zero result.
What's Wrong!
Thanks
My previous experience shows that using specifications is best for your case. Because it is more extendable and understandable. You can write specification like below.
public class Spec{
public static Specification<Person> filter(FilterParams filterParams) {
return (root, query, cb) -> {
List<Predicate> predicates = new ArrayList<>();
if(filterParams.getName()!=null){
predicates.add(cb.equal(root.get("name"),filterParams.getName()));
}
if(filterParams.getSurname()!=null){
predicates.add(cb.equal(root.get("surname"),filterParams.getSurname()));
}
....
return cb.and(predicates.toArray(new Predicate[predicates.size()]));
};
}
}
}
Then you can use like below:
#Service
public TestService{
#Autowired
PersonRepository personRepository;
void test(){
FilterParam filterParam = new FilterParam();
filterParam.setName("test);
...
personRepository.findAll(Spec.filter(filterParam));
}
}
Related
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
I am beginner with Java and Spring Boot, I use Pagination on Spring Boot, with this code I return the list of users, which I must if I want to also return the number of pages?
I know that with getTotalPages() I can get the page count but how to return it?
#Service
public class UserService{
#Autowired
UserRepository userRepository;
public List<UserDto> findAll(PageRequest pageRequest){
Page<User> userPage = userRepository.findAll(pageRequest);
List<UserDTO> dtos = new ArrayList<UserDTO>();
//return userPage.getContent();
for (User u : userPage.toList() ) {
dtos.add(new UserDTO(u));
}
return dtos;
}
}
The most common implementation of the Page interface is provided by the PageImpl class, you can use like this:
import org.springframework.data.domain.PageImpl;
...
Page<UserDTO> pageResult = new PageImpl<>(dtos,
userPage.getPageable(),
userPage.getTotalElements());
return pageResult;
If you want, you can also use the .map() function of page result, it can be preferred according to the approach. https://stackoverflow.com/a/39037297/2039546
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.
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>?
i am using Spring Boot with querydsl. I need to build this query somehow, but i can't do it:
SELECT address.id, address.city FROM address WHERE earth_box( ll_to_earth(48.97300592, 8.32746506), 3100) #> ll_to_earth(address.lattitude, address.longitude);
I try to customize the binding method of that parameter in my customize() method
How can i build this query?
EDIT:
My customize() method in ArticleRepositoryImpl
#Override
public void customize(QuerydslBindings bindings, QArticle root) {
bindings.bind(root.address.city).first((path, value) -> {
Predicate city = root.address.city.eq(value); // Here i need to build my Predicate, which is my SqL query in the top
return city;
});
And my Rest Controller:
#RequestMapping(method = RequestMethod.GET, value = "/article")
public ResponseEntity<Page<QArticle>> findAll(#QuerydslPredicate(root = Article.class) Predicate predicate,
Pageable p) {
System.out.println(predicate.toString());
Page<QArticle> resultPage = repo.findAll(predicate, p);
return new ResponseEntity<Page<QArticle>>(resultPage, HttpStatus.OK);
}