一、学习目标
1、了解HQL定义以及HQL语句形式;
2、掌握Query对象的使用;
3、能够编写出符合数据查询要求的HQL语句
二、了解HQL
1. 定义
(1) HQL:Hibernate Query Lauguage,Hibernate查询语言;
(2) HQL是面向对象的查询语言(即以面向对象的角度进行查询)。它查询的主体是映射配置的持久化类及其属性,与SQL查询主体是数据库表是完全不同的,除此之外,从数据库查询方面的功能上来讲,SQL能实现的数据库查询功能,HQL几乎可以同样实现。实际上作为一个ORM对象关系映射框架,Hibernate框架会将编写好的HQL语句解析成SQL语句来完成最终的数据库查询操作。
(3) HQL提供了丰富灵活的查询特性,是Hibernate官方推荐查询方式。
2. 语句形式
select... from... where... group by... having... order by...
select子句:用来指定查询结果中的对象和属性,并指定以何种数据类型返回 (在最前面)
from子句:用来指定hql语句的查询目标,即映射配置的持久化类及其属性
where子句:逻辑表达式,用来设置查询条件,限制返回结果和范围
group by子句:分组查询语句
having子句:对分组进行限制条件设置
order by子句:用来指定查询结果中的实例对象的排序
注:from子句在HQL语句中不可或缺的组成部分,一个最简单的HQL语句形式只要有from就可以了,其他的子句都可以省略,这点与SQL语句不同
3. 初学者需要注意的问题
(1) HQL是面向对象的查询语言,其查询主体是持久化类及其属性,而SQL查询主体是数据库表与表的字段;HQL对Java【类与属性大小写敏感】,SQL语句对大小写不敏感;
(2) HQL与SQL在形式上非常相似,特别是HQL在设计上尽量符合之前SQL开发人员的开发习惯,在使用上非常容易与SQL混淆,但是我们不能被表象所迷惑,要认清本质;
(3) HQL对关键字不区分大小写,但是为了代码的可读性与美观性,习惯上对HQL的所有关键字小写
三、准备查询
1. Query接口
(1) org.hibernate.Query接口定义有执行查询的方法(该方法完成HQL语句的解析与执行过程,并返回查询的结果);
(2) Query接口支持方法链编程风格,使得程序代码更为简洁(方法链编程:调用方法后,返回的结果依然是调用这个方法的对象,可以在调用方法后直接调用该对象的其他方法,这样可以使用一个程序语句完成多个方法的调用与执行)
(3) Query实例的创建:
a.Session的createQuery()方法创建Query实例
b.createQuery方在调用时需要传递一个参数(即要查询的HQL语句),createQuery(hql)
(4) Query执行查询
a.Query接口的list()方法执行HQL查询
b.list()方法返回结果数据类型为java.util.List,List集合中存放符合查询条件的持久化对象
2. 编程示例
@Test
public void testSeller() throws Exception {
String hql = "from Seller";
Query query = session.createQuery(hql);
List<Seller> list = query.list();
for(Seller seller : list) {
System.out.println(seller);
}
}
四、检索对象--from子句
1. from子句是HQL语句的最简形式
2. from子句指定了HQL语句查询的主体----映射配置的持久化类及其属性
HQL语句只有from子句时,HQL框架默认查询该持久化类的所有实例以及该持久化类映射配置的信息,当HQL框架将HQL解析成SQL时,就会查询该持久化类映射的数据表中的所有映射字段信息,并将返回的查询结果封装成该持久化类的list集合
3. 默认情况下,hibernate是不做外键的查询操作的,当需要用到外键时才会去查询外键所对应的持久类,在控制台输出语句中,已经输出过的外键所对应的持久类查询sql语句不再重复输出
例如test如下时,相应输出的SQL日志中查询Commodity的外键sellerid只查询一次,后面再遇到相同seller的商品,不再重复查询
@Test
public void testFromClause() throws Exception {
String hql = "from Commodity";
Query query = session.createQuery(hql);
List<Commodity> list = query.list();
for (Commodity c: list) {
System.out.println("name: " + c.getName());
System.out.println("seller's name: " + c.getSeller());
}
}
4. 不需要引入持久化类的全限定名,直接引入类名
全限定名:com.imooc.entity.Seller 类名:Seller
5. auto-import(自动引入)缺省情况,hibernate中解析hql语句时,会根据映射配置信息,自动完成持久化类的导入
6. from子句中别名的应用:
a. 可以通过AS关键字(也可以省略)为被查询的类指定别名
b. 在HQL语句其他部分通过别名引用该类(引用较多时会方便很多)
c. 别名命名习惯:与持久化类名相同,全部采用小写,可以参考Java变量名的命名;如果为了简洁,可以采用单字母命名
五、选择--select子句
1. 以Object[]形式返回选择的属性
select子句中未指定返回数据类型时,默认为Object[]
例如对Seller表的name,tel,address,star 4个字段进行查询
@Test
public void testSelectClauseObjectArray() {
String hql = "select s.name,s.tel,s.address,s.star from Seller AS s";
Query query = session.createQuery(hql);
List<Object[]> list = query.list();
for (Object[] objs : list) {
System.out.print("name:" + objs[0]);
System.out.print(" tel:" + objs[1]);
System.out.print(" address:" + objs[2]);
System.out.print(" star:" + objs[3]);
System.out.println();
}
}
注:如果指定了多个查询字段,则返回的是一个Object[]数组;如果只指定了一个查询字段,则返回的是一个Object对象。
2. 以List形式返回选择的属性
select子句中使用new list指定
例如:String hql = "select new list(s.id,s.name,s.tel) from Seller as s";
3. 以map形式返回选择的属性
select子句中使用new map(s.name,s.tel,s.address) from 指定
* key值为索引值,为字符串类型,"0","1"等;
* 若在map中使用了别名:(s.name as n,s.tel as t,s.address as a) 就可以使用map.get(别名),这里as关键字是必须的。
@Test
public void testSelectClauseMap() {
//String hql = "select new map(s.name,s.tel,s.address)from Seller s";
String hql = "select new map(s.name as name,s.tel as tel,s.address as address)from Seller s";
Query query = session.createQuery(hql);
List<Map> maps = query.list();
for (Map map : maps) {
// System.out.print("name:" + map.get("0") + "\t");
// System.out.print("tel:" + map.get("1") + "\t");
// System.out.print("address:" + map.get("2"));
System.out.print("name:" + map.get("name") + "\t");
System.out.print("name:" + map.get("tel") + "\t");
System.out.print("name:" + map.get("address") + "\t");
System.out.println();
}
}
注:决定使用Object,list或者map只是习惯问题或者有哪种需求
4. 以自定义类型返回选择的属性
a. 持久化类中定义对应的构造器
b. select子句中调用定义的构造器:select new Seller(s.name,s.address) from Seller s
5. 持久化类中无参构造方法的必要性
在没有指定查询返回形式时,会去调用默认的无参构造方法,如果没有该方法,则运行报错
6. 获取独特的结果-distinct关键字
distinct关键字用于去掉重复的元素,注意以下两点:
a. distinct使用时只能放在查询语句的第一个字段前面,且只对查询单个字段才有过滤效果。
b. 如果查询语句中包含多个字段时,那么distinct将不再具有过滤功能,但运行也不会报错。
@Test
public void testDistinct() throws Exception {
String hql = "select distinct c.sex from Customer c";
Query query = session.createQuery(hql);
List<Object> list = query.list();
for (Object obj : list) {
System.out.println("sex:" + obj);
}
}
六、限制--where子句
1.比较运算
a. =、<>、<、>、>=、<=
b. null值判断——is [not] null
@Test
public void testWhere1() {
String hql = "from Commodity c where c.price>400";
Query query = session.createQuery(hql);
List<Commodity> list = query.list();
for (Commodity c : list) {
System.out.print("name: " + c.getName() + "\t");
System.out.println("price: " + c.getPrice());
}
}
String hql = "from Commodity c where c.description is null ";
//在HQL中是等价的,但在SQL中不能用=
//String hql = "from Commodity c where c.description = null ";
2.范围运算
a. [not] int (值1,值2,值3,...),在这些值中是否存在相应数据,存在为true,否false
b. [not] between 值1 and 值2 是否在范围 [值1,值2] 之间
String hql = "from Customer c where c.age in (20,40)";
String hql = "from Customer c where c.age not in (20,40)";
String hql = "from Customer c where c.age between 20 and 40";
String hql = "from Customer c where c.age not between 20 and 40";
3.字符串模式匹配
a. like关键字
b. 通配符%(可以匹配任意个字符)、_(匹配一个字符)
//String hql = "from Customer c where c.name like '张_'";
String hql = "from Customer c where c.address like '%北京%'";
4.逻辑运算
and(逻辑与)、or(逻辑或)、not(逻辑非)
//String hql = "from Commodity c where c.price between 100 and 5000 and c.category like '%电脑%'";
String hql = "from Commodity c where c.price between 100 and 5000 or c.category like '%电脑%'";
5.集合运算
a. is [not] empty 集合[不]为空,不包含任何元素(转换为SQL语句中的exists运算)
b. member of 元素属于集合(转换为SQL语句中的in运算)
String hql = "from Order where o.orderItems is not empty ";
6.在HQL中使用+、-、*、/运算符
a. HQL语句中也可以使用+ - * / 四则运算
b. 四则运算可以在where子句和select子句中使用
String hql = "from Commodity c where c.price*3>2000
7.查询单个对象(uniqueResult方法)
Query接口的uniqueResult方法
a. 该方法的返回是实例对象而不是返回集。
b. 使用uniqueResult需要在where保证只有一个返回结果或者不存在,如果有多个,则会抛出异常
七、排序--order by子句
通过order by 子句对查询结果排序
1. 升序排序 asc
2. 降序排序 desc
@Test
public void testOrderBy() {
String hql = "from Commodity order by seller.id asc ,price desc, name asc ";
Query query = session.createQuery(hql);
List<Commodity> list = query.list();
for (Commodity c : list) {
System.out.print("name: " + c.getName() + "\t");
System.out.print("sellerID: " + c.getSeller().getName() + "\t");
System.out.println("price: " + c.getPrice());
}
}
八、总结
1. HQL语句形式
2. HQL语句大小写敏感,特别是持久化类及其属性的大小写
3. 别名的使用
4. select子句使用自定义类返回选择属性,持久化类构造器处理(一定要添加默认构造器)