using subqueries in jpa criteria api

后端 未结 2 1507
北荒
北荒 2020-12-20 07:54

I\'m studying JPA criteria api and my database contains Employee table. I am trying to find all the employees who are paid second highest salary. I was able to write JPQL su

2条回答
  •  隐瞒了意图╮
    2020-12-20 08:16

    After some more trial and error, I could write the query to select employees with second maximum salary. I would like to suggest that you should write a JPQL query first and write the criteria api accordingly. This is what I analyzed from JPQL.

    SELECT e FROM Employee e 
    WHERE e.salary = (SELECT MAX(emp.salary) FROM Employee emp 
    WHERE emp.salary < (SELECT MAX(employee.salary) FROM Employee employee) )
    

    Now we can see that

    • There are 2 subqueries, i.e. subquery of main query contains another subquery
    • The identification variables e, emp and employee correspond to the main query, subquery of main query and subquery of subquery.
    • Now while comparing the result of subqueries i.e. maximum salary compared with the employee salary of outer query, the identification variable from outer query is used. for e.g. WHERE emp.salary = (SELECT MAX(emp.salary) FROM Employee emp)

    Now let us convert this query in criteria api.

    First write CriteriaQuery that corresponds to outermost query i.e. SELECT e FROM Employee e WHERE e.salary =

    CriteriaQuery c1 = cb.createQuery(Employee.class);
    Root e3 = c1.from(Employee.class);
    c1.select(e3);
    

    Let us leave the WHERE e.salary = for now and go for the subquery

    Now this should have a subquery that selects the maximum salary of employees i.e. SELECT MAX(emp.salary) FROM Employee emp WHERE emp.salary < again let us leave the WHERE emp.salary < for now.

    Subquery sq1 = c1.subquery(Long.class);
    Root e4 = sq1.from(Employee.class);
    sq1.select(cb.max(e4. get("salary")));
    

    repeating this for subquery of above subquery,

    Subquery sq2 = sq1.subquery(Long.class);
    Root e5 = sq2.from(Employee.class);
    sq2.select(cb.max(e5. get("salary")));
    

    Now we have written subqueries but WHERE conditions need to be applied yet. So now the where condition in criteria api corresponding to WHERE emp.salary < (SELECT MAX(employee.salary) FROM Employee employee) will be as below.

    sq1.where(cb.lessThan(e4. get("salary"), sq2));
    

    Similarly, WHERE condition corresponding to WHERE e.salary = (SELECT MAX(emp.salary) FROM Employee emp will be as below.

    c1.where(cb.equal(e3. get("salary"), sq1));
    

    So the complete query which gives the employees with second highest salary can be written in criteria api as below.

            CriteriaQuery c1 = cb.createQuery(Employee.class);
        Root e3 = c1.from(Employee.class);
        c1.select(e3);
    
        Subquery sq1 = c1.subquery(Long.class);
        Root e4 = sq1.from(Employee.class);
        sq1.select(cb.max(e4. get("salary")));
    
        Subquery sq2 = sq1.subquery(Long.class);
        Root e5 = sq2.from(Employee.class);
        sq2.select(cb.max(e5. get("salary")));
    
        sq1.where(cb.lessThan(e4. get("salary"), sq2));
        c1.where(cb.equal(e3. get("salary"), sq1));
    
        employees = em.createQuery(c1).getResultList();
    
        for (Employee employee : employees) {
            System.out.println(employee.getName() + " " + employee.getSalary());
        }
    

提交回复
热议问题