Spring boot - subtraction inside sortBy (Pageable) - spring

Is it possible to subtract two fields(numbers) inside sortBy in pagerequest like this??
PageRequest.of(page, size, Sort.Direction.ASC, "price-discount")
.
This is error i get..
Sort expression 'price-discount: ASC' must only contain property references or aliases used in the select clause. If you really want to use something other than that for sorting, please use JpaSort.unsafe(…)!

Creates a new PageRequest with sort direction and properties applied.
PageRequest.of(page, size, Sort.Direction.ASC, "price", "discount");
OR
Creates a new PageRequest with sort parameters applied.
Sort sort = Sort.by(
Sort.Order.asc("price"),
Sort.Order.desc("discount"));
PageRequest.of(page, size, sort);

Related

Spring JPA repository method to get sorted distinct and non-null values

To get distinct data based on multiple columns and exclude NULL values on a column and sort the result in SQL, I would write query like:
SELECT DISTINCT CAR_NUMBER, CAR_NAME
FROM CAR
WHERE CAR_NUMBER IS NOT NULL
ORDER BY CAR_NUMBER
This would return me rows with distinct values for CAR_NUMBER and CAR_NAME and it would exclude any rows having CAR_NUMBER = NULL and finally, it would sort the result by CAR_NUMBER.
However, In Spring JPA, I gather you can use either methods named based on your entity fields or using #Query annotation.
I am trying to do this:
List<Car> findDistinctByCarNumberAndCarNameAndCarNumberIsNotNull(Sort sort);
, and to call this method like:
myRepo.findDistinctByCarNumberAndCarNameAndCarNumberIsNotNull(Sort.by("carNumber"));
but this is failing on Maven > Install with error like "findDistinctByCarNumberAndCarNameAndCarNumberIsNotNull(Sort sort) expects at least 1 arguments but only found 0".
Similarly, I tried using #Query like below but with same effect:
#Query(SELECT DISTINCT c.carNumber, c.carName FROM carEntity c WHERE c.carNumber IS NOT NULL ORDER BY c.carNumber)
List<Car> findAllCars();
I figured out the problem. Following is how I solved it:
In my repository:
#Query("select distinct c.carNumber, c.carName from CarEntity c where c.carNumber is not null")
List<Object> findAllDistinctRegions(Sort sort);
Important here to realize is that #Query returns List<Object>, not List<Car>.
Next, in my service, call this method:
List<Object> carData = carRepository.findAllDistinctCars(Sort.by("carNumber"));
That worked finally fine; however, I run into another problem where I had to do necessary conversion from List to List.
// This is bit tricky as the returned List<Object> is actually
// List<Object[]>. Basically, each field returned by the #Query
// is placed into an array element.
//To solve it, I had to do following:
List<Car> cars = new ArrayList<Car>();
for(Object data: carsData) {
Object[] obj = (Object[]) data;
cars.add(new CarDto((Short) obj[0], ((String) obj[1]));
}
I just remembered there is a better way to solve this than that helper function that you described in your answer and thought I would share it.
Projection in JPQL would be a cleaner way to create your DTO:
#Query("SELECT DISTINCT new com.yourdomain.example.models.MyDto(c.carNumber, c.carName)
FROM CarEntity c WHERE c.carNumber is not null")
List<CarDto> findAllDistinctRegions(Sort sort);

Spring boot JPA Specification API with Pagination and Sorting

Im trying to implement a search in spring boot. Since the search params is dynamic, i had to go for specification api. My requirement is to search for orders given certain params and sort those orders by creation date. Since data can be large, the api should also support for pagination.
Below is the code snipped of the predicates.
public search(OrderSearchCriteria searchCriteria, Integer pageNo, Integer pageSize) {
Pageable pageRequest = (!ObjectUtils.isEmpty(pageNo) && !ObjectUtils.isEmpty(pageSize))
? PageRequest.of(pageNo, pageSize)
: Pageable.unpaged();
Page<Order> ordersPage = this.dao.findAll(((Specification<Order>)(root, query, criteriaBuilder) -> {
// my search params, its more than what is shown here
Long id = searchCriteria.getId();
Priority priority = searchCriteria.getPriority();
List<Predicate> predicates = new ArrayList<>();
if(!ObjectUtils.isEmpty(id))
predicates.add(criteriaBuilder.and(criteriaBuilder.equal(root.get("id"), id)));
if(!ObjectUtils.isEmpty(priority))
predicates.add(criteriaBuilder.and(criteriaBuilder.equal(root.get("priority"), priority)));
// This line is causing the issue
query.orderBy(criteriaBuilder.desc(root.get("creationDate")));
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
}), pageRequest);
The issue is, if i add this sort by line query.orderBy(criteriaBuilder.desc(root.get("creationDate")))
Pagination is not working. page 0 and page 1 both shows same result(same order) given size as 1. But page 2 shows different result as expected. But if i remove the sort by line shown above, the code is working as expected. How do i support both pagination and sorting without having these issues? i tried applying sort in pagerequest as well. But same issue PageRequest.of(pageNo, size, sort.by(DESC, "creationDate")). Appreciate help.
What do you mean by "it shows same result"? There might be multiple entries that have the same creation date and if you omit a sort, Spring Data will by default sort by id to provide a consistent result.

Crud Repository get from position until position

So I have a table of tags. Tag has an id and a name.
As a first step I wanted to sort all the IDs by descending order
List<Tag> findAllByOrderByIdDesc()
Next I wanted just to get first three tags and got it done by doing
List<Tag> findTop3ByOrderByIdDesc()
Now I want to get all tags in descending order from position x until position x+3 but I can't seem to find or figure out what to do here.
You can pass Pageable parameter.
Example:
List<Tag> findTop3ByOrderByIdDesc(Pageable page);
In the Pageable parameter you need to pass page number and offset.
Consider if you want to get values range from id 20 to 30.
PageRequest.of(2,10);
pass this as your Pageable parameter.
Pageable is a good idea but you have to use PagingAndSortingRepository or JpaRepository, not CrudRepository.

Spring data - Order by multiplication of columns

I came to a problem where I need to put ordering by multiplication of two columns of entity, for the sake of imagination entity is:
#Entity
public class Entity {
#Column(name="amount")
private BigDecimal amount;
#Column(name="unitPprice")
private BigDecimal unitPrice;
.
.
.
many more columns
}
My repo interface implements JpaRepository and QuerydslPredicateExecutor,
but I am struggling to find a way to order my data by "amount*unitPrice",
as I can't find a way to put it into
PageRequest (new Sort.Order(ASC, "amount * unitPrice"))
without having PropertyReferenceException: No property amount * unitPrice... thrown.
I can't user named query, as my query takes quite massive filter based on user inputs (can't put where clause into query, because if user hasn't selected any value, where clause can't just be in query).
To make it simple. I need something like findAll(Predicate, Pageable), but I need to force that query to order itself by "amount * unitPrice", but also have my Preditate (filter) and Pageable (offset, limit, other sortings) untouched.
Spring Sort can be used only for sorting by properties, not by expressions.
But you can create a unique sort in a Predicate, so you can add this sort-predicate to your other one before you call the findAll method.

MongoTemplate method or query for finding maximum values from a fileds

I am using MongoTemplate for my DB operations. Now i want to fetch the maximum fields values from the selected result. Can someone guide me how i write the query so that when i pass the query to find method it will return me the desired maximum fields of document . Thanks in advance
Regards
You can find "the object with the maximum field value" in spring-data-mongodb. Mongo will optimize sort/limit combinations IF the sort field is indexed (or the #Id field). Otherwise it is still pretty good because it will use a top-k algorithm and avoid the global sort (mongodb sort doc). This is from Mkyong's example but I do the sort first and set the limit to one second.
Query query = new Query();
query.with(new Sort(Sort.Direction.DESC, "idField"));
query.limit(1);
MyObject maxObject = mongoTemplate.findOne(query, MyObject.class);

Resources