Hibernate 排序简介
1. 概述
本文说明了如何使用 Hibernate 查询语言 (HQL) 和 Criteria API 进行排序。
2. 使用 HQL 排序
使用 Hibernate 的 HQL 进行排序就像在 HQL 查询字符串中添加Order By子句一样简单:
String hql = "FROM Foo f ORDER BY f.name";
Query query = sess.createQuery(hql);
这段代码执行后,Hibernate 会生成如下 SQL 查询:
Hibernate: select foo0_.ID as ID1_0_, foo0_.NAME as NAME2_0_ from
FOO foo0_ order by foo0_.NAME
默认排序顺序方向是升序。这就是生成的 SQL 查询中不包含顺序条件asc的原因。
2.1.使用显式排序顺序
要手动指定排序顺序——您需要在HQL查询字符串中包含顺序方向:
String hql = "FROM Foo f ORDER BY f.name ASC";
Query query = sess.createQuery(hql);
在此示例中,在 HQL 中设置asc子句包含在生成的 SQL 查询中:
Hibernate: select foo0_.ID as ID1_0_, foo0_.NAME as NAME2_0_
from FOO foo0_ order by foo0_.NAME ASC
2.2. 按多个属性排序
可以将多个属性以及可选的排序顺序添加到 HQL 查询字符串中的Order By子句:
String hql = "FROM Foo f ORDER BY f.name DESC, f.id ASC";
Query query = sess.createQuery(hql);
生成的 SQL 查询会相应改变:
Hibernate: select foo0_.ID as ID1_0_, foo0_.NAME as NAME2_0_
from FOO foo0_ order by foo0_.NAME DESC, foo0_.ID ASC
2.3. 设置空值的排序优先级
默认情况下,当要排序的属性具有null值时,由 RDMS 决定优先级。通过在 HQL 查询字符串中放置 NULLS FIRST 或 NULLS LAST 子句,可以覆盖此default处理。
这个简单的示例将任何空值放在结果列表的末尾:
String hql = "FROM Foo f ORDER BY f.name NULLS LAST";
Query query = sess.createQuery(hql);
让我们看看生成的 SQL 查询中的is null then 1 else 0子句:
Hibernate: select foo0_.ID as ID1_1_, foo0_.NAME as NAME2_1_,
foo0_.BAR_ID as BAR_ID3_1_, foo0_.idx as idx4_1_ from FOO foo0_
order by case when foo0_.NAME is null then 1 else 0 end, foo0_.NAME
2.4. 对一对多关系进行排序
让我们分析一个复杂的排序案例:以一对多关系对实体进行排序——包含Foo实体集合的Bar。
我们将通过使用Hibernate @OrderBy来注解集合来做到这一点;我们将指定完成排序的字段以及方向:
@OrderBy(clause = "NAME DESC")
Set<Foo> fooList = new HashSet();
请注意注解的子句参数。与类似的*@OrderBy* JPA 注解相比,这是 Hibernate 的*@OrderBy所独有的。此方法与其 JPA 等效方法的另一个区别是,子句参数指示排序是基于FOO表的NAME列完成的,而不是基于Foo的name*属性。
现在让我们看看Bars和Foos的实际排序:
String hql = "FROM Bar b ORDER BY b.id";
Query query = sess.createQuery(hql);
生成的SQL 语句显示排序后的 Foo被放置在fooList 中:
Hibernate: select bar0_.ID as ID1_0_, bar0_.NAME as NAME2_0_ from BAR bar0_
order by bar0_.ID Hibernate: select foolist0_.BAR_ID as BAR_ID3_0_0_,
foolist0_.ID as ID1_1_0_, foolist0_.ID as ID1_1_1_, foolist0_.NAME as
NAME2_1_1_, foolist0_.BAR_ID as BAR_ID3_1_1_, foolist0_.idx as idx4_1_1_
from FOO foolist0_ where foolist0_.BAR_ID=? order by foolist0_.NAME desc
要记住的一件事是,不能像 JPA 那样**对列表进行排序。**Hibernate 文档指出:
“Hibernate 当前忽略了 @ElementCollection 上的 @OrderBy,例如 List<String>。元素的顺序由数据库返回,未定义。”
作为旁注,可以通过使用 Hibernate 的旧 XML 配置并用<Bag…>元素替换<List..>元素来解决此限制。
3. 使用 Hibernate Criteria 排序
Criteria Object API 提供Order类作为主要 API 来管理排序。
3.1. 设置排序顺序
Order类有两种设置排序顺序的方法:
- asc(字符串属性) :按属性升序对查询进行
- desc(字符串属性) :按属性降序对查询进行
让我们从一个简单的例子开始——按单个id属性排序:
Criteria criteria = sess.createCriteria(Foo.class, "FOO");
criteria.addOrder(Order.asc("id"));
请注意,asc方法的参数区分大小写,并且应该与要排序的属性的名称匹配。
Hibernate Criteria 的 Object API 显式设置了排序方向,这反映在代码生成的 SQL 语句中:
Hibernate: select this_.ID as ID1_0_0_, this_.NAME as NAME2_0_0_
from FOO this_ order by this_.ID sac
3.2. 按多个属性排序
按多个属性排序只需要在Criteria实例中添加一个Order对象,如下例所示:
Criteria criteria = sess.createCriteria(Foo.class, "FOO");
criteria.addOrder(Order.asc("name"));
criteria.addOrder(Order.asc("id"));
在 SQL 中生成的查询是:
Hibernate: select this_.ID as ID1_0_0_, this_.NAME as NAME2_0_0_ from
FOO this_ order by this_.NAME asc, this_.ID sac
3.3. 设置空值的排序优先级
默认情况下,当要排序的属性具有null值时,由 RDMS 决定优先级。Hibernate Criteria Object API 可以很容易地更改默认值并将空值放在升序列表的末尾:
Criteria criteria = sess.createCriteria(Foo.class, "FOO");
criteria.addOrder(Order.asc("name").nulls(NullPrecedence.LAST));
这是底层的SQL查询——带有is null then 1 else 0子句:
Hibernate: select this_.ID as ID1_1_1_, this_.NAME as NAME2_1_1_,
this_.BAR_ID as BAR_ID3_1_1_, this_.idx as idx4_1_1_, bar2_.ID as
ID1_0_0_, bar2_.NAME as NAME2_0_0_ from FOO order by case when
this_.NAME is null then 1 else 0 end, this_.NAME asc
或者,我们也可以将空值放在降序列表的开头:
Criteria criteria = sess.createCriteria(Foo.class, "FOO");
criteria.addOrder(Order.desc("name").nulls(NullPrecedence.FIRST));
相应的 SQL 查询如下——带有is null then 0 else 1子句:
Hibernate: select this_.ID as ID1_1_1_, this_.NAME as NAME2_1_1_,
this_.BAR_ID as BAR_ID3_1_1_, this_.idx as idx4_1_1_, bar2_.ID as
ID1_0_0_, bar2_.NAME as NAME2_0_0_ from FOO order by case when
this_.NAME is null then 0 else 1 end, this_.NAME desc
请注意,如果要排序的属性是像int 这样的原始类型,则会抛出PresisitenceException。
例如,如果f.anIntVariable的值为 null,则执行查询:
String jql = "Select f from Foo as f order by f.anIntVariable desc NULLS FIRST";
Query sortQuery = entityManager.createQuery(jql);
会抛出:
javax.persistence.PersistenceException: org.hibernate.PropertyAccessException:
Null value was assigned to a property of primitive type setter of
com.cc.jpa.example.Foo.anIntVariable