Hibernate setMaxResults paging - java

If I don't set setFirstResult(-) and recursively call criteria.setmaxresults(10) each time, will it automatically grab the next 10 items from the database?

No. You have to use criteria.setFirstResult(0) and page through yourself, something like this:
public List getCarters(final int firstResult, final int maxResults) {
final Criteria criteria = sessionFactory.getCurrentSession()
.createCriteria(SomePersistentClass.class);
.add(Restrictions.eq("name", "Carter"))
criteria.setFirstResult(firstResult);
criteria.setMaxResults(maxResults);
return criteria.list();
}

Of course, no. Criteria grabs data from database only when you call .list() or .uniqueResult()

Related

Hibernate pagination - how to get the next batch of results?

I understand pagination to improve performance on queries that, naturally, have a very large resultset.
I have the implemented paginatedList method that successfully returns the correct amount. But it seems more of a MySQL's LIMIT or SQLServer's TOP clause.
The method is thus implemented:
#SuppressWarnings("unchecked")
public List<T> paginatedList(int pageSize) {
Criteria criteria = getSession().createCriteria(persistentClass);
criteria.setFirstResult(0);
criteria.setMaxResults(pageSize);
return (List<T>) criteria.list();
}
And I call like this:
#Override
public List<AlertLog> findAll() {
return super.paginatedList(1000);
}
This implementation returns results 0 to 1000.
How to, on runtime, get the 1001st to 2000th and so on?
to get the next 1000 result you need to set firstResult to 1000 and maxresults to 1000, so it becomes..
criteria.setFirstResult(1000);
criteria.setMaxResults(noOfEntries);
Please make sure that the noOfEntries is less that the total count.

JPA update based on column name

I want to update value to a specific column entered by the user. Here are my codes, do anyone how to modify it to correct one?
public void updateValue(String value, String id, String ww){
Query q = em.createQuery("Update TableA e SET e.?1 = ?2 WHERE e.num = ?3");
q.setParameter(1, ww); //since has many columns, user require to specific column to update
q.setParameter(2, value);
q.setParameter(3, id);
q.executeUpdate();
}
You should go for criteria builder query for your case... if you are using JPA 2.1.. here is something you can should do
public void updateValue(String value, String id, String ww){
CriteriaBuilder cb = this.em.getCriteriaBuilder();
// create update
CriteriaUpdate<TableAEntity> update = cb.
createCriteriaUpdate(TableAEntity.class);
// set the root class
Root e = update.from(TableAEntity.class);
// set update and where clause
update.set(ww, value);
update.where(cb.equalTo(e.get("num"),
id));
// perform update
this.em.createQuery(update).executeUpdate();
}
You asked
Updating one column based on predicates (and possibly more advance operations).
More detail and Clean practice
The best practice for criteria base operations like updates is to use javax.persistence.criteria interfaces, like CriteriaUpdate and using where() clause restriction upon predicates.
Further more with respect of your predicates and column types you can use CriteriaBuilder api's for many operations and aggregations, like appending other columns or values to desired column (path).
CriteriaUpdate<T> criteriaUpdate = criteriaBuilder.createCriteriaUpdate(type);
Root<T> updateRoot = criteriaUpdate.from(type);
Path<String> path = updateRoot.get(update_column);
criteriaUpdate.set(path, 'some_value');
entityManager.createQuery(criteriaUpdate.where(...)).executeUpdate()
Note that the some_value could even be calculated with nested operations and aggregations with help of criteriaBuilder like
criteriaUpdate.set(path, criteriaBuilder.api(...));
Summarizing in your case it will look like below
CriteriaUpdate<TableAEntity> criteriaUpdate = builder.createCriteriaUpdate(TableAEntity.class);
Path<String> path = root.get("ww");
criteriaUpdate.set(path, value);
entityManager.createQuery(criteriaUpdate.where(criteriaBuilder.equal(root.get("num"), id) )).executeUpdate()
...

How to write QueryBuilder Query for update Collections(Set) in Cassandra

I want to write QueryBuilder query for the following CQL command in java.
UPDATE category_utility
SET imageurls = imageurls + {'http://image1.jpg','http://image2.jpg','http://image3.jpg'} WHERE category_title = 'cat1';
In JAVA, I try with this following. I do not know how to write add in set operation in QueryBuilder command .
public void addImageList(ArrayList<String> list, int categoryId) {
Statement = QueryBuilder.update("category_utility").with(QueryBuilder.set("imageurls", list.toString())).where(QueryBuilder.eq("img_category_id", categoryId));
}
You want to use QueryBuilder.addAll(String, Set<?>), but that method takes a Set as a parameter, not an ArrayList.
So you need to modify your method as follows:
public Statement addImages(Set<String> imageurls, int categoryId){
return QueryBuilder.update("category_utility")
.with(QueryBuilder.addAll("imageurls", imageurls))
.where(QueryBuilder.eq("img_category_id", categoryId));
}
If you look at the QueryBuilder class there are add, addAll and remove, removeAll methods for handling set updates.

