How can I set a Hibernate Parameter to "null"? Example:
Query query = getSession().createQuery("from CountryDTO c where c.status = :status and c.type =:type")
.setParameter("status", status, Hibernate.STRING)
.setParameter("type", type, Hibernate.STRING);
In my case, the status String can be null. I have debugged this and hibernate then generates an SQL string/query like this ....status = null... This however does not Work in MYSQL, as the correct SQL statement must be "status is null" (Mysql does not understand status=null and evaluates this to false so that no records will ever be returned for the query, according to the mysql docs i have read...)
My Questions:
Why doesnt Hibernate translate a null string correctly to "is null" (and rather and wrongly creates "=null")?
What is the best way to rewrite this query so that it is null-safe? With nullsafe I mean that in the case that the "status" String is null than it should create an "is null"?
I believe hibernate first translates your HQL query to SQL and only after that it tries to bind your parameters. Which means that it won't be able to rewrite query from param = ? to param is null.
Try using Criteria api:
Criteria c = session.createCriteria(CountryDTO.class);
c.add(Restrictions.eq("type", type));
c.add(status == null ? Restrictions.isNull("status") : Restrictions.eq("status", status));
List result = c.list();
This is not a Hibernate specific issue (it's just SQL nature), and YES, there IS a solution for both SQL and HQL:
#Peter Lang had the right idea, and you had the correct HQL query. I guess you just needed a new clean run to pick up the query changes ;-)
The below code absolutely works and it is great if you keep all your queries in orm.xml
from CountryDTO c where ((:status is null and c.status is null) or c.status = :status) and c.type =:type
If your parameter String is null then the query will check if the row's status is null as well. Otherwise it will resort to compare with the equals sign.
Notes:
The issue may be a specific MySql quirk. I only tested with Oracle.
The above query assumes that there are table rows where c.status is null
The where clause is prioritized so that the parameter is checked first.
The parameter name 'type' may be a reserved word in SQL but it shouldn't matter since it is replaced before the query runs.
If you needed to skip the :status where_clause altogether; you can code like so:
from CountryDTO c where (:status is null or c.status = :status) and c.type =:type
and it is equivalent to:
sql.append(" where ");
if(status != null){
sql.append(" c.status = :status and ");
}
sql.append(" c.type =:type ");
The javadoc for setParameter(String, Object) is explicit, saying that the Object value must be non-null. It's a shame that it doesn't throw an exception if a null is passed in, though.
An alternative is setParameter(String, Object, Type), which does allow null values, although I'm not sure what Type parameter would be most appropriate here.
It seems you have to use is null in the HQL, (which can lead to complex permutations if there are more than one parameters with null potential.) but here is a possible solution:
String statusTerm = status==null ? "is null" : "= :status";
String typeTerm = type==null ? "is null" : "= :type";
Query query = getSession().createQuery("from CountryDTO c where c.status " + statusTerm + " and c.type " + typeTerm);
if(status!=null){
query.setParameter("status", status, Hibernate.STRING)
}
if(type!=null){
query.setParameter("type", type, Hibernate.STRING)
}
HQL supports coalesce, allowing for ugly workarounds like:
where coalesce(c.status, 'no-status') = coalesce(:status, 'no-status')
I did not try this, but what happens when you use :status twice to check for NULL?
Query query = getSession().createQuery(
"from CountryDTO c where ( c.status = :status OR ( c.status IS NULL AND :status IS NULL ) ) and c.type =:type"
)
.setParameter("status", status, Hibernate.STRING)
.setParameter("type", type, Hibernate.STRING);
For an actual HQL query:
FROM Users WHERE Name IS NULL
You can use
Restrictions.eqOrIsNull("status", status)
insted of
status == null ? Restrictions.isNull("status") : Restrictions.eq("status", status)
Here is the solution I found on Hibernate 4.1.9. I had to pass a parameter to my query that can have value NULL sometimes. So I passed the using:
setParameter("orderItemId", orderItemId, new LongType())
After that, I use the following where clause in my query:
where ((:orderItemId is null) OR (orderItem.id != :orderItemId))
As you can see, I am using the Query.setParameter(String, Object, Type) method, where I couldn't use the Hibernate.LONG that I found in the documentation (probably that was on older versions). For a full set of options of type parameter, check the list of implementation class of org.hibernate.type.Type interface.
Hope this helps!
this seems to work as wel ->
#Override
public List<SomeObject> findAllForThisSpecificThing(String thing) {
final Query query = entityManager.createQuery(
"from " + getDomain().getSimpleName() + " t where t.thing = " + ((thing == null) ? " null" : " :thing"));
if (thing != null) {
query.setParameter("thing", thing);
}
return query.getResultList();
}
Btw, I'm pretty new at this, so if for any reason this isn't a good idea, let me know. Thanks.
Related
I am using spring boot v2.6.1 and Postgres DB for my application. I have the below method in one of my repositories.
#Query(value = "SELECT * from subscriptions s " +
"WHERE (:merchantID is NULL OR s.created_by = cast(:merchantID AS text)) " +
"AND (:status is NULL or s.status IN (:status))", nativeQuery = true)
List<Subscription> findWithStatus (#Param("merchantID")String merchantID, #Param("status") List<String> status);
When the parameter 'status' is not null, it works fine. But when it is null it throws an exception
org.postgresql.util.PSQLException: ERROR: operator does not exist: character varying = bytea
From this, I understand that the condition is not excluded from the query even though the specified parameter is null. I thought the problem is checking whether the parameter is null that is in :status is null, so I just changed the list parameter to String and passed a null value, still the same exception is thrown. So the problem is not with :status is null but with the IN operator in the query.
How to exclude this condition with IN operator when the parameter is null?
If you want to use this Native Query approximation, one solution is adding an additional Boolean parameter that indicates if the Status List is null.
#Query(value = "SELECT * from subscriptions s " +
"WHERE (:merchantID is NULL OR s.created_by = cast(:merchantID AS text)) " +
"AND (:statusIsNull or s.status IN (:status))", nativeQuery = true)
List<Subscription> findWithStatus (#Param("merchantID")String merchantID, #Param("statusIsNull") Boolean statusIsNull, #Param("status") List<String> status);
So function call would be something like this:
Boolean statusIsNull = (statusList == null || statusList.isEmpty()) ? true : false;
List<Subscription> subscriptionList = findWithStatus (merchantID, statusIsNull, statusList);
But I really recommend to use JPA Criteria API to build dynamic queries.
Regards.
I need to retrieve all the T1 objects from a table based on a condition where the expire date is null and the query should retrieve only T16_ID of three types which are A, B and C. I have the below simple SQL query for this which returns the correct result.
SELECT * FROM T1 d
WHERE d.T1_T2_ID_FK = '123'
AND d.T1_DATE_EXPIRED IS NULL
AND (d.T16_ID LIKE 'A' OR
d.T16_ID LIKE 'B'
OR d.T16_ID LIKE 'C');
I use JPA for object relational mapping in my application and below is my respective JPA query
TypedQuery<T1Docs> query = em.createQuery(
"**SELECT t FROM T1Docs t WHERE t.t1ID.t2Id = :idNum
AND t.t1DateExpired IS NULL
AND (t.t16DocType.t16Id LIKE :docType1 OR t.t16DocType.t16Id LIKE :docType2 OR t.t16DocType.t16Id LIKE :docType3**)",T1Docs.class);
query.setParameter("idNum", idNumber);
query.setParameter("docType1", "%" + "A" + "%");
query.setParameter("docType2", "%" + "B" + "%");
query.setParameter("docType3", "%" + "C" + "%");
above query doesnt return any result and when I debug the plogram I found that the query is generated as below.
SELECT t0.T1_ID, t0.T1_DATE_CAPTURED, t0.T1_DATE_EXPIRED, t0.T1_T16_ID_FK
FROM T1_OWNER_DOCUMENT t0, T16_DOC_TYPE t1 WHERE ((((t0.T1_T2_ID_FK = ?)
AND (t0.T1_DATE_EXPIRED IS NULL))
AND ((t1.T16_ID LIKE ? OR t1.T16_ID LIKE ?)
OR t1.T16_ID LIKE ?)) AND (t1.T16_ID = t0.T1_T16_ID_FK))
because of the incorrect usage of parenthesis, the generated query becomes incorrect and It doesn't return any result.
If anyone knows how to fix this issue in the JPA query because I am new to JPA and having trouble in understanding how to use OR with multiple checks along with AND and parenthesis.
I have query which filters items by certain conditions:
#NamedQueries({
#NamedQuery(
name = ITEM.FIND_ALL_PARAMS_BY_COMPANY_COUNTRY_GROUP,
query = "SELECT i FROM Item i where "
+ "((i.idCompany=:companyId AND i.idEMGroup=:groupId) "
+ "OR (i.idCompany=:companyId AND i.idEMCountry =:countryId AND i.idEMGroup is null) "
+ "OR (i.idCompany is null AND i.idEMCountry = :countryId AND i.idEMGroup is null)) "
+ "order by i.idEMCountry desc, i.idCompany desc, i.idEMGroup desc")
})
In some cases parameters idEMGroup o companyId can be null which generates sql looking like this IdEmCompany = 200630758) AND (IdEMGroup = NULL) and it is incorrect sql syntax is it possible to dynamically if value is null for it as 'Column IS NULL' instead of 'Column = NULL' without adding a lot of if's, or it's just better to rewrite this query using Criteria API and just check if value is present and add predicates on certain conditions ?
Correct answer would be to use CriteriaQuery.
Though it is also possible to construct the query dynamically but manipulating #NamedQuery is not possible or might require stuff that makes it not worth to do.
Instead you could construct the query first as a String and create TypedQuery by manipulating the query string
String strQuery = "SELECT i FROM Item i"; // .. + the rest of stuff
if(null==companyId) {
// add something like "companyId IS :companyId"
// ":companyId" coulöd also be NULL"
// but to enable using tq.setParameter("companyId", companyId)
// without checking if there is param "companyId" so there always will
} else {
// add something like "companyId=:companyId"
}
TypedQuery<Item> tq = entityManager.createQuery(strQuery, Item.class);
tq.setParameter("companyId", companyId);
There will be some IFs but so will be in CriteriaQuery construction also.
I'm very, very new to Hibernate and JPA. I want to be able to apply ORDER BY and LIMIT clauses to a Hibernate(?) query, but am coming up empty. NOTE: I inherited this code.
Here is the current code:
public SomeCoolResponse getSomeCoolResponse(String myId) {
String queryString = "select aThing from AWholeBunchOfThings aThing " +
"join aThing.thisOtherThing oThing join oThing.StillAnotherThing saThing " +
"where saThing.subthing.id = :id";
Query q = getEntityManager().createQuery(queryString);
q.setParameter("id", myId);
List<MyThings> list = q.getResultList();
if(list.size() > 0) {
return list.get(0);
}
return null;
}
Instead of getting an entire list and then just returning the first result (which is the only one we need), I'd like to be able to apply a LIMIT 0,1 clause so that the query will be faster. Also, the query needs to be sorted descending on aThing.created which is a UNIX timestamp integer.
I've tried altering queryString like this:
String queryString = "select aThing from AWholeBunchOfThings aThing " +
"join aThing.thisOtherThing oThing join oThing.StillAnotherThing saThing " +
"where saThing.subthing.id = :id ORDER BY aThing.created LIMIT 0,1";
But Hibernate still returns the entire set.
I've looked at using the JPA CriteriaBuilder API, but it hurt my brain.
I'm a total n00b when it comes to this, and any help is greatly appreciated!
I think you need
q.setMaxResults(1);
See also the accepted answer here.
How do you do a limit query in HQL?
As to the "order by" clause you may include it in the queryString.
The JPQL equivalent to LIMIT start,max is:
setFirstResult and setMaxResults:
q.setFirstResult(start);
q.setMaxResults(limit);
I wonder if there is a good solution to build a JPQL query (my query is too "expressive" and i cannot use Criteria) based on a filter.
Something like:
query = "Select from Ent"
if(parameter!=null){
query += "WHERE field=:parameter"
}
if(parameter2!=null) {
query += "WHERE field2=:parameter2"
}
But i would write WHERE twice!! and the casuistic explodes as the number of parameter increases. Because none or all could be null eventually.
Any hint to build these queries based on filters on a proper way?
select * from Ent
where (field1 = :parameter1 or :parameter1 is null)
and (field2 = :parameter2 or :parameter2 is null)
Why can't you use a criteria, like this.
Other options (less good imho):
Create two named queries one for each condition, then call the respective query.
Or build up a string and use a native query.
Oh, do you just mean the string formation(?) :
query = "Select from Ent where 1=1 "
if(parameter!=null){
query += " and field=:parameter"
}
if(parameter2!=null) {
query += " and field2=:parameter2"
}
(I think that string formation is ugly, but it seemed to be what was asked for)