Bind parameters for ORDER BY in NamedParameterJDBCTemplate - spring

I am trying to use NamedParameterJdbTemplate in a Spring MVC application. The issue is that the bind parameters do not seem to work (no sorting happens) when I include one of ORDER BY clauses listed below. However, a hard coded order by column name in the sql works.
ORDER BY column1
ORDER BY column1
ORDER BY column1 asc
ORDER BY column1 desc
For example, the below listed query does not work.
private static final String SEARCH_ALL_BY_SORT_ORDER=
" select FIRST_NM, MIDDLE_NM, LAST_NM, CUSTOMER_IDENTIFIER, EMAIL_ADDRESS, ACCOUNT_ID" +
" from VIEW " +
" where CUSTOMER_IDENTIFIER= :customerIdentifier " +
" and ( REGEXP_LIKE(FIRST_NM, :firstName, 'i') " +
" or REGEXP_LIKE(LAST_NM, :lastName, 'i') " +
" or REGEXP_LIKE(EMAIL_ADDRESS, :emailAddress, 'i') )" +
" order by :sortColumns";
The same query with a hard coded order by column works:
private static final String SEARCH_ALL_BY_SORT_ORDER=
" select FIRST_NM, MIDDLE_NM, LAST_NM, CUSTOMER_IDENTIFIER, EMAIL_ADDRESS, ACCOUNT_ID" +
" from VIEW " +
" where CUSTOMER_IDENTIFIER= :customerIdentifier " +
" and ( REGEXP_LIKE(FIRST_NM, :firstName, 'i') " +
" or REGEXP_LIKE(LAST_NM, :lastName, 'i') " +
" or REGEXP_LIKE(EMAIL_ADDRESS, :emailAddress, 'i') )" +
" order by LAST_NM";
Here's the relevant jdbctemplate code
Map <String, Object> params = new HashMap <String, Object>();
params.put("customerIdentifier", customerIdentifier);
params.put("firstName", searchCriteria );
params.put("lastName", searchCriteria );
params.put("emailAddress",searchCriteria);
// sortBy is COLUMN name
// sortOrder is either 'asc' or 'desc'
params.put("sortColumns", sortBy + " " + sortOrder);
// Using just the column name does not work either
//params.put("sortColumns", sortBy);
namedParameterJdbcTemplate.query(SEARCH_ALL_BY_SORT_ORDER, params, new MemberMapper());

Only values can be bound as parameters. Not parts of the query itself.
In the end, a prepared statement is generated and the parameters are bound to the prepared statement. The principle of a prepared statement is to prepare the execution plan of the query (of which the order by clause is a part), and to execute the query one or several times after, with varying parameters.
If the query is not complete, the execution plan can't be prepared and reused. So, for this part of the query, you'll need to generate the query dynamically using string concatenation, and not parameters.

As JB Nizet has already explained that parts of query cannot be used as bind keys (orderby :age). Therefore we will need to use concatenation here instead.
" order by "+ sortBy + " " + sortOrder;

Related

How to query the database based on current day using Hibernate?

I would like to retrieve database items based on the current day.
For that purpose I am trying to use the day() function as explained there and mentioned there.
I have a 'created' date field that is automatically filled when a new item is created in the table. It's configured using a model, like that:
#Column(name = "created", nullable = false)
#CreationTimestamp
private Date created;
I created a couple of items and the date is filled in the database as expected.
Now I am trying to call those items using a HQL query:
String hql = "" +
"FROM Item as item " +
"WHERE item.itemId = ?1" +
"AND item.created = day(current_date())";
The problem is that this query is returning a NoResultException.
It's weird because I am using the current_date() function in other scenarios, e.g.:
String hql = "" +
"FROM Item as item " +
"WHERE item.itemId = ?1" +
"AND item.createdAt >= current_date - ?2";
And it is working pretty well!
So I assume the issue is related with day() function.
Try use this:
String hql = "" +
"FROM Item as item " +
"WHERE item.itemId = ?1" +
"AND date(item.created) = current_date()";

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.

How can i add a QueryHint to a Hibernate Custom Query?

I'm trying to add the Oracle Query Hint /*+ GATHER_PLAN_STATISTICS PARALLEL(16) */ to my Custom Query which looks similar to this:
#Query("SELECT " +
" FROM myTable tab " +
" WHERE tab.create_date >= to_date(:dateFrom) " +
" AND tab.create_date < to_date(:dateTo)")
List<MyModel> findEntriesByDate( #Param("dateFrom") String dateFrom, #Param("dateTo") String dateTo);
I cannot find any way to make this work. I tried various things, for example adding this on top of the method:
#QueryHints({
#QueryHint(name = org.hibernate.annotations.QueryHints.COMMENT, value = "GATHER_PLAN_STATISTICS")
})
or
#QueryHints({
#QueryHint(name = GATHER_PLAN_STATISTIC, value = "GATHER_PLAN_STATISTICS")
})
Is there a way to make this work or do I have to change my approach?

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.

JDBC ResultSet get column from different tables

i wan to retrieve data from query involving many tables.
i have a query as follows
String sql = "SELECT "
+ "s.Food_ID AS 'Sales_FoodID', "
+ "f.Food_Name AS 'foodName' "
+ "FROM Ordering o, Sales s, Food f"
+ " WHERE o.Table_No = " + tableNo + ""
+ " AND o.Paid = '" + NOT_PAID + "'"
+ " AND s.Order_ID = o.Order_ID"
+ " AND f.Food_ID = s.Food_ID;";
resultSet = statement.executeQuery(sql);
no error were found when i run the program, but after i add this line to get a table's column data:
String orderID = resultSet.getString("foodName");
i'm given this error:
java.sql.SQLException: Column not found
anyone know why?
You have to use next() method.
You should know that ResultSet is implicitly positioned on position before first row so you need to call next to get current position and if is valid, it returns true, else returns false (cursor is positioned after the last row).
rs = statement.executeQuery(sql);
while (rs.next()) {
String orderID = rs.getString(2);
}
Note: You can use also rs.getString(<columnName>) but in case when you know how your statement looks i recommend to you use index instead of columnName.
After calling the resultSet.executeQuery() you need to call the next() to pulling the records from db
after that you can call setXxx() provided by Java API

Resources