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 时,会有一种时空穿越的感觉 ,OOPSQL 语法交替出现,很容易犯晕。使用 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(): 相当于 >;
  • Restrictions.ge(): 相当于 >=;
  • 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(): 相当于 in;
  • Restrictions.not(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 的子类。

比较原生 SQLCriteria 查询,会发现原生 SQL 语句要简单很多,使用 Criteria 查询需要掌握很多 API,而且代码量也比较大,这也可能是 Criteria 查询得不到普及的原因吧。

但是,Hibernate 既然推出了这种查询方案,想必也有它的考虑。比如说,创建动态查询语句,这点原生 SQLHQL 都没有 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 对象。

HibernateCriteria 查询提供各种各样的 API,适应于任何查询需求,相比较使用的已经很普遍的 SQL 查询,Criteria 查询充满了鸡肋的味道。但对于动态查询需求,Criteria 查询的优势又很明显。

3. 原生 SQL 查询

Hibernate 支持原生 SQL 查询,对于熟悉并钟情于 SQL 语句的开发者来讲,是一个很大的福音。
实例:

String sql="select * from student";
SQLQuery sqlQuery= session.createSQLQuery(sql);

Hibernate 提供了一个与原生 SQL 有关的 SQLQuery 对象。SQLQueryQuery 的子类,可适应不同的原生 SQL 语句查询。

4. 小结

本节课和大家聊到了 Criteria 查询,HQL 查询,原生 SQL 查询。原生 SQL 查询无所不能,HQL 查询是面向对象的 SQL ,具有混血身份。据说,混血的总是很美,也是建议大家选择的一种查询方案。

Criteria 查询是 Hibernate 提供的一种纯面向对象的解决方案,但是,为了构建一条 SQL 语句需要写许多代码,其应用领域会相对较窄。

本节课给大家介绍 Criteria 的目的,一是扩展学生的学习范围,如果需要使用动态查询,Criteria 则有着无可比拟的优越感。