How to use the IN clause in an JPA #Query annotation - java

I have defined this method
#Query("select cs from CenterStudy cs where cs.id in (?1)")
#EntityGraph(value = "centerStudyAndNotifiedUsers", type = EntityGraph.EntityGraphType.LOAD)
List<CenterStudy> findAllByIdsWithCenterAndUsers(List<Long> ids);
The list with the ids is not null, nor empty, but I always get the following exception:
java.lang.NullPointerException
at org.hibernate.param.NamedParameterSpecification.bind(NamedParameterSpecification.java:67)
at org.hibernate.loader.hql.QueryLoader.bindParameterValues(QueryLoader.java:613)
at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1900)
I'm pretty sure it has something to do with the way the IN clause it's defined.
Any suggestions are well welcomed!

It's a common issue, a bug in Hibernate which you can find at :
NullPointer when combining JPQL query with in clause and #NamedEntityGraph where it says that:
When you combine a JPQL query with an in clause and a #NamedEntityGraph
you get a NullPointerException in org.hibernate.param.NamedParameterSpecification.bind(NamedParameterSpecification.java:67) when you execute the query.
So you would have to remove the #EntityGraph annotation here in order to fix this problem.

public List<DealInfo> getDealInfos(List<String> dealIds) {
String queryStr = "SELECT NEW com.admin.entity.DealInfo(deal.url, deal.url, deal.url, deal.url, deal.price, deal.value) " + "FROM Deal AS deal where deal.id in :inclList";
TypedQuery<DealInfo> query = em.createQuery(queryStr, DealInfo.class);
query.setParameter("inclList", dealIds);
return query.getResultList();
}
Works with JPA 2

I've faced this issue before. I fixed it using named parameteres instead of positional parameters.
Example:
"Select p from product where p.id in(?)" -- not working
replaced by:
"Select p from product where p.id in(:idList)" -- OK

Related

Error while executing HQL with group by clause

I am using the following HQL query:
List<Object> object = session.createQuery("select userId, count(*) from Tweet " +
"group by userId", Object.class).getResultList();
It's causing the following error:
Caused by: org.hibernate.query.QueryTypeMismatchException: Query
result-type error - multiple selections: use Tuple or array at
org.hibernate.query.sqm.internal.QuerySqmImpl.checkQueryReturnType(QuerySqmImpl.java:367)
at
org.hibernate.query.sqm.internal.QuerySqmImpl.visitQueryReturnType(QuerySqmImpl.java:328)
at
org.hibernate.query.sqm.internal.QuerySqmImpl.(QuerySqmImpl.java:227)
at org.hibernate.internal.AbstractShare
what could be the reason for this?
Is it because I am selecting specific columns in it?
Your select clause is returning two things, therefore the type signature of the method should be List<Object[]>. Use this version:
List<Object[]> object = session.createQuery("select userId, count(*) from Tweet " +
"group by userId", Object[].class).getResultList();
But note that you could also define an entity which matches the select clause. Then, you could avoid the cumbersome Object[] result set type.

Is it possible to avoid adding " = true" for a boolean function in "where" clause of HQL query

I am using PostgreSQL and JPA/Hibernate.
I need to build an HQL query that has PostgreSQL regex comparison operator inside (name ~ 'pattern').
I have the following dialect:
public class PostgreSQLExtendedDialect extends PostgreSQL95Dialect {
public PostgreSQLExtendedDialect() {
registerFunction("regexp_matches", new SQLFunctionTemplate(BooleanType.INSTANCE, "(?1 ~ ?2)"));
}
}
However, the following query ...
entityManager.createQuery("select p from Person p where regexp_matches(p.name, ?1)")
.setParameter(1, "qwe")
.getResultList();
... fails with following exception:
antlr.NoViableAltException: unexpected AST node: (
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.logicalExpr(HqlSqlBaseWalker.java:2146)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.whereClause(HqlSqlBaseWalker.java:815)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:609)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:313)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:261)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:271)
In the same time, the following query ...
entityManager.createQuery("select p from Person p where regexp_matches(p.name, ?1) = true")
.setParameter(1, "qwe")
.getResultList();
... works, but produces the following weird SQL:
where
person0_.name ~ ?=true
I would like to know if it's possible to avoid adding weird " = true" suffix to HQL part that is already a boolean expression. Ideally, maybe there is some ability to register just operator, so that I could write "p.name ~ ?1" directly in HQL.
Note. I know that I could write native query, but I have rather complex HQL query that would be a huge giant in SQL version, so I need to keep HQL.
Thanks!
It's not possible. Mabye in Hibernate 6, but there is no way to do it before because functions are always supposed to produce scalar values and if you need a predicate, you need to use a JPQL/HQL predicate.
You could introduce a custom SQLFunction implementation that renders the HQL function invocation regexp_matches(p.name, ?1) to regexp_matches(p.name, ?1) and true = so that the overall generated query will be regexp_matches(p.name, ?1) and true = true when using regexp_matches(p.name, ?1) = true.
PostgreSQL can optimize away the tautology true = true.

