使用hibernate标准,有没有办法逃避特殊字符?

对于这个问题,我们希望避免编写特殊查询,因为查询必须在多个数据库中不同。 仅使用hibernate标准,我们希望能够转义特殊字符。

这种情况是需要能够转义特殊字符的原因:

假设我们在数据库中有表’foo’。 表’foo’只包含1个字段,称为’name’。 “名称”字段可以包含在数据库中可能被视为特殊的字符。 这种名称的两个例子是’name_1’和’name%1’。 “_”和“%”都是特殊字符,至少在Oracle中是这样。 如果用户想要在数据库中输入这些示例之后搜索其中一个示例,则可能会出现问题。

criterion = Restrictions.ilike("name", searchValue, MatchMode.ANYWHERE); return findByCriteria(null, criterion); 

在此代码中,“searchValue”是用户为应用程序提供的用于搜索的值。 如果用户想要搜索’%’,则用户将返回数据库中的每个’foo’条目。 这是因为’%’字符表示字符串匹配的“任意数量的字符”通配符,而hibernate生成的SQL代码将如下所示:

 select * from foo where name like '%' 

有没有办法告诉hibernate逃避某些字符,或创建一个非数据库类型特定的解决方法?

LikeExpression的构造函数都受到保护,因此它不是一个可行的选择。 此外,它有自己的问题 。

我和一位同事创建了一个非常好用的补丁。 补丁的要点是,对于使用MatchMode的LikeExpression构造函数,我们转义特殊字符。 对于使用Character(转义字符)的构造函数,我们假设用户自己转义特殊字符。

我们还对转义字符进行了参数化,以确保它们在使用类似\或引号字符的内容时不会破坏SQL查询。

 package org.hibernate.criterion; import org.hibernate.Criteria; import org.hibernate.HibernateException; import org.hibernate.dialect.Dialect; import org.hibernate.engine.TypedValue; public class LikeExpression implements Criterion { private final String propertyName; private final String value; private final Character escapeChar; protected LikeExpression( String propertyName, Object value) { this(propertyName, value.toString(), (Character) null); } protected LikeExpression( String propertyName, String value, MatchMode matchMode) { this( propertyName, matchMode.toMatchString( value .toString() .replaceAll("!", "!!") .replaceAll("%", "!%") .replaceAll("_", "!_")), '!' ); } protected LikeExpression( String propertyName, String value, Character escapeChar) { this.propertyName = propertyName; this.value = value; this.escapeChar = escapeChar; } public String toSqlString( Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException { Dialect dialect = criteriaQuery.getFactory().getDialect(); String[] columns = criteriaQuery.getColumnsUsingProjection( criteria, propertyName ); if ( columns.length != 1 ) { throw new HibernateException( "Like may only be used with single-column properties" ); } String lhs = lhs(dialect, columns[0]); return lhs + " like ?" + ( escapeChar == null ? "" : " escape ?" ); } public TypedValue[] getTypedValues( Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException { return new TypedValue[] { criteriaQuery.getTypedValue( criteria, propertyName, typedValue(value) ), criteriaQuery.getTypedValue( criteria, propertyName, escapeChar.toString() ) }; } protected String lhs(Dialect dialect, String column) { return column; } protected String typedValue(String value) { return value; } } 

如果您想知道lhs和typedValue方法的用途,那么新的IlikeExpression应该回答这些问题。

 package org.hibernate.criterion; import org.hibernate.dialect.Dialect; public class IlikeExpression extends LikeExpression { protected IlikeExpression( String propertyName, Object value) { super(propertyName, value); } protected IlikeExpression( String propertyName, String value, MatchMode matchMode) { super(propertyName, value, matchMode); } protected IlikeExpression( String propertyName, String value, Character escapeChar) { super(propertyName, value, escapeChar); } @Override protected String lhs(Dialect dialect, String column) { return dialect.getLowercaseFunction() + '(' + column + ')'; } @Override protected String typedValue(String value) { return super.typedValue(value).toLowerCase(); } } 

在此之后,唯一剩下的就是让Restrictions使用这些新类:

 public static Criterion like(String propertyName, Object value) { return new LikeExpression(propertyName, value); } public static Criterion like(String propertyName, String value, MatchMode matchMode) { return new LikeExpression(propertyName, value, matchMode); } public static Criterion like(String propertyName, String value, Character escapeChar) { return new LikeExpression(propertyName, value, escapeChar); } public static Criterion ilike(String propertyName, Object value) { return new IlikeExpression(propertyName, value); } public static Criterion ilike(String propertyName, String value, MatchMode matchMode) { return new IlikeExpression(propertyName, value, matchMode); } public static Criterion ilike(String propertyName, String value, Character escapeChar) { return new IlikeExpression(propertyName, value, escapeChar); } 

编辑:哦,是的。 这适用于Oracle。 但我们不确定其他数据库。

这不是一个非常干净的方法,但sqlRestrinction应该更容易:

 criterions.add(Restrictions.sqlRestriction(columnName+ " ilike '!%' escape '!'")); 

您甚至可以使用相同的原则开始搜索:

 criterions.add(Restrictions.sqlRestriction(columnName+ " ilike '!%%' escape '!'")); 

如果直接使用LikeExpression,则可以指定转义字符。 我认为应该是你所需要的一切。

如果你使用Hibernate 3.2+,你可以LikeExpression ,然后like / ilike方法like创建工厂:

 import org.hibernate.criterion.Criterion; import org.hibernate.criterion.LikeExpression; import org.hibernate.criterion.MatchMode; public class EscapedLikeRestrictions { private EscapedLikeRestrictions() {} public static Criterion likeEscaped(String propertyName, String value, MatchMode matchMode) { return likeEscaped(propertyName, value, matchMode, false); } public static Criterion ilikeEscaped(String propertyName, String value, MatchMode matchMode) { return likeEscaped(propertyName, value, matchMode, true); } private static Criterion likeEscaped(String propertyName, String value, MatchMode matchMode, boolean ignoreCase) { return new LikeExpression(propertyName, escape(value), matchMode, '!', ignoreCase) {/*a trick to call protected constructor*/}; } private static String escape(String value) { return value .replace("!", "!!") .replace("%", "!%") .replace("_", "!_"); } }