How to apply OR Condition in HQL? - java

I have an HQL query that takes msisdn and subServiceId on an Entity Unsubscription.
Below is the code for the same:
Query query = session.createSQLQuery(unsubscriptionInfodemo)
.addEntity(Unsubscription.class)
.setParameter("msisdn", msisdn)
.setParameter("subServiceId", subServiceId);
Now,I need to check in this Unsubscription entity whether subServiceId is the subServiceId I have passed or it should be the subServiceId I have hardCoded.
Can I apply like
public void checkUser(String msisdn,String subServiceID){
Query query = session.createSQLQuery(unsubscriptionInfodemo)
.addEntity(Unsubscription.class)
.setParameter("msisdn", msisdn)
.setParameter("subServiceId", subServiceId)
.setParameter("subServiceId", "XYZ");
}
HQL query:
unsubscriptionInfodemo=select * from unsubscription s where s.msisdn=:msisdn and s.subservice_id=:subServiceId
Can anyone guide me how to proceed?

How to apply OR Condition in HQL?
use OR on your query:
select * from unsubscription s where s.msisdn=:msisdn and s.subservice_id=:subServiceId OR
s.subservice_id=:subServiceId2
and name parameters shouldn't be the same:
.setParameter("msisdn", msisdn)
.setParameter("subServiceId", subServiceId)
.setParameter("subServiceId2", "XYZ"); // Make this subServiceId2

The dynamic functionality you want to implement cannot be done with hql without changing the hql string.
I would consider using the criteria api which provides a dynamic functionality.
The easiest way is to either create the hql string based on the value (if subServiceId is existent or not) or use the criteria api, thus you will just have to add one extra equals filter in case of subServiceId presence.
The question should be changed on how to add dynamic conditions to hql based on variable presence.
Also check this answer

Related

Is it possible to alter the SELECT/WHERE of jOOQ DSL query

