使用 JPA 标准 API 进行日期比较

标签 date jpa criteria-api

我有一个带有两个日期的范围日期选择器:startend ,其中两者都可以为空。我想过滤一个表,其中实体有一个确切的日期:date .

所以,这里有一些例子。我想配。图像匹配的日期是当前日期 (17/07/2016)。

  • null - 17/07/2016 -> 匹配
  • 17/07/2016 - 空 -> 匹配
  • 17/07/2016 - 17/07/2016 -> 匹配
  • null - null -> 匹配所有

  • 在我看来,这些是边缘情况,也是我如此挣扎的原因。

    其实我的代码看起来像:
    CriteriaBuilder cb = getEm().getCriteriaBuilder();
    CriteriaQuery<Transaction> cq = cb.createQuery(Transaction.class);
    Root<Transaction> root = cq.from(Transaction.class);
    
    List<Predicate> predicates = new ArrayList<>();
    
    Predicate startPredicate = cb.greaterThanOrEqualTo(root.get(Transaction_.date), start);
    Predicate endPredicate = cb.greaterThanOrEqualTo(root.get(Transaction_.date), end);
    
    predicates.add(startPredicate);
    predicates.add(endPredicate);
    cq.select(root).where(predicates.toArray(new Predicate[] {}));
    
    TypedQuery<Transaction> query = getEm().createQuery(cq);
    query.setFirstResult(firstRow);
    query.setMaxResults(maxRow);
    List<Transaction> resultList = query.getResultList();
    

    我想得到这样的查询:

    SELECT * FROM transaction
    WHERE ((cast(date AS DATE) >= '2016-07-16' OR cast(date AS DATE) IS NULL))
           AND ((cast(date AS DATE) <= '2016-07-17' OR cast(date AS DATE) IS NULL))
    

    请注意:静态日期用于模拟开始和结束日期。和 date是表列。

    我知道我的代码是错误的。它只匹配范围,不考虑空值。另外,如果开始和结束是同一天,我将得到零结果。

    你有什么想法吗?如何编辑我的代码以匹配所有提到的模式?

    最佳答案

    我有一个名为 discount 的现有数据库表两列类型 TIMESTAMP命名 discount_start_datediscount_end_date在 MySQL 数据库中。因此,请根据表的名称和该表中的相应列调整您的查询。

    基于问题中给出的 SQL 语句的完整条件查询可以构造如下(我希望代码是不言自明的)。

    CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
    CriteriaQuery<Discount> criteriaQuery = criteriaBuilder.createQuery(Discount.class);
    Root<Discount> root = criteriaQuery.from(entityManager.getMetamodel().entity(Discount.class));
    
    SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy");
    java.util.Date startDate = dateFormat.parse("24-02-2016");
    java.util.Date endDate = dateFormat.parse("24-03-2016");
    
    ParameterExpression<java.util.Date> parameter = criteriaBuilder.parameter(java.util.Date.class);
    
    Predicate startDatePredicate = criteriaBuilder.greaterThanOrEqualTo(root.get(Discount_.discountStartDate).as(java.sql.Date.class), parameter);
    Predicate endDatePredicate = criteriaBuilder.lessThanOrEqualTo(root.get(Discount_.discountEndDate).as(java.sql.Date.class), parameter);
    
    Predicate startDateOrPredicate = criteriaBuilder.or(startDatePredicate, root.get(Discount_.discountStartDate).isNull());
    Predicate endDateOrPredicate = criteriaBuilder.or(endDatePredicate, root.get(Discount_.discountEndDate).isNull());
    
    Predicate and = criteriaBuilder.and(startDateOrPredicate, endDateOrPredicate);
    criteriaQuery.where(and);
    
    List<Discount> list = entityManager.createQuery(criteriaQuery)
            .setParameter(parameter, startDate, TemporalType.DATE)
            .setParameter(parameter, endDate, TemporalType.DATE)
            .getResultList();
    

    它会生成您感兴趣的以下 SQL 查询(如您所述,这两个字段都包含在内)。

    select
        discount0_.discount_id as discount1_15_,
        discount0_.discount_code as discount2_15_,
        discount0_.discount_end_date as discount3_15_,
        discount0_.discount_percent as discount4_15_,
        discount0_.discount_start_date as discount5_15_ 
    from
        project.discount discount0_ 
    where
        (
            cast(discount0_.discount_start_date as date)>=? 
            or discount0_.discount_start_date is null
        ) 
        and (
            cast(discount0_.discount_end_date as date)<=? 
            or discount0_.discount_end_date is null
        )
    

    在 Hibernate 4.3.6 上测试最终但平均 ORM 框架应该生成相同的查询而无需任何修改。

    在这个方法中 setParameter(parameter, startDate, TemporalType.DATE) ,最后一个参数,即 TemporalType.DATE仅当您有 DATETIME 类型的列时才需要或 TIMESTAMP在您的数据库中,并且您想要比较日期而忽略此类列的时间部分。如果您的列没有像 DATE 这样的时间部分,您可以简单地排除该参数。 (MySQL)。

    如有必要,您还可以使用其他日期(时间)处理 API,例如 java.timeJodaTime更换 Date连同 SimpleDateFormat

    关于使用 JPA 标准 API 进行日期比较,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38424516/

    相关文章:

    java - 将 EntityManager 与数据库同步

    java - 映射 JPA 复合外键

    java - 如何对实体强制进行版本更新?

    java - Hibernate Criteria——返回列不同的记录

    java - 如何通过使用 Criteria API 将其子实体的属性列入白名单来选择 JPA 实体?

    r - 在 R 中将 ifelse 与日期一起使用

    javascript - 如何在使用 reSTLet 时将时区更改为 netsuite 中的另一个时区

    swift - 如何在表格 View 单元格swift 3中将当前时间添加到标签

    php - PHP 中秒到分钟的转换

    java - JPA Criteria API - 如何选择,如 "field not in"