Update record if somcolumn!="somevalue", using Hibernate saveorupdate()

I am trying to write Criteria in Hibernate, My desired output is if column empfield1's value is not 'REGULARIZE' then update else do not update record.
i have tried below one.
Session session = factory1.openSession();
Criteria criteria=session.createCriteria(EmployeePunch.class);
criteria.add(Restrictions.ne("empField1","REGULARIZE"));
EmployeePunch empPunch = (EmployeePunch)criteria.uniqueResult();
empPunch.setId(empPuncId);
empPunch.setSigninTime(inTime);
empPunch.setSigninDate(dateOfUpdate);
empPunch.setSignoutTime(outTime);
empPunch.setPresent(presentStatus);
empPunch.setLastUpdateBy(empcode);
empPunch.setLastUpdateDate(time);
empPunch.setEmpField1(remark);
session.saveOrUpdate(empPunch);
tx.commit();
but it gives me error
Exception : query did not return a unique result: 527
I think you forget to give id without giving id hibernate will return multiple records with empField1="REGULARIZE"
You should give id as well like below:
Criteria criteria=session.createCriteria(EmployeePunch.class);
criteria.add(Restrictions.ne("empField1","REGULARIZE"))
.add(Restrictions.eq("empPuncId",empPuncId));
Now it will return single matching record and then update it.
That means ,With that criteria there are multiple records are there in your Database.
To know how many records are there,
Try
List<EmployeePunch> emps = (ArrayList<EmployeePunch>)criteria.list();
So that emps will give you a list of EmployeePunch's which meets the criteria.
Then iterate the list and see how many items are there inside database.
Why not use a HQL in this way?
Query query = session.createQuery("update EmployeePunch set signinTime = :signinTime, signinDate = :signinDate where empField1 = 'REGULARIZE').setParameter("signinTime",signinTime).setParameter("signinDate",signinDate);
int updateRecordCount = query.executeUpdate();
Of course, you have to set values for other properties (except for Id if it is your #Id field); in updateRecordCount you get the count of updated records.

How to get distinct results in hibernate with joins and row-based limiting (paging)?

I'm trying to implement paging using row-based limiting (for example: setFirstResult(5) and setMaxResults(10)) on a Hibernate Criteria query that has joins to other tables.
Understandably, data is getting cut off randomly; and the reason for that is explained here.
As a solution, the page suggests using a "second sql select" instead of a join.
How can I convert my existing criteria query (which has joins using createAlias()) to use a nested select instead?
You can achieve the desired result by requesting a list of distinct ids instead of a list of distinct hydrated objects.
Simply add this to your criteria:
criteria.setProjection(Projections.distinct(Projections.property("id")));
Now you'll get the correct number of results according to your row-based limiting. The reason this works is because the projection will perform the distinctness check as part of the sql query, instead of what a ResultTransformer does which is to filter the results for distinctness after the sql query has been performed.
Worth noting is that instead of getting a list of objects, you will now get a list of ids, which you can use to hydrate objects from hibernate later.
I am using this one with my code.
Simply add this to your criteria:
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
that code will be like the select distinct * from table of the native sql.
A slight improvement building on FishBoy's suggestion.
It is possible to do this kind of query in one hit, rather than in two separate stages. i.e. the single query below will page distinct results correctly, and also return entities instead of just IDs.
Simply use a DetachedCriteria with an id projection as a subquery, and then add paging values on the main Criteria object.
It will look something like this:
DetachedCriteria idsOnlyCriteria = DetachedCriteria.forClass(MyClass.class);
//add other joins and query params here
idsOnlyCriteria.setProjection(Projections.distinct(Projections.id()));
Criteria criteria = getSession().createCriteria(myClass);
criteria.add(Subqueries.propertyIn("id", idsOnlyCriteria));
criteria.setFirstResult(0).setMaxResults(50);
return criteria.list();
A small improvement to #FishBoy's suggestion is to use the id projection, so you don't have to hard-code the identifier property name.
criteria.setProjection(Projections.distinct(Projections.id()));
The solution:
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
works very well.
session = (Session) getEntityManager().getDelegate();
Criteria criteria = session.createCriteria(ComputedProdDaily.class);
ProjectionList projList = Projections.projectionList();
projList.add(Projections.property("user.id"), "userid");
projList.add(Projections.property("loanState"), "state");
criteria.setProjection(Projections.distinct(projList));
criteria.add(Restrictions.isNotNull("this.loanState"));
criteria.setResultTransformer(Transformers.aliasToBean(UserStateTransformer.class));
This helped me :D
if you want to use ORDER BY, just add:
criteria.setProjection(
Projections.distinct(
Projections.projectionList()
.add(Projections.id())
.add(Projections.property("the property that you want to ordered by"))
)
);
I will now explain a different solution, where you can use the normal query and pagination method without having the problem of possibly duplicates or suppressed items.
This Solution has the advance that it is:
faster than the PK id solution mentioned in this article
preserves the Ordering and don’t use the 'in clause' on a possibly large Dataset of PK’s
The complete Article can be found on my blog
Hibernate gives the possibility to define the association fetching method not only at design time but also at runtime by a query execution. So we use this aproach in conjunction with a simple relfection stuff and can also automate the process of changing the query property fetching algorithm only for collection properties.
First we create a method which resolves all collection properties from the Entity Class:
public static List<String> resolveCollectionProperties(Class<?> type) {
List<String> ret = new ArrayList<String>();
try {
BeanInfo beanInfo = Introspector.getBeanInfo(type);
for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
if (Collection.class.isAssignableFrom(pd.getPropertyType()))
ret.add(pd.getName());
}
} catch (IntrospectionException e) {
e.printStackTrace();
}
return ret;
}
After doing that you can use this little helper method do advise your criteria object to change the FetchMode to SELECT on that query.
Criteria criteria = …
// … add your expression here …
// set fetchmode for every Collection Property to SELECT
for (String property : ReflectUtil.resolveCollectionProperties(YourEntity.class)) {
criteria.setFetchMode(property, org.hibernate.FetchMode.SELECT);
}
criteria.setFirstResult(firstResult);
criteria.setMaxResults(maxResults);
criteria.list();
Doing that is different from define the FetchMode of your entities at design time. So you can use the normal join association fetching on paging algorithms in you UI, because this is most of the time not the critical part and it is more important to have your results as quick as possible.
Below is the way we can do Multiple projection to perform Distinct
package org.hibernate.criterion;
import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.type.Type;
/**
* A count for style : count (distinct (a || b || c))
*/
public class MultipleCountProjection extends AggregateProjection {
private boolean distinct;
protected MultipleCountProjection(String prop) {
super("count", prop);
}
public String toString() {
if(distinct) {
return "distinct " + super.toString();
} else {
return super.toString();
}
}
public Type[] getTypes(Criteria criteria, CriteriaQuery criteriaQuery)
throws HibernateException {
return new Type[] { Hibernate.INTEGER };
}
public String toSqlString(Criteria criteria, int position, CriteriaQuery criteriaQuery)
throws HibernateException {
StringBuffer buf = new StringBuffer();
buf.append("count(");
if (distinct) buf.append("distinct ");
String[] properties = propertyName.split(";");
for (int i = 0; i < properties.length; i++) {
buf.append( criteriaQuery.getColumn(criteria, properties[i]) );
if(i != properties.length - 1)
buf.append(" || ");
}
buf.append(") as y");
buf.append(position);
buf.append('_');
return buf.toString();
}
public MultipleCountProjection setDistinct() {
distinct = true;
return this;
}
}
ExtraProjections.java
package org.hibernate.criterion;
public final class ExtraProjections
{
public static MultipleCountProjection countMultipleDistinct(String propertyNames) {
return new MultipleCountProjection(propertyNames).setDistinct();
}
}
Sample Usage:
String propertyNames = "titleName;titleDescr;titleVersion"
criteria countCriteria = ....
countCriteria.setProjection(ExtraProjections.countMultipleDistinct(propertyNames);
Referenced from https://forum.hibernate.org/viewtopic.php?t=964506
NullPointerException in some cases!
Without criteria.setProjection(Projections.distinct(Projections.property("id")))
all query goes well!
This solution is bad!
Another way is use SQLQuery. In my case following code works fine:
List result = getSession().createSQLQuery(
"SELECT distinct u.id as usrId, b.currentBillingAccountType as oldUser_type,"
+ " r.accountTypeWhenRegister as newUser_type, count(r.accountTypeWhenRegister) as numOfRegUsers"
+ " FROM recommendations r, users u, billing_accounts b WHERE "
+ " r.user_fk = u.id and"
+ " b.user_fk = u.id and"
+ " r.activated = true and"
+ " r.audit_CD > :monthAgo and"
+ " r.bonusExceeded is null and"
+ " group by u.id, r.accountTypeWhenRegister")
.addScalar("usrId", Hibernate.LONG)
.addScalar("oldUser_type", Hibernate.INTEGER)
.addScalar("newUser_type", Hibernate.INTEGER)
.addScalar("numOfRegUsers", Hibernate.BIG_INTEGER)
.setParameter("monthAgo", monthAgo)
.setMaxResults(20)
.list();
Distinction is done in data base! In opposite to:
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
where distinction is done in memory, after load entities!

Categories

Resources