16

この質問では、クエリが複数のデータベース間で異なる必要があるため、特別なクエリを作成する必要はありません。休止状態の基準のみを使用して、特殊文字をエスケープできるようにしたいと考えています。

この状況が、特殊文字をエスケープする機能が必要な理由です。

データベースにテーブル「foo」があるとします。テーブル 'foo' には、'name' という名前のフィールドが 1 つだけ含まれています。「名前」フィールドには、データベースで特殊と見なされる可能性のある文字を含めることができます。そのような名前の 2 つの例は、'name_1' と 'name%1' です。'_' と '%' は両方とも、少なくとも Oracle では特殊文字です。ユーザーがこれらの例をデータベースに入力した後で検索しようとすると、問題が発生する可能性があります。

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

このコードの「searchValue」は、ユーザーがアプリケーションに指定した、検索に使用する値です。ユーザーが「%」を検索したい場合、ユーザーはデータベース内のすべての「foo」エントリで返されます。これは、「%」文字が文字列一致の「任意の数の文字」ワイルドカードを表し、休止状態が生成する SQL コードが次のようになるためです。

select * from foo where name like '%' 

特定の文字をエスケープするように休止状態に指示する方法、またはデータベースの種類に固有ではない回避策を作成する方法はありますか?

4

5 に答える 5

13

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();
    }

}

この後、残っているのは、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で機能します。ただし、他のデータベースについてはよくわかりません。

于 2009-07-10T20:27:44.797 に答える
4

これを行うのはあまりクリーンな方法ではありませんが、sqlRestrinctionの方が簡単なはずです。

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

同じ原則を使用して、検索から開始することもできます。

criterions.add(Restrictions.sqlRestriction(columnName+ " ilike '!%%' escape '!'"));
于 2011-08-01T16:14:57.300 に答える
2

Hibernate 3.2+ を使用している場合は、 をサブクラス化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("_", "!_");
    }
}
于 2016-02-23T17:33:32.660 に答える