JPQL and Enum. If you need any Enum - enums

I have a JPQL query that includes several filters and sorting at once.
#Query("SELECT rest FROM Restaurant rest LEFT JOIN rest.votes vote " +
"WHERE (rest.name LIKE concat('%', :searchQuery, '%') OR rest.address LIKE concat('%', :searchQuery, '%')) " +
"AND rest.cuisine = :cuisine " +
"GROUP BY rest.id ORDER BY COUNT(vote.id) DESC")
Page<Restaurant> findAllSortedByRating(#Param("searchQuery") String searchQuery,
#Param("cuisine") СuisineType cuisine,
Pageable pageable);
}
In the third query line, :cuisine is an Enum. Everything works fine when the user has selected one of the options, but what if he wants to see all the options.
For example, with searchQuery, I simply pass an empty string and get the entire list. Of course, this is not possible with Enum, but maybe there is a way to implement such functionality?
So far, I just made two methods in one of which there is no this line, but this is not beautiful because the code is duplicated.

You could use is null
AND (rest.cuisine = :cuisine or :cuisine is null)
Or you could add СuisineType.ALL
AND (rest.cuisine = :cuisine or :cuisine = com.demo.СuisineType.ALL)

Related

How to pass operators like "<",">","=" (less than,greater than,equals to) as parameters to a native query in springboot?

