web-dev-qa-db-ja.com

最後のNULLによるJPA基準APIの順序

JPA基準APIを使用して、データベースからレコードをフェッチします。 nullの可能性があるフィールドdateTimeを持つエンティティRecordがあります。私はコーディングします:

public List<Record> find(RecordFilter recordFilter, int page, int pageSize) {
    CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
    CriteriaQuery<Record> criteriaQuery = criteriaBuilder.createQuery(Record.class);
    Root<Record> recordRoot = criteriaQuery.from(Record.class);

    /*
     * JOINS. Left Joins are used for optional fields, or fields inside of the optional fields.
     */
    Join<Record, Agency> recordAgencyJoin = recordRoot.join(RecordTable.FIELD_AGENCY);
    //Some other joins

    //This is where I had the problem. 
    applyOrderBy(criteriaQuery, criteriaBuilder, recordRoot);

    /*
     * Specify which columns to select and their order.
     * criteriaQuery.multiselect(....);
     */              
    applyMultiSelect(recordRoot, recordAgencyJoin, /*other joins*/ criteriaQuery);

    /*
     * criteriaQuery.where(somePredicate);
     */
    applyFilter(recordFilter, criteriaQuery, criteriaBuilder,
            recordRoot, recordAgencyJoin /*, other joins*/);
    TypedQuery<Record> query = entityManager.<Record>createQuery(criteriaQuery);
    RepositoryUtils.applyPagination(query, page, pageSize);
    return query.getResultList();
}


private void applyOrderBy(CriteriaBuilder criteriaBuilder, Root<Record> recordRoot, CriteriaQuery<Record> criteriaQuery) {
    //Other fields to be added to the final sort.

    Order dateTimeDescOrder = criteriaBuilder.desc(recordRoot.get(RecordTable.FIELD_DATE_TIME));
    criteriaQuery.orderBy(dateTimeDescOrder /*, other orders by*/);
}

結局、NULLのdateTimeFieldを持つレコードが最初に表示されます。 Postrgresデータベースを使用しています。解決策を見つけたので、この質問に答えます。これは同様の投稿です。 JPA Criteria Query APIと最後のnullによる順序

6
Yan Khonski

ここに私はこの仕事への答えを置きます。

まず、Postgresはデフォルトで最初にnullを返します。

SELECT * FROM record ORDER BY date_time_field DESC;

https://stackoverflow.com/a/7621232/4587961

SELECT * FROM record ORDER BY date_time_field DESC NULLS LAST;

次に、applyOrderByメソッドを変更する必要がありました

private void applyOrderBy(CriteriaBuilder criteriaBuilder, Root<Record> recordRoot, CriteriaQuery<Record> criteriaQuery) {
    //In the class code
    //private static final Date MIN_DATE = new Date(0L);
    final Date MIN_DATE = new Date(0L);

    //We treat records will NULL dateTimeField as if it was MIN_DATE.
    Order dateTimeDescOrder = criteriaBuilder.desc(
            //NULL values - last - WORKAROUND.
            criteriaBuilder.coalesce(recordRoot.get(RecordTable.FIELD_DATE_TIME), MIN_DATE));
    criteriaQuery.orderBy(dateTimeDescOrder);
}

Hibernate-jpa-2.1のCriteriaBuilderに注意してください。

/**
 * Create an expression that returns null if all its arguments
 * evaluate to null, and the value of the first non-null argument
 * otherwise.
 *
 * @param x expression
 * @param y value
 *
 * @return coalesce expression
 */
<Y> Expression<Y> coalesce(Expression<? extends Y> x, Y y);
9
Yan Khonski

JPA仕様には、NULLSの処理方法を制御するものはありません(すべてのRDBMSには、デフォルトの設定があります)。これは、JPQL(文字列ベースのクエリ)またはCriteriaAPIでは使用できません。 JPQLを使用すると、すべての主要なJPAプロバイダーで

NULLS [FIRST|LAST]

注文条項で。 Criteriaの場合、Criteria APIによって制約されるため、何もできません。 DataNucleusJPAがJPACriteriaAPIのカスタムバージョンを提供していることを知っています。

Order order = criteriaBuilder.asc(myExpression).nullsFirst();

しかし、明らかにそれはそのJPAプロバイダーに固有のものです。

4
Neil Stockton

JPAまたはEclipselinkには、CriteriaBuilderでユースケースを使用できるものはありません。

例:

Order order1 = cBuilder.desc(cBuilder.selectCase().when(cBuilder.isNotNull(from.get('ColumnName1')), from.get('ColumnName1')).otherwise(from.get('ColumnName1')));
CQuery.orderBy(order1);

それはあなたに最良の答えを与えるでしょう

2
HarishSRana

私はPostgresでeclipselinkを使用してJPAを使用します。これは、期待どおりに機能するnamedqueryです。これは抜粋です:

,@NamedQuery(name = "ServerPasswords.forTheServer",
            query = "SELECT ss FROM ServerPasswords ss WHERE ss.fkIds = :IDServer AND ss.private = FALSE "
            + "ORDER BY ss.servce, ss.active DESC, ss.datefrom DESC NULLS LAST, ss.dateto NULLS LAST")
0
dobrivoje