【发布时间】:2011-04-10 15:16:35
【问题描述】:
Hibernate 与 PostgreSQL DB 一起使用,同时按列对 desc 进行排序时,空值高于非空值。
SQL99 标准提供关键字“NULLS LAST”来声明空值应低于非空值。
可以使用 Hibernate 的 Criteria API 实现“NULLS LAST”行为吗?
【问题讨论】:
标签: java hibernate null sql-order-by
Hibernate 与 PostgreSQL DB 一起使用,同时按列对 desc 进行排序时,空值高于非空值。
SQL99 标准提供关键字“NULLS LAST”来声明空值应低于非空值。
可以使用 Hibernate 的 Criteria API 实现“NULLS LAST”行为吗?
【问题讨论】:
标签: java hibernate null sql-order-by
如前所述,此功能已在 Hibernate 4.2.x 和 4.3.x 版本中实现。
可以作为例子:
Criteria criteria = ...;
criteria.addOrder( Order.desc( "name" ).nulls(NullPrecedence.FIRST) );
Hibernate v4.3 javadocs 更少省略here。
【讨论】:
鉴于 HHH-465 尚未修复,并且由于 Steve Ebersole 给出的原因在不久的将来不会得到修复,您最好的选择是使用附加到问题的 CustomNullsFirstInterceptor 全局或专门用于更改 SQL 语句。
我在下面为读者发布(感谢 Emilio Dolce):
public class CustomNullsFirstInterceptor extends EmptyInterceptor {
private static final long serialVersionUID = -3156853534261313031L;
private static final String ORDER_BY_TOKEN = "order by";
public String onPrepareStatement(String sql) {
int orderByStart = sql.toLowerCase().indexOf(ORDER_BY_TOKEN);
if (orderByStart == -1) {
return super.onPrepareStatement(sql);
}
orderByStart += ORDER_BY_TOKEN.length() + 1;
int orderByEnd = sql.indexOf(")", orderByStart);
if (orderByEnd == -1) {
orderByEnd = sql.indexOf(" UNION ", orderByStart);
if (orderByEnd == -1) {
orderByEnd = sql.length();
}
}
String orderByContent = sql.substring(orderByStart, orderByEnd);
String[] orderByNames = orderByContent.split("\\,");
for (int i=0; i<orderByNames.length; i++) {
if (orderByNames[i].trim().length() > 0) {
if (orderByNames[i].trim().toLowerCase().endsWith("desc")) {
orderByNames[i] += " NULLS LAST";
} else {
orderByNames[i] += " NULLS FIRST";
}
}
}
orderByContent = StringUtils.join(orderByNames, ",");
sql = sql.substring(0, orderByStart) + orderByContent + sql.substring(orderByEnd);
return super.onPrepareStatement(sql);
}
}
【讨论】:
您可以在休眠属性中配置“nulls first”/“nulls last”,以便默认情况下任何条件调用都会拾取它:hibernate.order_by.default_null_ordering=last(或=first)。
【讨论】:
NullPrecedence 的选项
这是(Pascal Thivent)对课程的更新:
for (int i = 0; i < orderByNames.length; i++) {
if (orderByNames[i].trim().length() > 0) {
String orderName = orderByNames[i].trim().toLowerCase();
if (orderName.contains("desc")) {
orderByNames[i] = orderName.replace("desc", "desc NULLS LAST");
} else {
orderByNames[i] = orderName.replace("asc", "asc NULLS FIRST");
}
}
}
这解决了问题:
如果 sql 在 order by 后有限制/偏移量,这将中断 – Sathish 2011 年 4 月 1 日 14:52
以下是在 JPA(休眠)中使用它的方法:
Session session = entityManager.unwrap(Session.class);
Session nullsSortingProperlySession = null;
try {
// perform a query guaranteeing that nulls will sort last
nullsSortingProperlySession = session.getSessionFactory().withOptions()
.interceptor(new GuaranteeNullsFirstInterceptor())
.openSession();
} finally {
// release the session, or the db connections will spiral
try {
if (nullsSortingProperlySession != null) {
nullsSortingProperlySession.close();
}
} catch (Exception e) {
logger.error("Error closing session", e);
}
}
我已经在 postgres 上对此进行了测试,它解决了我们遇到的“空值高于非空值”问题。
【讨论】:
另一种变体,如果您动态创建 SQL 并且不使用 Criteria API:
ORDER BY COALESCE(,'0') [ASC|DESC]
这适用于 varchar 或数字列。
【讨论】:
我们可以使用以下 Sort 参数创建 Pageable 对象:
JpaSort.unsafe(Sort.Direction.ASC, "ISNULL(column_name), (column_name)")
我们也可以准备 HQL:
String hql = "FROM EntityName e ORDER BY e.columnName NULLS LAST";
【讨论】:
对于未来的旅行者...我通过覆盖 Hibernate 方言解决了这个问题。在 CriteriaQuery 中默认情况下,我需要先为 asc 添加 null,最后为 desc 添加 null,由于某种原因不支持。 (旧版 CriteriaAPI 支持)
package io.tolgee.dialects.postgres
import org.hibernate.NullPrecedence
import org.hibernate.dialect.PostgreSQL10Dialect
@Suppress("unused")
class CustomPostgreSQLDialect : PostgreSQL10Dialect() {
override fun renderOrderByElement(expression: String?, collation: String?, order: String?, nulls: NullPrecedence?): String {
if (nulls == NullPrecedence.NONE) {
if (order == "asc") {
return super.renderOrderByElement(expression, collation, order, NullPrecedence.FIRST)
}
if (order == "desc") {
return super.renderOrderByElement(expression, collation, order, NullPrecedence.LAST)
}
}
return super.renderOrderByElement(expression, collation, order, nulls)
}
}
【讨论】:
【讨论】: