JPA的Criteria查询

1. 概述

在本教程中,我们将讨论一个非常有用的JPA功能 - Criteria Queries。

它不仅使我们能够在不执行原始SQL的情况下编写查询,而且还为我们提供了一些对查询的面向对象控制,这是Hibernate的主要特性之一。Criteria API允许我们以编程方式构建条件查询对象,我们可以在其中应用不同类型的过滤规则和逻辑条件。

从Hibernate 5.2开始,不推荐使用Hibernate Criteria API,新开发的重点是JPA Criteria API。我们将探索如何使用Hibernate和JPA来构建Criteria Queries。

2. Maven依赖

为了说明API,我们将使用参考JPA实现 - Hibernate。

要使用Hibernate,请确保将最新版本添加到pom.xml文件中:

1
2
3
4
5
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.3.2.Final</version>
</dependency>

可以在这里找到最新版本的Hibernate。

3. 使用 Criteria 的简单示例

让我们首先看一下如何使用Criteria查询检索数据。我们将看看如何从数据库中获取特定类的所有实例。

我们有一个Item类,它表示数据库中的元组“ITEM”

1
2
3
4
5
6
7
8
9
public class Item implements Serializable {

private Integer itemId;
private String itemName;
private String itemDescription;
private Integer itemPrice;

// standard setters and getters
}

让我们看一个简单的条件查询,它将从数据库中检索“ ITEM”的所有行:

1
2
3
4
5
6
7
8
Session session = HibernateUtil.getHibernateSession();
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<Item> cr = cb.createQuery(Item.class);
Root<Item> root = cr.from(Item.class);
cr.select(root);

Query<Item> query = session.createQuery(cr);
List<Item> results = query.getResultList();

以上查询是如何获取所有项目的简单演示。让我们一步一步看看做了些什么:

  1. SessionFactory对象创建Session实例
  2. 通过调用getCriteriaBuilder()方法创建C riteriaBuilder的实例
  3. 通过调用 CriteriaBuilder createQuery()方法创建CriteriaQuery实例
  4. 通过调用 Session createQuery()方法创建Query实例
  5. 调用查询对象的getResultList()方法,该方法为我们提供结果

现在我们已经介绍了基础知识,让我们继续讨论条件查询的一些功能。

3.1 使用表达式

CriteriaBuilder可用于限制基于特定条件的查询结果。 通过使用 CriteriaQuery where()方法并提供CriteriaBuilder 创建的 表达式

以下是常用表达式的一些示例:

要获得价格超过1000的商品:

1
cr.select(root).where(cb.gt(root.get("itemPrice"), 1000));

接下来,获取itemPrice小于1000的项目:

1
cr.select(root).where(cb.lt(root.get("itemPrice"), 1000));

有项目itemNames中含有 chair

1
cr.select(root).where(cb.like(root.get("itemName"), "%chair%"));

记录itemPrice在100到200之间:

1
cr.select(root).where(cb.between(root.get("itemPrice"), 100, 200));

要检查给定属性是否为null:

1
cr.select(root).where(cb.isNull(root.get("itemDescription")));

要检查给定属性是否为null:

1
cr.select(root).where(cb.isNotNull(root.get("itemDescription")));

您还可以使用方法isEmpty()isNotEmpty()来测试类中的List是否为空。

现在问题不可避免地出现了,我们是否可以将上述两种或两种以上的比较结合起来。答案当然是肯定的 - Criteria API允许我们轻松地链接表达式

1
2
3
4
Predicate[] predicates = new Predicate[2];
predicates[0] = cb.isNull(root.get("itemDescription"));
predicates[1] = cb.like(root.get("itemName"), "chair%");
cr.select(root).where(predicates);

要使用逻辑运算添加两个表达式:

1
2
Predicate greaterThanPrice = cb.gt(root.get("itemPrice"), 1000);
Predicate chairItems = cb.like(root.get("itemName"), "Chair%");

具有上述定义条件的项目与Logical OR连接

1
cr.select(root).where(cb.or(greaterThanPrice, chairItems));

要获取与使用Logical AND连接的上述条件匹配的项目:

1
cr.select(root).where(cb.and(greaterThanPrice, chairItems));

3.2 排序

现在我们已经了解了Criteria的基本用法,让我们来看看Criteria的排序功能。

在下面的示例中,我们按名称的升序排列列表,然后按价格的降序排列:

1
2
3
cr.orderBy(
cb.asc(root.get("itemName")),
cb.desc(root.get("itemPrice")));

在下一节中,我们将了解如何进行聚合函数。

3.3 投影、聚合和分组功能

到目前为止,我们已经涵盖了大部分基本主题。现在让我们看看不同的聚合函数:

行数:

1
2
3
4
5
CriteriaQuery<Long> cr = cb.createQuery(Long.class);
Root<Item> root = cr.from(Item.class);
cr.select(cb.count(root));
Query<Long> query = session.createQuery(cr);
List<Long> itemProjected = query.getResultList();

以下是聚合函数的示例:

平均 的聚合函数:

1
2
3
4
5
CriteriaQuery<Double> cr = cb.createQuery(Double.class);
Root<Item> root = cr.from(Item.class);
cr.select(cb.avg(root.get("itemPrice")));
Query<Double> query = session.createQuery(cr);
List avgItemPriceList = query.getResultList();

其他有用的聚合方法是sum()max()min()count() 等。

3.4 CriteriaUpdate

从JPA 2.1开始,支持使用Criteria API 执行数据库更新。

CriteriaUpdate有一个set()方法,可用于为数据库记录提供新值:

1
2
3
4
5
6
7
8
CriteriaUpdate<Item> criteriaUpdate = cb.createCriteriaUpdate(Item.class);
Root<Item> root = criteriaUpdate.from(Item.class);
criteriaUpdate.set("itemPrice", newPrice);
criteriaUpdate.where(cb.equal(root.get("itemPrice"), oldPrice));

Transaction transaction = session.beginTransaction();
session.createQuery(criteriaUpdate).executeUpdate();
transaction.commit();

在上面的代码片段中,我们从 CriteriaBuilder创建了CriteriaUpdate 的实例, 并使用其set() 方法为itemPrice提供新值 要更新多个属性,我们只需要多次调用 set() 方法。

3.5 CriteriaDelete

CriteriaDelete,顾名思义,使用Criteria API 启用删除操作。我们所需要的只是创建一个CriteriaDelete实例 并使用 where() 方法来应用限制:

1
2
3
4
5
6
7
CriteriaDelete<Item> criteriaDelete = cb.createCriteriaDelete(Item.class);
Root<Item> root = criteriaDelete.from(Item.class);
criteriaDelete.where(cb.greaterThan(root.get("itemPrice"), targetPrice));

Transaction transaction = session.beginTransaction();
session.createQuery(criteriaDelete).executeUpdate();
transaction.commit();

4. 相比HQL的优势

在前面的部分中,我们介绍了如何使用Criteria Queries。

显然,Criteria查询优于HQL的主要和最强硬的优势是漂亮,干净,面向对象的API。

与普通的HQL相比,我们可以简单地编写更灵活的动态查询。逻辑可以通过IDE重构,并具有Java语言本身的所有类型安全优势。

当然也存在一些缺点,特别是在更复杂的连接处。

因此,一般来说,我们将不得不使用最好的工具 - 在大多数情况下可以使用Criteria API,但肯定有一些情况我们必须降低水平。