Implement JPA Projection with count - java

I want to implement JPA Projection with count. I tried this:
#Query(value = "SELECT new org.service.PaymentTransactionsDeclineReasonsDTO( id, count(id) as count, status, error_class, error_message) " +
" FROM payment_transactions " +
" WHERE terminal_id = :id AND (created_at > :created_at) " +
" AND (status != 'approved') " +
" GROUP BY error_message " +
" ORDER BY count DESC", nativeQuery = true)
List<PaymentTransactionsDeclineReasonsDTO> transaction_decline_reasons(#Param("id") Integer transaction_unique_id, #Param("created_at") LocalDateTime created_at);
But I get error: Caused by: java.sql.SQLException: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '.plugin.service.PaymentTransactionsDeclineReasonsDTO( id, count(id) as c' at line 1
How I can implement proper count when I have class based Projection?

Try Interface-based Projection instead of DTO:
public interface TransactionDeclineReason {
Integer getId();
Long getCount();
Status getStatus();
ErrorClass getErrorClass(); // I suppose it's enum...
String getErrorMessage();
}
#Query(value = "select " +
"id as id, " +
"count(id) as count, " +
"status as status, " +
"error_class as errorClass, " +
"error_message as errorMessage " +
"from " +
"payment_transactions " +
"where " +
"terminal_id = ?1 " +
"and created_at > ?2 " +
"and status != 'approved' " +
"group " +
"by error_message " +
"order by " +
"2 desc", nativeQuery = true)
List<TransactionDeclineReason> getTransactionDeclineReasons(Integer transactionId, LocalDateTime createdAt);
Pay attention on aliases (i.e. id as id) - they are mandatory.

You are mixing JPQL and SQL syntax.
The constructor expression (new ...) is JPQL, but in the annotation you mark it as a nativeQuery i.e. as SQL so you have to make up your mind.
If it is to be SQL I don't think you can use aliases in the ORDER BY clause, so you might have to either repeat the expression or wrap it in a subselect as described here: Using an Alias in a WHERE clause.
If it is to be JPQL it doesn't support aliases in a constructor expression, so I guess you have to repeat the expression in the order by clause.

Related

Java Hibernate Error org.hibernate.hql.ast.QuerySyntaxException: unexpected token

Hello I have a problem with the execution of a Query inside Java using Hibernate.
When I use this Query:
Query query = dbSession.createQuery("SELECT activitydate, userid, sum(time) as homeofficeTime FROM Hours AS hours " +
"WHERE (hours.comment ILIKE ANY(ARRAY['%homeoffice%', '%home office%']) OR " +
"remark ILIKE ANY(ARRAY['%homeoffice%', '%home office%'])) AND " +
"hours.activitydate BETWEEN :from AND :to " +
"AND userid = :user " +
"GROUP BY activitydate, userid ORDER BY activitydate ");
query.setLong("user", employee.getUserid());
query.setCalendar("from", from);
query.setCalendar("to", to);
I get this Error:
org.hibernate.hql.ast.QuerySyntaxException: unexpected token: ILIKE near line 1, column 129 [SELECT activitydate, userid, sum(time) as homeofficeTime FROM com.thiesen.timesheet.sql.dbo.Hours AS hours WHERE (hours.comment ILIKE ANY(ARRAY['%homeoffice%', '%home office%']) OR remark ILIKE ANY(ARRAY['%homeoffice%', '%home office%'])) AND hours.activitydate BETWEEN :from AND :to AND userid = :user GROUP BY activitydate, userid ORDER BY activitydate ]
However when I use this Query:
Query query = dbSession.createQuery("SELECT activitydate, userid, sum(time) as homeofficeTime FROM Hours AS hours " +
"WHERE hours.comment = 'test Homeoffice tst' AND " +
"hours.activitydate BETWEEN :from AND :to " +
"AND userid = :user " +
"GROUP BY activitydate, userid ORDER BY activitydate ");
query.setLong("user", employee.getUserid());
query.setCalendar("from", from);
query.setCalendar("to", to);
It works and I get a Result back: [[Ljava.lang.Object;#7221ae50]
Both of the Query work if I test them inside Adminer and run the Querys there but for some Reason the first one wont work in my Project. Does Hibernate not know what to do with "ILIKE" or "LIKE" or the "Array[]" Part?

MySQL SELECT WHERE... AND (based on parameter value)

#Query(value =
"SELECT * " +
"WHERE (type REGEXP ?1) " +
"AND status= ?2 " +
"AND date(createdAt)..." +
"ORDER BY id DESC " +
"LIMIT ?4",
nativeQuery = true)
Optional<List<Item>> findLatest(String type, String status, String date, int limit);
I have this query where I'd like to filter by createdAt, only if the date value from the parameter is not null.
If String date = null, the query should not consider the AND date(createdAt)...
However if String date = "today" the query should include something like AND date(createdAt) = CURRENT_DATE
How can I achieve this?
Simplified you can make
If you have more choice you need case when
The idea is when the condition is not met, we let the and part always be true
so that the where calsue is true if all the other conditions are met
SELECT * FROM
A
WHERE
status= 72
AND IF( date = "today" , date(createdAt), current_date) = current_date
The way we usually handle this is by adding a logical check to the WHERE clause which allows a null date.
#Query(value = "SELECT * " +
// ...
"WHERE (type REGEXP ?1) AND " +
" status = ?2 AND " +
" (DATE(createdAt) = ?3 OR ?3 IS NULL) " +
"ORDER BY id DESC " +
nativeQuery = true)
Optional<List<Item>> findLatest(String type, String status, String date);
Note that it is not possible to bind parameters to the LIMIT clause, therefore I have removed it. Instead, you should use Spring's pagination API.

