Pergunta

Para esta pergunta, queremos evitar ter que escrever uma consulta especial, pois a consulta teria de ser diferente em vários bancos de dados. Usando apenas hibernate critérios, nós queremos ser capazes de escapar caracteres especiais.

Esta situação é a razão para a necessidade da capacidade de escapar caracteres especiais:

Suponha que tenhamos tabela 'foo' no banco de dados. Tabela 'foo' contém apenas 1 campo, chamado de 'nome'. O campo 'nome' pode conter caracteres que podem ser considerados especial em um banco de dados. Dois exemplos de tal nome um são 'nome_1' e 'nome% 1'. Tanto o '_' e '%' são caracteres especiais, pelo menos em Oracle. Se um usuário quiser procurar um desses exemplos depois que eles são inseridos no banco de dados, podem ocorrer problemas.

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

Neste código, 'searchValue' é o valor que o utilizador tenha dado o aplicativo para usar para a sua pesquisa. Se o usuário quiser procurar '%', o usuário vai ser devolvido com cada entrada 'foo' no banco de dados. Isso ocorre porque o caractere '%' representa a "qualquer número de caracteres" wildcard para correspondência de corda e o código SQL que o Hibernate produz será algo como:

select * from foo where name like '%' 

Existe uma maneira de dizer hibernate para escapar certos caracteres, ou para criar uma solução que não é o tipo de banco de dados específico?

Foi útil?

Solução

construtores de LikeExpression são todos protegidos, por isso não é uma opção viável. Além disso, tem seus próprios problemas .

Um colega e eu criado um patch que funciona muito bem. A essência do patch é que, para o construtor LikeExpression que consome MatchMode, nós escapar os caracteres especiais. Para o construtor que consome um caractere (o caractere de escape), assumimos que o usuário escapa os caracteres especiais por conta própria.

Nós também parametrizado o caractere de escape para garantir que ele não pode corromper a consulta SQL se usarem algo como \ ou um carácter de citação.

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

}

Se você está se perguntando o que os lhs e métodos typedValue são para, a nova IlikeExpression deve responder a essas perguntas.

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

}

Depois disso, a única coisa que resta é fazer com que as restrições de uso destas novas classes:

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

Edit: Ah, sim. Isso funciona para Oracle. Não temos certeza sobre outras bases de dados embora.

Outras dicas

Não é uma forma muito limpa para fazer isso, mas uma sqlRestrinction deve ser mais fácil:

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

Você pode até fazer um começo com a pesquisa usando o mesmo princípio:

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

Se você usar LikeExpression diretamente, ele permite que você especifique o caractere de escape. Presumo que deve ser tudo que você precisa.

Se você usar Hibernate 3.2+, você pode subclasse LikeExpression e crie like métodos de fábrica / 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("_", "!_");
    }
}

Se você usar sqlRectrictions, a maneira correta de fazê-lo é o seguinte:

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

É como uma consulta SQL, não trabalho ilike => utilização como usar o Oracle 12i.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top