Get record with max id, using Hibernate Criteria - java

Using Hibernate's Criteria API, I want to select the record within a table with the maximum value for a given column.
I tried to use Projections, creating an alias for max(colunName), then using it in restrictions.eq(), but it keeps telling me "invalid number".
What's the correct way to do that with Hibernate?

You can use a DetachedCriteria to express a subquery, something like this:
DetachedCriteria maxId = DetachedCriteria.forClass(Foo.class)
.setProjection( Projections.max("id") );
session.createCriteria(Foo.class)
.add( Property.forName("id").eq(maxId) )
.list();
References
Hibernate Core Reference Guide
15.8. Detached queries and subqueries

I found that using addOrder and setMaxResults together worked for me.
Criteria c = session.createCriteria(Thingy.class);
c.addOrder(Order.desc("id"));
c.setMaxResults(1);
return (Thingy)c.uniqueResult();
Using the MySQL dialect, this generates a SQL prepared statement about like this (snipping out some of the fields):
select this_.id ... from Thingy this_ order by this_.id desc limit ?
I am not sure if this solution would be effective for dialects other than MySQL.

Use
addOrder(Order.desc("id"))
and fetch just the first result :)

HQL:
from Person where person.id = (select max(id) from Person)
Untested. Your database needs to understand subselects in the where clause.
Too lazy to find out if/how such a subselect can be expressed with the criteria api. Of course, you could do two queries: First fetch the max id, then the entity with that id.

The cleaner solution would also be :
DetachedCriteria criteria = DetachedCriteria.forClass(Foo.class).setProjection(Projections.max("id"));
Foo fooObj =(Foo) criteria.getExecutableCriteria(getCurrentSession()).list().get(0);

Date maxDateFromDB = null;
Session session = (Session) entityManager.getDelegate();
//Register is and Entity and assume maxDateFromDB is a column.
//Status is another entity with Enum Applied.
//Code is the Parameter for One to One Relation between Register and Profile entity.
Criteria criteria = session.createCriteria(Register.class).setProjection(Projections.max("maxDateFromDB") )
.add(Restrictions.eq("status.id", Status.Name.APPLIED.instance().getId()));
if(code != null && code > 0) {
criteria.add(Restrictions.eq("profile.id", code));
}
List<Date> list = criteria.list();
if(!CollectionUtils.isEmpty(list)){
maxDateFromDB = list.get(0);
}

To do it entirely with Detached Criteria (because I like to construct the detached criteria without a session)
DetachedCriteria maxQuery = DetachedCriteria.forClass(Foo.class)
.setProjection( Projections.max("id") );
DetachedCriteria recordQuery = DetachedCriteria.forClass(Foo.class)
.add(Property.forName("id").eq(maxId) );

For the max() function in hibernate:
criteria.setProjection(Projections.max("e.encounterId"));

Related

Is there equivalent clause in hibernate to Oracle 12c's "fetch first ..." clause? [duplicate]