How to map result got from native query in hibernate?

I have a query that has more columns then what my entity class has.
In order to not let hibernate complaints, I have to add an annotation to the field like
#Transient
private Integer count;
But by doing this makes hibernate not able to map count. Let's say my query is
session.createSQLQuery("SELECT p.*, count(p.id), sqrt(1+2) as distance FROM post p group by p.id")
I know the query doesn't make any logical sense. This is just for example. The columns return from query above will have everything in post and two extra columns, count and distance. I wanted to map the result to my entity with count and distance are annotated with #Transient, or if there's a better way to map the result. I'm more than happy to do so. The goal is not to do this in an entity but a class with mapped result. I've tried calling addEntity() but doesn't seem to help.
You can use Result Set Transformer to achieve this.
Step 1 ) Create a new DTO class with all the fields that you query going to return
Step 2 ) Add the below line
setResultTransformer( Transformers.aliasToBean(DTO.class))
Example :
List resultWithAliasedBean = session.createQuery(
"SELECT p.*, count(p.id), sqrt(1+2) as distance FROM post p group by p.id")
.setResultTransformer(Transformers.aliasToBean(DTO.class))
.list();
DTO dto = (DTO) resultWithAliasedBean.get(0);
Note : Make sure the field names in the DTO class match the column name which your query is returning.
I see that you are using Hibernate so Yathish answer works fine.
But if you want to do it with JPA spec then you can use Result Set Mapping
Query q = em.createNativeQuery(
"SELECT c.id, c.name, COUNT(o) as orderCount, AVG(o.price) AS avgOrder " +
"FROM Customer c " +
"JOIN Orders o ON o.cid = c.id " +
"GROUP BY c.id, c.name",
"CustomerDetailsResult");
#SqlResultSetMapping(name="CustomerDetailsResult",
classes={
#ConstructorResult(targetClass=com.acme.CustomerDetails.class,
columns={
#ColumnResult(name="id"),
#ColumnResult(name="name"),
#ColumnResult(name="orderCount"),
#ColumnResult(name="avgOrder", type=Double.class)})
})
There you have to specifiy the mappin of the columns from the SQL result set to the DTO.
And if you think this is to complicated there is a open source project called QLRM (Query Lanaguage Result Mapper) that mapps any SQL statement to a POJO.
http://simasch.github.io/qlrm/
And last but not least if you will do extensive SQL processing why not have a look at jOOQ: https://www.jooq.org/

HQL, can I parameterize the FROM clause?

I have this HQL query:
Query q = em.createQuery (
"DELETE FROM Annotation a WHERE a.id IN ( " +
" SELECT ja.id FROM :entityName an JOIN an.annotations ja)"
);
and I'm being told: QuerySyntaxException: unexpected token: : near line 1
Do I have any hope of making the entity name after FROM a parameter? I have a list of entities to send to this query and I'm afraid that string concatenation is too slow.
You can't substitute the Entity name the parameters work for entity properties not instead.
You could select the entities ids to be deleted with one query and then pass them to a second delete query, but for READ_COMMITED transaction isolation you might still end up with someone else inserting one child entity that would have matched your query. SERIALIZABLE will solve this issue.

java.lang.String cannot be cast HQL

Updated
Error says:
ava.lang.String cannot be cast to com.test.test.classes.TblTaxType
what is happening is when I add the tag select distinct taxtcode error is appearing. But when I removed the select tag like FROM tblTaxType tbl_tax_type WHERE bfnsCode = ? everything is fine. What is the cause? this is my code:
String hql = "SELECT DISTINCT TAXT_CODE FROM tbl_tax_type WHERE BFNS_CODE = ?";
try {
setSession(HibernateUtil.getSession());
#SuppressWarnings("unchecked")
List <TblTaxType> resultList = getSession().createSQLQuery(hql)
.setString(0, bfnsCode)
.list();
Your entity is probably named TblTaxType, not tblTaxType. Case matters.
Side note: don't name sql an HQL query. SQL and HQL are different languages.
Solved it using GROUP BY instead by using DISTINCT.
String hql = "FROM TblTaxType tbl_tax_type WHERE bfnsCode = ? GROUP BY taxtCode";
Your query returns TAXT_CODE, this field is a property of your TblTaxType entity, so you can't cast one property (string) in your main entity. This is the reason of your error.
If you need complete entity you must change your query but DISTINCT is not useful in this case because if you extract complete entity, there's ID field (different for each row). If you want a first element, you can add in your query ORDER BY clause with LIMIT 1 (is MySql).
A solution with GROUP BY works only if you use MySql as DBMS because if you have Sql Server the correct behaviour of field list / group by is: a field in field list must be in GROUP BY cluse or must be in aggregate function.

Categories