Spring Data JPA: native summary gets ConverterNotFoundException - java

Update
I've changed the first method to return a List, but I get the same exception.
I have 2 native queries in my Repository class. One is typed to return all records, the other returns just one row. See below:
#Query(nativeQuery = true, value = NATIVE_SUMMARY_QUERY_PARTIAL + "group by (rjd.refresh_job_identifier)) as rc")
List<RefreshSummary> getRefreshJobDetailSummary();
#Query(nativeQuery = true, value = NATIVE_SUMMARY_QUERY_PARTIAL + " WHERE rjd.refresh_job_identifier = :refreshJobId" +
" group by (rjd.refresh_job_identifier)) as rc")
List<RefreshSummary> getRefreshJobDetailSummaryById(#Param("refreshJobId") String refreshJobId);
interface RefreshSummary {
String getRefreshJobId();
Date getRefreshJobStart();
Date getRefreshJobComplete();
String getUserId();
long getTotalRecords();
long getSuccessfulRecords();
long getPendingRecords();
long getErrorRecords();
long getCancelledRecords();
String getRefreshJobStatus();
}
String NATIVE_SUMMARY_QUERY_PARTIAL = "SELECT rc.refresh_job_identifier as refresh_job_id, ..."
The first method, getRefreshJobDetailSummary, works fine. But the second method, where I want only one row, gives me this exception:
org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [com.company.repository.RefreshJobDetailRepository$RefreshSummary]
The full query looks like this:
String NATIVE_SUMMARY_QUERY_PARTIAL = "SELECT rc.refresh_job_identifier as refresh_job_id, " +
"rc.refresh_job_start_time as refresh_job_start, " +
"rc.record_completion_time as refresh_job_complete, " +
"rc.user_id as user_id, " +
"rc.pending + rc.successful + rc.cancelled + rc.error as total_records, " +
"rc.successful as successful_records, " +
"rc.pending as pending_records, " +
"rc.error as error_records, " +
"rc.cancelled as cancelled_records, " +
"CASE WHEN pending > 0 THEN 'In progress' " +
"ELSE 'Complete' " +
"END as refresh_job_status " +
"FROM " +
"(SELECT rjd.refresh_job_identifier as refresh_job_identifier, " +
"MAX(rjd.refresh_job_start_time) as refresh_job_start_time, " +
"MAX(rjd.record_completion_time) as record_completion_time, " +
"MAX(rjd.org_usr_nu) as user_id, " +
"SUM(CASE WHEN LOWER(record_status) = 'pending' THEN 1 ELSE 0 END) as pending, " +
"SUM(CASE WHEN LOWER(record_status) = 'successful' THEN 1 ELSE 0 END) as successful, " +
"SUM(CASE WHEN LOWER(record_status) = 'cancelled' THEN 1 ELSE 0 END) as cancelled, " +
"SUM(CASE WHEN LOWER(record_status) = 'error' THEN 1 ELSE 0 END) as error " +
"from erd_cfg_owner.refresh_job_detail rjd " ;
And the value the query returns looks like this:
'{20191218204913458hc35, 2019-12-18 20:49:13.314, 2019-12-18 20:49:24.335, hc35, 1, 1, 0, 0, 0, Complete}'
Can someone shed any light on this? Why would one method work but not the other?

I would replace the return type of your 2nd method from RefreshSummary to List<RefreshSummary>. In theory your method is supposed to return a list not a single value

Could you try using constructor expression in query declaration or by creating custom converter.
Eg-SELECT NEW com.example.MyClass(e.attribute1, e.attribute2...) FROM MyEntity e");

Related

How to format Date in JPA query

I have a SpringBoot application where I use Repository class to query my Oracle DB table.
Here is how the query and associated function are defined :
#Query( value =" SELECT status "+
" FROM tb1 " +
" WHERE " +
" to_date(cob_Date,'dd-MON-yy') = to_date(:cobDate,'yyyy-mm-dd') " +
" AND business_Day ='BD3' " +
" AND intra_day ='INTRA_06' " +
" AND datasource_name =:datasource" +
" AND upper(status) = 'COMPLETED' " +
" AND frequency = 'MONTHLY' " +
" AND processed = 'Y' " +
" ORDER BY create_date desc FETCH FIRST 1 rows only"
, nativeQuery=true)
List<String> getImpalaJobStatus(#Param("intraDay") String intraDay,
#Param("businessDay") String businessDay,
#Param("cobDate") LocalDate cobDate,
#Param("datasource") String datasource);
If I run this query in SQL developer then I am getting my results back, however if I run it from my SpringBoot Application it returns nothing.
I suspect I am doing something wrong with the Date field "COB_DATE" and this clause under WHERE:
" to_date(cob_Date,'dd-MON-yy') = to_date(:cobDate,'yyyy-mm-dd') " +
I tried it as :
" cob_Date =:cobDate "
but it didn't work either.
That cobDate is being declared as a LocalDate in the method signature implies that you already have that value in date format. If so, then the call to to_date() in the query is not needed. Try binding the LocalDate value directly:
#Query( value =" SELECT status "+
" FROM tb1 " +
" WHERE " +
" to_date(cob_Date,'dd-MON-yy') = :cobDate " +
" AND business_Day ='BD3' " +
" AND intra_day ='INTRA_06' " +
" AND datasource_name =:datasource" +
" AND upper(status) = 'COMPLETED' " +
" AND frequency = 'MONTHLY' " +
" AND processed = 'Y' " +
" ORDER BY create_date desc FETCH FIRST 1 rows only"
, nativeQuery=true)
List<String> getImpalaJobStatus(#Param("intraDay") String intraDay,
#Param("businessDay") String businessDay,
#Param("cobDate") LocalDate cobDate,
#Param("datasource") String datasource);
Note that your Oracle JBDC driver should know how to marshall the LocalDate value to the database such that the query works.

Issue when adding NULLS LAST

When adding "NULLS LAST" on the following query I'm getting the exception below:
Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: The value is not set for the parameter number 11.
If I remove the NULLS LAST everything works fine
If I remove the CASE WHEN code and order by just one specific column it works with the NULLS LAST, but I need all the columns on the CASE WHEN.
#Query("SELECT c FROM ClassSpecificationTableEntity c"
+ " LEFT JOIN c.owner o "
+ " LEFT JOIN c.domain d "
+ "WHERE ISNULL(c.classStructure.classification.description, '') LIKE :SEARCH "
+ " OR ISNULL(c.classStructure.description, '') LIKE :SEARCH "
+ " OR ISNULL(d.description, '') LIKE :SEARCH "
+ " OR ISNULL(o.description, '') LIKE :SEARCH "
+ " OR ISNULL(c.measurementUnit, '') LIKE :SEARCH "
+ " OR ISNULL(c.defaultValue, '') LIKE :SEARCH "
+ " OR ISNULL(c.dataType, '') LIKE :SEARCH "
+ " OR ISNULL(c.tooltip, '') LIKE :SEARCH "
+ " ORDER BY "
+ " CASE :ORDERBY" +
" WHEN 0 THEN c.classStructure.classification.description " +
" WHEN 1 THEN c.assetAttribId " +
" WHEN 2 THEN c.dataType " +
" WHEN 3 THEN c.measurementUnit " +
" WHEN 4 THEN c.domain.description " +
" WHEN 5 THEN c.owner.description " +
" WHEN 6 THEN c.defaultValue " +
" WHEN 7 THEN c.tooltip " +
" END DESC NULLS LAST"
)
Page<ClassSpecificationTableEntity> findByLikeSearchDESC(#Param(value="SEARCH") final String searchCrit, final Pageable pageable, #Param(value="ORDERBY") final String orderBy);
There is no NULLS LAST syntax is T-SQL.
If you want to order NULL values last the common way is to use a CASE expression or IIF:
--CASE Expression
CASE WHEN {expression} IS NULL THEN 1 ELSE 0 END
--IIF funciton (which is actually a shorthand CASE expression)
IIF({Expression} IS NULL, 1, 0)
If you have a complicated expression, that you want to order the NULL values last for, and then the expression, and that expression does not appear in the SELECT (so cannot be referenced by its Alias), then you can move the expression to the FROM, to avoid typing the expression multiple times:
FROM ...
JOIN ...
LEFT JOIN ...
...
CROSS APPLY (VALUES({Expression}))V(Alias)
WHERE ...
ORDER BY IIF(V.Alias IS NULL,1,0),
V.Alias

Issue with JPQL request take long time to execute with more then 600000 registrations

I have a query in jpql that normally walk with a reduced data count, the problem is when the table to them over 600000 data records.
I use spring data, with an entity that contains no relation (no OneToMany, OneToOne, ManyToOne .....)with oracle as database..
I used JpaRepository, crudRepository, I even tried with JDBC directly, the return takes between 8 min up to 30 min.
I thought the problem was coming from the request, so I tried a findAll () and the processing time remained the same.
I changed the settings of the JVM -Xmx and -Xms to give more memory, but nothing helps.
Here is the request that I make:
public interface TestRepository extends CrudRepository {
#Query(value = "select new Test(CONCAT(t.date1, t.stringTarget, t.numInfo), t.name, t.phone, t.numInfo, t.date1, t.cotations, t.stringTarget, p.code, p.design)"
+ " from Test t, PointVente p"
+ " WHERE t.ePdv = p.numero"
+ " AND t.date1 BETWEEN :dateBegin AND :dateEnd"
+ " AND (t.state <> 'ANCL' or t.state is null)"
+ " AND t.game in :game"
+ " AND t.type in :type"
+ " AND t.participe = 1"
+ " AND NOT EXISTS (select t2.numInfo, t2.date1"
+ " from Test t2"
+ " WHERE t2.date1 BETWEEN :dateBegin AND :dateEnd"
+ " AND (t2.state <> 'ANCL' or t2.state is null)"
+ " AND t2.game in :game"
+ " AND t2.type in :type"
+ " AND t2.participe = 1"
+ " AND t2.numInfo = t.numInfo"
+ " AND t2.date1 = t.date1"
+ " AND (t2.phone is null or t2.phone NOT IN (select b.phone from BlacklistTest b))"
+ " group by CONCAT(t2.date1, t2.numInfo), t2.name, t2.phone, t2.numInfo, t2.date1, t2.ePdv"
+ " having sum(t2.cotations) <= :target)"
+ " AND t.cotations > :target"
+ " AND (t.phone is null or t.phone NOT IN (select b.phone from BlacklistTest b))")
List<TestResult> findTest(#Param("dateBegin") Date dateBegin, #Param("dateEnd") Date dateEnd, #Param("game") List<String> game, #Param("type") List<String> type, #Param("target") BigDecimal target);
}
is it possible to reduce the response time?
May I have your help please.

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.

JPA could not locate named parameter

I keep getting the following error: "could not locate named parameter [articleCommentId]" but it doesn't make sense to me because to me the named parameter is very much in place.
public ArticleCommentForDisplay getCommentByArticleCommentId(BigInteger articleCommentId) {
String queryString = "select c.article_comment_id, "
+ " c.article_id, "
+ " c.parent_comment_id, "
+ " p.nickname, "
+ " c.title, "
+ " c.comment, "
+ " c.person_id, "
+ " c.confirmed_user, "
+ " c.comment_depth, "
+ " c.moderation_rank, "
+ " c.moderation_reason, "
+ " c.hide, "
+ " c.hide_reason, "
+ " c.session_id, "
+ " c.confirmation_uuid, "
+ " c.created_timestamp, "
+ " c.created_by_id, "
+ " c.updated_timestamp, "
+ " c.updated_by_id, "
+ " c.update_action, "
+ " null as comment_path "
+ "from article_comment c "
+ " join person p "
+ " on p.person_id = c.person_id "
+ "where c.article_comment_id = :articleCommentId; ";
Query query = em.createNativeQuery(queryString, "ArticleCommentMap");
query.setParameter("articleCommentId", articleCommentId);
List <ArticleCommentForDisplay> articleComments = new ArrayList<>();
articleComments = query.getResultList();
ArticleCommentForDisplay theComment = articleComments.get(0);
return (theComment);
}
Here is an extract of the stack trace with the relevant error:
Caused by: java.lang.IllegalArgumentException: org.hibernate.QueryParameterException: could not locate named parameter [articleCommentId]
at org.hibernate.ejb.QueryImpl.setParameter(QueryImpl.java:379)
at org.hibernate.ejb.QueryImpl.setParameter(QueryImpl.java:72)
at com.extremelatitudesoftware.content.ArticleCommentFacade.getCommentByArticleCommentId(ArticleCommentFacade.java:293)
I bet it is due to the extra ; in your query string.
SQL/HQL does not need to be terminated by semicolon
The named parameters is not defined for native queries in JPA Specification.
Replace
where c.article_comment_id = :articleCommentId;
with
where c.article_comment_id = ?1;
....
query.setParameter(1, articleCommentId)
Mine was an extra ' in the sql query. Oh my gosh, kept looking until my eyes nearly pooooopped out `-)
So, ensure that you don't have anything "extra" in your query, make sure that your (, ", ' etc...have matching pairs, because the error message in that case is not relevant and has nothing to do with your named parameter! JPA is right as it could not locate it, but that's because something else in your query is messing up...
You can also use it like this
where c.article_comment_id = ?,
and c.any_other_field = ?;
....
query.setParameter(1, articleCommentId)
query.setParameter(2, anyOtherValue)
it will take it by sequence.
And you can also give numbers like
where c.article_comment_id = ?1,
and c.any_other_field = ?2;
....
query.setParameter(1, articleCommentId)
query.setParameter(2, anyOtherValue)
If you are using named parameter at end of your query the remove the ; from your query
In my case, I didn't add the extra space after the named parameter.
example:
+ "WHERE\n" + " s.something = 'SOME'\n" + "START WITH\n"
+ " s.country_type = :countryName" + "CONNECT BY\n"
changed to (notice the space after named parameter :countryName
+ "WHERE\n" + " s.something = 'SOME'\n" + "START WITH\n"
+ " s.country_type = :countryName " + "CONNECT BY\n"

Categories