我正在学习JPA标准api,我的数据库包含Employee表。 我试图找到所有薪水第二高的员工。我能够成功编写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) )
但现在我正在尝试将其转换为条件api并尝试过以下操作。
CriteriaQuery<Employee> c = cb.createQuery(Employee.class);
Root<Employee> e1 = c.from(Employee.class);
c.select(e1);
Subquery<Number> sq = c.subquery(Number.class);
Root<Employee> e2 = sq.from(Employee.class);
sq.select(cb.max(e2.<Number> get("salary")));
Subquery<Number> sq1 = sq.subquery(Number.class);
Root<Employee> e3 = sq1.from(Employee.class);
sq1.select(cb.max(e3.<Number> get("salary")));
c.where(cb.lessThan(e2.<Number>get("salary"), e3.<Number>get("salary")));// error here
c.where(cb.equal(e1.get("salary"), sq));
我收到参数与lessThan
方法不兼容的错误。我不明白如何才能解决这个问题。我的做法是对的吗?
编辑: - 在Mikko回答后更新问题。
上面提供的jpql提供了以下结果,即薪水第二高的员工。
Harish Taware salary 4000000.0
Nilesh Deshmukh salary 4000000.0
Deodatta Chousalkar salary 4000000.0
Deodatta Chousalkar salary 4000000.0
但更新的条件查询如下,
CriteriaQuery<Employee> c = cb.createQuery(Employee.class);
Root<Employee> e1 = c.from(Employee.class);
c.select(e1);
Subquery<Long> sq = c.subquery(Long.class);
Root<Employee> e2 = sq.from(Employee.class);
sq.select(cb.max(e2.<Long> get("salary")));
Subquery<Long> sq1 = sq.subquery(Long.class);
Root<Employee> e3 = sq1.from(Employee.class);
sq1.select(cb.max(e3.<Long> get("salary")));
c.where(cb.lessThan(e2.<Long> get("salary"), e3.<Long> get("salary")));
c.where(cb.equal(e1.get("salary"), sq));
employees = em.createQuery(c).getResultList();
for (Employee employee : employees) {
System.out.println(employee.getName() + "salary"
+ employee.getSalary());
}
这为员工提供最高薪水。结果如下。
Pranil Gildasalary5555555.0
请告诉我我错在哪里。非常感谢您的解释。
答案 0 :(得分:7)
经过多次试验和错误后,我可以编写查询来选择具有第二个最高薪水的员工。我想建议您首先编写一个JPQL查询并相应地编写标准api。这是我从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) )
现在我们可以看到
WHERE emp.salary = (SELECT MAX(emp.salary) FROM Employee emp)
现在让我们在条件api中转换此查询。
首先编写与最外层查询对应的CriteriaQuery,即SELECT e FROM Employee e WHERE e.salary =
CriteriaQuery<Employee> c1 = cb.createQuery(Employee.class);
Root<Employee> e3 = c1.from(Employee.class);
c1.select(e3);
让我们暂时离开WHERE e.salary =
并转到子查询
现在这应该有一个子查询,选择员工的最高工资,即SELECT MAX(emp.salary) FROM Employee emp
WHERE emp.salary <
,让我们暂时离开WHERE emp.salary <
。
Subquery<Long> sq1 = c1.subquery(Long.class);
Root<Employee> e4 = sq1.from(Employee.class);
sq1.select(cb.max(e4.<Long> get("salary")));
对上面子查询的子查询重复此操作,
Subquery<Long> sq2 = sq1.subquery(Long.class);
Root<Employee> e5 = sq2.from(Employee.class);
sq2.select(cb.max(e5.<Long> get("salary")));
现在我们已经编写了子查询,但还需要应用WHERE
条件。那么现在对应于WHERE emp.salary < (SELECT MAX(employee.salary) FROM Employee employee)
的标准api中的where条件如下所示。
sq1.where(cb.lessThan(e4.<Long> get("salary"), sq2));
类似地,对应于WHERE e.salary = (SELECT MAX(emp.salary) FROM Employee emp
的WHERE条件如下。
c1.where(cb.equal(e3.<Long> get("salary"), sq1));
因此,给员工第二高薪的完整查询可以用下面的标准api编写。
CriteriaQuery<Employee> c1 = cb.createQuery(Employee.class);
Root<Employee> e3 = c1.from(Employee.class);
c1.select(e3);
Subquery<Long> sq1 = c1.subquery(Long.class);
Root<Employee> e4 = sq1.from(Employee.class);
sq1.select(cb.max(e4.<Long> get("salary")));
Subquery<Long> sq2 = sq1.subquery(Long.class);
Root<Employee> e5 = sq2.from(Employee.class);
sq2.select(cb.max(e5.<Long> get("salary")));
sq1.where(cb.lessThan(e4.<Long> get("salary"), sq2));
c1.where(cb.equal(e3.<Long> get("salary"), sq1));
employees = em.createQuery(c1).getResultList();
for (Employee employee : employees) {
System.out.println(employee.getName() + " " + employee.getSalary());
}
答案 1 :(得分:1)
作为documented,它无效,因为Number不是Comparable:
<Y extends java.lang.Comparable<? super Y>> Predicate lessThan(Expression<? extends Y> x,
Expression<? extends Y> y)
对于带有Number的表达式,方法Criteriabuilder.lt采用了这样的参数:
c.where(cb.lt(e2.<Number>get("salary"), e3.<Number>get("salary")));
其他选项是将类型参数从Number更改为更具体的更新。如果薪水很长,以下工作应该有效:
Subquery<Long> sq = c.subquery(Long.class);
Root<Employee> e2 = sq.from(Employee.class);
sq.select(cb.max(e2.<Long> get("salary")));
Subquery<Long> sq1 = sq.subquery(Long.class);
Root<Employee> e3 = sq1.from(Employee.class);
sq1.select(cb.max(e3.<Long> get("salary")));
c.where(cb.lessThan(e2.<Long>get("salary"), e3.<Long>get("salary")));
c.where(cb.equal(e1.get("salary"), sq));