complex query to equivalent criteriabuilder query(EntityManager) - java

My Query is this:
query1 = select a.id from entity1 a where a.id in (:List1)
and not exists (select ex2 from entity2 ex2 where ex2.assignedId = a.id)
union
select ex.assignedId from entity2 ex ,entity3 pi
where ex.entity3Id = pi.id and ex.assignedId in (:List1)
and ex.assignedTypeId = :assignedTypeId and pi.processStatus = :status
and not exists
(select ex1.assignedId from entity2 ex1 , entity3 pi1
where ex1.entity3Id = pi1.id and ex1.assignedId = ex.assignedId
and ex1.assignedTypeId = :assignedTypeId
and pi1.processStatus <> :status);
and while trying to execute query,
Query existingIds=em.createQuery(query1); //With all parameters set
throws NullPointerException in line 87 of org.hibernate.hql.ast.ParameterTranslationsImpl
completely checked all the braces and parameters. The equivalent conversion works in mysql.
Can someone assist me in converting the query with CriteriaBuilder, finding it difficult to make the conversion.

Not sure if JPQL supports union operation at all. Are you putting this as NamedQuery or you are creating on the fly (entityManager.createQuery()) ?

Related

Convert SQL Query to Criteria Query in Spring Boot

I'm relatively new to Spring JPA CriteriaQuery. Im trying to convert my old native query in my program to criteria query and haven't been successful on join query for multiple table with conditions. I need help converting native SQL query into Criteria Query for these query below :
select * from student s inner join (
select distinct on (student_id) * from class where status = 'Active' order by
student_id,date_register desc) c on s.id = c.user_id
inner join teacher t on t.subject_id = c.subject_id
where t.status = 'Active' and s.status='Active' order by s.name desc
Update :
Below code is as far as I can go cause I dont really know much. Am i in the right direction? I'm opting for Expression because i dont know how to use Join.
CriteriaQuery<Student> query = cb.createQuery(Student.class);
Root<Student> sRoot= query.from(Student.class);
query.select(sRoot);
Subquery<Integer> subquery = query.subquery(Integer.class);
Root<Class> cRoot= subquery.from(CLass.class);
Expression<Integer> max = cb.max(cRoot.get("dateRegister"));
subquery.select(max);
subquery.groupBy(cRoot.get("student"));
query.where(
cb.and(
cb.in(cRoot.get("dateRegister")).value(subquery)
)
);
Thanks in advance!

Hibernate: Query to select values from multiple tables using CriteriaQuery

Let's say, I have a query like
Select a.valA, b.valB
from tableA a join tableB b on a.joinCol = b.joinCol
where a.someCol = 1.
I want to execute it using Hibernate (and Spring Data) in one query to the database. I know, I can write just
Query query = em.createQuery(...);
List<Object[]> resultRows = (List<Object[]>)query.getResultList();
But my question would be - is it possible to do it in a typesafe way, using CriteriaQuery for example? The difficulty is, that, as you see, I need to select values from different tables. Is there some way to do this?
Simple example where an Employee has many to many relation to several jobs that he may have :
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<Tuple> criteria = builder.createTupleQuery();
Root<TableA> root = criteria.from(TableA.class);
Path<Long> qId = root.get("id");
Path<String> qTitle = root.get("title");
Join<TableA, TableB> tableTwo = root.join("joinColmn", JoinType.INNER);
criteria.multiselect(qId, qTitle, tableTwo);
List<Tuple> tuples = session.createQuery(criteria).getResultList();
for (Tuple tuple : tuples)
{
Long id = tuple.get(qId);
String title = tuple.get(qTitle);
TableB tableB= tuple.get(tableTwo);
}
but saw that there is an alternate answer here :
JPA Criteria API - How to add JOIN clause (as general sentence as possible)

How to rewrite subquery in ORDER BY clause in JPA CriteriaQuery

