休止状態の基準を使用して、特殊文字をエスケープする方法はありますか?
質問
この問題については、我々は、クエリが複数のデータベース間で異なるでなければならないので、特別なクエリを記述することを避けるためにしたいです。のみ休止基準を用いて、我々は特殊文字をエスケープすることができるようにしたい。
このような状況では、特殊文字をエスケープする能力を必要とする理由はあります:
私たちは、データベース内のテーブル「foo」を持っていると仮定します。テーブル「fooが」「名前」と呼ばれる1つだけのフィールドが含まれています。 「名前」フィールドには、データベースに特別な考慮することができる文字を含めることができます。そのような名前の2つの例は、「NAME_1」と「名前%1」です。どちらも「_」と「%」は、少なくともオラクルでは、特殊文字です。これらはデータベースに入力された後、ユーザはこれらの例のいずれかを検索したい場合は、問題が発生する可能性があります。
criterion = Restrictions.ilike("name", searchValue, MatchMode.ANYWHERE);
return findByCriteria(null, criterion);
このコードでは、「searchValue」は、ユーザが検索に使用するようにアプリケーションを与えた値です。ユーザーは「%」を検索したい場合は、ユーザーはデータベース内のすべての「FOO」エントリで返されることになるだろう。
:「%」文字は、「任意の数の文字が」文字列マッチングと生産休止SQLコードのワイルドカードは次のようになりますを示しているためですselect * from foo where name like '%'
特定の文字をエスケープするために休止状態、または特定のデータベースタイプではありません回避策を作成するために伝えるためにそこに方法は何ですか?
解決
LikeExpressionのコンストラクタはすべて、保護されています。また、それは、独自するの問題があります。
の同僚と私はかなりうまく機能パッチを作成しました。パッチの要旨はMatchModeを消費LikeExpressionコンストラクタのために、我々は特殊文字をエスケープすることです。文字(エスケープ文字)を消費コンストラクタのために、我々は、ユーザーが自分で特殊文字をエスケープすると仮定します。
私たちはまた、彼らは\または引用符のようなものを使用している場合、それが破損していないことを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();
}
}
この後、残された唯一のものは、制限事項がこれらの新しいクラスを使用することです。
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
をサブクラス化して、工場like
/ ilike
メソッドを作成することができます:
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("_", "!_");
}
}
、それを行うための正しい方法は次のとおりです。
criterions.add(Restrictions.sqlRestriction(columnName+" LIKE '!%' ESCAPE '!'"));
は動作しませんiLikeの=>は、Oracle 12iとを使用してLIKE使用します。
、SQLクエリのようなものです