Spring #PageableDefault default direction changes after passing sort - java

Given I have this argument in my #RestController GET method:
#PageableDefault(size = 20, sort = "updated_at", direction = Direction.DESC)
When I GET without specifying sort then everything is fine: sort == update_at and direction == DESC.
But when I GET ...?page=1&size=33&sort=asdasd it ignores default direction and sets it to ASC.
Did not get any results of this being a bug. Is it not ?

#PageableDefault() has defaut direction ASC value, you can add as following
#SortDefault.SortDefaults({
#SortDefault(sort = "name", direction = Sort.Direction.DESC)
})
So the request controller look like
public ResponseEntity<Page<Brand>> findAll(
#PageableDefault(sort = { "name", "displayOrder" }, value = 10)
#SortDefault.SortDefaults({
#SortDefault(sort = "name", direction = Sort.Direction.DESC) })
Pageable pageable) {
Page<Brand> brandPage = brandService.findAll(pageable);
}

Related

Methods of adding many (unspecified number) objects form database via Rest service

I'm trying to write my own app. It should provide adding food products with parameters, what I was already done , further aim is to sum all property form added products, compare with daily balance, etc.
I have a problem with concept of connecting products to a meal. Im wondering if is some pretty alternative to function with specific number of (optional) parameters, witch is a certain limitation of functionality. Here is a makeshift solution, but I don't like it, so I'm not developing it for now. Is there a better way to do this?
#RequestMapping("/add")
public Integer adding(#RequestParam("i") Long index,
#RequestParam("i2") Long index2,
#RequestParam(value="i3", required = false, defaultValue = "0") Long index3,
#RequestParam(value="i4", required = false, defaultValue = "0") Long index4,
#RequestParam(value="i5", required = false, defaultValue = "0") Long index5,
#RequestParam(value="i6", required = false, defaultValue = "0") Long index6
){
Integer sum = null;
Integer i1 = productManager.findById(index).get().getCalories();
Products second = productManager.findById(index2).get();
Integer i2 = second.getCalories();
Integer i3,a,b,c;
if (index3==0){
i3=0;
} else {
Products thrid = productManager.findById(index3).get();
i3 = thrid.getCalories();
}
sum= i1+i2+i3;
return sum;
}
You can use list as query param.
#RequestMapping("/add")
public Integer adding(#RequestParam("index") List<Long> indicies){...}
The URL will be the following: http://yourhost:port/add?index=1&index=2&index=3
I suggest to use better name to method and url param then add. Maybe sumCalories or something like that.
If you use RDBMS to persist your products (can you give me more detail please?), you can write a query to sum the requiered value:
#Query("select sum(p.calorie) from Products p where p.id in :ids")
Long sumCaloriesByIds(#Param("ids") List<Long> ids);

Set default sort order in Pageable request

I want to use this code with specification-arg-resolver with #PageableDefault(sort = "createdAt", direction = Sort.Direction.DESC) in order to implement search and to set sort order but it's not working. I tried this:
#GetMapping("find")
public Page<PaymentTransactionsDTO> getAllBySpecification(
#PageableDefault(sort = "createdAt", direction = Sort.Direction.DESC)
#And({
#Spec(path = "unique_id", spec = LikeIgnoreCase.class)
}) Specification<PaymentTransactions> specification,
Pageable pageable
) {
return transactionService.getAllBySpecification(specification, pageable));
}
Do you know how I can set the sort order with annotations into the above code>
I don't know the library but I strongly expect the #DefaultPageable annotation needs to go on the Pageable argument.

JPA Pageable Sort order case sensitive