Bind #param in a Jpa repository native query inside single quotations

I am looking for a way to bind a given param in a native query where the value has to be inside single quotations, like so:
#Transactional(readOnly = true)
#Query(value = " SELECT c.ID " +
" FROM table1 clh " +
" LEFT JOIN table2 nks " +
" on clh.SERIAL = nks.SERIAL_REF " +
" WHERE clh.CREATED_DATE >= :now - interval ':timeThreshold' HOUR " +
" AND nks.SERIAL_REF IS NULL" , nativeQuery = true)
List<Long> getIdsWithoutAnswer (#Param("timeThreshold") Integer timeThreshold, #Param("now") LocalDateTime now);
However, when I try to run this, it results in hibernate not being able to bind the timeThreshold value as it is provided inside the single quotations ''.
Does anyone know how this can be resolved?
The problem you are having with your native Oracle query has to do with trying to bind a value to an interval literal. You can't do that. Instead, use the NUMTODSINTERVAL() function:
#Transactional(readOnly = true)
#Query(value = " SELECT c.ID " +
" FROM table1 clh " +
" LEFT JOIN table2 nks " +
" on clh.SERIAL = nks.SERIAL_REF " +
" WHERE clh.CREATED_DATE >= :now - numtodsinterval(:timeThreshold, 'hour') " +
" AND nks.SERIAL_REF IS NULL" , nativeQuery = true)
List<Long> getIdsWithoutAnswer (#Param("timeThreshold") Integer timeThreshold, #Param("now") LocalDateTime now);

Converting MySQL query into JPA to get count

Here I'm having a SQL query which is working fine to get count from the MySQL database which is as
#Query("SELECT count(is_accepted) as value, post_type, is_accepted from agent_activities where "
+ "YEAR(created_date_time) as year and post_type = 'ticket' " + "GROUP BY is_accepted")
And when I'm trying into Java as JPA query it's not working.
public interface AgentActivitiesRepo extends JpaRepository<AgentActivitiesEntity, Long> {
#Query("select new ProductIssuesModel"
+ "(count(data.isAccepted) as acceptCount) "
+ "from data where YEAR(data.createdDate) as :year "
+ "and data.postType = :postType " + "group by data.isAccepted")
public List<ProductIssuesModel> findAgentActivitiesYearly(Long year, String postType);
}
Here ProductIssuesModel is like:
public class ProductIssuesModel {
private Long acceptCount;
private Long rejectCount;
private Long year;
private Long month;
...}
By running above query, I face an error as:
Caused by: java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: as near line 1, column 137 [select new com.accenture.icoe.fm.model.ProductIssuesModel(count(data.isAccepted) as acceptCount) from data where YEAR(data.createdDate) as :year and data.postType = :postType group by data.isAccepted]
Please let me know if you see any error.
There are two ways to write a JPQL -
Indexed Query Parameters
#Query("select new ProductIssuesModel"
+ "(count(data.isAccepted) as acceptCount) "
+ "from data where YEAR(data.createdDate) = ?1 "
+ "and data.postType = ?2 " + "group by data.isAccepted")
public List<ProductIssuesModel> findAgentActivitiesYearly(Long year, String postType);
The values of the parameters will be bind to the query based on the position/index.
Named Parameters
#Query("select new ProductIssuesModel"
+ "(count(data.isAccepted) as acceptCount) "
+ "from data where YEAR(data.createdDate) = :year "
+ "and data.postType = :postType " + "group by data.isAccepted")
public List<ProductIssuesModel> findAgentActivitiesYearly(#Param("year") Long year, #Param("postType") String postType);
In your case, the second approach is applicable.
Here , YEAR(data.createdDate) as :year is not a condtional expression.
You may be try to do this YEAR(data.createdDate) = :year.
And use #Param to use parameter in JPQL
#Query("select new ProductIssuesModel"
+ "(count(data.isAccepted) as acceptCount) "
+ "from data where YEAR(data.createdDate) = :year "
+ "and data.postType = :postType " + "group by data.isAccepted")
public List<ProductIssuesModel> findAgentActivitiesYearly(#Param("year") Long year, #Param("postType") String postType);

Order By in #Query select not working

I have this Query in my JPA repository - and it works EXCEPT the " order by " part. Am i doing this wrong ? is it different in hql ?
#Query(value = "select wm.WagerIdentification, wm.BoardNumber, wm.MarkSequenceNumber, wm.MarkNumber," +
" pt.CouponTypeIdentification, pt.WagerBoardQuickPickMarksBoard " +
"from WagerBoard wb " +
"inner join wb.listOfWagerMarks wm " +
"inner join wb.poolgameTransaction pt " +
"where wb.WagerIdentification = wm.WagerIdentification and wb.BoardNumber = wm.BoardNumber and wb.GameIdentification = wm.GameIdentification and wm.meta_IsCurrent = 1 " +
"and wb.TransactionIdentification = pt.TransactionIdentification and pt.meta_IsCurrent = 1 " +
"and wb.meta_IsCurrent = 1 order by wm.WagerIdentification asc, wm.BoardNumber asc, wm.MarkNumber asc")
Instead of ordering result within the #Query, you can add a method parameter of type Sort, like in Spring Data JPA reference

Categories

Resources