在我们最近的一个项目中,我们的客户要求一个搜索字段,该字段可以搜索名字,姓氏和电子邮件地址,唯一的通配符是星号“ *”,表示部分匹配。 听起来很简单,但它使我们陷入了混乱。 在我们的项目中,我们将QueryDSL与JPA和MySQL结合使用作为基础关系数据库管理系统。
这意味着默认通配符对于一个字符为'_',对于多个字符为'%'。 支持“ *”进行部分匹配很容易:我们只需在搜索字符串中将“ *”替换为“%”,然后再将其作为值传递给JPA Query LIKE表达式即可。
我们的代码如下所示:
searchText = searchText.replace( "*", "%" );
query.where( user.email.like( searchText ).or(user.firstName.like( searchText ).or(user.lastName.like( searchText ))
) )
在此时搜索firstname_lastname@example.org时,当前查询仍返回两个结果:firstname_lastname@example.org和firstname.lastname@example.org。
当然,'_'通配符需要转义。 但是,事实证明,这并不是那么简单。
根据JPQLTemplates类的定义,默认转义字符为'!'。 这意味着,在搜索字符串替换“_”由“!_” 应该是足够了。
searchText = searchText.replace( "_", "!_" );
searchText = searchText.replace( "*", "%" );
但是,测试表明事实并非如此。 经过一些调试,然后打开SQL日志记录(默认情况下我们将其关闭),我们发现生成的SQL解析为:
SELECT * FROM users WHERE email like 'firstname!!_lastname@example.org' escape '!';
事实证明转义字符本身已被转义。 最初,我们的Google Fu使我们有些失败,但后来从2013年开始偶然发现了GitHub问题 。
显然,只有通过明确说明转义字符才能强制进行实际转义 ,即使它与默认转义字符相同也是如此。
将转义符添加到QueryDSL表达式中解决了我们的问题:
searchText = searchText.replace( "_", "!_" );
searchText = searchText.replace( "*", "%" );
query.where( user.email.like( searchText, '!' ).or(user.firstName.like( searchText, '!' ).or(user.lastName.like( searchText, '!' ))
) )
解决我们想要的SQL:
SELECT * FROM users WHERE email like 'firstname!_lastname@example.org' escape '!';
并生成满足客户要求的搜索结果:firstname_lastname@example.org
这是我们永远不会忘记的!
翻译自: https://www.javacodegeeks.com/2018/06/matching-string-wild-card-querydsl.html