I'm trying to write an SQL query using CriteriaQuery, but I'm having a hard time doing so. This query basically gets a list of shipments and sorts them by their authorization date. This authorization date is represented as the date attribute of the first record in the status transition messages table with an initial status of 3 and a final status of 4. This is my query:
SELECT s.id
FROM shipment s
ORDER BY (SELECT min(stm.date)
FROM status_transition_message stm
WHERE stm.initial_status = 1 AND stm.final_status = 3 AND stm.shipment_id = s.id) desc;
I've tried multiple different solutions, but none have worked so far.
My current iteration is as follows:
private void sortByAuthDate(Root<ShipmentTbl> root, CriteriaQuery<?> query, CriteriaBuilder builder, ListSort sort) {
Subquery<Timestamp> authDateQuery = query.subquery(Timestamp.class);
Root<StatusTransitionMessageTbl> stmRoot = authDateQuery.from(StatusTransitionMessageTbl.class);
Predicate shipmentId = builder.equal(stmRoot.<ShipmentTbl>get("shipment").<String>get("id"), root.<String>get("id"));
Predicate initialStatus = builder.equal(stmRoot.<Integer>get("initialStatus"), 3);
Predicate finalStatus = builder.equal(stmRoot.<Integer>get("finalStatus"), 4);
// returns the authorization date for each queried shipment
authDateQuery.select(builder.least(stmRoot.<Timestamp>get("date")))
.where(builder.and(shipmentId, initialStatus, finalStatus));
Expression<Timestamp> authDate = authDateQuery.getSelection();
Order o = sort.getSortDirection() == ListSort.SortDirection.ASC ? builder.asc(authDate) : builder.desc(authDate);
query.multiselect(authDate).orderBy(o);
}
The problem with this solution is that the SQL query generated by the CriteriaQuery does not support subqueries in the ORDER BY clause, causing a parsing exception.
My CriteriaQuery-fu is not good enough to help you with that part, but you could rewrite your SQL query to this:
SELECT s.id
FROM shipment s
LEFT JOIN status_transition_message stm
ON stm.initial_status = 1 AND stm.final_status = 3 AND stm.shipment_id = s.id
GROUP BY s.id
ORDER BY min(stm.date) DESC;
To me, this quite likely seems to be a faster solution anyway than running a correlated subquery in the ORDER BY clause, especially on RDBMS with less sophisticated optimisers.
So I attempted to follow #Lukas Eder solution and reached this solution:
private void sortByAuthDate(Root<ShipmentTbl> root, CriteriaQuery<?> query, CriteriaBuilder builder, ShipmentListSort sort) {
Join<ShipmentTbl, StatusTransitionMessageTbl> shipmentStatuses = root.join("shipmentStatus", JoinType.LEFT);
Predicate initialStatus = builder.equal(shipmentStatuses.<Integer>get("initialStatus"), 1);
Predicate finalStatus = builder.equal(shipmentStatuses.<Integer>get("finalStatus"), 3);
Expression<Timestamp> authDate = builder.least(shipmentStatuses.<Timestamp>get("date"));
Order o = sort.getSortDirection() == ShipmentListSort.SortDirection.ASC ? builder.asc(authDate) : builder.desc(authDate);
shipmentStatuses.on(builder.and(initialStatus, finalStatus));
query.multiselect(authDate).groupBy(root.<String>get("id")).orderBy(o);
}
}
But now it's throwing this exception:
ERROR o.h.e.jdbc.spi.SqlExceptionHelper - ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
This happens because the query is only going to get distinct shipments later on and it's asking for the sorting column also appear in the select. The problem is I don't know how to force CriteriaQuery to keep that column in the SELECT statement. It automatically only puts in the ORDER BY.
Here's the JPQL query it's executing in my test:
select
distinct generatedAlias0
from
ShipmentTbl as generatedAlias0
left join
generatedAlias0.shipmentStatus as generatedAlias1 with ( generatedAlias1.initialStatus=:param0 )
and (
generatedAlias1.finalStatus=:param1
)
where
lower(generatedAlias0.shipmentName) like :param2
group by
generatedAlias0.id
order by
min(generatedAlias1.date) desc
and the generated SQL query:
select
distinct shipmenttb0_.id as id1_13_,
shipmenttb0_.archived_date as archived2_13_,
shipmenttb0_.auth_code as auth_cod3_13_,
shipmenttb0_.authorization_date as authoriz4_13_,
shipmenttb0_.booked_in_by_user as booked_i5_13_,
shipmenttb0_.business_channel as business6_13_,
shipmenttb0_.courier as courier7_13_,
shipmenttb0_.courier_amount as courier_8_13_,
shipmenttb0_.courier_currency as courier_9_13_,
shipmenttb0_.ship_to as ship_to39_13_,
shipmenttb0_.estimated_shipment_date as estimat10_13_,
shipmenttb0_.last_updated_date as last_up11_13_,
shipmenttb0_.measurement_unit as measure12_13_,
shipmenttb0_.original_submitted_date as origina13_13_,
shipmenttb0_.packaging_type as packagi14_13_,
shipmenttb0_.placeholder_message as placeho15_13_,
shipmenttb0_.scheduled_period_of_day as schedul16_13_,
shipmenttb0_.scheduled_shipment_date as schedul17_13_,
shipmenttb0_.ship_from as ship_fr40_13_,
shipmenttb0_.ship_origin as ship_or41_13_,
shipmenttb0_.shipment_name as shipmen18_13_,
shipmenttb0_.status as status19_13_,
shipmenttb0_.submitted_date as submitt20_13_,
shipmenttb0_.supplier_contact_email as supplie21_13_,
shipmenttb0_.supplier_contact_name as supplie22_13_,
shipmenttb0_.supplier_contact_phone_number as supplie23_13_,
shipmenttb0_.supplier_email as supplie24_13_,
shipmenttb0_.supplier_secondary_contact_email as supplie25_13_,
shipmenttb0_.supplier_secondary_contact_name as supplie26_13_,
shipmenttb0_.supplier_secondary_contact_phone_number as supplie27_13_,
shipmenttb0_.tenant as tenant28_13_,
shipmenttb0_.total_received_boxes as total_r29_13_,
shipmenttb0_.total_units as total_u30_13_,
shipmenttb0_.total_value as total_v31_13_,
shipmenttb0_.total_volume as total_v32_13_,
shipmenttb0_.total_weight as total_w33_13_,
shipmenttb0_.tracking_number as trackin34_13_,
shipmenttb0_.tt_note as tt_note35_13_,
shipmenttb0_.tt_priority as tt_prio36_13_,
shipmenttb0_.updated_by_user as updated37_13_,
shipmenttb0_.weight_unit as weight_38_13_
from
shipment shipmenttb0_
left outer join
status_transition_message shipmentst1_
on shipmenttb0_.id=shipmentst1_.shipment_id
and (
shipmentst1_.initial_status=?
and shipmentst1_.final_status=?
)
where
lower(shipmenttb0_.shipment_name) like ?
group by
shipmenttb0_.id
order by
min(shipmentst1_.date) desc limit ?

