首页 > Java框架应用 > Hibernate 入门教程 > Hibernate 查询语言(HQL)

Hibernate 查询语言(HQL)

1. 前言

本节课程和大家一起学习 Hibernate 中的 HQL ( Hibernate 查询语言)。通过本节课程的学习,你将了解到:

  • HQL 基础语法;
  • HQL 查询的具体实现。-

2. HQL

查询?前面不是讲过?用过吗?

但是,前面的查询都是简单查询,真实项目中的查询需求要远比这个复杂。仅仅依靠 get()、load() 是远远达不到要求。

Hibernate 提供了灵活多样的查询机制,几乎能做到无死角查询。

  1. 标准化对象查询 (Criteria Query): 以对象的方式进行查询,将查询语句封装为对象操作。

  2. 优点: 可读性好,符合 Java 程序员的编码习惯。

  3. 缺点: 不够成熟,不支持投影(projection)或统计函数(aggregation)

  4. Hibernate 语言查询(Hibernate Query Language,HQL): 它是完全面向对象的查询语句,查询功能非常强大,具有继承、多态和关联等特性 。Hibernate 官方推荐使用 HQL 进行查询。

  5. Native SQL Queries(原生 SQL 查询): 直接使用数据库提供的 SQL 语句进行查询。

原生 SQL 的查询能力是最强的,当其它查询不能达到完成任务要求时,可使用它。

本次课程主要是介绍 HQL 查询。

2.1 HQL 基础语法

还是从查询需求入手吧。

查询需求: 查询所有学生。

前提:确定数据库中有测试数据。

查询流程:

首先编写 HQL 语句,如:

select stuName,stuId from  Student

HQL 整体结构上类似于 SQL ,可以认为 HQL 语句由两部分组成,一部分直接借用 SQL 中的关键字,如 select、from、where 等,和 SQL 中要求是一样的,编写时可不区分大小写。

另一部分就是纯 JAVA 概念。

HQL 语句中,原生 SQL 语法中的 表名类名 替换, 字段名属性名 替换,类和属性是 Java 概念,所以要区分大小写。

下面的 HQL 语句用来查询所有学生信息,在 HQL 中同样有别名一说。

from  Student s

HQL 是面向对象的、类似于 SQL 的查询语言,本质上是不能直接交给数据库的,在提交之前,需要把 HQL 转译成 SQL ,在转译时把类名换成表名、把属性名换成字段名。

虽然替换工作由 Hibernate 自己完成,但是,你需要有所了解。

从中可得出一个结论, HQL 查询是没有原生 SQL 查询快的。

Hibernate 提供了 Query 组件执行 HQL 语句。

Query query=session.createQuery(hql);

Query 与原生 JDBC 中的 Statement 组件很相似,但其功能更高级、强大。

调用 Query 对象中 list() 查询方法,就能查询出开发者所需要的数据。

List<Student> stus= query.list();

最后,享受数据的时刻:

for (Student student : stus) {
    System.out.println(student);
}

完整的实例:

HibernateTemplate<Student> hibernateTemplate = new HibernateTemplate<Student>();
hibernateTemplate.template(new Notify<Student>() {
@Override
public Student action(Session session) {    
    String hql="from  Student s";
    Query query=session.createQuery(hql);
    List<Student> stus= query.list();
    for (Student student : stus) {
        System.out.println(student);
}
    return null;
}
});

执行实例,在控制台可看到输出了所有学生信息。简直是简单得不要不要。

如果只想查询某几个属性的值,又该如何查询呢?

很简单,重构一下 HQL 语句:

String hql="select s.stuId,s.stuName from  Student s";

此时查询出来的数据不是一个完整的对象。 list() 方法查询出来的结果不能直接封装到 Student 类型中。

Hibernate 会把查询出来的每一行数据封装到一个数组中,所以 List 应该 是一个数组的集合,完整实例代码如下:

HibernateTemplate<Student> hibernateTemplate = new HibernateTemplate<Student>();
hibernateTemplate.template(new Notify<Student>() {
@Override
public Student action(Session session) {    
String hql="select s.stuId,s.stuName from  Student s";
Query query=session.createQuery(hql);
List<Object[]> stus= query.list();
for (Object[] student : stus) {
    System.out.println(student[0]+":"+student[1]);
}
return null;
}
});

控制台输出结果:

Hibernate: 
    select
        student0_.stuId as col_0_0_,
        student0_.stuName as col_1_0_ 
    from
        Student student0_
1:Hibernate
2:Session
3:SessionFactory
24:Configuration

如果一定要使用 Student 类型接收查询数据则必须保证 Student 类中存在如下的构造方法:

public Student(Integer stuId, String stuName) {
    this.stuId = stuId;
    this.stuName = stuName;
}

然后修改 HQL 语句:

String hql="select new Student(s.stuId,s.stuName) from  Student s";
    Query query=session.createQuery(hql);
    List<Student> stus= query.list();
    for (Student student : stus) {
        System.out.println(student);
       }

建议大家使用上面的方案,毕竟很 OOP 嘛。其实也可以使用 Map 封装查询出来的数据。

String hql="select new Map(s.stuId as sid,s.stuName as sname) from  Student s";
Query query=session.createQuery(hql);
List<Map<String,Object>> stus= query.list();
for (Map<String,Object> map : stus) {
            System.out.println(map.get("sid")+":"+map.get("sname"));
}

道路千万条,选择在你手上。

2.2 HQL 高级查询

强参数查询

使用 SQL 查询时,可以指定查询条件,这个地球人都知道。 HQL 中同样能使用条件查询:

from  Student s where s.stuId> 2

HQL 中,如果查询条件中的数据需要通过参数传递,则会有两种方案:

  • 匿名方案,已经司空见惯,对不对;

    from Student s where s.stuId> ?

  • 命名参数方案。

    from Student s where s.stuId> :id

参数名前面一定要有一个冒号 :id。

完整实例献上:

String hql="from  Student s where s.stuId> :id";
Query query=session.createQuery(hql);
query.setInteger("id", 2);
List<Student> stus= query.list();
for (Student student : stus) {
    ystem.out.println(student);
}
return null;

可自行查看控制台上的输出结果。强命名参数和 ? 占位符作用是一样的,但是,强命名参数可减少指定实参时的出错率。

分页查询

分页查询是很实用的查询机制。

使用原生 SQL 分页查询时,需要自己构建查询 SQL 语句,不同的数据库中的分页查询语句编写也有差异性。 Hibernate 通过其提供的分页查询功能很好地避开了这些问题。

分页查询之前,先搞清楚几个与查询有关的参数:

  • pageSize: 每一页大小;
  • pageNum: 页码。

假如数据库中有 20 行数据,分页查询时指定 pageSize5 ,则每 5 条数据为一个逻辑页,总共有 4 页。

如果要查询第 3 页数据,即 pageNum=3

则需要跳过去的记录数为:(pageNum-1)pageSize=(3-1)5=10 ,也就是从第 11 条数据开始查询。

现在直接上实例代码:

String hql = "from  Student s order by stuId" ;
Query query = session.createQuery(hql);
int pageNum=3;
int pageSize=5;
int passNum=(pageNum-1)*pageSize;
query.setFirstResult(passNum);
query.setMaxResults(pageSize);
List<Student> stus = query.list();
for (Student student : stus) {
    System.out.println(student.getStuName());
    }
return null;

HIbernate 会从第 11 条记录开始,查询出 5 条记录。针对不同的数据库系统, Hibernate 会给出最佳的 SQL 分页方案。

联合查询

程序中所需要的数据可不一定在同一张表中,往往都是在多张表中。原生 SQL 通过多表连接或子查询方式解决这个问题。

使用 HQL 一样能表达出多表连接的意图。

可能你会问:

前面的一对一、一对多、多对多映射关联关系后,不就已经能够查询出多张表中的数据吗。

如下面表数据:
图片描述

在学生类中采用立即查询策略:

@ManyToOne(targetEntity = ClassRoom.class, cascade = CascadeType.REMOVE,fetch=FetchType.EAGER)
@JoinColumn(name = "classRoomId")
public ClassRoom getClassRoom() {
    return classRoom;
}

查询所有学生:

String hql = "from  Student s";
Query query = session.createQuery(hql);
List<Student> stus = query.list();
System.out.println("-----------------------------");
for (Student student : stus) {
System.out.println("学生姓名:"+student.getStuName());
System.out.println("班级名称: "+student.getClassRoom().getClassRoomName());             
}
return null;

不要怀疑,结果一定是会出现的。但是,可以看到控制台输出了很多 SQL 语句。

那是因为, Hibernate 会先查询出所有学生,然后根据班级 ID 再进入班级表进行查询,这就是 Hibernate 查询过程的 1+N 问题。

可改成下面的关联查询方式:

String hql = "select s.stuName,c.classRoomName from  Student s,ClassRoom c where s.classRoom=c";
Query query = session.createQuery(hql);
List<Object[]> stus = query.list();
System.out.println("-----------------------------");
for (Object[] student : stus) {
    System.out.println("学生姓名:"+student[0]);
    System.out.println("班级名称: "+student[1]);                
}
return null;

控制台输入结果:

Hibernate: 
    select
        student0_.stuName as col_0_0_,
        classroom1_.classRoomName as col_1_0_ 
    from
        Student student0_ cross 
    join
        ClassRoom classroom1_ 
    where
        student0_.classRoomId=classroom1_.classRoomId

Hibernate 仅构建了一条 SQL 语句,直接查询出来了所有数据,看得出来,其性能要大于 1+N 方案。

HQL 比想象中要简单,比你预期的功能要强大。有了它,再也不怕查询不到我们需要的数据。

2.3 HQL 与函数

先把上一节课程中遗留的内容向大家介绍一下。

使用原生 SQL 查询时,可以在查询语句中嵌入聚合函数, HQL 查询中也可以使用 聚合函数

数据的用途之一是用于逻辑,产生新的数据。另一个用途是可产生报表,用于决策。

聚合函数的作用在于数据统计和分析,其现实意义很大。

HQL 中能使用哪些聚合函数?

答案是: SQL 中的聚合函数全部可照搬过来。

实例:统计学生相关信息。

String hql = "select count(*),sum(s.stuId),avg(s.stuId),max(s.stuId),min(s.stuId)  from  Student s";
Query query = session.createQuery(hql);
Object[] stus = (Object[]) query.uniqueResult();
System.out.println("学生总人数:" + stus[0]);
System.out.println("学生编号求和:" + stus[1]);
System.out.println("学生编号平均值:" + stus[2]);
System.out.println("学生编号最大值:" + stus[3]);
System.out.println("学生编号最小值:" + stus[4]);

使用过程很简单,上面实例中用到了 Query 对象的 uniqueResult() 方法,当确定查询结果只有一行记录时,可以使用此方法。

好了,有句话说得好,师傅引进门,学艺在个人。因为 HQL 是对 SQLOOP 封装,其内涵是一样。对于很熟悉 SQL 语法的你们来讲,全完掌握 HQL 也只时间的问题,不会存在技术上的难点。

3. 小结

又到了要总结的时候,本课程给大家介绍了 HQL 查询语法,还有更多细节留待后面慢慢研究。

HQL 语法是一种类似于 SQL 语法,形式上与 SQL 一样,但本质有很大区别。 HQL 是面向对象的查询语法结构。注意语句中哪些地方不区分大小,哪些地方 区分大小写。

本节课也聊到了 HQL 的关联查询语法,很好解决了 1+N 问题。

本文来自互联网用户投稿,不拥有所有权,该文观点仅代表作者本人,不代表本站立场。
访问者可将本网站提供的内容或服务用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯本网站及相关权利人的合法权利。
本网站内容原作者如不愿意在本网站刊登内容,请及时通知本站,邮箱:80764001@qq.com,予以删除。
© 2023 PV138 · 站点地图 · 免责声明 · 联系我们 · 问题反馈