I would like manipulate a jOOQ DSL query changing its SELECT columns and WHERE conditions.
For example:
DSLContext ctx = ...;
SelectHavingStep query = ctx.select(MyEntity.MY_ENTITY.ZIP, DSL.count(MyEntity.MY_ENTITY.ZIP))
.from(MyEntity.MY_ENTITY)
.where(MyEntity.MY_ENTITY.ID.gt("P"))
.groupBy(MyEntity.MY_ENTITY.ZIP);
Use case 1:
I would like to pass the above query to a utility class that will produce the same query just with with a different SELECT, for example:
ctx.select(DSL.count())
.from(MyEntity.MY_ENTITY)
.where(MyEntity.MY_ENTITY.ID.gt("P"))
.groupBy(MyEntity.MY_ENTITY.ZIP);
This particular example is to be able to create paginated results showing the total number of rows of the query.
Use case 2:
I would like to pass the above query to a utility class that will produce the same query just with with a modified WHERE clause, for example:
SelectHavingStep query =
ctx.select(MyEntity.MY_ENTITY.ZIP, DSL.count(MyEntity.MY_ENTITY.ZIP))
.from(MyEntity.MY_ENTITY)
.where(
MyEntity.MY_ENTITY.ID.gt("P")
.and(MyEntity.MY_ENTITY.ZIP.in("100", "200", "300"))
)
.groupBy(MyEntity.MY_ENTITY.ZIP);
This particular example is to further restrict a query based on some criteria (i.e. data visibility based on the user doing the query).
Is this possible?
Currently I'm using helper classes to do this at query construction time in the application code. I would like to move the responsibility to a library so it can be enforced transparently to the app.
Thanks.
You shouldn't try to alter jOOQ objects, instead you should try to create them dynamically in a functional way. There are different ways to achieve your use-cases, e.g.
Use case 1:
An approach to generic paginated querying can be seen here: https://blog.jooq.org/calculating-pagination-metadata-without-extra-roundtrips-in-sql/
Ideally, you would avoid the extra round trip for the COUNT(*) query and use a COUNT(*) OVER () window function. If that's not available in your SQL dialect, then you could do this, instead:
public ResultQuery<Record> mySelect(
boolean count,
Supplier<List<Field<?>>> select,
Function<? super SelectFromStep<Record>, ? extends ResultQuery<Record>> f
) {
return f.apply(count ? ctx.select(count()) : ctx.select(select.get()));
}
And then use it like this:
mySelect(false,
() -> List.of(MY_ENTITY.ZIP),
q -> q.from(MY_ENTITY)
.where(MY_ENTITY.ID.gt("P"))
.groupBy(MY_ENTITY.ZIP)
).fetch();
This is just one way to do it. There are many others, see the below link.
Use case 2:
Just take the above example one step further and extract the logic used to create the WHERE clause in yet another function, e.g.
public Condition myWhere(Function<? super Condition, ? extends Condition> f) {
return f.apply(MY_ENTITY.ID.gt("P"));
}
And now use it as follows:
mySelect(false,
() -> List.of(MY_ENTITY.ZIP),
q -> q.from(MY_ENTITY)
.where(myWhere(c -> c.and(MY_ENTITY.ZIP.in("100", "200", "300")))
.groupBy(MY_ENTITY.ZIP)
).fetch();
Again, there are many different ways to solve this, depending on what is the "common part", and what is the "user-defined part". You can also abstract over your MY_ENTITY table and pass around functions that produce the actual table.
More information
See also these resources:
https://www.jooq.org/doc/latest/manual/sql-building/dynamic-sql/
https://blog.jooq.org/a-functional-programming-approach-to-dynamic-sql-with-jooq/

Can #Query annotation in Spring Data JPA take in a list of enums?

I've been trying to figure out how to take an input of a list of enums for my sql query. This is what it looks like:
#Query(value = "Select * from employees where city in :cities", nativeQuery = true)
List<Employee> findByCities(#Param("cities") List<City> cities);
I understand that for simple queries we can rely on the JPA Criteria API but I want to know if I can actually do it this way instead. Because if so, i can create more complicated queries (such as joins with another table) if I could have this flexibility of specifying the list.
Yes spring-data-jpa's #Query can take a list of enums.
This is my repository method
#Query("Select u from User u where u.userType in :types")
List<User> findByType(#Param("types") List<UserType> types);
This is my repository call
userRepository.findByType(Arrays.asList(AppConstant.UserType.PRINCIPLE)))
And this is the query logs
SELECT user0_.id AS id1_12_,
user0_.date_created AS date_created2_12_,
...
...
FROM users user0_
WHERE user0_.user_type IN ( ? )
Hope this helps.
PS: I tested this in my local machine with positive results.
Update 1
The same doesn't work with nativeQuery=true. I tested it on my local system and it doesn't seem to be working with native queries. However with JPQL it works fine as mentioned in the above answer.
May be this answer will help.

Spring Data JPA doesn't specify lower keyword in method names

I need to lowercase all emails when querying my table, but the documentation specifies only method-name-keyowrd for UPPER():
IgnoreCase findByFirstnameIgnoreCase … where UPPER(x.firstame) = UPPER(?1)
How the LOWER() could be used?
I have debug it and can see that PredicateBuilder doesn't seem to be considering it.
Are you aware if that is a limitation? Or could this be achieved in different way?
As per the Spring JPA reference guide, findXXXByIgnoreCase(...) by default uses UPPER(...) to perform case insensitive search.
To force it to use LOWER keyword we can use a custom query with #Query annotation and specifying SELECT .... LOWER(email)= LOWER(?1).
In my case I have used custom query as below to force it to use LOWER keyword for email column:
#Query("SELECT p from Person p where LOWER(email) = LOWER(?1)")
List<Person> findByEmailIgnoreCase(#Param("email") String email);
and this resulted in creating following query:
Hibernate: select person0_.id as id1_0_, person0_.email as email2_0_, person0_.first_name as first_na3_0_, person0_.last_name as last_nam4_0_ from person person0_ where lower(person0_.email)=lower(?)
This should help the query to use the function index i.e., LOWER(email).
The Spring Data JPA docs say this about case sensitivy of properties:
// Enabling ignoring case for an individual property
List<Person> findByLastnameIgnoreCase(String lastname);
// Enabling ignoring case for all suitable properties
List<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);
Using IgnoreCase in your method name will automatically generate a query which is case insensitiv for one or all specified properties.
Otherwise you always have the possibility to specify a custom query for your method by annotating it like this:
#Query("select u from User u")
Stream<User> findAllByCustomQueryAndStream();
By using the #Query annotation you can use the plain old JPQL to query your database.

Hibernate Criteria equivalent for IN clause in Subqueries?

I would like to translate a query like this one:
FROM Entity_1 obj
WHERE obj IN (FROM Entity2) OR
obj IN (FROM Entity3)
To hibernate Criteria form, and the official documentation of Hibernate is not enough because it doesn't say how to apply the IN statement.
Any hint?
The criteria API does not have a provision to add another query as a restriction.. i think what #Niroshan Abayakoon was trying to say is that you need to execute the queries for the IN clause seperatly & add the result to the Restrictions.in() condition.
List<?> entity2Data=//get data from either a query or criteria
List<?> entity3Data=//get data from either a query or criteria
Criteria c = // obtain criteria from session
// basically creates an OR condition chain
Disjunction orConditions = Restrctions.disjunction();
orConditions.add(Restrictions.in("obj", entity2Data));
orConditions.add(Restrictions.in("obj", entity3Data));
c.add(orConditions);
this would get hibernate to consider the list within the IN clause.
Its always better to fallback to HQL in situations like this.

Java-Hibernate-Newbie: How do I acces the values from this list?

I have this class mapped
#Entity
#Table(name = "USERS")
public class User {
private long id;
private String userName;
}
and I make a query:
Query query = session.createQuery("select id, userName, count(userName) from User order by count(userName) desc");
return query.list();
How can I access the values returned by the query?
I mean, how should I treat the query.list()? As a User or what?
To strictly answer your question, queries that specify a property of a class in the select clause (and optionally call aggregate functions) return "scalar" results i.e. a Object[] (or a List<Object[]>). See 10.4.1.3. Scalar results.
But your current query doesn't work. You'll need something like this:
select u.userName, count(u.userName)
from User2633514 u
group by u.userName
order by count(u.userName) desc
I'm not sure how Hibernate handles aggregates and counts, but I'm not sure if your query is going to work at all. You're trying to select a aggregate (i.e. the "count(userName)"), but you don't have a "group by" clause for userName.
If the query does in fact work, and Hibernate can figure out what to do with it, the results you get back will most likely be a raw Object[], because Hibernate will not be able to map your "count(userName)" data into any field on your mapped objects.
Overall, when you get into using aggregates in queries, Hibernate can get a little more tricky, since you're no longer mapping tables/columns directly into classes/fields. It might be a good idea to read up more on how to do aggregates in Hibernate, from their documentation.

Categories

Resources