Hibernate Criteria Query ...Adding a new condition - java

I have a criteria query that works just fine.
Criteria criteria = getSession().createCriteria(getPersistentClass());
criteria.add(Restrictions.eq("employerId",employerId))
.setFetchMode("card", FetchMode.JOIN)
.createCriteria("card")
.addOrder(Order.desc("cardId"))
.createCriteria("salary")
.add(Restrictions.eq("salaryType",SalaryIdentifierType.CONTRACTOR))
.add(Restrictions.eq("active","YES"));
Now, i need to add another filter(condition) to this criteria. The new filter is joinDate. If the joinDate value has been passed from the Frontend, i will have to add it to this query or else, the filter joinDate should not be added.
I have managed to do this using a disjunction(I know, its weird...but AFAIK, only disjunction gives the the facility to add the filter at the runtime, only it if is present). I did it like this, but i do not want to use a disjunction here and I am looking for other options.
Criteria criteria = getSession().createCriteria(getPersistentClass());
-------------------------------------------------------------------------
Disjunction disjunctionDate = Restrictions.disjunction();
if(utilDate!=null){
disjunctionDate = (Disjunction) disjunctionDate.add(Restrictions.ge("startDate", utilDate));
}
-----------------------------------------------------------------------
criteria.add(Restrictions.eq("employerId",employerId))
.setFetchMode("card", FetchMode.JOIN)
.createCriteria("card")
.addOrder(Order.desc("cardId"))
.createCriteria("salary")
.add(Restrictions.eq("salaryType",SalaryIdentifierType.CONTRACTOR))
.add(Restrictions.eq("active","YES"))
.add(disjunctionDate);
I have tried just using if condition, like this but does not work(To even make this compile, i had to modify the query format from above to having criteria in every line and semicolon in every line).
Criteria criteria = getSession().createCriteria(getPersistentClass());
criteria.add(Restrictions.eq("employerId",employerId));
criteria.setFetchMode("card", FetchMode.JOIN);
criteria.createCriteria("card");
criteria.addOrder(Order.desc("cardId"));
criteria.createCriteria("salary");
criteria.add(Restrictions.eq("salaryType",SalaryIdentifierType.CONTRACTOR));
criteria.add(Restrictions.eq("active","YES"));
if(startDate!=null){
criteria.add(Restrictions.ge("startDate",startDate));
}
Any ideas on how to make this work with better options than using disjunction?

So you are saying this is not working for you?
Criteria criteria = getSession().createCriteria(getPersistentClass())
.add(Restrictions.eq("employerId",employerId))
.setFetchMode("card", FetchMode.JOIN)
.createCriteria("card")
.addOrder(Order.desc("cardId"))
.createCriteria("salary", "sl") // <- alias for salary
.add(Restrictions.eq("sl.salaryType",SalaryIdentifierType.CONTRACTOR))
.add(Restrictions.eq("sl.active","YES"));
if(startDate!=null) {
criteria.add(Restrictions.ge("sl.startDate",startDate));
}
See http://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html_single/#querycriteria-associations

Related

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()
...

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.

Creating a hibernate disjunction query programatically

