hebernate,JPA select 子查询语句问题

hebernate select 子查询语句的问题

表class

字段名类型描述
idint班级id
namevarchar(30)名称

表student

字段名类型描述
idint学生id
namevarchar(30)学生姓名
c_idint班级id

现在有个需求,需要查询出所有班级,并按照每个班级的人数倒序排序。

  1. group by

理论上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 之后的结果。所以这条查询语句肯定是行不通的。于是我就想到给它嵌套一个子查询。

  1. 子查询
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;
    }


}

实体类这边有必要提醒注意一点:

  • 给一个字段加@Id 注解,表示主键,虽然视图是没有主键一说的,但Hibernate要求实体类必须有主键,所以直接给一个不会重复的字段加上即可,因为我们只做查询用途。

其他的和正常数据库表的实体类没有区别

三、连接表查询

这一步和普通的链表查询没有任何区别

我们加入query是CriteriaQuery

Root<CsView> 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可以直接在代码中定义查询视图,也叫临时实体类,大家有兴趣可以搜搜,不过窃以为直接使用数据库视图会更清晰一些。

欢迎关注我的个人公众号:逍遥的心。主推程序员写的生活类文章,有兴趣的朋友可以共同探讨下:这里写图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值