第5章 检索方式
Hibernate提供了以下几种检索对象的方式
●OID检索方式:按照对象的OID来检索对象get/load
●对象图导航检索方式:根据已经加载的对象导航到其他对象
order.getCustomer().getCustomerName();
●HQL检索方式:使用面向对象的HQL(Hibernate Query Language)查询语言
Query
●QBC检索方式:使用QBC(Query By Criteria) API来检索对象。这种API封装了基于字符串形式的查询语句,提供了更加面向对象的查询接口。
●本地SQL检索方式:使用本地数据库的SQL查询语句。
SQLQuery extends Query
HQL(Hibernate Query Language)是面向对象的查询语言,它和SQL查询语言有些相似。在Hibernate提供的各种检索方式中,HQL是使用最广的一种检索方式。它有如下特点:
在SQL中,FROM子句是用于指定要查询的数据库表,在HQL中,替换为与数据库表对应的Java实体类即可。
例如:
SQL |
SELECT * FROM EMPS |
HQL |
FROM Employee |
此时HQL语句将查询数据库表中的所有字段,并自动注入到对应实体类对象的对应属性中。
同时需要说明的是:HQL语句中FROM等关键字不区分大小写,但Java类的类名严格区分大小写。
在Hibernate中,HQL语句由Query对象执行,Query对象可以通过Session对象获取。
Query对象大体上有两种方式获取查询结果:list()方法和uniqueResult()方法。list()方法获取多条记录的查询结果,uniqueResult()方法获取单一记录的查询结果。
//1.通过session对象创建Query对象 String hql = "FROM Employee"; Query query = session.createQuery(hql);
//2.调用Query对象的方法获取查询结果 //结果集中有多条记录:list() //结果集中只有一条记录:uniqueResult() List for (Employee employee : list) { System. out.println(employee); }
|
和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对象的引用。
将上例中的具体值使用占位符“?”代替,并调用Query对象的setXxx()方法按照参数的不同类型动态填充即可,需要注意的是和JDBC中的PreparedStatement接口不同,这里占位符的索引从0开始。
HQL |
FROM Employee e WHERE e.salary>? |
填充占位符 |
query.setDouble(0, 8000.00); |
在HQL中不但能够使用基于位置的占位符参数,还能够使用基于名称的具名参数,使用具名参数的好处是不必关心当前参数的索引值。
具名参数的格式是:“:参数名称”。
例如:
HQL |
FROM Employee e WHERE e.salary>:salaryParam |
填充占位符 |
query.setDouble( "salaryParam", 9000); |
对于已经通过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会自动按照关联关系中规定的主外键关系进行查询。
使用ORDER BY子句可以进行排序
HQL |
FROM Employee e WHERE e.salary>:salaryParam ORDER BY e.salary DESC |
和SQL一样,默认按照升序排列。DESC表示降序,ASC表示升序。
分页查询是HQL的一大亮点,不必关心底层数据库的具体实现是什么,使用HQL调用固定的方法就能够实现垮数据库平台的分页查询。
在分页时我们需要指定两个最基本的数据,一个是当前页的页码:pageNo,一个是每页显示多少条数据:pageSize。
下表列出了Query接口中与分页相关的两个函数
函数名 |
作用 |
setFirstResult(int index) |
指定查询结果从index位置开始取,index从0开始 |
setMaxResults(int maxResults) |
指定查询结果取maxResults条数据 |
index和pageNo的关系是:index=(pageNo-1)*pageSize(pageNo:当前页的页码; pageSize:每页显示数据的数量)
具体操作方法是:
List .setMaxResults(pageSize) .list(); |
这里我们使用了连缀的方式调用Query对象的API,之所以能够实现连缀是因为每个方法的返回值都仍然是Query对象本身。
所谓投影查询其实就是仅查询实体类对象的部分字段,这里用到了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 for (Object[] objects : list) { System. out.println(objects[0]+" "+objects[1]); } |
接收投影查询结果可以仍然使用实体类的对象,但要求实体类中提供对应的构造器。
HQL |
SELECT new Employee(e.empName,e.salary) From Employee e |
这样得到的每一条数据都将被封装到Employee对象中。
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 |
使用distinct去重
String hql = “select distinct d From Department d left join fetch d.empSet”;
和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数组中。
子查询是SQL语句中非常重要的功能,它可以在SQL语句中利用另外一条SQL语句的查询结果。HQL同样对子查询功能提供了支持。与SQL子查询不同的是,HQL不支持在FROM子句中使用子查询。
例如:查询员工数量大于5的部门
HQL |
From Department d where (select count(empSet) From d.empSet empSet)>5 |
查询部门名以“A”开头的部门的员工姓名和部门名称
HQL |
select e.empName,e.department.deptName from Employee e where e.department in (From Department d where d.deptName like 'A%') |
HQL |
Delete From Employee e WHERE e.empId=117 |
session.createQuery(queryString).executeUpdate() |
HQL |
UPDATE Employee e Set e.empName=’Tom’ WHERE e.empId=115 |
session.createQuery(queryString).executeUpdate() |
QBC查询就是通过使用Hibernate提供的Query By Criteria API来查询对象,这种API 封装了SQL语句的动态拼装,对查询提供了更加面向对象的功能接口。
创建Criteria对象是进行QBC查询的第一步,类似于HQL查询中创建Query对象,只不过Criteria对象和Query对象的用法差别很大。
Criteria对象可以通过session对象的createCriteria()方法创建,创建时需要提供目标持久化类的Class类对象(即目标持久化类的运行时类)。
Criteria criteria = session.createCriteria(Employee.class); |
Criteria对象大体上有两种方式获取查询结果:list()方法和uniqueResult()方法。list()方法获取多条记录的查询结果,uniqueResult()方法获取单一记录的查询结果。
Criteria criteria = session.createCriteria(Employee.class); List |
这个例子表示不带任何查询条件,查询全部Employee对象,并返回全部Employee对象组成的List集合。
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 .add(like) .add(gt) .list(); |
显然add()方法也支持连缀调用。
在QBC API中使用Conjunction类表示AND,使用Disjunction表示OR,它们都可以连接Criterion对象生成AND或OR语句。
Conjunction:AND连接,结合
Disjunction: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 .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 .add( disjunction) .list() ;
|
甚至AND和OR作为整体也可以连接在一起
Conjunction conjunction01 = Restrictions.conjunction(); |
Conjunction conjunction02 = Restrictions.conjunction(); |
Disjunction disjunction = Restrictions.disjunction(); |
disjunction.add(conjunction01).add(conjunction02); |
在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 .setProjection(projectionList) .list(); |
可以使用groupProperty(String propertyName)方法对查询结果进行分组
Criteria criteria = session.createCriteria(Employee.class);
PropertyProjection groupProperty = Projections.groupProperty("department");
criteria.setProjection(groupProperty).list(); |
Order asc = Order.asc("salary"); Order desc = Order.desc("empName"); |
List .addOrder(asc) .addOrder(desc) .list(); |
QBC分页和HQL分页的操作大体上是一致的
int pageNo = 2; int pageSize = 5;
List .setFirstResult((pageNo - 1)*pageSize) .setMaxResults(pageSize) .list(); |
有些情况下我们需要直接执行原始的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() |