【问题标题】:JPA criteria API order by NULL lastJPA 标准 API order by NULL last
【发布时间】:2017-01-07 11:35:08
【问题描述】:

我使用 JPA 标准 API 从数据库中获取记录。 我有实体 Record 字段 dateTime 可以为空。我会编码:

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 的记录首先显示。 我使用 Postgres 数据库。 我会回答这个问题,因为我找到了解决方案。 这是一个类似的帖子。 JPA Criteria Query API and order by null last

【问题讨论】:

    标签: java jpa criteria-api


    【解决方案1】:

    我在这里给出了这个任务的答案。

    首先,Postgres 默认首先返回空值。

    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);
    }
    

    注意,CriteriaBuilder 来自 hibernate-jpa-2.1。

    /**
     * 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);
    

    【讨论】:

    • 我试过这个并得到ERROR: COALESCE types timestamp without time zone and bytea cannot be matched。可能与我们构建cb的具体方式有关:cb.coalesce(root.get('joinedModel').get('someDate'), MIN_DATE)
    • 真的吗?它在我的代码中有效。请检查休眠版本,MIN_DATE。我还使用了 join 方法而不是 get 来浏览实体的关系 recordRoot = externalRoot.jolin("recordRelation");然后recordRoot.get("field")
    • 发现了根本原因。我使用LocalDateTime 而不是日期。在 Postgres DB 上,这不起作用。按照答案中的说明切换回Date
    • 我在 Hibernate 中使用 LocalDateTime。默认不支持。这是帮助:hibernate-java8 stackoverflow.com/questions/27750026/….
    • 也可以使用criteriaBuilder.coalesce().value(recordRoot.get(RecordTable.FIELD_DATE_TIME))而不需要MIN_DATE
    【解决方案2】:

    JPA 规范中没有任何内容来控制 NULLS 的处理方式(所有 RDBMS 都偏好默认值)。这在 JPQL(基于字符串的查询)或 Criteria API 中均不可用。使用 JPQL,所有主要的 JPA 提供者都允许使用

    NULLS [FIRST|LAST]
    

    在排序子句中。对于 Criteria,您受到 Criteria API 的限制,因此没有任何可能。我知道 DataNucleus JPA 提供了一个自定义版本的 JPA Criteria API,它允许规范

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

    但显然这是特定于该 JPA 提供程序的。

    【讨论】:

      【解决方案3】:

      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);
      

      它会给你最好的答案

      【讨论】:

        【解决方案4】:

        我将 JPA 与 eclipselink 与 Postgres 一起使用,这里的命名查询按预期工作。 这是摘录:

        ,@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")
        

        【讨论】:

          猜你喜欢
          • 2019-01-03
          • 2011-04-10
          • 2016-03-09
          • 1970-01-01
          • 2013-09-12
          • 1970-01-01
          • 2012-06-13
          • 2016-12-30
          • 1970-01-01
          相关资源
          最近更新 更多