Hiberate 条件查询示例

Hiberate 条件查询示例

原文: https://howtodoinjava.com/hibernate/hibernate-criteria-queries-tutorial/

Hiberate 提供了三种不同的方法来从数据库检索数据。 我们已经讨论过 HQL 和本机 SQL 查询。 现在,我们将讨论我们的第三个选项,即 Hiberate 条件查询。 使用条件查询 API,您可以使用 Java 构建嵌套的结构化查询表达式,从而提供了 HQL 或 SQL 等查询语言无法进行的编译时语法检查。

条件 API 还包括示例查询(QBE)功能。 这样,您就可以提供示例对象,这些对象包含要检索的属性,而不必逐步说明查询的组件。 它还包括包括count()的投影和聚合方法。 让我们详细探讨它的不同功能。

Table of Contents

1\. Hibernate criteria example
2\. Hibernate criteria - using Restrictions
3\. Hibernate criteria - paging through the result set
4\. Hibernate criteria - Obtain unique result
5\. Hibernate criteria - obtaining distinct results
6\. Hibernate criteria - sort query results
7\. Hibernate criteria - perform associations (joins)
8\. Hibernate criteria - add projections 
9\. Hibernate criteria - query by example (QBE)
10\. Summary

1. Hiberate 条件示例

条件 API 允许您以编程方式构建条件查询对象; org.hibernate.Criteria接口定义这些对象之一的可用方法。 Hiberate Session接口包含几种重载的createCriteria()方法。

将持久化对象的类或其实体名称传递给createCriteria()方法,然后 hibernate 将创建一个Criteria对象,当您的应用执行条件查询时,该对象将返回持久化对象的类的实例。

条件查询的最简单示例是没有可选参数或限制的条件查询 - 条件查询将仅返回与该类相对应的每个对象。

Criteria crit = session.createCriteria(Product.class);
List<Product> results = crit.list();

从这个简单的条件示例继续,我们将向我们的条件查询添加约束,以便我们可以缩减结果集。

2. Hiberate 条件 – 使用限制

使用条件 API,您可以轻松地在查询中使用限制来有选择地检索对象。 例如,您的应用只能检索价格超过 30 美元的产品。 您可以使用add()方法将这些限制添加到Criteria对象。 add()方法采用代表单个限制的org.hibernate.criterion.Criterion对象。 一个条件查询可以有多个限制。

2.1 Restrictions.eq()示例

要检索属性值“等于”你的限制的对象,请使用Restrictions上的eq()方法,如下所示:

Criteria crit = session.createCriteria(Product.class);
crit.add(Restrictions.eq("description","Mouse"));
List<Product> results = crit.list()

上面的查询将搜索所有描述为“Mouse”的产品。

2.2 Restrictions.ne()示例

要检索具有属性值“不等于”你的限制的对象,请使用Restrictions上的ne()方法,如下所示:

Criteria crit = session.createCriteria(Product.class);
crit.add(Restrictions.ne("description","Mouse"));
List<Product> results = crit.list()

上面的查询将搜索所有具有描述的产品,但不包含“Mouse”。

您不能使用不相等的限制来检索该属性在数据库中具有NULL值的记录(在 SQL 中,因此在 Hibernate 中,NULL表示缺少数据,因此无法与数据进行比较)。 如果需要检索具有NULL属性的对象,则必须使用isNull()限制。

2.3 Restrictions.like()Restrictions.ilike()示例

除了搜索完全匹配之外,我们还可以检索具有与给定模式的一部分属性匹配的所有对象。 为此,我们需要使用like()ilike()方法创建一个 SQL LIKE子句。 ilike()方法不区分大小写。

Criteria crit = session.createCriteria(Product.class);
crit.add(Restrictions.like("name","Mou%",MatchMode.ANYWHERE));
List<Product> results = crit.list();

上面的示例使用org.hibernate.criterion.MatchMode对象指定如何将指定的值与存储的数据进行匹配。 MatchMode对象(类型安全的枚举)具有四个不同的匹配项:

  • ANYWHERE:字符串的任意位置
  • END:字符串的末尾
  • EXACT:完全匹配
  • START:字符串的开头

