최대 절전 모드 기준을 사용하면 특수 캐릭터를 피할 수있는 방법이 있습니까?

StackOverflow https://stackoverflow.com/questions/673508

문제

이 질문에서는 쿼리가 여러 데이터베이스마다 다르기 때문에 특별 쿼리를 쓰지 않아도됩니다. 최대 절전 모드 기준 만 사용하여 특수 문자를 탈출 할 수 있기를 원합니다.

이 상황은 특수 캐릭터를 피할 수있는 능력이 필요한 이유입니다.

데이터베이스에 'foo'테이블이 있다고 가정하십시오. 'foo'테이블에는 '이름'이라는 필드 만 포함되어 있습니다. '이름'필드에는 데이터베이스에서 특별한 것으로 간주 될 수있는 문자가 포함될 수 있습니다. 그러한 이름의 두 가지 예는 '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 '%' 

Hibernate에게 특정 문자를 피하거나 데이터베이스 유형이 아닌 해결 방법을 작성하는 방법이 있습니까?

도움이 되었습니까?

해결책

Likeexpression의 생성자는 모두 보호되므로 실행 가능한 옵션이 아닙니다. 또한, 그것은 있습니다 자체 문제.

동료와 나는 잘 작동하는 패치를 만들었습니다. 패치의 요점은 매치 모드를 소비하는 Likeexpression 생성자의 경우 특수 문자를 피하는 것입니다. 캐릭터 (탈출 문자)를 소비하는 생성자의 경우 사용자가 스스로 특수 캐릭터를 스스로 탈출한다고 가정합니다.

또한 Escape 문자를 매개 변수화하여 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 '!'"));

Like Expression을 직접 사용하면 탈출 문자를 지정할 수 있습니다. 나는 당신이 필요한 전부라고 생각합니다.

최대 절전 모드 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("_", "!_");
    }
}

sqlrectrictions를 사용하는 경우 올바른 방법은 다음과 같습니다.

criterions.add(Restrictions.sqlRestriction(columnName+" LIKE '!%' ESCAPE '!'"));

SQL 쿼리와 같으며 Oracle 12i를 사용하는 것처럼 Ilike => 사용하지 않습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top