列でdescを並べ替えるときにPostgreSQL DBでHibernateを使用すると、null値がnullではない値よりも高くなります。
SQL99標準では、キーワード「NULLS LAST」を提供して、null値を非nullよりも低くする必要があることを宣言しています。
「最後のNULL」動作は、Hibernateの基準APIを使用して実現できますか?
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);
}
}
この機能は、前述のようにHibernate 4.2.xおよび4.3.xリリース中に実装されました。
たとえば、次のように使用できます。
Criteria criteria = ...;
criteria.addOrder( Order.desc( "name" ).nulls(NullPrecedence.FIRST) );
Hibernate v4.3のjavadocは、省略性が低くなっています here 。
Hibernateプロパティで「nulls first」/「nulls last」を構成して、デフォルトで条件呼び出しによってピックアップされるようにすることができます:hibernate.order_by.default_null_ordering=last
(または=first
)。
詳細は this hibernate commit を参照してください。
SQLをその場で作成し、Criteria APIを使用しない場合の別のバリアント:
ORDER BY COALESCE(、 '0')[ASC | DESC]
これは、varchar列または数値列のどちらでも機能します。
(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が注文後に制限/オフセットを持っている場合、これは壊れます– Sathish Apr 1 '11 at 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でテストし、私たちが抱えていた 'nullがnon-nullsよりも高い'という問題を修正しました。