I have a chunk of java code which hard codes a hibernate disjunction query that looks like this
session = HibernateUtils.beginTransaction("outpatient");
Criteria criteria = session.createCriteria(AugmentToken.class);
session.beginTransaction();
if (type == Constants.ICD9CPT) {
criteria.add(Restrictions.disjunction()
.add(Restrictions.eq("codeType", "d"))
.add(Restrictions.eq("codeType", "p"))
.add(Restrictions.eq("codeType", "c")));
} else if (type == Constants.EM) {
criteria.add(Restrictions.disjunction()
.add(Restrictions.eq("codeType", "eros"))
.add(Restrictions.eq("codeType", "ehpi"))
.add(Restrictions.eq("codeType", "epe")));
}
But this is not very elegant code. What I would like to do is pass an array of codetypes to a method, and dynamically construct the dijunction criteria. Every website I look at provides examples of disjunctive queries that look like the above, but this will not work for me because I don't want to hard code the construction of the restriction for the criteria since the number of code types can vary.
How do I do this?
Thank you,
Elliott
I think I figured this out. You create the disjunction as a variable, then sequentially add to it.
Specifically:
String [] codeTypes = new String[3];
codeTyes[0]="d";
codeTypes[1]="p";
codetypes[2]="c";
/* note the above would normally be passed into the method containing the code below */
Criteria criteria = session.createCriteria(AugmentToken.class);
session.beginTransaction();
Disjunction disjunction = Restrictions.disjunction();
for (int x = 0; x < codeTypes.length; x++ ) {
disjucntion.add(Restrictions.eq("codeType",codeTypes[x]);
}
criteria.add(disjunction);
I found the answer in Beginning Hibernate on page 214. The book is accessible from books.google.com.

How to properly determine whether an "exists" JPA Criteria Query clause returned true or false?

I don't know how to perform a JPA criteria query that returns with a boolean output.
The goal is to have a criteria query that looks like this when rendered on Oracle:
select 1 from dual where exists ( ... );
The where exists (...) part I performed with a subquery. I'm struggling with the external query.
The practical use of this is to determine whether that subquery in the exists clause returns true or false.
This is what I've written:
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<Object> query = criteriaBuilder.createQuery();
query.from(Boolean.class);
query.select(criteriaBuilder.literal(true));
Subquery<Location> subquery = query.subquery(Location.class);
Root<Location> subRootEntity = subquery.from(Location.class);
subquery.select(subRootEntity);
Path<?> attributePath = subRootEntity.get("State");
Predicate predicate = criteriaBuilder.equal(attributePath, criteriaBuilder.literal("TX"));
subquery.where(predicate);
query.where(criteriaBuilder.exists(subquery));
TypedQuery<Object> typedQuery = em.createQuery(query);
The last line outputs an error, stating that "Boolean is not an entity". I think my issue is not knowing how to express the "from" part of the query so that the result outputs 1 or 0/ true or false - not an entity.
I know I could retrieve any entity and then check if the list of results has size of 1.
I'm asking how to get a boolean result, both to avoid the unnecessary task of retrieving those columns and also to learn how to do it.
Is this possible at all?
Thanks!
Eduardo
Yes, this is possible. Assuming that you have an entity corresponding to your dual table, you will want to use that entity class in CriteriaQuery#from.
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<Boolean> query = criteriaBuilder.createQuery(Boolean.class);
query.from(dual.class);
query.select(criteriaBuilder.literal(true));
Subquery<Location> subquery = query.subquery(Location.class);
Root<Location> subRootEntity = subquery.from(Location.class);
subquery.select(subRootEntity);
Path<?> attributePath = subRootEntity.get("State");
Predicate predicate = criteriaBuilder.equal(attributePath, criteriaBuilder.literal("TX"));
subquery.where(predicate);
query.where(criteriaBuilder.exists(subquery));
TypedQuery<Boolean> typedQuery = em.createQuery(query);
Hibernate 5 is working:
Subquery<Integer> subquery = query.subquery(Integer.class);
Root<Location> subRootEntity = subquery.from(Location.class);
subquery.select(criteriaBuilder.literal(1));
Path<?> attributePath = subRootEntity.get("State");
Predicate predicate = criteriaBuilder.equal(attributePath, criteriaBuilder.literal("TX"));
subquery.where(predicate);
query.where(criteriaBuilder.exists(subquery));
You could do a select for one property (e.g. the ID) and set the max results returned to 1 so that you make sure the DB does not do more work than necessary (like counting all instances). Then your results list will either be empty (exists = false) or have one element (exists = true).
I know this is an older question, but for anyone else looking: How about trying to use Spring's jpa #Query annotation and a select case query (depending on your db implementation) with a method that returns a boolean. For example (MySQL):
#Query("SELECT CASE WHEN COUNT(l) > 0 THEN TRUE ELSE FALSE END FROM Location l WHERE l.state=?1")
boolean locationForStateExists(String state);
Sometimes just using the query string in #Query can be a life saver when the named JPA or query builder methods don't quite do what you want them to.
The maximum optimized and full solution for existance checking via Criteria API based on dimo answer and with help of user3158918 answer:
EntityManager em = ...;
val cb = em.getCriteriaBuilder();
val query = cb.createQuery(Integer.class);
val root = query.from(YourEntity.class);
val predicate = cb.equal(root.get(attribute), attributeValue);
query.select(cb.literal(1)).where(predicate);
return !em.createQuery(query).setMaxResults(1).getResultList().isEmpty();
I used a lombok to be clean.
This code generates SQL query:
SELECT 1 FROM your_entity_table WHERE your_entity_table.attribute = attributeValue LIMIT 1
Please post your solution, if its faster...
I think the problem is the the query.from(Boolean.class). It tries to create a "select object from boolean" query. If you want a boolean as return type you need to use
CriteriaQuery<Boolean> query = criteriaBuilder.createQuery(Boolean.class)
Then query from any existing entity table to create a valid query (perhaps from the subquery's table). I don't think that create from dual works except if you managed to map the dual table.
Is there any reason that all the logic has to be in JPA? If not, why not use SELECT COUNT and then a conditional to set the boolean?
Boolean exists = false;
int count = selectCountQuery();
if (count > 0) {
exists = true;
}

Is there a way to get the count size for a JPA Named Query with a result set?

I like the idea of Named Queries in JPA for static queries I'm going to do, but I often want to get the count result for the query as well as a result list from some subset of the query. I'd rather not write two nearly identical NamedQueries. Ideally, what I'd like to have is something like:
#NamedQuery(name = "getAccounts", query = "SELECT a FROM Account")
.
.
Query q = em.createNamedQuery("getAccounts");
List r = q.setFirstResult(s).setMaxResults(m).getResultList();
int count = q.getCount();
So let's say m is 10, s is 0 and there are 400 rows in Account. I would expect r to have a list of 10 items in it, but I'd want to know there are 400 rows total. I could write a second #NamedQuery:
#NamedQuery(name = "getAccountCount", query = "SELECT COUNT(a) FROM Account")
but it seems a DRY violation to do that if I'm always just going to want the count. In this simple case it is easy to keep the two in sync, but if the query changes, it seems less than ideal that I have to update both #NamedQueries to keep the values in line.
A common use case here would be fetching some subset of the items, but needing some way of indicating total count ("Displaying 1-10 of 400").
So the solution I ended up using was to create two #NamedQuerys, one for the result set and one for the count, but capturing the base query in a static string to maintain DRY and ensure that both queries remain consistent. So for the above, I'd have something like:
#NamedQuery(name = "getAccounts", query = "SELECT a" + accountQuery)
#NamedQuery(name = "getAccounts.count", query = "SELECT COUNT(a)" + accountQuery)
.
static final String accountQuery = " FROM Account";
.
Query q = em.createNamedQuery("getAccounts");
List r = q.setFirstResult(s).setMaxResults(m).getResultList();
int count = ((Long)em.createNamedQuery("getAccounts.count").getSingleResult()).intValue();
Obviously, with this example, the query body is trivial and this is overkill. But with much more complex queries, you end up with a single definition of the query body and can ensure you have the two queries in sync. You also get the advantage that the queries are precompiled and at least with Eclipselink, you get validation at startup time instead of when you call the query.
By doing consistent naming between the two queries, it is possible to wrap the body of the code to run both sets just by basing the base name of the query.
Using setFirstResult/setMaxResults do not return a subset of a result set, the query hasn't even been run when you call these methods, they affect the generated SELECT query that will be executed when calling getResultList. If you want to get the total records count, you'll have to SELECT COUNT your entities in a separate query (typically before to paginate).
For a complete example, check out Pagination of Data Sets in a Sample Application using JSF, Catalog Facade Stateless Session, and Java Persistence APIs.
oh well you can use introspection to get named queries annotations like:
String getNamedQueryCode(Class<? extends Object> clazz, String namedQueryKey) {
NamedQueries namedQueriesAnnotation = clazz.getAnnotation(NamedQueries.class);
NamedQuery[] namedQueryAnnotations = namedQueriesAnnotation.value();
String code = null;
for (NamedQuery namedQuery : namedQueryAnnotations) {
if (namedQuery.name().equals(namedQueryKey)) {
code = namedQuery.query();
break;
}
}
if (code == null) {
if (clazz.getSuperclass().getAnnotation(MappedSuperclass.class) != null) {
code = getNamedQueryCode(clazz.getSuperclass(), namedQueryKey);
}
}
//if not found
return code;
}

Categories

Resources