Spring custom sorting in Sort.of - spring

Is there any way to use Sort.of with this kind of sorting?
#Query("SELECT l FROM Livestream l WHERE l.status IN ('LIVE', 'FUTURE') ORDER BY CASE WHEN l.status = 'LIVE' THEN -1 ELSE 0 END ASC")
fun findLiveAndFuture(pageable: Pageable): Page<Livestream>

I'm not sure about the usage of the case at the end since the result will most probably have both LIVE and FUTURE results, so the case won't work as expected.
You can use the object Sort in Pageable to define the sorting you want to have. This will require you to use something like PageRequest to achieve it.
Or you can use pass the Sort object in the repository, like so:
fun findLiveAndFuture(pageable: Pageable, sort: Sort): Page<Livestream>

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

Sorting by case using Hibernate

We have a typeahead that allows our customers to do a global search of their clients. Based on a 'filterText', we want to retrieve all the clients where any of the following fields contain the filterText: clientName, clientStreet, clientCity... but now there's a requirement and we want to prioritize the results where the clientName contains the filterText. (They should be shown first)
We currently create a customerSpecification, and the Predicate that it's being used is the following one:
return criteriaBuilder.or(
criteriaBuilder.like(customerName, likeFilter),
criteriaBuilder.like(customerStreet, likeFilter),
criteriaBuilder.like(customerCity, likeFilter),
criteriaBuilder.like(customerState, likeFilter),
criteriaBuilder.like(customerZip, likeFilter),
criteriaBuilder.like(customerCountry, likeFilter),
tempPred
);
and then we use it to get the Page with all the results
customerRepository.findAll(customerSpecification, pageable);
How can we introduce this new requirement? Any approaches?
Some pages suggest to use selectCase, doing something like this:
Expression<Object> caseExpression = criteriaBuilder.selectCase()
.when(criteriaBuilder.like(root.get(CustomerEntity_.name), likeFilter), 1)
.otherwise(2);
Order order = criteriaBuilder.desc(caseExpression);
criteriaQuery.orderBy(order);
But I can't find the way to make it works. I also found some talks about PageRequest, which receives a Sort parameter, but I didn't find how it would help, as this object is too simple for what we're looking for.
Thanks

Getting first row from a resultset with sort by descending using QueryDsl predicate and spring jpa

I am new to spring JPA . I have one query such that i've to get the resultset and take only the row at the top.I dont know how to do it in spring JPA.And i dont want it to be done using #Query annotation,Since i was asked not to go with any queries inside the code.This is the uery i want to convert
My Query
SELECT id,name FROM example_table ORDER BY id DESC LIMIT 1;
I tried something like this in my predicate file:
public Predicate getLatest(){
QExampleTable example = QExampleTable.exampleTable;
return (Predicate) example.id.desc();
}
and this is how my jpa repository looks like:
public ExampleTable findOne(MyPredicate.getLatest());
But this is'nt working out and i know it wont clearly.But I seriously dont know how to convert this above query.Can anyone help me out with this
You can do it using just QueryDSL without Predicate and Repositories.
List<ExampleTable> examples = new JPAQuery()
.from(QExampleTable.exampleTable)
.limit(1)
.orderBy(new OrderSpecifier<>(Order.DESC, QExampleTable.exampleTable.id))
.list(QExampleTable.exampleTable);
you can use offset or limit functions.
In your case .limit(1) should be enough
This won't work because desc() returns an OrderSpecifier that must be used with Query.orderBy().
The following could work, though it's not a "pure" Predicate:
public Predicate getLatest() {
QExampleTable example = QExampleTable.exampleTable;
return example.id.eq(new JPASubQuery().from(example).unique(example.id.max()));
}
I'm afraid a more clean solution is that you provide a CustomRepository + implementation.
With QueryDSL for me, the option that is working is using max().
JPAExpressions.selectFrom(QExampleTable.exampleTable)
.select(QExampleTable.exampleTable.max())

Rearranging active record elements in Yii

I am using a CDbCriteria with its own conditions, with & order clauses. However, the order i want to give to the elements in the array is way too complex to specify in the order clause.
The solution i have in mind consists of obtaining the active records with the defined criteria like this
$theModelsINeed = MyModel::model()->findAll($criteria);
and then rearrange the order from my php code. How can i do this? I mean, i know how to iterate through its elements, but i donĀ“t know if it is possible to actually change them.
I have been looking into this link about populating active records, but it seems quite complicated and maybe someone could have some better advice.
Thanks
There is nothing special about Yii's active records. The find family of methods will return an array of objects, and you can sort this array like any other array in PHP.
If you have complex sort criteria, this means that probably the best tool for this is usort. Since you will be dealing with objects, your user-defined comparison functions will look something like this:
function compare($x, $y)
{
// First sort criterion: $obj->Name
if ($x->Name != $y->Name) {
return $x->Name < $y->Name ? -1 : 1; // this is an ascending sort
}
// Second sort criterion: $obj->Age
if ($x->Age != $y->Age) {
return $x->Age < $y->Age ? 1 : -1; // this is a descending sort
}
// Add more criteria here
return 0; // if we get this far, the items are equal
}
If you do want to get an array as a result, you can use this method for fetching data that supports dbCriteria:
$model = MyModel::model()->myScope();
$model->dbCriteria->condition .= " AND date BETWEEN :d1 AND :d2";
$model->dbCriteria->order = 'field1 ASC, field2 DESC';
$model->dbCriteria->params = array(':d1'=>$d1, ':d2'=>$d2);
$theModelsINeed = $model->getCommandBuilder()
->createFindCommand($model->tableSchema, $model->dbCriteria)
->queryAll();
The above example shows using a defined scope and modifying the condition with named parameters.
If you don't need Active Record, you could also look into Query Builder, but the above method has worked pretty well for me when I want to use AR but need an array for my result.

Using LINQ Expression Instead of NHIbernate.Criterion

If I were to select some rows based on certain criteria I can use ICriterion object in NHibernate.Criterion, such as this:
public List<T> GetByCriteria()
{
SimpleExpression newJobCriterion =
NHibernate.Criterion.Expression.Eq("LkpStatu", statusObject);
ICriteria criteria = Session.GetISession().CreateCriteria(typeof(T)).SetMaxResults(maxResults);
criteria.Add(newJobCriterion );
return criteria.List<T>();
}
Or I can use LINQ's where clause to filter what I want:
public List<T> GetByCriteria_LINQ()
{
ICriteria criteria = Session.GetISession().CreateCriteria(typeof(T)).SetMaxResults(maxResults);
return criteria.Where(item=>item.LkpStatu=statusObject).ToList();
}
I would prefer the second one, of course. Because
It gives me strong typing
I don't need to learn yet-another-syntax in the form of NHibernate
The issue is is there any performance advantage of the first one over the second one? From what I know, the first one will create SQL queries, so it will filter the data before pass into the memory. Is this kind of performance saving big enough to justify its use?
As usual it depends. First note that in your second snippet there is .List() missing right after return criteria And also note that you won't get the same results on both examples. The first one does where and then return top maxResults, the second one however first selects top maxResults and then does where.
If your expected result set is relatively small and you are likely to use some of the results in lazy loads then it's actually better to take the second approach. Because all entities loaded through a session will stay in its first level cache.
Usually however you don't do it this way and use the first approach.
Perhaps you wanted to use NHibernate.Linq (located in Contrib project ). Which does linq translation to Criteria for you.
I combine the two and made this:
var crit = _session.CreateCriteria(typeof (T)).SetMaxResults(100);
return (from x in _session.Linq<T>(crit) where x.field == <something> select x).ToList();

Resources