Hibernate Criteria to find Duplicates - java

I want to get the duplicate entries. I want to use the query as criteria, for this i am using the below code.
SELECT * from A
WHERE field in (
SELECT field from A
GROUP BY field HAVING COUNT(field) > 1
);
The Hibernate mapping is like
#Entity
class A{
...
private String field;
...
}
How can I get list of A that have duplication in 'field' column?

You can pretty much translate your query one to one to HQL (warning: untested).
select A
from A a
where a.field in (
select ai.field
from A ai
group by ai.field -- assumes that by f you mean field
having count(a1.field) > 1
)

My own answer according prompted Anthony Accioly
final Criteria searchCriteria = session.createCriteria(A.class);
...
final DetachedCriteria d1 = DetachedCriteria.forClass(A.class);
d1.setProjection(Projections.count("field"));
d1.add(Restrictions.eqProperty("field", "AA.field"));
final DetachedCriteria d2 = DetachedCriteria.forClass(A.class, "AA");
d2.setProjection(Projections.projectionList()
.add(Projections.groupProperty("field")));
d2.add(Subqueries.lt(1L, d1));
criteria.add(Property.forName("field").in(d2));

Related

Select count of occurrences of each status in another table using jpa criteria

I need to select my main table and the number of occurrences of each status in another table using criteria api in just one query.
My current solution is in native query, which is working, but I want to do it in a more object-based way.
I tried doing it in criteria by using a specific query just to select all status and then count it manually. But with that approach, I'm calling two queries: One to fetch the details in my main table, and another to select all status where the id is same with main table.
Is there a more efficient way to do this?
Here is my native query (simplified):
SELECT * FROM(
SELECT a.id, a.type, b.count_pending, b.count_failed, b.count_processed
FROM CM AS a
LEFT JOIN ( SELECT
COUNT( CASE WHEN status = 'PENDING' THEN 1 ELSE NULL END ) count_pending,
COUNT( CASE WHEN status = 'FAILED' THEN 1 ELSE NULL END ) count_failed,
COUNT( CASE WHEN status = 'PROCESSED' THEN 1 ELSE NULL END ) count_processed
FROM CM_PARAM WHERE id_cm = :cmId
GROUP BY id_cm
) AS b ON a.id_cm = b.id_cm
WHERE a.id_cm = :cmId) AS a
Here is my CM entity (simplified):
#Entity
public class Cm {
#Id
private Long idCm;
private String type;
// other fields
// setters and getters
}
Here is my CM_PARAM entity (simplified):
#Entity
public class CmParam {
#Id
private Long idCmp;
#ManyToOne
#JoinColumn(name = "id_cm")
private Cm cm;
private String status;
// other fields
// setters and getters
}
Using the native query approach, I can add transient fields in my Cm entity:
#Transient
private Long countPending;
#Transient
private Long countFailed;
#Transient
private Long countProcessed;
How can I do it using criteria api, and if possible in just one transaction.
The expected output would be somehow like this:
{
"idCm": 1,
"type": "sms",
"countPending": 5,
"countFailed": 3,
"countProcessed": 9
}
Your query can be rewritten without the subquery join:
SELECT
a.id_cm,
a.type
COUNT(CASE WHEN b.status = 'PENDING' THEN 1 ELSE NULL END) countPending,
COUNT(CASE WHEN b.status = 'FAILED' THEN 1 ELSE NULL END) countFailed,
COUNT( CASE WHEN b.status = 'PROCESSED' THEN 1 ELSE NULL END ) countProcessed
FROM CM AS a
LEFT JOIN CM_PARAM AS b ON a.id_cm = b.id_cm
WHERE a.id_cm = ?1
GROUP BY a.id_cm, a.type
You will have to add the inverse side of the association to Cm:
#OneToMany(mappedBy = "cm")
private Set<CmParam> params;
(otherwise, you would need a RIGHT JOIN from CmParam to Cm, something Hibernate does not support)
The Criteria query then becomes:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<? extends Object[]> cq = cb.createQuery(new Object[0].getClass());
Root<Cm> a = cq.from(Cm.class);
Join<Cm, CmParam> b = a.join("params", JoinType.LEFT);
cq.where(cb.equal(a.get("idCm"), cb.parameter(Long.class, "idCm")));
cq.groupBy(a.get("idCm"), a.get("type"));
cq.multiselect(
a.get("idCm"),
a.get("type"),
cb.count(cb.selectCase()
.when(cb.equal(b.get("status"), "PENDING"), 1L)
.otherwise(cb.nullLiteral(Long.class))),
cb.count(cb.selectCase()
.when(cb.equal(b.get("status"), "FAILED"), 1L)
.otherwise(cb.nullLiteral(Long.class))),
cb.count(cb.selectCase()
.when(cb.equal(b.get("status"), "PROCESSED"), 1L)
.otherwise(cb.nullLiteral(Long.class))));
Note that the result is of type Object[]. If you want to use your current approach with transient fields, the easiest way would be to add the appropriate constructor to Cm and use the cb.construct() method:
cq.select(cb.construct(Cm.class, a.get("idCm"), a.get("type"), ...))
Note that:
if you'd rather not add the params field to Cm, but you're fine with an INNER JOIN, you can just use Root<CmParam> b = cq.from(CmParam.class) and Join<CmParam, Cm> a = b.join("cm") instead.
if in your actual query you're selecting more attributes from Cm than just cmId and status, you will probably need to list them all in groupBy as well

