Hibernate named query issue - java

I have below name query
#NamedQuery(name="ScInstantTrack.getCustomerDetails",
query="select b.cardDetail.mstCustomer.customerId, last_day(b.endDate), " +
"LISTAGG(b.txnId,'|') WITHIN GROUP (ORDER BY b.endDate), " +
"count(b.txnId), sum(b.amount), sum(b.balanceAmt), sum(b.redemptionAmt) " +
"from ScInstantTrack b " +
"where b.cardNo = b.cardDetail.cardBarcode " +
"AND b.cardDetail.mstCustomer.customerId = :customerId " +
"and b.startDate <= trunc(:todayDate) " +
"and b.endDate >= trunc(:todayDate) " +
"and b.cardDetail.mstStatus.statusId = 3003 group by b.cardDetail.mstCustomer.customerId, last_day(b.endDate)")
When I am executing this query then getting below error :
unexpected token: WITHIN
I am using Oracle Database.
Why I am getting this error? How to solve this issue?

Try to use #NamedNativeQuery instead of #NamedQuery.
Also check this explanation of difference between them.
Basically you are using expressions that are exclusive in Oracle DB. In other words - you want to execute native query (query in native for Oracle DB language). Named queries use Java Persistence Query Language (HQL i.e.).

The error happen because LISTAGG is an oracle specific function.
That function is not avaliable in HQL and there is nothing you can use instead for HQL.
In order to get the result you have to use a SQLQuery wich perform native SQL queryes. This way You have to implement a version of thw query for each database, but it will work.

Related

JPA Passing a list of Strings in to IN clause in a native query (using citext)

