使用JPA Criteria API比较日期实体

64

使用EclipseLink实现的JPA 2。

我正在尝试构建一个动态查询,该查询应获取一些在给定日期之后持久化的记录。

CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Event> criteria = builder.createQuery(Event.class);
Root<Event> root = criteria.from(Event.class);
criteria.select(root);
criteria.distinct(true);
List<Predicate> predicates = new ArrayList<Predicate>();
//...
if (dateLimit != null){
    ParameterExpression<Date> param = builder.parameter(Date.class, "dateLimit");
    predicates.add(builder.lessThanOrEqualTo(root.get("dateCreated"), param));
}

lessThanOrEqualTo()le()是API中唯一看起来可能对我有帮助的两个方法。尽管如此,Eclipse抛出了这个警告:

Bound mismatch: The generic method lessThanOrEqualTo(Expression<? extends Y>, Expression<? extends Y>)
of type CriteriaBuilder is not applicable for the arguments (Path<Object>, ParameterExpression<Date>).
The inferred type Object is not a valid substitute for the bounded parameter
<Y extends Comparable<? super Y>>

我能想象我可能没有采取正确的方法来解决这个问题,但我找不到任何关于可能解决方案的提示或指针。

4个回答

93
问题在于使用基于字符串的API时,它无法推断get操作的结果值类型。例如,在路径(Path)的Javadoc中有解释
如果您使用
predicates.add(builder.lessThanOrEqualTo(root.<Date>get("dateCreated"), param));

相反,它将正常工作,因为它可以从类型参数中推断出返回类型,并找到它是可比较的。请注意,使用参数化方法调用 root.<Date>get(...)(参见,例如,何时有用参数化方法调用?)。

另一个(我认为更好的)解决方案是使用基于元模型的API而不是基于字符串的API。关于规范元模型的简单示例在此处 给出。如果您有更多时间来投资,这是一篇关于静态元模型的好文章:JPA 2.0中动态、类型安全的查询


1
NetBeans 8.2仍然会出现警告,我不知道为什么。builder.greaterThanOrEqualsTo(root.get(Some_.date), date) <> builder.greaterThanOrEqualsTo(root.<Date>get(Some_.date), date) - Jin Kwon
@Jin Kwon:请确保导入的是 javax.persistence.criteria.Predicate,而不是 java.util.function.Predicate。这对我有用 :) - Hank
2
这不是一个糟糕的导入问题,而是一个非常古老的错误。为了解决这个问题,您必须将其声明为变量并将其用作参数: Path path = root.get( .....); builder.greaterThan(path, date); - FiruzzZ

18

你需要使用生成的元模型以一种非常安全的方式访问属性。如果你使用字符串来引用你的属性,类型只能从在调用方法时使用的显式泛型类型中推断出来,或通过类型转换,或通过编译器执行的自动类型推断来推断:

Path<Date> dateCreatedPath = root.get("dateCreated");
predicates.add(builder.lessThanOrEqualTo(dateCreatedPath, dateLimit));

3

我曾经遇到过一个类似的错误,但是错误信息是这样的predicates.add(cb.greaterThan(article.get(Article_.created), since));。后来我发现了这个网页。对于我而言,问题的原因是我将我的项目从Java 1.7升级到了1.8,并且在这个过程中配置了Maven以便能够编译为Java 1.8版本。我只需要将Maven重新配置为编译Java 1.7版本即可解决问题,同时其他部分还可以保留在1.8版本。


0

我曾经遇到过同样的问题,当我使用谓词时。它可以很好地处理所有类型,除了日期类型。我尝试了所有方法,对我来说最简单的方法是:

predicates.add(builder.greaterThanOrEqualTo(root.get(criteria.getKey()), (Date)criteria.getValue()));

我在 criteria.getValue() 前面添加了 (Date),这有助于将我的值对象识别为日期类型。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接