how to write with clause using criteria in hibernate

I have a query where i used WITH clause to generate results.
WITH employee AS (SELECT * FROM Employees)
SELECT * FROM employee WHERE ID < 20
UNION ALL
SELECT * FROM employee WHERE Sex = 'M'
Could you please any one let me know how to write this query in hibernate using criteria specially when with clause present in query.
First, you've dramatically over-complicated your SQL. Here's how your query should look in pure SQL:
SELECT * FROM Employees WHERE ID < 20 or Sex = 'M'
Next, assuming you have a Hibernate entity (let's call it Employee.java for now), your HQL equivalent would be:
from Employee e where e.id < 20 or e.sex = 'M'
To do it with a Criteria, you'd do the following:
final Criteria criteria = getSession().createCriteria(Employee.class);
criteria.add(Restrictions.or(
Restrictions.lt("id", 20),
Restrictions.eq("sex", "M")
)
);
criteria.list();

Java Hibernate Restcrition Criteria OR with DetachedCritera

Criteria criteria = session.createCriteria(TableA.class)
.add(Subqueries.propertyNotIn("id_a", DetachedCriteria.forClass(TableB.class)
.createAlias("id_a_from_tableB", "b")
setProjection(Property.forName("b.id_a"))));
I use this to get id_a from TableA if id_a is not in TableB. I need also get id_a if is in TableB but field "message" is not null.
I think this post can help you.
with criteria I think hibernate does not support UNION ALL but you
can use two criteria queries to get the expected result:
Criteria cr1 = session.createCriteria(Suppliers.class);
cr1.setProjection(Projections.projectionList()
.add( Projections.property("supplier_id"), "supplier_id" )
);
List results1 = cr1.list();
Criteria cr2 = session.createCriteria(Orders.class);
cr2.setProjection(Projections.projectionList()
.add( Projections.property("supplier_id"), "supplier_id" )
);
List results2 = cr2.list();
results1.add(results2);
List unionAllList = results1; //this is the expected result.
For example, you could add new criteria when you get from TableB if message is not null, and later just join two Java collections:
Criteria criteria1 = session.createCriteria(TableB.class)
.add( Restrictions.isNotNull("message")
P.S. Or I recommend using Criteria API from JPA2.1 (Chapter 6) instead of hibernate Criteria, because it's look like simple SQL and this solution is much more universally.

JPA select using IN

Here is my select from the database:
SELECT * FROM testlogging.employees
where
EMPLOYEE_NO in (
select EMPLOYEES_EMPLOYEE_NO from testlogging.test_logging
where ID in (
select TEST_LOGGING_ID from testlogging.test_logging_detail
where APPROVAL_LEVELS_ID = '4'
)
)
How would i do this in JPA?
SELECT e FROM Employees e ???
If you're asking for the JPA INsyntax you would do this:
SELECT e FROM Employees e where e.employee_no IN :employeelist
as well ass
query.setParameter( "employeelist" , yourlist );
and of course build yourlist accordingly. If you don't really need to parameterize the inner queries, you can disregard this and just go the straight forward route.
Cheers,
Just in case you're using the JPA Criteria metamodel queries, the IN usage goes like this
CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
cq.where(pet.get(Pet_.color).in("brown", "black"));
as stated at http://docs.oracle.com/javaee/6/tutorial/doc/gjivm.html

Hibernate Criteria with Projections.groupProperty cannot return full hibernate object (ClassCastException)

I am relatively new to Hibernate, and I have a problem when adding a "distinct" restriction on my hibernate class.
#Entity
public class TaggedOffer {
private Long tagged_offers_id;
private String brand;
private Long cid;
private Date created_date;
//Getter and Setter and more fields
}
Previously, we were creating a hibernate query as follows:
public DetachedCriteria build(final TaggedOfferRequest request) {
DetachedCriteria criteria = DetachedCriteria.forClass(TaggedOffer.class);
criteria.add(Restrictions.eq("brand", request.getBrand()));
criteria.add(Restrictions.in("cid", request.getCids()));
// sort by date
criteria.addOrder(Property.forName("createdDate").desc());
return criteria;
}
This would create the following (working) SQL query:
select
this_.tagged_offers_id as tagged1_2_3_,
this_.brand as brand2_3_,
this_.cid as cid2_3_,
this_.created_date as created6_2_3_
from
site.tagged_offers this_
where
this_.brand=?
and this_.country_code=?
and this_.cid in (
?, ?
)
order by
this_.created_date desc limit ?
Here comes the tricky part. We now need to ensure that the results that are returned are distinct on the cid field. Meaning, return as many results as possible, providing each record has a unique cid associated with it.
I looked into this in SQL, and it seems that the easiest way to do this is just to have a group by cid in the query. In terms of the hibernate criteria, this is basically what I've been trying:
public DetachedCriteria build(final TaggedOfferRequest request) {
DetachedCriteria criteria = DetachedCriteria.forClass(TaggedOffer.class);
criteria.add(Restrictions.eq("brand", request.getBrand()));
criteria.add(Restrictions.in("cid", request.getCids()));
// sort by date
criteria.addOrder(Property.forName("createdDate").desc());
// ** new ** distinct criteria
criteria.setProjection(Projections.groupProperty("cid"));
return criteria;
}
This almost creates the SQL that I am looking for, but it later throws a class cast exception (as it's just selecting the cid field as opposed to the entire object).
select
this_.cid as y0_
from
site.tagged_offers this_
where
this_.brand=?
and this_.country_code=?
and this_.cid in (
?, ?
)
and tagtype1_.tag_type=?
group by
this_.cid
order by
this_.created_date desc limit ?
And the exception:
java.lang.ClassCastException: java.lang.Long cannot be cast to com.mycompany.site.taggedoffers.dao.model.TaggedOffer
Any idea how I can use projections to do what I want?
Thanks for your help.
Add projections for all columns which you need.
ProjectionList projectionList = Projections.projectionList();
projectionList.add(Projections.groupProperty("cid"));
projectionList.add(Projections.property("tagged_offers_id"));
...
criteria.setProjection(projectionList);
I have a doubt why there is a need to use group by here without any aggregate calculations!!
In case if you use group by with projection , then the particular column used in group by will be included again in fetch sql ignoring even the same column is used already in select statement.
To map the extra columns generated by hibernate due to group by you have to write a field in entity class and marking it as #Transient or you can use setResultTransformer and map to another class

Categories