I am trying to pass a list of Strings in to IN clause in a native query using citext for a case insensitive search. Everytime I try to test this query out using postman I get the following error:
org.hibernate.QueryException: Named parameter not bound : ingredientFilter
Below is the query I am trying to test:
#Query(value = "select personal_recipes.name, personal_recipes.type, personal_recipes.comments, " +
"personal_recipes.instructions, personal_recipes.rating, ingredients.name, ingredients.quantity " +
"from personal_recipes " +
"inner join ingredients on personal_recipes.name = ingredients.recipe_name " +
"where (ingredients.name::citext in (:ingredientFilter))" , nativeQuery = true)
List<PersonalRecipesEntity> getPersonalRecipesByIngredient(#Param(value = "ingredient") List<String> ingredientFilter);
I am confused as this query works when I test it out in Dbeaver.
I tried removing the brackets around :ingredientFilter. It did not change anything. I am new to Spring boot and havent been able to find anything online.
Parameters can be exposed via the name declared using #Param. You should rename ingredient by ingredientFilter. You can find more about the subject here

Hibernate HQL setMaxResults does not work?

I'm building a small program in Java Hibernate handling a subpart of the DBLP database (parsed from XML into a SQL db).
I've queries manipulating big chuncks of data so I want to limit the output result to 10 so it goes faster.
Query query = this.session.createQuery("select A.author_id "
+ "from publication as P "
+ "join P.authors as A "
+ "where P.year <= 2010 and P.year >= 2008 "
+ "group by A.author_id "
+ "having count(distinct P.year) = 3");
query.setMaxResults(10);
this.authors = query.iterate();
That piece of code is supposed to retrieve all the authors who published at least once every year between 2008 and 2010 included.
My problem is that the line "query.setMaxResults(10);" simply does not have effect, the SQL command generated by Hibernate is
select author2_.Author_id as col_0_0_ from publication publicatio0_ inner join author_publication authors1_ on publicatio0_.Publication_ID=authors1_.publication_id inner join author author2_ on authors1_.author_id=author2_.Author_id where publicatio0_.Year<=2010 and publicatio0_.Year>=2008 group by author2_.Author_id having count(distinct publicatio0_.Year)=3
limit ?
Remarque the "limit ?" at the end.
So my question is simple, how do I use properly setMaxResults to get a correct SQL Limit ?
EDIT: all the limit stuff works fine, I misunderstood the use of limit in SQL, what I'm looking for is a way to stop the execution of the query after a given number of rows corresponding to the conditions is found, so that it does not take days to get thousands useless rows but simply returns the 10 first found rows for example.
Thanks in advance !

Native query produces MySQL syntax error and unexpected result

In my application, when a User removes a Message, I need to remove the relationship between the 2 entities while keeping them intact. Hence, I tried to delete rows directly from the relationship table using the following ways:
1.
Query q = em.createNativeQuery("DELETE FROM User_Inbox WHERE User_USERNAME = :username AND Message_ID = :messageID");
q.setParameter("username", username);
q.setParameter("messageID", messageID);
q.executeUpdate();
2.
Query q = em.createNativeQuery("DELETE FROM User_Inbox WHERE User_USERNAME = :username AND Message_ID = :messageID");
q.setParameter("username", "'" + username + "'");
q.setParameter("messageID", "'" + messageID + "'");
q.executeUpdate();
The 1st and 2nd approaches produced the following exception:
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException:
You have an error in your SQL syntax; check the manual that corresponds to your
MySQL server version for the right syntax to use near ':username AND
Message_ID = :messageID' at line 1
Error Code: 1064
3.
Query q = em.createNativeQuery("DELETE FROM User_Inbox WHERE User_USERNAME = ':username' AND Message_ID = ':messageID'");
q.setParameter("username", username);
q.setParameter("messageID", messageID);
q.executeUpdate();
The 3rd approach did not produce any exceptions. It went through normally. However, nothing was deleted from the User_Inbox table.
4.
Query q = em.createNativeQuery("DELETE FROM User_Inbox WHERE User_USERNAME = '" + username + "' AND Message_ID = '" + messageID + "'");
q.executeUpdate();
The 4th approach worked perfectly. The query went through smoothly and a record was deleted properly.
The last approach worked but the code doesn't look very neat to me. I'd be very grateful if someone could show me what I did wrong with the 1st three approaches.
UPDATE:
Based on the answer from D. Moore, I've just found out that named parameters cannot be used with native queries. This has been mentioned in this answer by Pascal Thivent.
Named parameters follow the rules for identifiers defined in Section
4.4.1. The use of named parameters applies to the Java Persistence query language, and is not defined for native queries. Only
positional parameter binding may be portably used for native
queries.
I agree that (4.) is certainly not the right way to be doing things. It's tedious and unsafe.
(3.) are working, but not producing the expected results since they have extra quotes.
As to why (1.) isn't working, you would want to look into the SQL that is being generated (I believe the settings are specific to the JPA system you are using). You seem to have the code correct, but the error implies that the parameters are not being substituted.
Do positional parameters do anything different?
Query q = em.createNativeQuery("DELETE FROM User_Inbox WHERE User_USERNAME = ?1 AND Message_ID = ?2");
q.setParameter(1, username);
q.setParameter(2, messageID);
q.executeUpdate();

Issue with a JPA query

I am trying to execute the following JPA query:
public static final String UPDATE_INVENTORY_CUSTOMER_FOR_AMS_MAPPING = "UPDATE Inventory inventory SET"
+ " inventory.customer.id = :" + DataAccessConstants.PARAM_CUSTOMER_ID
+ " ,inventory.lastUpdateUserId = :" + DataAccessConstants.PARAM_USER_ID
+ " where inventory.amsConsignorName = :" + DataAccessConstants.PARAM_AMS_CONSIGNOR_NAME
+ " and inventory.amsConsignorOrgCd = :" + DataAccessConstants.PARAM_AMS_CONSIGNOR_ORG_CD
+ " and inventory.amsConsignorTypeName = :" + DataAccessConstants.PARAM_AMS_CONSIGNOR_TYPE
+ " and inventory.status.code in (:" + DataAccessConstants.PARAM_STATUS + ")";
but it is seeing the following:
update ATL_INVENTORY, set CONSIGNOR_ID=?, LAST_UPDATE_USER_ID=? where AMS_CONSIGNOR_NAME=? and AMS_CONSIGNOR_ORG_CD=? and AMS_CONSIGNOR_TYPE_NAME=? and (CODE in (? , ? , ? , ?))
Any ideal as to why there is a comma after the table name?
Solution
I had to change the original query to the following:
update Inventory inv set "
+ "inv.customer.id = :" + DataAccessConstants.PARAM_CUSTOMER_ID + " "
+ "where inv.amsConsignorName =:" + DataAccessConstants.PARAM_AMS_CONSIGNOR_NAME + " "
+ "and inv.amsConsignorOrgCd =:" + DataAccessConstants.PARAM_AMS_CONSIGNOR_ORG_CD + " "
+ "and inv.amsConsignorTypeName =:" + DataAccessConstants.PARAM_AMS_CONSIGNOR_TYPE + " "
+ "and exists(select 1 from Code code where inv.status = code and code.code in (:" + DataAccessConstants.PARAM_STATUS + "))
Which then produced this:
update ATL_INVENTORY set CONSIGNOR_ID=? where AMS_CONSIGNOR_NAME=? and AMS_CONSIGNOR_ORG_CD=? and AMS_CONSIGNOR_TYPE_NAME=? and (exists (select 1 from ATL_CODE code1_ where ATL_INVENTORY.STATUS=CODE_ID and (code1_.CODE in (? , ? , ? , ?))))
Based on a clarification located here: Incorrect SQL generated for JPA QL Update statement involving multiple entities
Your query is code as UPDATE Inventory inventory SET, but the generated SQL says update ATL_INVENTORY, set. Why is the literal SQL string not what you coded? When I encounter mysteries like this, they're usually caused by assuming that one thing is being done when in fact another is in play.
This suggests that the SQL you coded isn't being used to generate that SQL the way you're assuming. See where else this query might be coming from. I'd bet that the real source has a misplaced comma in it.
Which JPA implementation are you using? If I'm incorrect about a bad assumption, it says that there's a bug in the implementation. Have you used it before? Have you had success with UPDATE? If yes, it's definitely buried somewhere in your code base.
You have an interface with a bunch of constants in it. Personally, I don't care for a design like that. It's an anti-pattern with a name.
A bug in the JPA provider is very unlikely in my opinion so, as #duffymo said, are you sure you're using the right constant, that the code or maybe dependencies are up-to-date? I'd dig in that direction.
That being said, I really wonder why you're not using named queries (that are most of time pre-compiled by the persistence implementation at deployment time), either in the Java code or in meta-data mapping files (the fun part is that people didn't find having EJB-QL queries externalized in XML very manageable in EJB 2.x, hence the #NamedQuery annotation of JPA).

I need help with this error: java.lang.NoSuchMethodError

I have this Java code (JPA):
String queryString = "SELECT b , sum(v.votedPoints) as votedPoint " +
" FROM Bookmarks b " +
" LEFT OUTER JOIN Votes v " +
" on (v.organizationId = b.organizationId) " +
"WHERE b.userId = 101 " +
"GROUP BY b.organizationId " +
"ORDER BY votedPoint ascending ";
EntityManager em = getEntityManager();
Query query = em.createQuery(queryString);
query.setFirstResult(start);
query.setMaxResults(numRecords);
List results = query.getResultList();
I don't know what is wrong with my query because it gives me this error:
java.lang.NoSuchMethodError: org.hibernate.hql.antlr.HqlBaseParser.recover(Lantlr/RecognitionException;Lantlr/collections/impl/BitSet;)V
at org.hibernate.hql.antlr.HqlBaseParser.fromJoin(HqlBaseParser.java:1802)
at org.hibernate.hql.antlr.HqlBaseParser.fromClause(HqlBaseParser.java:1420)
at org.hibernate.hql.antlr.HqlBaseParser.selectFrom(HqlBaseParser.java:1130)
at org.hibernate.hql.antlr.HqlBaseParser.queryRule(HqlBaseParser.java:702)
at org.hibernate.hql.antlr.HqlBaseParser.selectStatement(HqlBaseParser.java:296)
at org.hibernate.hql.antlr.HqlBaseParser.statement(HqlBaseParser.java:159)
at org.hibernate.hql.ast.QueryTranslatorImpl.parse(QueryTranslatorImpl.java:271)
at org.hibernate.hql.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:180)
at org.hibernate.hql.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:134)
at org.hibernate.engine.query.HQLQueryPlan.(HQLQueryPlan.java:101)
at org.hibernate.engine.query.HQLQueryPlan.(HQLQueryPlan.java:80)
at org.hibernate.engine.query.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:94)
at org.hibernate.impl.AbstractSessionImpl.getHQLQueryPlan(AbstractSessionImpl.java:156)
at org.hibernate.impl.AbstractSessionImpl.createQuery(AbstractSessionImpl.java:135)
at org.hibernate.impl.SessionImpl.createQuery(SessionImpl.java:1650)
Thanks.
You definitely have an issue with the version of hibernate and ANTLR jars that you are using. The recover method wasn't present in the ANTLR Parser class until version 2.7.6? If you are using an earlier version of ANTLR, such as 2.7.2, then you will see this problem.
Using maven can cause this sort of situation, where you depend on Hibernate and its transitive dependencies, but something 'closer'; e.g. Struts; providers a different, earlier version of ANTLR and that earlier version gets resolved in your application.
If you can provide the version of jars involved, we would be able to help some more. Once you have fixed the issue with the jar versions, you should get a more revealing error message which shows what is wrong with your HQL expression.
Stab in the dark - Are you sure you have a consistent set of jars - perhaps you need to get the antlr jar that comes with the hibernate distribution you are using...
I've found the problem:
because this is a native query, java classes for this 2 tables must have some special attributes:
in Bookmarks.java class
#OneToMany(mappedBy = "bookmarkId")
private Collection votesCollection;
and in Votes.java class
#JoinColumn(name = "bookmark_id", referencedColumnName = "bookmark_id")
#ManyToOne
[private Bookmarks bookmarkId;
and i have also changed the query to work
tring queryString = "SELECT b, sum(v.votedPoints) " +
"FROM Bookmarks b " +
"LEFT OUTER JOIN b.votesCollection v " +
"WHERE b.userId = 101 " +
"GROUP BY b.organizationId " +
"ORDER BY sum(v.votedPoints) asc ";
thanks for the help
May be you have some double-quotes " missing or which should be doubled in your HQL.
Illustration here.
Or you miss some simple quotes as illustrated there
The query seems to be invalid unless it's an artifact of formatting.
I think you meant this:
Select b, ...
to be:
Select b.organizationId, ...
??
I have the consistent set of jars because simple queries like this one
"SELECT b FROM table_name b WHERE b.userId = 102 "
are working. I have verified all double quotes and everything is alright.
My database is: mysql, and I use jpa to connect to it. I don't know what is causing the problem. Maybe this type of join, i don't know
Er, isnt your query trying to select b which is a table alias and thats not allowed as far as I know.
I'd probably guess that something is going wrong with your query, because the method HqlBaseParser fails to lookup is called recover(RecognitionException, Bitset). Perhaps this query fails for some reason the other simpler queries don't (and the NoSuchMethod exception is thrown when attempting to recover from that error).
java.lang.NoSuchMethodError: org.hibernate.hql.antlr.HqlBaseParser.recover(Lantlr/RecognitionException;Lantlr/collections/impl/BitSet;)V
Your query is still wrong. Maybe it works with your driver/db but it isn't standard SQL. You should be selecting b.* or b.organizationId.

Categories