Using parameters in JPQL query with aggregate functions - java

I use JPQL queries in my SpringBoot project. I have a query with COUNT function. The query is:
#Query(value =
"SELECT new ua.edu.chdtu.deanoffice.service.course.selective.statistics.StudentsRegistrationOnCoursesPercent(" +
"2021 - scsd.studentDegree.studentGroup.creationYear + scsd.studentDegree.studentGroup.realBeginYear, " +
"COUNT(DISTINCT scsd.studentDegree.id)) " +
"FROM SelectiveCoursesStudentDegrees AS scsd " +
"GROUP BY scsd.selectiveCourse.studyYear, scsd.studentDegree.specialization.degree.id, " +
"scsd.studentDegree.active, " +
"2021 - scsd.studentDegree.studentGroup.creationYear + scsd.studentDegree.studentGroup.realBeginYear " +
"having scsd.selectiveCourse.studyYear = :studyYear AND " +
"scsd.studentDegree.specialization.degree.id = :degreeId AND " +
"scsd.studentDegree.active = true")
List<StudentsRegistrationOnCoursesPercent> findPercentStudentsWhoChosenSelectiveCourse(#Param("studyYear") int studyYear,
#Param("degreeId") int degreeId);
And it works fine.
But when I change a literal 2021 to a JPQL query parameter :currYear (only substitute literal with parameter, nothing else), I get an error:
2022-04-13 15:10:17 [XNIO-2 task-1] ERROR o.h.e.jdbc.spi.SqlExceptionHelper - ERROR: column "studentgro2_.creation_year" must appear in the GROUP BY clause or be used in an aggregate function
Position: 11
2022-04-13 15:10:17 [XNIO-2 task-1] ERROR u.e.c.d.a.g.ExceptionHandlerAdvice - ERROR
org.springframework.dao.InvalidDataAccessResourceUsageException: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:261)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:244)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:503)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:209)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:147)
How can I fix it?
The changed query is the following:
#Query(value =
"SELECT new ua.edu.chdtu.deanoffice.service.course.selective.statistics.StudentsRegistrationOnCoursesPercent(" +
":currYear - scsd.studentDegree.studentGroup.creationYear + scsd.studentDegree.studentGroup.realBeginYear, " +
"COUNT(DISTINCT scsd.studentDegree.id)) " +
"FROM SelectiveCoursesStudentDegrees AS scsd " +
"GROUP BY scsd.selectiveCourse.studyYear, scsd.studentDegree.specialization.degree.id, " +
"scsd.studentDegree.active, " +
":currYear - scsd.studentDegree.studentGroup.creationYear + scsd.studentDegree.studentGroup.realBeginYear " +
"having scsd.selectiveCourse.studyYear = :studyYear AND " +
"scsd.studentDegree.specialization.degree.id = :degreeId AND " +
"scsd.studentDegree.active = true")
List<StudentsRegistrationOnCoursesPercent> findPercentStudentsWhoChosenSelectiveCourse(#Param("studyYear") int studyYear,
#Param("degreeId") int degreeId,
#Param("currYear") int currYear);

probably there is some problem with parameter substitution and
:currYear - scsd.studentDegree.studentGroup.creationYear + scsd.studentDegree.studentGroup.realBeginYear
is not recognized as th same "column" in select and group by parts
you could try using group by 1 as in What does SQL clause "GROUP BY 1" mean?

Related

Spring Data native query with params don't work

I have a native query like the following one:
#Query(value = "SELECT * FROM (" +
" SELECT result.*, ROWNUM rn FROM (" +
" SELECT tmp.* FROM (" +
" SELECT " +
" e.id, " +
" e.employee_number, " +
" d.name, " +
" d.surname " +
" FROM employee e INNER JOIN detail d ON e.id_detail = d.id " +
" WHERE e.status = :status " +
" ) tmp " +
" ORDER BY :sortColumn :sortDirection " +
" ) result " +
" WHERE ROWNUM <= (:pageIndex + :pageSize) " +
") " +
"WHERE rn > :pageIndex "
, nativeQuery = true)
ArrayList<Object> getEmployeeDetails( #Param("status") EmployeeStatus status,
#Param("pageSize") int pageSize,
#Param("pageIndex") int pageIndex,
#Param("sortDirection") String sortDirection,
#Param("sortColumn") String sortColumn);
and I'm getting the following errors:
org.springframework.dao.InvalidDataAccessResourceUsageException: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet
// ...
Caused by: org.hibernate.exception.SQLGrammarException: could not extract ResultSet
// ...
Caused by: java.sql.SQLSyntaxErrorException: ORA-01745: invalid host/bind variable name
What I tried is different return type (and didn't manage to find out which one to use eventually), inserting params with #Param() annotations.
The query itself does work - I tried it directly in the database, but I'm experiencing problems with handling it in Spring.
The query itself for easy debugging:
SELECT * FROM (
SELECT result.*, ROWNUM rn FROM (
SELECT tmp.* FROM (
SELECT
e.id,
e.employee_number,
d.name,
d.surname
FROM employee e INNER JOIN detail d ON e.id_detail = d.id
WHERE e.status = 'status'
) tmp
ORDER BY tmp.name desc
) result
WHERE ROWNUM <= (0 + 5)
)
WHERE rn > 0
EDIT:
I've updated the question with comment suggestions of removing all of the \n's and checking for missing whitespaces.
Plain query, without using any parameters also work, but as I start to insert parameters through #Param() annotations or binds (?1) it stops working giving the errors I updated above.
if EmployeeStatus is enum you have to use this in your query
WHERE e.status = :#{#status.name()}

org.postgresql.util.PSQLException: ERROR: syntax error at or near ":"

I am running an SQL query from PgAdmin4 which runs successfully, but when i take that same query to run it from my Spring Boot application i have an error " org.postgresql.util.PSQLException: ERROR: syntax error at or near ":" "
Here is what the SQL query looks like:
SELECT
student.surname, student.first_name, student.last_name,
jsonb_pretty(elems)
FROM assessment, jsonb_array_elements(assessment.assessment) AS elems
JOIN student ON student.id = (elems->>'student_id')::uuid
WHERE subject_id = 2
AND academy_year_id =3
AND form_id = 2
AND term_id = 1
AND section_id = 4;
And this run successfully from PgAdmin4.
This is my implementation in my Repository
#Query(nativeQuery = true, value = "SELECT\n" +
" student.surname, student.first_name, student.last_name,\n" +
" jsonb_pretty(elems)\n" +
"FROM assessment, jsonb_array_elements(assessment.assessment) AS elems\n" +
"JOIN student ON student.id = (elems->>'student_id')::uuid\n" +
"WHERE subject_id = 2\n" +
"AND academy_year_id =3\n" +
"AND form_id = 2\n" +
" AND term_id = 1\n" +
"AND section_id = 4;")
Object[] getSubjectAssessments();
And when my API calls this method i get this error message
org.postgresql.util.PSQLException: ERROR: syntax error at or near ":"
I don't know what I am doing wrong.
JPA can't handle the Postgres specific cast operator ::.
Use cast() instead:
JOIN student ON student.id = cast(elems->>'student_id' as uuid)

Spring data jpa failed to pass named parameters in native subquery for db2

Unable to pass named parameters in #NamedNativeQuery in spring data jpa
my repo:
#Query(value = "select stat.desc as desc," +
" stat.priority as priority," +
" (case when sum(activeUser) is null then 0 else sum(activeUser) end) as activeUser," +
" (case when sum(totalUser) is null then 0 else sum(totalUser) end) as totalUser" +
" from lookup.user stat left outer join" +
" (" +
" select user.role as role, " +
" sum (case when user.STATUS = 'ACTIVE' then 1 else 0 end) as activeUser," +
" count(*) as totalUser," +
" user.group as group" +
" from Ctrl.user user" +
" where user.group =:userGroup " +
" and user.branch_code =:branchCode " +
" group by user.role,user.group" +
" ) as tbl on stat.role = tbl.role and stat.group = tbl.group" +
" where stat.group =:userGroup " +
" group by stat.desc, stat.priority" +
"", nativeQuery = true)
public List<com.cimb.dto.UserStatusSummary> getSummaryReport(#Param(value = "userGroup") String userGroup, #Param(value = "branchCode") String branchCode);
The underlying database is DB2
When I tried to access that method I am hitting following error.
DB2 SQL Error: SQLCODE=-302, SQLSTATE=22001, SQLERRMC=null, DRIVER=4.25.13
if I hard code those named parameters with values then it's working.
I can not use jpql as real queries have some subqueries in it, so I cant use JPQL
Edit Update
After some digging, I have found out that, the parameters I am passing are in the subquery, since JPA don't have subquery concept it's not injecting into named parameters which resulting in a syntax error.
Now how to work with Subqueries in JPA
Please help.

Stacktrace with SQL injection?

If i insert a quote symbol " in the codContract parameter I receive the following error.
Error querying database. Cause: java.sql.SQLSyntaxErrorException:
ORA-00972: identifier too long
The error may exist in mappers/existence.xml The error may involve
com.iv.queryinterface.AssistenzaMapper.getTitlesFromCodContratct-Inline
The error occurred while setting parameters
SQL:
SELECT t.id_title,
c.des_lastname,
c.des_firstname,
to_char(t.dta_raw, 'DD/MM/YYYY') AS DTA_RAW,
DECODE(t.cod_statustitle, '1', 'Raw', '2', 'Stated') AS STATUS_TITLE
FROM Ivf_Policy p,
Ivf_Title t,
Ivg_Client c,
Ivf_Price pr
WHERE Cod_Contract = TEST
AND p.id_policy = t.id_policy
AND t.cod_type_title IN(2, 3, 13)
AND t.cod_statustitle IN(1, 2)
AND t.cod_client = c.cod_client
AND t.id_price = pr.id_price;
Cause: java.sql.SQLSyntaxErrorException: ORA-00972: identifier too
long
In this example, i set " TEST as value for the codContract parameter. My questions are:
Is this an exploitable SQL injection or a false positive that just prints an sql error into the stack trace?
The code is susceptible to SQL injection, and does no escaping. All that is avoidable by the use of PreparedStatement. Where the query string is not composed dynamically.
Now "TEST is the first part of an SQL identifier till a closing double quote.
I do not want to instruct readers on hacking, but think what something like
"'' OR 1=1 "
+ "UNION SELECT u.login, u.password, '', '', '', '' "
+ "FROM users"
+ "\u0000-- ";
might reveal on data.
Use java.sql.PreparedStatement for avoiding SQL injection.
String query =
"SELECT " +
" t.id_title , " +
" c.des_lastname , " +
" c.des_firstname , " +
" TO_CHAR(t.dta_raw, 'DD/MM/YYYY') AS DTA_RAW, " +
" DECODE(t.cod_statustitle, '1', 'Raw', '2', 'Stated') AS STATUS_TITLE " +
"FROM " +
" Ivf_Policy p, " +
" Ivf_Title t, " +
" Ivg_Client c, " +
" Ivf_Price pr " +
"WHERE " +
"1 = 1 AND " +
" Cod_Contract = ? " +
"AND p.id_policy = t.id_policy " +
"AND t.cod_type_title IN(2, " +
" 3, " +
" 13) " +
"AND t.cod_statustitle IN(1, " +
" 2) " +
"AND t.cod_client = c.cod_client " +
"AND t.id_price = pr.id_price;";
PreparedStatement stmt = connection.prepareStatement(query);
stmt.setString(1, 'TEST');
ResultSet rs = stmt.executeQuery();
To avoid SQL injection, do not append parameter values directly to SQL queries.
Use bind variables instead.
Thanks.

Join Query for unrelated entities in Spring boot JPA

I am trying to write a query like the below on unrelated entities A and B
Select a.x,a.y,a.z FROM A a ,B b Where
b.Parameter_Name='APPLICABLE_RULE_TYPE' And b.Parameter_KEY='MC' AND
b.Parameter_Value=a.rule_type And a.Rule_Status='ON' Order By Rule_Priority;
I am not able to figure out how this should be written in the Repository class of a Spring boot application.
Can someone please suggest a way do this
Thanks in advance.
Edit:
I have tried it as a native query like
#Query(value = "Select Rule_Id, Rule_Sql,S_System,S_Entity, s_table_name,Rule_Sql_Type "
+ "FROM rule_master TRM ,T_SYSTEM_CONFIG TSC Where"
+ " Tsc.Parameter_Name='APPLICABLE_RULE_TYPE' "
+ " And Tsc.Parameter_KEY= :systemType"
+ " AND Tsc.Parameter_Value=trm.rule_type "
+ " And Rule_Status='ON'"
+ " Order By Rule_Priority", nativeQuery = true)
public List<RuleMaster> findByRuleStatus(#Param("systemType" String systemType);
but getting this error
o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 17006, SQLState: null
o.h.engine.jdbc.spi.SqlExceptionHelper : Invalid column name
org.springframework.orm.jpa.JpaSystemException: could not execute query; nested exception is org.hibernate.exception.GenericJDBCException: could not execute query
I was able to solve this by using native query itself.
The error was because i was selecting only part of the columns from the table. Instead i have used the below query which works fine:
#Query(value = "Select * "
+ "FROM rule_master TRM ,T_SYSTEM_CONFIG TSC Where"
+ " Tsc.Parameter_Name='APPLICABLE_RULE_TYPE' "
+ " And Tsc.Parameter_KEY= :systemType"
+ " AND Tsc.Parameter_Value=trm.rule_type "
+ " And Rule_Status='ON'"
+ " Order By Rule_Priority", nativeQuery = true)
public List<RuleMaster> findByRuleStatus(#Param("systemType" String systemType);

Categories