1 概述
Hibernate提供了以下几种检索对象的方式
●导航对象图检索方式:根据已经加载的对象导航到其他对象
●OID检索方式:按照对象的OID来检索对象
●HQL检索方式:使用面向对象的HQL查询语言
●QBC检索方式:使用QBC(Query By Criteria) API来检索对象。这种API封装了基于字符串形式的查询语句,提供了更加面向对象的查询接口。
●本地SQL检索方式:使用本地数据库的SQL查询语句。
2 HQL
2.1 概述
HQL(Hibernate Query Language)是面向对象的查询语言,它和SQL查询语言有些相似。在Hibernate提供的各种检索方式中,HQL是使用最广的一种检索方式。它有如下特点:
以面向对象的方式查询数据库表
在查询语句中设定各种查询条件
支持投影查询,即仅检索出对象的部分属性
支持分页查询
支持连接查询
支持分组查询,允许使用HAVING和GROUP BY关键字
提供内置聚集函数,如sum(),min()和max()等
支持子查询
支持动态绑定参数
2.2 FROM子句
在SQL中,FROM子句是用于指定要查询的数据库表,在HQL中,替换为与数据库表对应的Java实体类即可。
例如:
SQL SELECT * FROM EMPS
HQL FROM Employee
此时HQL语句将查询数据库表中的所有字段,并自动注入到对应实体类对象的对应属性中。
同时需要说明的是:HQL语句中FROM等关键字不区分大小写,但Java类的类名严格区分大小写。
2.3 执行HQL查询语句
在Hibernate中,HQL语句由Query对象执行,Query对象可以通过Session对象获取。
//1.通过session对象创建Query对象
String hql = "FROM Employee";
Query query = session.createQuery(hql);
//2.调用Query对象的方法获取查询结果
//结果集中有多条记录:list()
//结果集中只有一条记录:uniqueResult()
List<Employee> list = query.list();
for (Employee employee : list) {
System. out.println(employee);
}
2.4 WHERE子句
和SQL的语法一样,HQL中的WHERE子句也用来指定查询条件。只不过这里指定查询条件使用的不是数据库表的字段,而是Java类的属性。
例如:
SQL SELECT * FROM EMPS WHERE EMPS.SALARY>5000
HQL FROM Employee e WHERE e.salary>5000
这里Employee e的语法非常像Java中声明一个Employee类型的变量:e。其实也确实可以这样理解——使用e作为Employee对象的引用。
2.5 使用基于位置的占位符参数
将上例中的具体值使用占位符“?”代替,并调用Query对象的setXxx()方法按照参数的不同类型动态填充即可,需要注意的是和JDBC中的PreparedStatement接口不同,这里占位符的索引从0开始。
HQL FROM Employee e WHERE e.salary>?
填充占位符 query.setDouble(0, 8000);
2.6 使用具名参数
在HQL中不但能够使用基于位置的占位符参数,还能够使用基于名称的具名参数,使用具名参数的好处是不必关心当前参数的索引值。
具名参数的格式是:“:参数名称”。
例如:
HQL FROM Employee e WHERE e.salary>:salaryParam
填充占位符 query.setDouble( "salaryParam", 9000);
2.7 以实体类对象作为参数
对于已经通过Hibernate关联关系映射建立了关联关系的实体类,HQL还支持直接使用实体类对象本身作为参数值。
例如:Employee和Department之间建立了单向(或双向)多对一关联关系,Employee类中使用department属性关联Department类的对象,那么HQL语句可以为:
HQL from Employee e where e.department=?
这里填充占位符可以使用一个Department对象:
Department department = new Department();
department.setDeptId(5);
填充占位符 query.setEntity(0, department)
Hibernate会自动按照关联关系中规定的主外键关系进行查询。
2.8 ORDER BY子句
使用ORDER BY子句可以进行排序
HQL FROM Employee e WHERE e.salary>:salaryParam ORDER BY e.salary DESC
和SQL一样,默认按照升序排列。DESC表示降序,ASC表示升序。
2.9 分页查询
分页查询是HQL的一大亮点,不必关心底层数据库的具体实现是什么,使用HQL调用固定的方法就能够实现垮数据库平台的分页查询。
在分页时我们需要指定两个最基本的数据,一个是当前页的页码:pageNo,一个是每页显示多少条数据:pageSize。
下表列出了Query接口中与分页相关的两个函数
函数名————作用
setFirstResult(int index)——指定查询结果从index位置开始取,index从0开始
setMaxResults(int maxResults)——指定查询结果取maxResults条数据
index和pageNo的关系是:index=(pageNo-1)*pageSize
具体操作方法是:
List<Employee> list = query.setFirstResult((pageNo - 1)*pageSize)
.setMaxResults(pageSize)
.list();
这里我们使用了连缀的方式调用Query对象的API,之所以能够实现连缀是因为每个方法的返回值都仍然是Query对象本身。
2.10 投影查询方式一
所谓投影查询其实就是仅查询实体类对象的部分字段,这里用到了HQL语句的SELECT关键词。
HQL SELECT e.empName,e.salary From Employee e WHERE e.salary>9000
那么此时的查询结果以什么形式返回呢?HQL并没有将使用空的Employee对象接收empName和salary的值,而是把它们放在了一个Object数组中。
Query query = session.createQuery(queryString);
List<Object[]> list = query.list();
for (Object[] objects : list) {
System. out.println(objects[0]+" "+objects[1]);
}
2.11 投影查询方式二
接收投影查询结果可以仍然使用实体类的对象,但要求实体类中提供对应的构造器。
HQL SELECT new Employee(e.empName,e.salary) From Employee e
这样得到的每一条数据都将被封装到Employee对象中。
2.12 多表查询
HQL支持使用外连接、内连接等方式进行连表查询,甚至支持使用FETCH关键字进行“迫切”连接查询。
①迫切左外连接
HQL From Department d LEFT JOIN FETCH d.empSet
这里使用LEFT JOIN表示进行“左外连接”查询,生成的SQL语句将查询关联的Employee类对应的全部数据,所以Employee的数据既然查询得到了,那么就应该将它们设置到Department对象的empSet属性中,否则这个已经执行了的操作就浪费了。而是否对empSet属性进行设置就看是否包含了FETCH关键字,包含就设置,不包含就不设置,这就是“迫切”的含义。
②迫切内连接
HQL From Department d INNER JOIN FETCH d.empSet
2.13 报表查询
和SQL一样,HQL也使用GROUP BY和HAVING子句配合起来进行分组,再结合统计函数进行报表查询。
HQL SELECT min(e.salary),max(e.salary)
From Employee e
GROUP BY e.department
HAVING min(e.salary)>3000
min()和max()统计函数的结果最终被放在了一个Object数组中。
2.14 子查询
子查询是SQL语句中非常重要的功能,它可以在SQL语句中利用另外一条SQL语句的查询结果。HQL同样对子查询功能提供了支持。与SQL子查询不同的是,HQL不支持在FROM子句中使用子查询。
例如:查询员工数量大于5的部门
HQL From Department d where (select count(emp) From d.empSet emp)>4
查询部门名以“A”开头的部门的员工姓名和部门名称
HQL select e.empName,e.department.deptName
from Employee e
where e.department in
(From Department d where d.deptName like 'A%')
2.15 删除数据
HQL Delete From Employee e WHERE e.empId=117
session.createQuery(queryString).executeUpdate()
2.16 更新数据
HQL UPDATE Employee e Set e.empName=’Tom’ WHERE e.empId=115
session.createQuery(queryString).executeUpdate()
3 QBC
3.1 概述
QBC查询就是通过使用Hibernate提供的Query By Criteria API来查询对象,这种API 封装了SQL语句的动态拼装,对查询提供了更加面向对象的功能接口。
3.2 创建Criteria对象
创建Criteria对象是进行QBC查询的第一步,类似于HQL查询中创建Query对象,只不过Criteria对象和Query对象的用法差别很大。
Criteria对象可以通过session对象的createCriteria()方法创建,创建时需要提供目标持久化类的Class类对象。
Criteria criteria = session.createCriteria(Employee.class);
3.3 取得查询结果
Criteria对象大体上有两种方式获取查询结果:list()方法和uniqueResult()方法。list()方法获取多条记录的查询结果,uniqueResult()方法获取单一记录的查询结果。
Criteria criteria = session.createCriteria(Employee.class);
List<Employee> list = criteria.list();
这个例子表示不带任何查询条件,查询全部Employee对象,并返回全部Employee对象组成的List集合。
3.4 封装查询条件
QBC查询最大的特点是可以将SQL语句中的查询条件以面向对象的方式封装起来,并根据封装好的查询条件返回结果。
封装查询条件要用到Restrictions类的各个静态方法。
方法名 示例 对应SQL
eq(String propertyName, Object value) eq(“salary”,5000.00); where salary=5000.00
like(String propertyName, Object value) like(“empName”,”%a%”); where emp_name like ‘%a%’
gt(String propertyName, Object value) gt(“salary”,300); where salary>300
lt(String propertyName, Object value) lt(“salary”,300); where salary<300
le(String propertyName, Object value) le(“salary”,300); where salary<=300
ge(String propertyName, Object value) ge(“salary”,300); where salary>=300
between(String propertyName, Object lo, Object hi) between(“salary”,300,500); where salary between 300 and 500
in(String propertyName, Object[] values) in(“salary”,[300,500,600]); where salary in (300,500,600)
封装查询条件之后,上述静态方法都会返回一个Criterion接口的实例,拿到这个Criterion对象后可以将其添加到Criteria对象中。例如:
Criterion like = Restrictions.like("empName","%a%");
Criterion gt = Restrictions.gt("salary",9000.00);
List<Employee> list = criteria
.add(like)
.add(gt)
.list();
显然add()方法也支持连缀调用。
3.5 使用AND和OR连接查询条件
在QBC API中使用Conjunction类表示AND,使用Disjunction表示OR,它们都可以连接Criterion对象生成AND或OR语句。
Criterion like = Restrictions. like("empName", "%a%");
Criterion gt = Restrictions. gt("salary", 9000.00);
//创建AND条件对象
Conjunction conjunction = Restrictions.conjunction();
//使用AND连接like和 gt
conjunction.add(like).add(gt);
List<Employee> list = session.createCriteria(Employee.class)
.add(conjunction)
.list();
Criterion like = Restrictions. like("empName", "%a%");
Criterion gt = Restrictions. gt("salary", 9000.00);
//创建OR条件对象
Disjunction disjunction = Restrictions.disjunction();
//使用OR对象将两个条件连接起来
disjunction.add(like).add(gt);
List<Employee> list = session.createCriteria(Employee.class)
.add( disjunction)
.list() ;
甚至AND和OR作为整体也可以连接在一起
Conjunction conjunction01 = Restrictions.conjunction();
Conjunction conjunction02 = Restrictions.conjunction();
Disjunction disjunction = Restrictions.disjunction();
disjunction.add(conjunction01).add(conjunction02);
3.6 报表查询
在QBC查询中可以使用Projections类的静态方法封装SQL中的统计函数。
AggregateProjection max = Projections.max("salary");
criteria.setProjection(max);
如果有多个统计函数需要执行,则创建ProjectionList对象,用于包含多个Projection对象
AggregateProjection max = Projections.max("salary");
AggregateProjection min = Projections.min("salary");
ProjectionList projectionList = Projections.projectionList();
projectionList.add(max).add(min);
List<Object[]> list = session.createCriteria(Employee.class)
.setProjection(projectionList)
.list();
可以使用groupProperty(String propertyName)方法对查询结果进行分组
Criteria criteria = session.createCriteria(Employee.class);
PropertyProjection groupProperty = Projections.groupProperty("department");
criteria.setProjection(groupProperty).list();
3.7 QBC排序
Order asc = Order.asc("salary");
Order desc = Order.desc("empName");
List<Employee> list = session.createCriteria(Employee.class)
.addOrder(asc)
.addOrder(desc)
.list();
3.8 QBC分页
QBC分页和HQL分页的操作大体上是一致的。
int pageNo = 2;
int pageSize = 5;
List<Employee> list = session.createCriteria(Employee.class)
.setFirstResult((pageNo - 1)*pageSize)
.setMaxResults(pageSize)
.list();
4 执行本地SQL
有些情况下我们需要直接执行原始的SQL语句,这时可以使用session对象创建SQLQuery对象,并调用list()或uniqueResult()方法返回查询结果,如果是执行增删改操作,可以调用executeUpdate()方法。
创建SQL语句
String sql = "SELECT `EMP_ID`,`emp_name`,`SALARY`,`birthday`,`TELEPHONE`,`dept_id_fk` FROM `emps` WHERE `EMP_ID`=8";
创建SQLQuery对象
SQLQuery query = session.createSQLQuery(sql);
返回查询结果
Object[] object = (Object[]) query.uniqueResult()