En utilisant les critères de mise en veille prolongée, est-il un moyen d'échapper à des caractères spéciaux?

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

Question

Pour cette question, nous voulons éviter d'avoir à écrire une requête spéciale puisque la requête devrait être différente sur plusieurs bases de données. En utilisant des critères hiberner seulement, nous voulons être en mesure d'échapper à des caractères spéciaux.

Cette situation est la raison pour avoir besoin de la capacité d'échapper à des caractères spéciaux:

Supposons que nous ayons table « foo » dans la base de données. La table « foo » ne contient que 1 champ, appelé « nom ». Le champ « nom » peut contenir des caractères qui peuvent être considérés comme spéciaux dans une base de données. Deux exemples de ce nom sont « NAME_1 » et « nom% 1 ». Tant le « _ » et « % » sont des caractères spéciaux, au moins dans Oracle. Si un utilisateur veut rechercher un de ces exemples après leur entrée dans la base de données, les problèmes peuvent se produire.

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

Dans ce code, « searchValue » est la valeur que l'utilisateur a donné l'application à utiliser pour la recherche. Si l'utilisateur veut rechercher « % », l'utilisateur va être retourné avec chaque entrée « foo » dans la base de données. En effet, le caractère « % » représente « un certain nombre de caractères » joker pour la correspondance de chaîne et le code SQL qui hibernent produit ressemblera à ceci:

select * from foo where name like '%' 

Y at-il un moyen de mise en veille prolongée dire pour échapper à certains personnages, ou pour créer une solution de contournement qui ne sont pas le type de base de données spécifique?

Était-ce utile?

La solution

constructeurs de LikeExpression sont tous protégés, il est donc pas une option viable. En outre, il a .

Un collègue et moi avons créé un patch qui fonctionne assez bien. L'essentiel du patch est que pour le constructeur LikeExpression qui consomme un MatchMode, nous échapper les caractères spéciaux. Pour le constructeur, qui consomme un caractère (le caractère d'échappement), nous supposons que l'utilisateur échappe aux caractères spéciaux eux-mêmes.

Nous avons également paramétrées le caractère d'échappement pour assurer qu'il ne peut pas corrompre la requête SQL s'ils utilisent quelque chose comme \ ou un caractère de citation.

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

}

Si vous vous demandez ce que les LHS et les méthodes sont TypedValue pour la nouvelle IlikeExpression devrait répondre à ces questions.

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

}

Après cela, la seule chose qui reste est de faire restrictions utiliser ces nouvelles 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 oui. Cela fonctionne pour Oracle. Nous ne sommes pas sûr d'autres bases de données cependant.

Autres conseils

Il est pas une façon très propre de le faire, mais un sqlRestrinction devrait être plus facile:

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

Vous pouvez même faire un départ avec la recherche en utilisant le même principe:

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

si vous utilisez LikeExpression directement, il vous permet de spécifier le caractère d'échappement. Je suppose que devrait être tout ce dont vous avez besoin.

Si vous utilisez Hibernate 3.2+, vous pouvez sous-classe LikeExpression, puis créer l'usine like / ilike Méthodes:

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("_", "!_");
    }
}

Si vous utilisez sqlRectrictions, la bonne façon de le faire est le suivant:

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

est comme une requête SQL, ne fonctionne pas iLike => utiliser comme l'utilisation d'Oracle 12i.

scroll top