2.4 Restrictions.isNull()Restrictions.isNotNull()示例

isNull()isNotNull()限制使您可以搜索具有(或不具有)空属性值的对象。

Criteria crit = session.createCriteria(Product.class);
crit.add(Restrictions.isNull("name"));
List<Product> results = crit.list();

2.5 Restrictions.gt()Restrictions.ge()Restrictions.lt()Restrictions.le()示例

一些限制对于进行数学比较很有用。 大于比较为gt(),大于或等于比较为ge(),小于比较为lt(),小于或等于比较为le()。 我们可以使用 Java 的类型提升来快速检索价格超过 25 美元的所有此类产品,以转换为Double

Criteria crit = session.createCriteria(Product.class);
crit.add(Restrictions.gt("price", 25.0));
List<Product> results = crit.list();

2.6 结合两个或更多个条件示例

继续,我们可以开始使用条件 API 进行更复杂的查询。 例如,我们可以在逻辑表达式中组合 AND 和 OR 限制。 当我们向条件查询添加多个约束时,它将被解释为 AND,如下所示:

Criteria crit = session.createCriteria(Product.class);
crit.add(Restrictions.lt("price",10.0));
crit.add(Restrictions.ilike("description","mouse", MatchMode.ANYWHERE));
List<Product> results = crit.list();

如果我们希望有两个限制来返回满足两个或两个限制的对象,则需要在Restrictions类上使用or()方法,如下所示:

Criteria crit = session.createCriteria(Product.class);
Criterion priceLessThan = Restrictions.lt("price", 10.0);
Criterion mouse = Restrictions.ilike("description", "mouse", MatchMode.ANYWHERE);
LogicalExpression orExp = Restrictions.or(priceLessThan, mouse);
crit.add(orExp);
List results=crit.list();

我们在此处创建的orExp逻辑表达式将被视为任何其他条件。 因此,我们可以对条件添加另一个限制:

Criteria crit = session.createCriteria(Product.class);
Criterion price = Restrictions.gt("price",new Double(25.0));
Criterion name = Restrictions.like("name","Mou%");
LogicalExpression orExp = Restrictions.or(price,name);
crit.add(orExp);
crit.add(Restrictions.ilike("description","blocks%"));
List results = crit.list();

2.7 将析取对象与条件一起使用

如果我们要创建一个具有两个以上不同条件的 OR 表达式(例如,“price > 25.0 OR name like Mou% OR description not like blocks%”),则可以使用org.hibernate.criterion.Disjunction对象来表示析取。

您可以从Restrictions类的disjunction()工厂方法获取此对象。 分离比在代码中构建 OR 表达式树更方便。 要表示具有两个以上条件的 AND 表达式,可以使用conjunction()方法,尽管可以轻松地将它们添加到Criteria对象中。 与在代码中构建 AND 表达式树相比,该连接更方便。 这是一个使用析取的示例:

Criteria crit = session.createCriteria(Product.class);
Criterion priceLessThan = Restrictions.lt("price", 10.0);
Criterion mouse = Restrictions.ilike("description", "mouse", MatchMode.ANYWHERE);
Criterion browser = Restrictions.ilike("description", "browser", MatchMode.ANYWHERE);
Disjunction disjunction = Restrictions.disjunction();
disjunction.add(priceLessThan);
disjunction.add(mouse);
disjunction.add(browser);
crit.add(disjunction);
List results = crit.list();

2.8 Restrictions.sqlRestriction()示例

sqlRestriction()限制使您可以在条件 API 中直接指定 SQL。 如果您需要使用 Hibernate 通过条件 API 不支持的 SQL 子句,这将非常有用。

您的应用代码无需知道您的类使用的表的名称。 使用{alias}来表示类的表,如下所示:

Criteria crit = session.createCriteria(Product.class);
crit.add(Restrictions.sqlRestriction("{alias}.description like 'Mou%'"));
List<Product> results = crit.list();

3. Hiberate 条件 – 对结果集分页