I am using JpaRepository Pageable query for pagination. All the things are working fine except the sort field case sensitive issue. Following query is used for getting list.
Pageable pageable = null;
if (paginationRequestDTO.getSortOrder().equalsIgnoreCase("desc"))
pageable = new PageRequest(page, size, Sort.Direction.DESC, sortfiled);
else
pageable = new PageRequest(page, size, Sort.Direction.ASC, sortfiled);
Page<Audi> audiPage = null;
audiencePage = audiRepository.search(paginationRequestDTO.getSearchKey(), pageable);
Audi table values are: apple,az,Ajay,Bala.
when i search with sortorder of asc and sort field name,
original output : Ajay,Bala,apple,az.
Expected output: Ajay,apple,az,Bala.
I am using mysql database. table engine - Innodb,characterst-utf8,collate-utf8_bin.
Please note that its not duplicate question.i didn't get exact answer for this question.thanks in advance.
Edited: as harsh pointed out correctly, this needs to be solved on database level, using correct collation. This is important, because you probably want to have an index on the sort column for best performance.
But there are other use cases, which could combine filtering together with sorting by something other, than a pure column value, e.g. by length of description, sum or average of a column, etc. For that reason, I am including a JPA solution:
I struggled with this recently and I am afraid, that the Pageable interface does not support this out of box.
The solution was to use EntityManager, CriteriaBuilder, CriteriaQuery, Specification and implement the paging manually. You can find the solution here.
You need to construct the Page object manually:
public Page<Audi> getPage(int pageNumber, int pageSize, String descriptionFilter, Sorting sorting) {
return new PageImpl<>(
getPageItems(pageNumber, pageSize, descriptionFilter, sorting),
PageRequest.of(pageNumber, pageSize, Sort.by(Sort.Direction.ASC, sorting.name())),
getTotalCount(descriptionFilter)
);
}
getPageItems selects the page using LIMIT and OFFSET
private List<Audi> getPageItems(int pageNumber, int pageSize, String descriptionFilter, Sorting sorting) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Audi> query = cb.createQuery(Audi.class);
Root<Audi> root = query.from(Audi.class);
query.where(createSpecification(descriptionFilter).toPredicate(root, query, cb));
if (sorting.equals(Sorting.descriptionCaseInsensitive)) {
query.orderBy(cb.asc(cb.lower(root.get("description"))));
} else {
throw new UnsupportedOperationException("Unsupported sorting: " + sorting.name());
}
query.select(root);
return em.createQuery(query)
.setFirstResult(pageNumber * pageSize)
.setMaxResults(pageSize)
.getResultList();
}
getTotalCount selects count(distinct(*)),
private long getTotalCount(String descriptionFilter) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Long> query = cb.createQuery(Long.class);
Root<Audi> root = query.from(Audi.class);
query.where(createSpecification(descriptionFilter).toPredicate(root, query, cb));
query.select(cb.countDistinct(root));
// getSingleResult can return null, if no rows fulfill the predicate
return Optional.ofNullable(em.createQuery(query).getSingleResult()).orElse(0L);
}
Both reuse the same predicate, which filters rows:
private Specification<Audi> createSpecification(String descriptionFilter) {
return Specification.where(
(root, query, criteriaBuilder) ->
criteriaBuilder.like(criteriaBuilder.lower(root.get("description")), "%" + descriptionFilter.toLowerCase() + "%")
);
}

JPA dynamically EntityGraph and Subgraphs

I created an util method:
public <T> Optional<T> fetch(Class<T> clazz, Object id, String... relations) {
EntityGraph<T> graph = entityManager.createEntityGraph(clazz);
Stream.of(relations).forEach(graph::addSubgraph);
return Optional.ofNullable(entityManager.find(clazz, id, Collections.singletonMap("javax.persistence.loadgraph", graph)));
}
So if for example if User has lazy orders and wallets, I can do this:
Optional<User> user = fetch(User.class, 1, "orders", "wallets");
But I don't know how to take orders's or wallets lazy collections. It would be greate if I would call method like this:
Optional<User> user = fetch(User.class, 1, "orders", "orders.products", "wallet");
How can I extend the method to achieve this?
I decided to use the next method:
public <T> Optional<T> fetch(Class<T> clazz, Object id, String... relations) {
EntityGraph<T> graph = entityManager.createEntityGraph(clazz);
Stream.of(relations).forEach(path -> {
String[] splitted = path.split("\\.");
Subgraph<T> root = graph.addSubgraph(splitted[0]);
for (int i = 1; i < splitted.length; i++)
root = root.addSubgraph(splitted[i]);
});
return Optional.ofNullable(entityManager.find(clazz, id, Collections.singletonMap("javax.persistence.loadgraph", graph)));
}
It has only one defect. The next two will work:
Optional<User> user = fetch(User.class, 1, "orders", "orders.products", "wallet");
Optional<User> user = fetch(User.class, 1, "orders.products", "wallet");
The next one will not:
Optional<User> user = fetch(User.class, 1, "orders.products", "orders", "wallet");
That's because orders overrides orders.products. I think it's enough, because logically if you want to load orders.products, you have to load orders anyway.

Representing multiple ordering through Spring #RequestParam

My Application currently serves requests for data, and can order the data to the users requirements, using RequestParams
#RequestParam(value = "orderBy", required = false, defaultValue = "severity") String orderBy,
#RequestParam(value = "order", required = false, defaultValue = "desc") String order,
You get the idea.
However, I want to implement multi sorting, (ordering by severity, then matching severities are ordered by date). Code wise, this is easy (google-collections to the rescue), but how would I expose this to the user calling the service?
Ideally, I wouldn't want multiple orderBy #RequestParams (orderBy2, orderBy3, orderBy4) , as that's just plain ugly.
Thoughts?
Cheers
Usually you should be able to just turn your request param into an array like:
#RequestParam(value = "orderBy", required = false, defaultValue = "severity") String[] orderBy,
#RequestParam(value = "order", required = false, defaultValue = "desc") String[] order,

Categories

Resources