JPA2:不区分大小写,喜欢在任何地方匹配

新手上路,请多包涵

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

现在,我使用 JPA 2.0 和 EclipseLink 作为驱动程序,所以我必须使用“限制”内置 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();

问题:

有没有类似Hibernate驱动的功能?

我是否正确使用了 JPA 2.0 标准?与 Hibernate Restrictions 相比,这是一个笨拙和不舒服的解决方案。

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

非常感谢。

原文由 Gaim 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 660
2 个回答

起初看起来有点笨拙,但它是类型安全的。从字符串构建查询不是,因此您会在运行时而不是编译时注意到错误。您可以通过使用缩进或单独执行每个步骤来使查询更具可读性,而不是在一行中编写整个 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() + "%"
        )
    )
);

原文由 weltraumpirat 发布,翻译遵循 CC BY-SA 2.5 许可协议

正如我在(当前)接受的答案中评论的那样,一方面使用 DBMS 的 lower() 函数存在缺陷,另一方面使用 java 的 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() 能够正确地降低重音字符。 (但这超出了我的拙见)

*带有“C”语言环境的 PostgreSQL 9.5.1 的 Db 值

原文由 Ghurdyl 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
Stack Overflow 翻译
子站问答
访问
宣传栏