Pregunta

¿Es posible realizar una consulta de criterios de JPA usando la declaración de contenido de Oracle Text, y de ser así, ¿cómo?

¿Fue útil?

Solución

Dudo. La API está ahí en todos los RDBMS, y proporciona ciertas construcciones como "como"/"subcadena" que podría asignarse a algo de esa forma cuando se usa en Oracle para una columna de texto, pero nuevamente pueden usar estándar SQL. No hay una forma que cumpla estándar de insistir en eso

Otros consejos

Criterios admite una API functamen () que permite que una función de base de datos se llame por nombre.

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

ECLIPSELINK también admite esto en JPQL usando la palabra clave FUNC.

Acabo de escribir un OracletExtDictionary para OpenJPA, que convierte los operadores ordinarios de 'operadores en' contiene ', cuando el argumento tiene prefijo un marcador "mágico".

De esta manera, es posible utilizar el lenguaje de consulta o criterio (o JPQL) con el texto de Oracle.

El diccionario detecta declaraciones similares con un marcador mágico en el argumento, y reescribe el SQL para usar un CTX contiene una llamada.

Un inconveniente es que el puntaje no es accesible de una manera simple, pero sería posible mejorar el controlador para ordenar por el puntaje. No dude en editar el código :-)

Supongo que es posible puerto para hibernar, suponiendo que haya un mecanismo similar para ajustar las consultas de la base de datos a un DB específico.

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



}

Ejemplo: la siguiente entrada (obviamente artificial) produce con los parámetros:

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

Como nota al margen: la consulta en realidad tiene un operador de "Contains", supuestamente para el backend de Lucene, para el cual el JPA y SQL Backends genera una declaración "similar".

No he descubierto una forma de sobrecargar el operador Contiene, para que pueda usarse. (Aparte de reescribir el código, que no puedo hacer, ya que estoy usando la versión incluida con WebSphere).

Por lo tanto, recurro a un pequeño método estático para que se vea bien al usar quertydsl.

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

Sería aún más agradable si JPQL pudiera proporcionar algunas abstracciones (o complementos) para motores de búsqueda de texto completo ...

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top