表class
字段名 | 类型 | 描述 |
---|---|---|
id | int | 班级id |
name | varchar(30) | 名称 |
表student
字段名 | 类型 | 描述 |
---|---|---|
id | int | 学生id |
name | varchar(30) | 学生姓名 |
c_id | int | 班级id |
现在有个需求,需要查询出所有班级,并按照每个班级的人数倒序排序。
理论上SQL语句我们可以直接这样写:
select c.* from class c
cross join student s
and c.id = s.c_id
group by s.c_id
order by count(s.c_id) desc
这样查询,排序是没有问题的,但他的返回结果无法是一行数据,即使select c.* 改成select count(c),它依旧返回的是多行数据,因为这里使用了group by,但我们不得不按c.id分组,否则就无法按班级人数数量排序了。
这里就会产生一个问题,在JPA数据查询中个,他首先是查询返回一个总的数量,然后再查具体字段,当查询返回数量时,它的返回行数不为1,即有group by语句,他的返回结果为忽略掉group by 之后的结果。所以这条查询语句肯定是行不通的。于是我就想到给它嵌套一个子查询。
select count(c) from class c
cross join (select s.id,count(c_id) as count from student group by c_id order by count(c_id) desc) s
and c.id = s.c_id
使用这条语句直接在mysql中执行是行得通的,返回的结果没有问题,并且排序也是正确的,但当我试着去将这条语句在HQL中实现的时候,就碰到了问题了,子查询如何写呢?我花费了蛮多的时间去寻找子查询的解决方法,百思不得其解,无论是使用Criteria或者是原生的HQL语句,这条语句都无法实现的,最终在官方文档找到了一句话:
Note that HQL subqueries can occur only in the select or where clauses.
子查询语句只支持select和where子查询,也就是除此之外的from等语句是不支持子查询的。。。然后这个问题我到现在还没能找到解决方法,只能改变sql语句了,另外,换成exist是行不通的,效率太低。
HQL语句不能使用select接子查询语句,所以力求在一条sql语句中使用Criteria包含group by和count,order by这几个关键字,几乎是不可实现的。
那究竟怎样连接表查询才能实现我们的查询目的呢?
很遗憾,查找了很多资料都没有成功直接使用Criteria或原生HQL语句能做到的。
那如何解决呢?
解决问题不能只把眼光一直专注于一个地方啊,其实方法也比较简单,在数据库新增查询视图就可以了,Hibernate和JPA对数据库视图的支持都是非常良好的,很方便的是,我们不需要学习任何新的注解或者添加配置文件,直接像使用普通的数据库表对应的实体类就可以了,我沿用上一次的数据库表,举个栗子:
两张表如下:
我们的目标SQL语句:
select count(c) from class c
cross join (select s.id,count(c_id) as count from student group by c_id order by count(c_id) desc) s
and c.id = s.c_id
create or replace view cs_view
as
select s.id,count(c_id) as count from student group by c_id order by count(c_id) desc
@Entity
@Table(name = "cs_view", catalog = "对应数据库名")
public class CsView implements Serializable {
private static final long serialVersionUID = 1L;
private String id;
private Long count;
public CsView(String id, Long count) {
this.id = id;
this.count = count;
}
@Id
@Basic
@Column(name = "id")
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Basic
@Column(name = "count")
public Long getCount() {
return count;
}
public void setCount(Long count) {
this.count = count;
}
}
实体类这边有必要提醒注意一点:
其他的和正常数据库表的实体类没有区别
这一步和普通的链表查询没有任何区别
我们加入query是CriteriaQuery
Root viewRoot = query.from(CsView.class);
predicates.add(cb.equal(viewRoot.get("id"), root.get("id")));
orderList.add(cb.desc(viewRoot.get("count")));
这样可以成功使用group by ,order by ,count等数据库函数了。
另外,或许Hibernate可以直接在代码中定义查询视图,也叫临时实体类,大家有兴趣可以搜搜,不过窃以为直接使用数据库视图会更清晰一些。
欢迎关注我的个人公众号:逍遥的心。主推程序员写的生活类文章,有兴趣的朋友可以共同探讨下: