Oracle -Textkriterien Abfrage in JPA
-
26-10-2019 - |
Frage
Ist es möglich, eine JPA -Kriterienquery mithilfe von Oracle Text zu erfolgen, die Anweisung enthält, und wenn ja, wie?
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 ...