简体   繁体   English

如何使用JPA Criteria API在字符串的日期字段中搜索

[英]How to search date field for a String using JPA Criteria API

This is a question that spins off my other Question here . 这是我在这里提出的另一个问题的一个问题。 I thought it would be best put as a different question after someone( @Franck ) pointed me to this link and this one too . 我认为最好在有人( @Franck )指向我指向此链接以及这个 链接之后再提出一个不同的问题。

I'm stumped on how to search for a string in a database Date column (in my case MySQL DATETIME) using the JPA Criteria API. 我很困惑如何使用JPA Criteria API在数据库“日期”列(在我的情况下为MySQL DATETIME)中搜索字符串。

Here's what I've done; 这就是我所做的;

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Client> cq = cb.createQuery(Client.class);
Root<Client> entity = cq.from(Client.class);
cq.select(entity);

List<Predicate> predicates = new ArrayList<Predicate>();
predicates.add(cb.like(cb.lower(entity.get("dateJoined").as(String.class)), "%"+search.toLowerCase()+"%")); 

cq.where(predicates.toArray(new Predicate[]{}));
TypedQuery<Client> query = em.createQuery(cq); //<--- Error gets thrown here
return query.getResultList();

But it fails with the following exception; 但是它失败,但以下例外;

 java.lang.IllegalArgumentException: Parameter value [%10-2015%] did not match expected type [java.lang.Character] 

where 10-2015 is the String being searched for; 其中10-2015是要搜索的字符串;

I'm stuck on how to go by achieving this. 我坚持如何实现这一目标。 I need some help. 我需要协助。

Ok, after lots of experimenting with various strategies, here's what I did that finally worked. 好的,在尝试了各种策略之后,这就是我最终完成的工作。

I saw this post here and suddenly remembered the JPA Tuple Interface which is an Object that can return multiple result Type(s). 我在这里看到了这篇文章,突然想起了JPA Tuple接口,它是一个可以返回多个结果类型的对象。 So to perform my like comparison, and since Date cannot be simply cast to a String here are the steps; 因此,执行我的like比较,由于不能简单地将Date转换为String,因此下面是步骤:

  1. I get the column as a Tuple 我将其列为Tuple
  2. do a check on The Tuple Object to see if it's assignable from Date 检查元组对象是否可以从日期分配
  3. if it is, then get the Date-Format expression and pass it to the like expression. 如果是,则获取Date-Format表达式并将其传递给like表达式。

So essentially, here's what I initially had which was apparently failing; 所以从本质上讲,这是我最初拥有的东西,但是显然失败了。

predicates.add(cb.like(cb.lower(entity.get("dateJoined").as(String.class)), "%"+search.toLowerCase()+"%")); 

Now, this is what I have that works beautifully; 现在,这就是我工作得很好的东西。

Path<Tuple> tuple = entity.<Tuple>get("dateJoined");
if(tuple.getJavaType().isAssignableFrom(Date.class)){
    Expression<String> dateStringExpr = cb.function("DATE_FORMAT", String.class, entity.get("dateJoined"), cb.literal("'%d/%m/%Y %r'"));
    predicates.add(cb.like(cb.lower(dateStringExpr), "%"+search.toLowerCase()+"%"));
}

NOTE-WORTHY CONSIDERATIONS - 注意事项 -

  1. I am aware that from wherever the search would be initiated, all my Dates are presented in this form 07/10/2015 10:25:09 PM hence my ability to know how to format the Date for the comparison in my like expression as "'%d/%m/%Y %r'" . 我知道,在任何地方搜索将启动,我的所有时间都以这种形式提出07/10/2015 10:25:09 PM知道如何格式化用于比较的日期在我,因此我的能力like表达"'%d/%m/%Y %r'"
  2. This is just one step that works for Dates. 这只是适用于Dates的一步。 Most other Types eg int, long, char ...etc... can all be directly Cast to String and as I explore more Types of data, I'll definitely do the same for any other Type that cannot be directly Cast to String. 大多数其他类型,例如int,long,char ...等都可以直接转换为String,而且随着我探索更多的数据类型,对于任何其他不能直接转换为String的Type,我肯定也会做同样的事情。

Though this works perfectly for me, but before I mark this as the right answer, I'm going to subject it to some more extensive tests and in the process keep it open for comments by anyone that has any reservations about my strategy. 尽管这对我来说非常有效,但是在我将其标记为正确答案之前,我将对其进行一些更广泛的测试,并在此过程中让任何对我的策略有保留的人都可以公开评论。

And finally, to that one person that this helped out in any way... Cheers! 最后,向以任何方式提供帮助的那个人加油!

This works in my case H2 (I use it for unit-tests), and I hope will work as well in Postgresql and Oracle, since TO_CHAR function seems to be cross-DB supported. 这在我的情况下H2(我将其用于单元测试)中有效,并且我希望在Postgresql和Oracle中也能正常工作,因为TO_CHAR函数似乎受跨数据库支持。

Path<Date> path = ua.get(MyEntity_.timestamp);
Expression<String> dateStringExpr = cb.function("TO_CHAR", String.class, path, cb.literal("DD.MM.YYYY HH24:MI:SS"));
predicates.add(cb.like(dateStringExpr, "%" + value + "%"));

PS. PS。 MyEntity_ stands for metamodel generated for real MyEntity . MyEntity_代表为实际MyEntity生成的元模型。 You may read about Metamodels in Oracle docuemntation for Criteria API . 您可以在Oracle文档Criteria API中阅读有关元模型的信息。

I would suggest you convert you search string to Date object, and do the comparison 我建议您将搜索字符串转换为Date对象,然后进行比较

SimpleDateFormat dateFormat = new SimpleDateFormat(...desired date format here...);
Date dateSearchParam = dateFormat.format(search);
predicates.add(cb.eq(entity.get("dateJoined"), dateSearchParam);

Or if you want, you can change the type of your dateJoined attribute in your Entity to String, while your MySQL DB type remains DATETIME. 或者,如果需要,您可以将Entity中的dateJoined属性的类型更改为String,而MySQL DB类型仍为DATETIME。 You can utilize JPA @Convert to convert DATETIME to java.lang.String when Entity is retrieved from DB (and vice-versa when Entity is being persisted to DB). 当从数据库中检索实体时,可以使用JPA @Convert将DATETIME转换为java.lang.String(当将实体持久化到DB时,反之亦然)。

See a sample here . 在这里查看示例。

Attribute Converters are only available in JPA 2.1 version. 属性转换器仅在JPA 2.1版本中可用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM