Question

Est-il possible d'effectuer un critère de requête à l'aide JPA contient du texte Oracle déclaration, et si oui, comment?

Était-ce utile?

La solution

doute. L'API est là à travers tous les SGBDR, et fournit certaines constructions comme « LIKE » / « SUBSTRING » qui pourrait être mis en correspondance avec quelque chose de cette forme lorsqu'il est utilisé sur Oracle pour une colonne de texte, mais là encore, ils peuvent simplement utiliser standard SQL. Il n'y a aucun moyen standard conforme à insister sur ce que

Autres conseils

Critères prend en charge une API fonction () qui permet une fonction de base de données à appeler par son nom.

qb.gt(qb.function("CONTAINS", root.get("name"), qb.parameter("name"), qb.literal(1)), 1)

EclipseLink prend également en charge cela dans JPQL en utilisant le mot-clé FUNC.

Je viens d'écrire un OracleTextDictionary pour openjpa, que les convertis ordinaires « comme » les opérateurs dans « contient » des opérateurs, lorsque l'argument est préfixé avec un marqueur « magique ».

De cette façon, il est possible d'utiliser QueryDSL ou critères de langue (ou JPQL) avec le texte Oracle.

Le dictionnaire Détecte comme les relevés avec un marqueur magique dans l'argument et ré-écrit le SQL d'utiliser un CTX CONTIENT appel.

Un inconvénient est que le score n'est pas accessible d'une manière simple, mais il serait possible d'améliorer le conducteur à l'ordre par le score. Ne hésitez pas à modifier le code: -)

Je suppose qu'il est possible de port mise en veille prolongée, en supposant qu'il existe un mécanisme similaire pour les requêtes de base de données à un réglage spécifique db.

package se.grynna.dict;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
import org.apache.openjpa.jdbc.sql.OracleDictionary;
import org.apache.openjpa.jdbc.sql.SQLBuffer;
import org.apache.openjpa.jdbc.sql.Select;

public class OracleTextDictionary extends OracleDictionary {

    public static final String CTX_MAGIC_MARKER = "@CTX@";
    final static Pattern likePattern = Pattern
        .compile("t(\\d+)\\.(\\S+) LIKE (\\?)");


    @Override
    protected SQLBuffer toSelect(SQLBuffer select,
      JDBCFetchConfiguration fetch, SQLBuffer tables, SQLBuffer where,
      SQLBuffer group, SQLBuffer having, SQLBuffer order,
      boolean distinct, boolean forUpdate, long start, long end,Select sel) {

        SQLBuffer sqlBuffer = super.toSelect(select, fetch, tables, where,
          group, having, order, distinct, forUpdate, start, end, sel);

        SQLBuffer tmpBuf = sqlBuffer;

        String sql = tmpBuf.getSQL();

        int label = 1;

        for (Matcher m = likePattern.matcher(sql); m.find(); sql = tmpBuf.getSQL()) {


        int argPos = m.start(3);
        int argIdx = findArgIdx(sql, argPos);
        Object o = tmpBuf.getParameters().get(argIdx);
        if( o == null) break;
        String arg = o.toString();

        if (arg.startsWith(CTX_MAGIC_MARKER)) {

            if (tmpBuf == sqlBuffer) {
                tmpBuf = new SQLBuffer(sqlBuffer);
            }


        arg = arg.substring(CTX_MAGIC_MARKER.length());
        setParameter(tmpBuf, argIdx, arg);

        String aliasNo = m.group(1);
        String colName = m.group(2);

        }

        String replace = String.format("(CONTAINS(t%s.%s,?,%d)>0)",
                    aliasNo, colName, label++);
        tmpBuf.replaceSqlString(m.start(), m.end(), replace);
                m.reset(tmpBuf.getSQL());
        }

      }

    return tmpBuf;
    }

    @SuppressWarnings("unchecked")
    private void setParameter(SQLBuffer tmpBuf, int argIdx, String arg) {
        tmpBuf.getParameters().set(argIdx, arg);

    }

    private int findArgIdx(String sql, int argPos) {
        int count = -1;
        for (int i = 0; i <= argPos; i++) {
            char c = sql.charAt(i);
            if (c == '?') {
                count++;
            }
        }
        return count;
    }



}

Exemple: Le tableau suivant donne entrée (évidemment ménagé) est appelée avec les paramètres suivants:

:1 "@CTX@omg near ponies"
:2 "@CTX@rainbow"
:3 "@CTX@rain%"
:4 "abc1%"                     <-- an ordinary like :-)
:5 "@CTX@mushroom%"  

JPQL

select distinct customer
from Customer customer
where customer.custName like :a1 and customer.custName like :a2 and customer.custName like :a1 and customer.custId in (select d.custId
from Customer d
where d.custName like :a3 or d.custName like :a1)

SQL

SELECT t0.custId,
  t0.custName
FROM Customer t0
WHERE ((CONTAINS(t0.custName,?,1)>1)
AND (CONTAINS(t0.custName,?,2)   >1)
AND (CONTAINS(t0.custName,?,3)   >1)
AND t0.custId                   IN
  (SELECT t1.custId
  FROM Customer t1
  WHERE (t1.custName LIKE ?              <---- the like survives....
  OR (CONTAINS(t1.custName,?,1)>1))
  ))
AND ROWNUM <= ?

Comme une note de côté:. QueryDsl a bel et bien un opérateur de'contains, soi-disant pour le back-end Lucene, pour lequel la JPA et backends sql génère une 'déclaration comme'

Je ne l'ai pas trouvé un moyen de surcharger l'opérateur contient, de sorte qu'il peut être utilisé. (Autres que de réécrire le code, que je ne peux pas faire depuis que je suis en utilisant la version fournie avec WebSphere.)

Alors, je recours à une petite méthode statique pour le faire bien paraître lors de l'utilisation QuertyDSL.

// x.where(c.custName.like(CTX.contains("omg near ponies"))));

Il serait encore mieux si JPQL pourrait fournir des abstractions (ou plug-ins) pour les moteurs de recherche en texte intégral ...

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top