In Hibernate 3, is there a way to do the equivalent of the following MySQL limit in HQL?
select * from a_table order by a_table_column desc limit 0, 20;
I don't want to use setMaxResults if possible. This definitely was possible in the older version of Hibernate/HQL, but it seems to have disappeared.
This was posted on the Hibernate forum a few years back when asked about why this worked in Hibernate 2 but not in Hibernate 3:
Limit was never a supported clause
in HQL. You are meant to use
setMaxResults().
So if it worked in Hibernate 2, it seems that was by coincidence, rather than by design. I think this was because the Hibernate 2 HQL parser would replace the bits of the query that it recognised as HQL, and leave the rest as it was, so you could sneak in some native SQL. Hibernate 3, however, has a proper AST HQL Parser, and it's a lot less forgiving.
I think Query.setMaxResults() really is your only option.
// SQL: SELECT * FROM table LIMIT start, maxRows;
Query q = session.createQuery("FROM table");
q.setFirstResult(start);
q.setMaxResults(maxRows);
If you don't want to use setMaxResults() on the Query object then you could always revert back to using normal SQL.
The setFirstResult and setMaxResults Query methods
For a JPA and Hibernate Query, the setFirstResult method is the equivalent of OFFSET, and the setMaxResults method is the equivalent of LIMIT:
List<Post> posts = entityManager.createQuery("""
select p
from Post p
order by p.createdOn
""")
.setFirstResult(10)
.setMaxResults(10)
.getResultList();
The LimitHandler abstraction
The Hibernate LimitHandler defines the database-specific pagination logic, and as illustrated by the following diagram, Hibernate supports many database-specific pagination options:
Now, depending on the underlying relational database system you are using, the above JPQL query will use the proper pagination syntax.
MySQL
SELECT p.id AS id1_0_,
p.created_on AS created_2_0_,
p.title AS title3_0_
FROM post p
ORDER BY p.created_on
LIMIT ?, ?
PostgreSQL
SELECT p.id AS id1_0_,
p.created_on AS created_2_0_,
p.title AS title3_0_
FROM post p
ORDER BY p.created_on
LIMIT ?
OFFSET ?
SQL Server
SELECT p.id AS id1_0_,
p.created_on AS created_on2_0_,
p.title AS title3_0_
FROM post p
ORDER BY p.created_on
OFFSET ? ROWS
FETCH NEXT ? ROWS ONLY
Oracle
SELECT *
FROM (
SELECT
row_.*, rownum rownum_
FROM (
SELECT
p.id AS id1_0_,
p.created_on AS created_on2_0_,
p.title AS title3_0_
FROM post p
ORDER BY p.created_on
) row_
WHERE rownum <= ?
)
WHERE rownum_ > ?
The advantage of using setFirstResult and setMaxResults is that Hibernate can generate the database-specific pagination syntax for any supported relational databases.
And, you are not limited to JPQL queries only. You can use the setFirstResult and setMaxResults method seven for native SQL queries.
Native SQL queries
You don't have to hardcode the database-specific pagination when using native SQL queries. Hibernate can add that to your queries.
So, if you're executing this SQL query on PostgreSQL:
List<Tuple> posts = entityManager.createNativeQuery(
SELECT
p.id AS id,
p.title AS title
from post p
ORDER BY p.created_on
""", Tuple.class)
.setFirstResult(10)
.setMaxResults(10)
.getResultList();
Hibernate will transform it as follows:
SELECT p.id AS id,
p.title AS title
FROM post p
ORDER BY p.created_on
LIMIT ?
OFFSET ?
Cool, right?
Beyond SQL-based pagination
Pagination is good when you can index the filtering and sorting criteria. If your pagination requirements imply dynamic filtering, it's a much better approach to use an inverted-index solution, like ElasticSearch.
If you don't want to use setMaxResults, you can also use Query.scroll instead of list, and fetch the rows you desire. Useful for paging for instance.
You can easily use pagination for this.
#QueryHints({ #QueryHint(name = "org.hibernate.cacheable", value = "true") })
#Query("select * from a_table order by a_table_column desc")
List<String> getStringValue(Pageable pageable);
you have to pass new PageRequest(0, 1)to fetch records and from the list fetch the first record.
You need to write a native query, refer this.
#Query(value =
"SELECT * FROM user_metric UM WHERE UM.user_id = :userId AND UM.metric_id = :metricId LIMIT :limit", nativeQuery = true)
List<UserMetricValue> findTopNByUserIdAndMetricId(
#Param("userId") String userId, #Param("metricId") Long metricId,
#Param("limit") int limit);
String hql = "select userName from AccountInfo order by points desc 5";
This worked for me without using setmaxResults();
Just provide the max value in the last (in this case 5) without using the keyword limit.
:P
My observation is that even you have limit in the HQL (hibernate 3.x), it will be either causing parsing error or just ignored. (if you have order by + desc/asc before limit, it will be ignored, if you don't have desc/asc before limit, it will cause parsing error)
If can manage a limit in this mode
public List<ExampleModel> listExampleModel() {
return listExampleModel(null, null);
}
public List<ExampleModel> listExampleModel(Integer first, Integer count) {
Query tmp = getSession().createQuery("from ExampleModel");
if (first != null)
tmp.setFirstResult(first);
if (count != null)
tmp.setMaxResults(count);
return (List<ExampleModel>)tmp.list();
}
This is a really simple code to handle a limit or a list.
Criteria criteria=curdSession.createCriteria(DTOCLASS.class).addOrder(Order.desc("feild_name"));
criteria.setMaxResults(3);
List<DTOCLASS> users = (List<DTOCLASS>) criteria.list();
for (DTOCLASS user : users) {
System.out.println(user.getStart());
}
Below snippet is used to perform limit query using HQL.
Query query = session.createQuery("....");
query.setFirstResult(startPosition);
query.setMaxResults(maxRows);
You can get demo application at this link.
You can use below query
NativeQuery<Object[]> query = session.createNativeQuery("select * from employee limit ?");
query.setparameter(1,1);
#Query(nativeQuery = true,
value = "select from otp u where u.email =:email order by u.dateTime desc limit 1")
public List<otp> findOtp(#Param("email") String email);

Hibernate criteria restriction on multiple criterias

I am writing a Criteria query. My query is on multiple criterias corresponding to tables property and user. It returns the result if prop is 12 or 13 no matter who the user is OR if prop is NULL then user must be loggedInUser.
The Sql query has where condition as follows which returns 4 rows
where (property.PROP in (12,13) or (property.PROP is null
and user.loggedInUser = 'XYZ'))
My criteria
Criteria userQuery = session.createCriteria(User.class);
Criteria propertyQuery = userQuery .createCriteria("property");
Criterion crt = (Criterion) userQuery.add(Restrictions.eq("loggedInUser", userId));
propertyQuery.add(
Restrictions.or(
Restrictions.in("prop",propList),
Restrictions.and(Restrictions.isNull("prop"),crt)
)
);
My issue is that Restrictions.and(criterion,criterion) takes two criterion as parameter. However, the second criterion 'crt' on userQuery is not valid when type casted (Criterion). Hibernate will give error. How can I achieve this functionality in Criteria. or how to write criteria Restrictions.and(Restrictions.isNull("prop") , userQuery.add(Restrictions.eq("loggedInUser", userId)))
Use Joins using hibernate criteria as below example code:
List cats = session.createCriteria(Cat.class)
.createAlias("kittens", "kit")
.add( Restrictions.like("kit.name", "Iz%") )
.list();
Note: The code above is just an example of how to use Join in hibernate criteria.

Hibernate Criteria filter Entity where ManyToMany relation contains multiple objects

I need help with Hibernate Criteria API.
I have a class Job that contain a list of Skill in ManyToMany relationship.
I need to select jobs based on a skill list given as parameter.
I've tried with Restriction.in("skill.id",ids) but this gives me wrong list.If i've selected 2 skills in search form i want the jobs that contain BOTH of them,so i need somehow to implement AND clause.
I've tried with Conjunction:
Conjunction and = Restrictions.conjunction();
for(Skill skill:job.getSkills()){
and.add(Restrictions.eq("skill.id",skill.getId()));
}
And this:
Criteria crit = criteria.createCriteria("skills",JoinType.LEFT_OUTER_JOIN);
for(Skill skill:job.getSkills()){
crit.add(Restrictions.eq("id", skill.getId()));
}
but it creates same alias for skill and it gives me no result.
sql is and (skill1_.ID=? and skill1_.ID=?)
Can anyone help me with this ?thanks
UPDATE:
HQL Query will be like:
select a from Job a where exists (select skill1.id from Skill skill1 join skill1.jobs r where r.id=a.id and skill1.id=1) and exists (select skill2.id from Skill skill2 join skill2.jobs r where r.id=a.id and skill2.id=4)
I need Criteria based on this.
for(Skill skill:job.getSkills()){
DetachedCriteria subquery = DetachedCriteria.forClass(Skill.class,"skill");
subquery.add(Restrictions.eq("id",skill.getId()));
subquery.setProjection(Projections.property("id"));
subquery.createAlias("jobs", "job");
subquery.add(Restrictions.eqProperty("job.id", "Job.id"));
criteria.add(Subqueries.exists(subquery));
}
I managed to solve it.now it works perfect.

Hibernate Criteria join to table containing foreign key

I have two tables:
Client (clientId, firstName, lastName, gender)
Event (clientId, eventId)
I need to represent a query similar to the following using Criteria:
SELECT c.clientId, c.firstName, c.lastName, c.gender, MAX(eventId)
FROM Client c JOIN Event e ON c.clientId = e.clientId
GROUP BY c.clientId, c.firstName, c.lastName, c.gender
I have tried this:
final Criteria criteria = session.createCriteria(Client.class);
criteria.setFetchMode("Event", FetchMode.JOIN);
criteria.setProjection(Projections.projectionList().add(Projections.groupProperty("clientId")).add(Projections.max("eventId")));
but it throws an exception on the last line with the message:
HibernateQueryException: could not resolve property: eventId of:
Client
How can I specify the join between the Client table which itself contains no column related to the Event table but the clientId column on the Event table is a foreign key back into the Client table?
As you can see, it's really driven off the Client table and that I only need to select the maximum eventId from the Event table. Also, as I mentioned, I am trying to make a change to an existing Criteria query which is based on the Client class. It is used to retrieve all the columns for all active clients. I just need to add one extra column to the query results - the maximum eventId.
Use alias
Criteria criteria = session.createCriteria(Event.class, "et").
createAlias("et.Client", "ct").
setProjection(Projections.projectionList().
add(Projections.groupProperty("et.clientId")).
add(Projections.max("et.eventId")));
For more details on criteria, refer Criteria Queries
That is obvious. Because Client class does not have eventId property, and your criteria is defined for Client class.
When trying to use a property of B class inside a Criteria for A, you have to use Aliases.
All you have to do is to modify your code like this:
final Criteria criteria = session.createCriteria(Event.class, "event");
criteria.createAlias("event.client", "client");
criteria.setProjection(Projections.projectionList().add(Projections.groupProperty("clientId")).add(Projections.max("eventId")));
UPDATED (based on your comment)
As your query needs Event class, you have to have a Criteria for this class. So you have to something like this:
final Criteria criteria = session.createCriteria(Event.class, "event");
criteria.createAlias("event.client", "client");
//The criteria below, is returning clientId
DetachedCriteria eventCr = DetachedCriteria.forClass(Event.class, "event");
eventCr.setProjection(Projections.projectionList().add(Projections.groupProperty("clientId")).add(Projections.max("eventId")));
//Now using subqueries you can achieve your goal
criteria.add(Subqueries.propertyIn("clientId", eventCr));
I don't know for sure what you're looking for, but I hope I have given you some good hints. You might want to try Subqueries.propertyEq instead if your query must return a single id.

Hibernate subquery

I have a problem in creating subqueries with Hibernate. Unfortunately the Subqueries class is almost entirely undocumented, so I have absolutely no clue how to convert the following SQL into a Hibernate Criteria:
SELECT id
FROM car_parts
WHERE car_id IN ( SELECT id FROM cars WHERE owner_id = 123 )
I was hoping the following would 'just work':
session.createCriteria(CarParts.class).add(eq("car.owner", myCarOwner));
but unfortunately it does not. So it seems I actually have to use the Subqueries class to create the Criteria. But I was unable to find a reasonable example though Google, so that leads me to asking it here.
Try Like this:
Table details): Category (id, name, desc, parentId, active)
DetachedCriteria subCriteria = DetachedCriteria
.forClass(Category.class);
subCriteria.add(Restrictions.isNull("parent"));
subCriteria.add(Restrictions.eq("active", Boolean.TRUE));
subCriteria.add(Restrictions.eq("name", categoryName));
subCriteria.setProjection(Projections.property("id"));
Criteria criteria = getSession().createCriteria(Category.class);
criteria.add(Restrictions.eq("active", Boolean.TRUE));
criteria.add(Subqueries.propertyEq("parent", subCriteria));
It will generate the query like:
select
*
from
Categories this_
where
this_.active=1
and this_.parentId = (
select
this0__.id as y0_
from
Categories this0__
where
this0__.parentId is null
and this0__.active=1
and this0__.name='Health Plan'
)
Good Luck!
-Rohtash Singh
Try to create an alias for the "car" property before adding the eq expression like this:
session.createCriteria(CarParts.class)
.createAlias("car", "c")
.add(eq("c.owner", myCarOwner));
As first check the ORM configuration between Car and CarPart entities, usually you need the setup the relationship between them. After that try to execute the following code:
List result = session.createQuery("from " + CarPart.class.getName() +
" as parts join parts.car as car where car.owner = :myOwner")
.setParameter("myOwner", 123)
.list();

Categories

Resources