Hibernate ilike中转义字符的处理

来源:互联网 发布:机票低价提醒软件 编辑:程序博客网 时间:2024/06/06 12:46

不知道大家有没有碰到,还是没有这种需求。就是用like来查询,我们没有用Lucene,Compass这种全文索引的方案,我们只是简单的添加%进行like查询。用户搜索的时候就使用*和?来代表任意和一个。所以要对"%"和"_"进行转义,我们使用的是Oracle数据库。sql语句看起来可能是这样的。

 

[java] view plain copy
  1. Select * FROM t_user where nickname like '%Goo/_D' escape '/'  

这里对_进行转义了。因为用户昵称包含下划线,如果不进行转义就表示一个任意字符。有时候我们可能还需要对%进行转义。同样的方法在%前加/% 但是比起普通的like语句。多了一个声明转义符的语句。所以我们会想到这样的语句

[java] view plain copy
  1. DetachedCriteria criteria = DetachedCriteria.forClass(User.class);  
  2. criteria.add(Restrictions.like("nickname", user.getNickname()+"' escape'/"));  

但是这样是不管用的。
接下来可能会想到使用Hibernate3的原生sql查询,其实我们不需要这样做。我们还是使用Criteria条件查询。

[java] view plain copy
  1. criteria.add(Restrictions.sqlRestriction("{alias}.nickname like ? escape'/'", StringUtil.escapeSQLLike(user.getNickname()), <a href="http://lib.csdn.net/base/javaee" class='replace_word' title="Java EE知识库" target='_blank' style='color:#df3434; font-weight:bold;'>hibernate</a>.STRING));  

这样Hibernate产生的语句就是我们想要的语句了。

[java] view plain copy
  1. /** 
  2.      * 转义like语句中的 
  3.      * <code>'_'</code><code>'%'</code> 
  4.      * 将<code>'?'</code>转成sql的<code>'/_'</code> 
  5.      * 将<code>'%'</code>转成sql的<code>'/%'</code> 
  6.      * <p> 
  7.      *   例如搜索<code>?aa*bb?c_d%f</code>将转化成<br/> 
  8.      *   <code>_aa%bb_c/_d/%f</code> 
  9.      * </p> 
  10.      * @param likeStr 
  11.      * @return 
  12.      * @author <a href="http://jdkcn.com" mce_href="http://jdkcn.com">somebody</a> 
  13.      */  
  14.     public static String escapeSQLLike(String likeStr) {  
  15.         String str = StringUtils.replace(likeStr, "_""/_");  
  16.         str = StringUtils.replace(str, "%",    "/%");  
  17.         str = StringUtils.replace(str, "?""_");  
  18.         str = StringUtils.replace(str, "*""%");  
  19.         return str;  
  20.     }  

 

上段引自:http://blog.csdn.net/newfox/archive/2006/10/16/1337445.aspx

 

总觉得自己被面象对象中毒太深,如果用上面的方法,就必须把数据库表的字段表写入SQL内,有点不爽,所以想方设法改造。

新建两个类:IlikeExpressionEx和RestrictionsUtils,一看就知道是对Hibernate的IlikeExpression和Restrictions的扩展,可惜Restrictions没有提供protected级别以上的构造方法,没法继承。不过也没关系,毕竟Restrictions中的方法都是静态的,于是:

[java] view plain copy
  1. public class RestrictionsUtils{  
  2.     public RestrictionsUtils(){  
  3.     }  
  4.     /** 
  5.      *  
  6.      * @description:处理字符串中含转义字符问题 
  7.      * @return 
  8.      */  
  9.     public static Criterion ilike(final String propertyName, String value, MatchMode matchMode) {  
  10.         return new IlikeExpressionEx(propertyName, value, matchMode);  
  11.     }  
  12. }  

[java] view plain copy
  1. public class IlikeExpressionEx extends IlikeExpression{  
  2.       
  3.     private final String propertyName;  
  4.     private final Object value;  
  5.       
  6.     protected IlikeExpressionEx(String propertyName, Object value) {  
  7.         super(propertyName, value);  
  8.         this.propertyName = propertyName;  
  9.         this.value = value.toString();  
  10.     }  
  11.     protected IlikeExpressionEx(String propertyName, String value, MatchMode matchMode) {  
  12.         this( propertyName, matchMode.toMatchString(StringUtils.escapeSQLLike(value.toString())));  
  13.     }  
  14.     public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery)  
  15.     throws HibernateException {  
  16.         Dialect dialect = criteriaQuery.getFactory().getDialect();  
  17.         String[] columns = criteriaQuery.getColumnsUsingProjection(criteria, propertyName);  
  18.         if (columns.length!=1throw new HibernateException("ilike may only be used with single-column properties");  
  19.         if ( dialect instanceof PostgreSQLDialect ) {  
  20.             return columns[0] + " ilike ? escape '/'";  
  21.         }  
  22.         else {  
  23.             return dialect.getLowercaseFunction() + '(' + columns[0] + ") like ? escape '/'";  
  24.         }  
  25.     }  
  26. }  

之后,要用到类似于Restrictions.ilike("contactTelphone", userTel, MatchMode.ANYWHERE)的地方,只要稍作修改:

RestrictionsUtils.ilike("contactTelphone", userTel, MatchMode.ANYWHERE)即可