条件可以解决的一种常见应用模式是通过数据库查询的结果集进行分页。 与查询一样,Criteria接口上有两种分页方法:setFirstResult()setMaxResults()setFirstResult()方法采用一个代表结果集中第一行的整数,从第 0 行开始。您可以使用setMaxResults()方法告诉 Hibernate 检索固定数量的对象。 结合使用这两者,我们可以在 Web 或 Swing 应用中构造一个分页组件。

Criteria crit = session.createCriteria(Product.class);
crit.setFirstResult(1);
crit.setMaxResults(20);
List<Product> results = crit.list();

如您所见,这使得分页结果集变得容易。 您可以增加返回的第一个结果(例如,从 1 到 21,再到 41 等)以翻页结果集。

4. Hiberate 条件 – 获得独特的结果

有时您知道您将从给定查询中仅返回零或一个对象。 这可能是因为您正在计算汇总,或者是因为您的限制自然会导致唯一的结果。 如果要获取单个Object引用而不是List,则Criteria对象上的uniqueResult()方法将返回一个对象或null。 如果结果不止一个,则uniqueResult()方法将抛出HibernateException

以下简短示例演示了一个结果集,该结果集将包含多个结果,但该结果集受setMaxResults()方法限制:

Criteria crit = session.createCriteria(Product.class);
Criterion price = Restrictions.gt("price",new Double(25.0));
crit.setMaxResults(1);
Product product = (Product) crit.uniqueResult();

同样,请注意,如果使用uniqueResult()方法,则需要确保查询仅返回一或零个结果。 否则,Hibernate 将抛出NonUniqueResultException异常。

5. Hiberate 条件 – 获得不同的结果

如果您想使用条件查询的不同结果,则 Hibernate 提供了针对不同实体的结果转换器org.hibernate.transform.DistinctRootEntityResultTransformer,以确保查询的结果集中不会出现重复项。 不同于使用 SQL 的SELECT DISTINCT,不同的结果转换器使用其默认的hashCode()方法比较每个结果,并且仅将具有唯一哈希码的结果添加到结果集中。 这可能是或可能不是您期望从其他等效的 SQL DISTINCT 查询中得到的结果,因此对此要小心。

Criteria crit = session.createCriteria(Product.class);
Criterion price = Restrictions.gt("price",new Double(25.0));
crit.setResultTransformer( DistinctRootEntityResultTransformer.INSTANCE )
List<Product> results = crit.list();

另一个性能说明:比较是在 Hibernate 的 Java 代码中完成的,而不是在数据库中完成的,因此非唯一的结果仍将通过网络传输。

6. Hiberate 条件 – 对查询结果进行排序

使用条件对查询结果进行排序的方式与使用 HQL 或 SQL 进行排序的方式几乎相同。 条件 API 提供了org.hibernate.criterion.Order类,可以根据对象的属性之一将结果集按升序或降序排序。

此示例演示如何使用Order类:

Criteria crit = session.createCriteria(Product.class);
crit.add(Restrictions.gt("price",10.0));
crit.addOrder(Order.desc("price"));
List<Product> results = crit.list();

您可以向Criteria对象添加多个Order对象。 Hibernate 会将它们传递给基础 SQL 查询。 您的结果将按第一顺序排序,然后第一排序中的所有相同匹配项将按第二顺序排序,依此类推。 在幕后, Hibernate 在将属性替换为适当的数据库列名称之后,将其传递给 SQL ORDER BY子句。

7. Hiberate 条件 – 执行关联(连接)

一对多或从多对一连接时,关联有效。 首先,我们将演示如何使用一对多关联来获取价格超过 25 美元的供应商。 请注意,我们为产品属性创建了一个新的Criteria对象,为我们刚刚创建的产品条件添加了限制,然后从供应商Criteria对象获取结果:

Criteria crit = session.createCriteria(Supplier.class);
Criteria prdCrit = crit.createCriteria("products");
prdCrit.add(Restrictions.gt("price",25.0));
List results = crit.list();