For example let my query be as shown below:
String query="select * from table_name where column_name1 > ?1 and column_name2 < ?2";
#Query(value = query, nativeQuery = true)
public List<Object[]> getResult(String filterValue1,Integer filterValue2);
how to pass the operator(<,>,=) also as a parameter?
I am using postgreSQL database.
Thank you.
If you have an option to construct/concat a String prior to run the query, there is no problem:
public String methodOne(String firstOperator
, String secondOperator) {
return "select * from table_name where column_name1 "
+ firstOperator + " ?1 and column_name2 "
+ secondOperator +" ?2";
}
It is more complicated if you use SpringData repositories.
There isn't a lot you can do with native queries parameters because SpringData is looking for native SQL operators inside the query string. But you can try to do some tricks with LIKE operator and built-in functions (in SQL, sometimes >,< can be replaced with LIKE)
(not completely an answer to your question, but)
A condition that can be omitted
#Query(value =
... AND column_name LIKE IIF(:myParam <> '%', :myParam,'%')
<skipped>
... repositoryMethod(#Param("myParam") String myParam);
IIF - a ternary operator function in MSSQL, you can find something like this in your RDBMS
when you send myParam='actualValue' it will be transformed into
and column_name LIKE 'actualValue'
i.e. column_name='actualValue'
when you send myParam='%' it will be transformed into
and column_name LIKE '%'
i.e. "and TRUE"

Spring Data JPA: Parameterize #Query Annotation String, Refactor Two Similar #Query Methods

In Spring Data I have 2 very large queries which are essentially identical, but with small differences. I don't want to duplicate the methods for both queries. Suppose I have
Method 1
#Query(value = ".. " +
" .. " +
//... big query
"...")
public List<Bean> getResult(#Param("studyId") long studyId);
Method 2
#Query(value = ".. " +
" .. " +
//... big query, after WHERE:
" and (:startDate is null or :startDate = '' or r.recall_date >= to_date(cast(:startDate as TEXT) " +
"...")
public List<Bean> getResult(#Param("studyId") long studyId, #Param("startDate" String startDate);
My goal is:
1) Parameterize the #Query string so that it can either take or omit the optional additional WHERE as a sub-string.
2) Somehow refactor the methods so they don't call separate SQL. The only difference is the additional parameter in Method 2.
Is this possible?
Something like this should work
interface ReportTypeRepository extends PagingAndSortingRepository<ReportType,String> {
final String report = " select r from ReportType r ";
final String where = " where r.active=:active ";
final String sort = " order by r.id asc ";
#Query(report + sort) // <-- all with sort
List<ReportType> findByQuery();
#Query(report + where + sort) // <-- all with where and sort
List<ReportType> findByActiveQuery(#Param("active") boolean active);
}
Another (probably better) solution is to use Spring Data JPA with Querydsl or the JPA 2 Criteria API where you can define some Predicates and combine then to use multiple constraints.
You could also take a look on Specification if you plan to do dynamic queries.

HQL query returing null value on use of like operator for search

query = genericDAOImpl.getHibernateSession().createQuery("select t from ticket t where createdBy=:user and t.subject like concat("%", :summary, "%")
.setParameter("user", userId)
.setParameter("summary", inputBean.getSummary);
this is my query when I search for some values it returns the proper output but when I give null value for the search field it returns empty list actually it should return all the values when the search field is empty
You should just use a plain named placeholder for the predicate of the LIKE expression, and then bind the wildcard string from the Java side:
String sql = "select t from ticket t where createdBy = :user and t.subject like :summary";
query = genericDAOImpl.getHibernateSession().createQuery(sql);
query.setParameter("user", userId);
query.setParameter("summary", "%" + inputBean.getSummary + "%");

Spring JPA - Issue while sorting on non entity column

I have requirement where I need to get the records based in join of three table with pagination(addition requirement are also there). So I have written a nativeQuery to get the records. Below is the sample query
#Query(value = "SELECT "
+ "a.GROUP_REF_ID as refId "
+ "count(case when c.STAT_CD in :userStatus then (c.grp_user_id) end) as numberOfUsers, "
+ "count(case when b.STAT_CD in :itemStatus then (b.grp_item_id) end) as numberOfItems "
+ "from grp a left join grp_item b on a.grp_id=b.grp_id left join grp_user c on a.grp_id=c.grp_id "
+ "where a.stat_cd in :status and a.co_id in :cids "
+ "group by a.GROUP_REF_ID,a.grp_nam,a.GRP_DESC,a.co_id,a.co_nam,a.CRTE_BY, "
+ "a.CRTE_DT,a.UPDT_BY,a.UPDT_DT ", countQuery = "select count(*) from grp where stat_cd in :status and co_id in :cids ", nativeQuery = true)
public Page<Object> findByStatusAndCompanyIdIn(#Param("status") String status, #Param("cids") List<Long> companyIds,
#Param("userStatus") List<GroupUserStatus> userStatus,
#Param("itemStatus") List<GroupItemStatus> itemStatus, Pageable pageable);
Now the requirement is also that these records are to be sorted on any of the column in select part. So, if user passes numberOfItems, the records are to be sorted on it. But I am facing an issue here because if I pass Sort parameter as numberOfItems, spring prepends an a. before numberOfItems which results in error that not able to find a.numberOfItems.
Is there a way I can stop spring from prepending table alias while creating a query with Sort, or should I write my logic in a different approach
Making my comment an answer so the question can be marked as answered:
Wrap the whole select in another one: select * from (<your current select>) x
I have solved the issue by creating a projection. (Kotlin was used but you’ll get the gist.)
class ShowRepository : JpaRepository<Int, Show> {
#Query("SELECT s AS show, (CASE WHEN (s.status = 'scheduled') THEN s.scheduledStartTime ELSE s.startTime END) AS time FROM Show s")
fun findShows(pageable: Pageable): Page<ShowWithTime>
}
interface ShowWithTime {
val show: Show,
val time: Date?
}
This allows Spring-Data to work its full magic, and using Sort.by(Order.desc("time")) works like a charm.
I’ve written it up with a little bit more detail here: Sorting by a Non-Entity Field.

Spring Data JPA Repository returns Object[] instead of MyType while using Sum Function

Until today I was using this statement:
#Query(value = "select top 5 p.*, sum(po.quantity) as total_quantity from product p " +
"inner join productorder po " +
"on p.id = po.product_id " +
"group by p.id, p.name " +
"order by total_quantity desc", nativeQuery = true)
List<Product> findTopFiveBestSellerNative();
Here as i define the return type as a list of Products, i exactly get what i need. And the selected column total_quantity is simply ignored.
Lastly i needed to integrate pagination into this query. Since Spring does not support pagination handling with native queries, i wanted to first transform this query to JPQL (then i will add pagination). And now it looks like this:
#Query(value = "select p, sum(po.quantity) as total_quantity " +
"from Product p, ProductOrder po " +
"where p.id = po.pk.product " +
"group by p.id, p.name " +
"order by total_quantity desc")
List<Product> findTopFiveBestSeller();
The return type is now a list of object arrays, where the first element of array is Product, and second one is the total_quantity. (Although the method signature says List..)
How can i change my statement or somehow achieve this, so that i do not have to deal with array, and simply just get my Product objects?
EDIT: I had the idea to use this query as a subquery, and just select the products from the subquery. It turns out that the JPQL cannot do subqueries in the 'from' clause..
Fact is your query is not returning only the columns needed to build a Product object, so Spring JPA is mapping it to an object.
Create an aggregate entity that extends product and that contains the property totalQuantity and map your query to it (possibly in another repository)

Resources