JPA2:不区分大小写,就像在任何地方匹配一样

我一直在JPA 1.0(休眠驱动程序)中使用休眠限制。定义了哪些关键字在任何地方与列匹配,并且不区分大小写。Restrictions.ilike("column","keyword", MatchMode.ANYWHERE)

现在,我将JPA 2.0与EclipseLink一起使用作为驱动程序,因此我必须使用内置的JPA 2.0的“限制”。我发现和方法,我也发现了如何使它匹配任何地方(虽然它是令人敬畏和手动的),但我仍然没有弄清楚如何做到这一点不区分大小写。CriteriaBuilderlike

有我目前令人敬畏的解决方案:

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

问题:

是否有任何类似于休眠驱动程序的功能?

我是否正确使用了 JPA 2.0 标准?与休眠限制相比,这是一个尴尬且不舒服的解决方案。

或者任何人都可以帮助我如何将我的解决方案更改为不区分大小写,请?

多谢。


答案 1

乍一看似乎有点尴尬,但它是类型安全的。从字符串生成查询不是,因此您会在运行时而不是编译时注意到错误。通过使用缩进或单独执行每个步骤,而不是在一行中编写整个 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() + "%"
        )
    )
);

答案 2

正如我在(当前)接受的答案中所评论的那样,一方面使用DBMS的函数,另一方面使用java存在一个陷阱,因为这两种方法都不能保证为相同的输入字符串提供相同的输出。lower()String.toLowerCase()

我终于找到了一个更安全(但不是防弹)的解决方案,即让DBMS使用文字表达式完成所有降低:

builder.lower(builder.literal("%" + keyword + "%")

因此,完整的解决方案将如下所示:

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 + "%")
        )
    )
);

编辑:
正如@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

不过,我更喜欢我的解决方案,因为它不会比较两个应该工作相同的不同函数的结果。我将相同的函数应用于所有字符数组,以便比较输出变得更加“稳定”。

防弹解决方案将涉及区域设置,以便 SQL 能够正确降低重音字符。(但这超出了我的谦卑知识)lower()

*带有 PostgreSQL 9.5.1 的 Db 值,带有 'C' locale


推荐