换句话说,我们使用多对一关联从供应商 MegaInc 获取所有产品:

Criteria crit = session.createCriteria(Product.class);
Criteria suppCrit = crit.createCriteria("supplier");
suppCrit.add(Restrictions.eq("name","Hardware Are We"));
List results = crit.list();

8. Hiberate 条件 – 添加预测和汇总

您可以将结果集中的结果视为一组行和列,也称为数据投影,而不是使用结果集中的对象。 这类似于您将通过 JDBC 使用SELECT查询中的数据的方式类似。

要使用投影,请从org.hibernate.criterion.Projections工厂类中获取所需的org.hibernate.criterion.Projection对象。 Projections类与Restrictions类相似,因为它提供了几种用于获取Projection实例的静态工厂方法。 获得Projection对象后,使用setProjection()方法将其添加到Criteria对象中。 执行Criteria对象时,该列表包含可以转换为适当类型的对象引用。

8.1 单个聚合(获取行数)

Criteria crit = session.createCriteria(Product.class);
crit.setProjection(Projections.rowCount());
List<Long> results = crit.list();

通过Projections工厂类可用的其他聚合函数包括:

  1. avg(String propertyName):给出属性值的平均值
  2. count(String propertyName):计算属性发生的次数
  3. countDistinct(String propertyName):计算属性包含的唯一值的数量
  4. max(String propertyName):计算属性值的最大值
  5. min(String propertyName):计算属性值的最小值
  6. sum(String propertyName):计算属性值的总和

8.2 多个集合

我们可以将多个投影应用于给定的Criteria对象。 若要添加多个投影,请从Projections类的projectionList()方法中获取一个投影列表。 org.hibernate.criterion.ProjectionList对象具有采用Projection对象的add()方法。 您可以将投影列表传递给Criteria对象上的setProjection()方法,因为ProjectionList实现了Projection接口。

Criteria crit = session.createCriteria(Product.class);
ProjectionList projList = Projections.projectionList();
projList.add(Projections.max("price"));
projList.add(Projections.min("price"));
projList.add(Projections.avg("price"));
projList.add(Projections.countDistinct("description"));
crit.setProjection(projList);
List<object[]> results = crit.list();

8.3 获取选定列

投影的另一个用途是检索单个属性,而不是实体。 例如,我们可以仅从产品表中检索名称和描述,而不是将整个对象表示加载到内存中。

Criteria crit = session.createCriteria(Product.class);
ProjectionList projList = Projections.projectionList();
projList.add(Projections.property("name"));
projList.add(Projections.property("description"));
crit.setProjection(projList);
crit.addOrder(Order.asc("price"));
List<object[]> results = crit.list();

9. Hiberate 条件 – 示例查询(QBE)

在 QBE 中,您可以部分填充该对象的实例,而不必以编程方式使用Criterion对象和逻辑表达式构建Criteria对象。 您可以将此实例用作模板,并让 Hibernate 根据其值为您构建条件。 这可以使您的代码保持整洁,并使您的项目更易于测试。

例如,如果我们有一个用户数据库,则可以构造一个用户对象的实例,设置类型和创建日期的属性值,然后使用条件 API 运行 QBE 查询。 Hibernate 将返回一个结果集,其中包含与设置的属性值匹配的所有用户对象。 在后台,Hibernate 检查Example对象,并构造一个与Example对象的属性相对应的 SQL 片段。

下面的基本示例搜索与示例Supplier对象上的名称匹配的供应商:

Criteria crit = session.createCriteria(Supplier.class);
Supplier supplier = new Supplier();
supplier.setName("MegaInc");
crit.add(Example.create(supplier));
List results = crit.list();

10. 总结

使用条件 API 是开始使用 HQL 开发的绝佳方法。 Hibernate 的开发人员提供了一个干净的 API,用于为使用 Java 对象的查询添加限制。 尽管 HQL 不太难学,但是一些开发人员更喜欢条件查询 API,因为它提供了编译时语法检查,尽管直到运行时才能检查列名和其他与模式相关的信息。

学习愉快!

阅读更多:

Hiberate 文档