Frage

Ist es möglich, eine JPA -Kriterienquery mithilfe von Oracle Text zu erfolgen, die Anweisung enthält, und wenn ja, wie?

War es hilfreich?

Lösung

Bezweifel es. Die API gibt Standard Sql. Es gibt keine standardmäßige konforme Möglichkeit, darauf zu bestehen

Andere Tipps

Die Kriterien unterstützen eine Funktion () () API, mit der eine Datenbankfunktion mit Namen aufgerufen werden kann.

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

EclipSelink unterstützt dies auch in JPQL mit dem Func -Schlüsselwort.

Ich habe gerade ein OracletTextDictionary für OpenJPA geschrieben, das gewöhnliche "Operatoren" -Personen in "Operatoren" umwandelt, wenn das Argument mit einem "magischen" Marker vorangestellt ist.

Auf diese Weise ist es möglich, QueryDSL- oder Kriteriensprache (oder JPQL) mit Oracle -Text zu verwenden.

Das Wörterbuch erkennt wie Aussagen mit einem magischen Marker im Argument und schreibt die SQL zur Verwendung eines CTX -Aufrufs neu.

Ein Nachteil ist, dass die Punktzahl auf einfache Weise nicht zugänglich ist, aber es wäre möglich, den Treiber für die Bestellung durch die Punktzahl zu verbessern. Fühlen Sie sich frei, den Code zu bearbeiten :-)

Ich würde annehmen, dass es möglich ist, einen Hibernate zu verbringen, da es einen ähnlichen Mechanismus zum Abtun von Datenbankabfragen auf eine bestimmte DB gibt.

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



}

Beispiel: Die folgende (offensichtlich erfundene) Eingabe wird mit den Parametern aufgerufen:

: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 <= ?

Als Randnotiz: QueryDSL hat tatsächlich einen ´Containes 'Operator, angeblich für das Lucene -Backend, für das die JPA- und SQL -Backends eine "ähnliche" Aussage generieren.

Ich habe keinen Weg herausgefunden, den enthaltenden Operator zu überladen, damit er verwendet werden kann. (Abgesehen von dem Code, den ich nicht umschreiben kann, da ich die mit WebSphere gebündete Version verwende.)

Ich greife also auf eine kleine statische Methode zurück, damit sie bei der Verwendung von QuertyDSL gut aussieht.

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

Es wäre noch schöner, wenn JPQL einige Abstraktionen (oder Plugins) für Volltext -Suchmaschinen liefern könnte ...

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top