Gai*_*aim 47 java criteria eclipselink hibernate-criteria jpa-2.0
我一直在JPA 1.0(Hibernate驱动程序)中使用Hibernate Restrictions.定义了Restrictions.ilike("column","keyword", MatchMode.ANYWHERE)哪些测试关键字是否匹配列的任何位置,并且它不区分大小写.
现在,我使用JPA 2.0和EclipseLink作为驱动程序,因此我必须使用"Restrictions"内置JPA 2.0.我发现CriteriaBuilder和方法like,我也发现了如何使它匹配任何地方(尽管它是令人讨厌和手动),但我仍然没有想出如何做它不区分大小写.
我目前有一个很好的解决方案:
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<User> query = builder.createQuery(User.class);
EntityType<User> type = em.getMetamodel().entity(User.class);
Root<User> root = query.from(User.class);
// Where
// important passage of code for question
query.where(builder.or(builder.like(root.get(type.getDeclaredSingularAttribute("username", String.class)), "%" + keyword + "%"),
builder.like(root.get(type.getDeclaredSingularAttribute("firstname", String.class)), "%" + keyword + "%"),
builder.like(root.get(type.getDeclaredSingularAttribute("lastname", String.class)), "%" + keyword + "%")
));
// Order By
query.orderBy(builder.asc(root.get("lastname")),
builder.asc(root.get("firstname")));
// Execute
return em.createQuery(query).
setMaxResults(PAGE_SIZE + 1).
setFirstResult((page - 1) * PAGE_SIZE).
getResultList();
Run Code Online (Sandbox Code Playgroud)
问题:
是否有像Hibernate驱动程序中的任何功能?
我正确使用JPA 2.0标准吗?与Hibernate Restrictions相比,这是一个尴尬和不舒服的解决方案.
或者,任何人都可以帮我改变我的解决方案,使其不区分大小写吗?
非常感谢.
wel*_*rat 86
起初看起来有点尴尬,但它是类型安全的.从字符串构建查询不是,因此您在运行时而不是在编译时发现错误.您可以通过使用缩进或单独执行每个步骤来使查询更具可读性,而不是在一行中编写整个WHERE子句.
要使查询不区分大小写,请将关键字和比较字段都转换为小写:
query.where(
builder.or(
builder.like(
builder.lower(
root.get(
type.getDeclaredSingularAttribute("username", String.class)
)
), "%" + keyword.toLowerCase() + "%"
),
builder.like(
builder.lower(
root.get(
type.getDeclaredSingularAttribute("firstname", String.class)
)
), "%" + keyword.toLowerCase() + "%"
),
builder.like(
builder.lower(
root.get(
type.getDeclaredSingularAttribute("lastname", String.class)
)
), "%" + keyword.toLowerCase() + "%"
)
)
);
Run Code Online (Sandbox Code Playgroud)
Ghu*_*dyl 10
正如我在(当前)接受的答案中所评论的那样,一方面使用DBMS的lower()功能存在缺陷,另一方面使用java,String.toLowerCase()因为两种方法都不能保证为同一输入字符串提供相同的输出.
我终于找到了一个更安全(但不是防弹)的解决方案,让DBMS使用文字表达式完成所有降低:
builder.lower(builder.literal("%" + keyword + "%")
Run Code Online (Sandbox Code Playgroud)
所以完整的解决方案看起来像:
query.where(
builder.or(
builder.like(
builder.lower(
root.get(
type.getDeclaredSingularAttribute("username", String.class)
)
), builder.lower(builder.literal("%" + keyword + "%")
),
builder.like(
builder.lower(
root.get(
type.getDeclaredSingularAttribute("firstname", String.class)
)
), builder.lower(builder.literal("%" + keyword + "%")
),
builder.like(
builder.lower(
root.get(
type.getDeclaredSingularAttribute("lastname", String.class)
)
), builder.lower(builder.literal("%" + keyword + "%")
)
)
);
Run Code Online (Sandbox Code Playgroud)
编辑:
由于@cavpollo要求我举例,我不得不三思而后行,并意识到它并不比接受的答案安全得多:
DB value* | keyword | accepted answer | my answer
------------------------------------------------
elie | ELIE | match | match
Élie | Élie | no match | match
Élie | élie | no match | no match
élie | Élie | match | no match
Run Code Online (Sandbox Code Playgroud)
尽管如此,我更喜欢我的解决方案,因为它没有比较两个不同功能的结果.我将相同的函数应用于所有字符数组,以便比较输出变得更"稳定".
防弹解决方案将涉及区域设置,以便SQL lower()能够正确地降低重音字符.(但这超出了我的谦虚知识)
*带有'C'语言环境的PostgreSQL 9.5.1的Db值
Tho*_*ker 10
如果您使用像 Postgres 这样的数据库,ilike它支持提供更好的性能,因为使用该lower()功能所提供的解决方案都不能正确解决问题。
解决方案可以是自定义函数。
您正在编写的 HQL 查询是:
SELECT * FROM User WHERE (function('caseInSensitiveMatching', name, '%test%')) = true
Run Code Online (Sandbox Code Playgroud)
其中caseInSensitiveMatching是我们自定义函数的函数名称。是name您要比较的属性的路径,是%test%您要与之匹配的模式。
目标是将 HQL 查询转换为以下 SQL 查询:
SELECT * FROM User WHERE (name ilike '%test%') = true
Run Code Online (Sandbox Code Playgroud)
为了实现这一点,我们必须使用注册的自定义函数来实现我们自己的方言:
SELECT * FROM User WHERE (function('caseInSensitiveMatching', name, '%test%')) = true
Run Code Online (Sandbox Code Playgroud)
lower与具有 Postgres 可以利用相应列上的索引的功能的版本相比,上述优化在我们的情况下产生了 40 倍的性能提升。在我们的情况下,查询执行时间可以从 4.5 秒减少到 100 毫秒。
这lower会妨碍索引的有效使用,因此速度要慢得多。
小智 7
这项工作对我来说:
CriteriaBuilder critBuilder = em.getCriteriaBuilder();
CriteriaQuery<CtfLibrary> critQ = critBuilder.createQuery(Users.class);
Root<CtfLibrary> root = critQ.from(Users.class);
Expression<String> path = root.get("lastName");
Expression<String> upper =critBuilder.upper(path);
Predicate ctfPredicate = critBuilder.like(upper,"%stringToFind%")
critQ.where(critBuilder.and(ctfPredicate));
em.createQuery(critQ.select(root)).getResultList();
Run Code Online (Sandbox Code Playgroud)