Hibernate 中的 Criteria 查询
1. 前言
今天给大家介绍一个绝对纯正的 OOP 查询方案:Criteria 查询。通过本节课程的内容,你将了解到:
- 什么是 Criteria 查询
- Criteria 实现复杂查询;
2. Criteria 查询
什么是 Criteria 查询?
Criteria 查询从字面翻译就是标准查询。所谓 标准查询,指的是 HIbernate 提供了纯正的 OOP API 查询方案。不像 HQL 还掺杂了一些 SQL 层面的内容。
来一个查询需求:查询所有的学生。
> 想必这学生会很生气,总是被搬来搬去的。
上实例之前,先认识 Hibernate 兄弟会中的一名新成员:Criteria。
在使用 Criteria 查询之前,必须先创建 Criteria 对象:
Criteria cr = session.createCriteria(Student.class);
List<Student> stus = cr.list();
是不是很 OOP。使用 HQL 时,会有一种时空穿越的感觉 ,OOP 和 SQL 语法交替出现,很容易犯晕。使用 Criteria 进行查询时则不会。
而且,Criteria 不是一个人在战斗,它也有属于自己的兄弟会,为开发者提供了更强有力的支持。
先介绍一下它的几个兄弟,并且它们的作用已经从字面告诉了你。
- Criterion: 这位兄弟长得好生面熟,其实它就 Criteria 的单数存在形式;
- Oder: 提供排序功能;
- Restrictions: 限制、约束的意思,和 SQL 中的 where 关键字的作用是一样。所以,它提供了很多类似于运算符的方法,可以对查询数据进行过滤。
Criteria 面子上很 OOP ,但是无论你怎么逃,都是在 SQL 的手掌心,也就是说 Criteria 查询最终还是会被 Hibernate 转译成 SQL 语句。
> 只要是使用关系型数据库,SQL 就是逃不掉的宿命。只是直接、间接使用的区别。
所以,Criteria 查询中总会找到 SQL 的影子。
2.1 基础查询
为了更好地理解它们,来一个实例:查询姓名叫 “Hibernate” 的学生。
Criteria criteria = session.createCriteria(Student.class);
Criterion criterion = Restrictions.eq("stuName", "Hibernate");
criteria.add(criterion);
Student student = (Student) criteria.uniqueResult();
System.out.println(student);
Criteria 查询封装了关系型数据库的概念,所以,一定要注意,使用方法进行数据过滤时,都是属性进行比较。
确认查询出来的数据只有一条记录时,可以使用 uniqueResult() 方法。条件查询的关键是了解 Restrictions,它所提供的很多类似于逻辑运算符的方法:
* **Restrictions.eq():** 相当于 =;
* **Restrictions.not(Exprission.eq()) :** 相当于 <>;
* **Restrictions.le():** 相当于 <=;
* **[Restrictions.gt](http://Restrictions.gt)():** 相当于 >;
* **[Restrictions.ge](http://Restrictions.ge)():** 相当于 >=;
* **[Restrictions.lt](http://Restrictions.lt)():** 相当于 <;
* **Restrictions.isnull():** 相当于 is null;
* **Restrictions.isNotNull():** 相当于 is not null ;
* **Restrictions.like():** 相当于 like;
* **Restrictions.and():** 相当于 and;
* **Restrictions.conjunction():** 相当于 and;
* **Restrictions.or():** 相当于 or;
* **Restrictions.disjunction() :** 相当于 or;
* **Restrictions.not():** 相当于 not;
* **[Restrictions.in](http://Restrictions.in)():** 相当于 in;
* **Restrictions.not([Restrictions.in](http://Restrictions.in)()):** 相当于 not in;
* **Restrictions.between():** 相当于 between x and y;
* **Restrictions.not(Restrictions…between()) :** 相当于 not between x and y。
如上方法,几乎涵盖了所有 SQL 条件运算符,任意组合上面方法,没有查询不出来的结果。
如查询学生编号是 1 或 2 或 4 的学生。
使用 SQL,则是:
select \* from student where stuId in (1,2,4)
使用 Criteria 查询,则如下所示:
Criterion criterion = Restrictions.in("stuId",new Integer[] {1,2,4} );
criteria.add(criterion);
如查询学生编号大于 2 且班级编号为 1 的学生。
使用 SQL:
select \* from student where stuId>2 and classRommId=1
如果使用 Criteria 查询,则先构建两个约束对象:
Criterion criterion = Restrictions.gt("stuId", 2);
Criterion criterion1 = Restrictions.eqOrIsNull("classRoom.classRoomId", 1);
再把这两个约束作为参数,构建一条联合约束:
LogicalExpression logicalExpression = Restrictions.and(criterion, criterion1);
criteria.add(logicalExpression);
LogicalExpression API 用来表示一个逻辑表达式。是 Criterion 的子类。
比较原生 SQL 和 Criteria 查询,会发现原生 SQL 语句要简单很多,使用 Criteria 查询需要掌握很多 API,而且代码量也比较大,这也可能是 Criteria 查询得不到普及的原因吧。
但是,Hibernate 既然推出了这种查询方案,想必也有它的考虑。比如说,创建动态查询语句,这点原生 SQL 或 HQL 都没有 Criteria 好。
还是那句话,存在就是合理的。
如果,你对原生 SQL 有情怀,Criteria 查询中也是可以用的。
criteria.add( Restrictions.sqlRestriction("stuId>2 and clasRoomId=1"));
注意,不要在 Sql 片段中使用 where 关键字。既然是原生 SQL,所以语句中是字段概念,而不是属性概念。
前面讲解 HQL 时,提到了分页查询。Criteria 一样可以实现分页查询,和 HQL 中分页方法一样:
Criteria criteria = session.createCriteria(Student.class);
criteria.setFirstResult(1);
criteria.setMaxResults(5);
List results = criteria.list();
2.2 高级查询
排序查询使用 order API 实现:
criteria.addOrder(Order.desc("stuId"));
criteria.addOrder(Order.asc("stuName"));
一样,可以多字段排序。
使用聚合函数:聚合函数的功能封装在 projections API 中:
criteria.setProjection(Projections.rowCount());
criteria.setProjection(Projections.avg("stuId"));
criteria.setProjection(Projections.max("stuId"));
criteria.setProjection(Projections.min("stuId"));
criteria.setProjection(Projections.sum("stuId"));
Criteria 也能实现关联查询:
Criteria criteria = session.createCriteria(Student.class);
criteria.add(Restrictions.like("stuName", "Hibernate%"));
Criteria criteria01 = criteria.createCriteria("classRoom");
criteria01.add(Restrictions.like("classRoomName", "c19%"));
List<Student> students = criteria.list();
可以把一个 Criteria 实例看成对一张表的查询,如果需要关联多张表,则可以通过一个 Criteria 再创建一个 Criteria 对象。
Hibernate 为 Criteria 查询提供各种各样的 API,适应于任何查询需求,相比较使用的已经很普遍的 SQL 查询,Criteria 查询充满了鸡肋的味道。但对于动态查询需求,Criteria 查询的优势又很明显。
3. 原生 SQL 查询
Hibernate 支持原生 SQL 查询,对于熟悉并钟情于 SQL 语句的开发者来讲,是一个很大的福音。
实例:
String sql="select \* from student";
SQLQuery sqlQuery= session.createSQLQuery(sql);
Hibernate 提供了一个与原生 SQL 有关的 SQLQuery 对象。SQLQuery 是 Query 的子类,可适应不同的原生 SQL 语句查询。
4. 小结
本节课和大家聊到了 Criteria 查询,HQL 查询,原生 SQL 查询。原生 SQL 查询无所不能,HQL 查询是面向对象的 SQL ,具有混血身份。据说,混血的总是很美,也是建议大家选择的一种查询方案。
Criteria 查询是 Hibernate 提供的一种纯面向对象的解决方案,但是,为了构建一条 SQL 语句需要写许多代码,其应用领域会相对较窄。
本节课给大家介绍 Criteria 的目的,一是扩展学生的学习范围,如果需要使用动态查询,Criteria 则有着无可比拟的优越感。