Issue
I'm developing a Spring Boot application with Spring Data JPA. I'm using a custom JPQL query to group by some field and get the count. Following is my repository method.
@Query(value = "select count(v) as cnt, v.answer from Survey v group by v.answer")
public List<?> findSurveyCount();
It's working and result is obtained as follows:
[
[1, "a1"],
[2, "a2"]
]
I would like to get something like this:
[
{ "cnt":1, "answer":"a1" },
{ "cnt":2, "answer":"a2" }
]
How can I achieve this?
Solution
Solution for JPQL queries
This is supported for JPQL queries within the JPA specification.
Step 1: Declare a simple bean class
package com.path.to;
public class SurveyAnswerStatistics {
private String answer;
private Long cnt;
public SurveyAnswerStatistics(String answer, Long cnt) {
this.answer = answer;
this.count = cnt;
}
}
Step 2: Return bean instances from the repository method
public interface SurveyRepository extends CrudRepository<Survey, Long> {
@Query("""
SELECT
new com.path.to.SurveyAnswerStatistics(v.answer, COUNT(v))
FROM
Survey v
GROUP BY v.answer""")
List<SurveyAnswerStatistics> findSurveyCount();
}
Important notes
- Make sure to provide the fully-qualified path to the bean class, including the package name. For example, if the bean class is called
MyBeanand it is in packagecom.path.to, the fully-qualified path to the bean will becom.path.to.MyBean. Simply providingMyBeanwill not work (unless the bean class is in the default package). - Make sure to call the bean class constructor using the
newkeyword.SELECT new com.path.to.MyBean(...)will work, whereasSELECT com.path.to.MyBean(...)will not. - Make sure to pass attributes in exactly the same order as that expected in the bean constructor. Attempting to pass attributes in a different order will lead to an exception.
- Make sure the query is a valid JPA query, that is, it is not a native query.
@Query("SELECT ..."), or@Query(value = "SELECT ..."), or@Query(value = "SELECT ...", nativeQuery = false)will work, whereas@Query(value = "SELECT ...", nativeQuery = true)will not work. This is because native queries are passed without modifications to the JPA provider, and are executed against the underlying RDBMS as such. Sincenewandcom.path.to.MyBeanare not valid SQL keywords, the RDBMS then throws an exception.
Solution for native queries
As noted above, the new ... syntax is a JPA-supported mechanism and works with all JPA providers. However, if the query itself is not a JPA query, that is, it is a native query, the new ... syntax will not work as the query is passed on directly to the underlying RDBMS, which does not understand the new keyword since it is not part of the SQL standard.
In situations like these, bean classes need to be replaced with Spring Data Projection interfaces.
Step 1: Declare a projection interface
package com.path.to;
public interface SurveyAnswerStatistics {
String getAnswer();
int getCnt();
}
Step 2: Return projected properties from the query
public interface SurveyRepository extends CrudRepository<Survey, Long> {
@Query(nativeQuery = true, value =
"""
SELECT
v.answer AS answer, COUNT(v) AS cnt
FROM
Survey v
GROUP BY v.answer""")
List<SurveyAnswerStatistics> findSurveyCount();
}
Use the SQL AS keyword to map result fields to projection properties for unambiguous mapping.
Answered By - manish
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.