Hibernate Query for equivalent sql

Can someone help me in writing equivalent hql for this query
SELECT IFNULL(oea.`organization_name`,'') FROM `consultation_appointment` ca
JOIN `organization_employee_association` oea ON ca.`consultation_id` = oea.id
JOIN professional_profile pp ON oea.`employee_profile_id` = pp.profile_id
I could able to join the first JOIN like this
select ca.name from ConsultationAppointment ca join ca.consultation oea
Because the ConsultationAppointment class having organization_employee_association variable so it easier to join, difficulty is organization_employee_association not having direct mapping to organization_employee_association class.
Even GORM criteria Query is helpful.
HQL does not allow joining two unassociated entity, you should use cartesian product instead.
String query = "SELECT ca.name FROM consultation_appointment ca JOIN organization_employee_association oea, professional_profile pp WHERE oea.employee_profile_id = pp.profile_id";
List<String> caNames = session.createQuery(query).list();
One another possibility is to use the method createSQLQuery(). It provides more flexibility to execute an arbitrary join.
String query = "SELECT ca.name FROM consultation_appointment ca JOIN organization_employee_association oea ON ca.consultation_id = oea.id JOIN professional_profile pp ON oea.employee_profile_id = pp.profile_id";
List<String> caNames = session.createSQLQuery(query).list();

Subquery in where clause with CriteriaQuery

Can anybody give me some hints on how to put that kind of subquery in a CriteriaQuery? (I'm using JPA 2.0 - Hibernate 4.x)
SELECT a, b, c FROM tableA WHERE a = (SELECT d FROM tableB WHERE tableB.id = 3) - the second select will always get a single result or null.
Try something like the following example to create a subquery:
CriteriaQuery<Object[]> cq = cb.createQuery(Object[].class);
Root tableA = cq.from(TableA.class);
Subquery<String> sq = cq.subquery(TableB.class);
Root tableB = cq.from(TableB.class);
sq.select(tableB.get("d"));
sq.where(cb.equal(tableB.get("id"), 3));
cq.multiselect(
cb.get("a"),
cb.get("b"),
cb.get("c"));
cq.where(cb.equal(tableA.get("a"), sq));
List<Object[]> = em.createQuery(cq).getResultList();
Note the code has not been tested due to the lack of an IDE nearby.
You can use DetachedCriteria to represend the sub-query. Your code should look something like:
DetachedCriteria subCriteria = DetachedCriteria.forClass(TableB.class);
subCriteria.add(Property.forName("id").eq(3)); //WHERE tableB.id = 3
subCriteria.setProjection(Projections.property("d")); // SELECT d from
DetachedCriteria criteria = DetachedCriteria.forClass(getPersistentClass());
criteria.add(Property.forName("a").eq(subCriteria)); //a = (sub-query)
criteria.setProjection(Projections.property("a"); //SELECT a
criteria.setProjection(Projections.property("b"); //SELECT b
criteria.setProjection(Projections.property("c"); //SELECT c
return getHibernateTemplate().findByCriteria(criteria);

Categories

Resources