6

是否可以使用 Oracle Text 的 contains 语句执行 JPA 标准查询,如果可以,如何?

4

3 回答 3

10

Criteria 支持 function() API,它允许按名称调用数据库函数。

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

EclipseLink 还使用 FUNC 关键字在 JPQL 中支持这一点。

于 2011-11-29T18:24:52.000 回答
2

怀疑。该 API 存在于所有 RDBMS 中,并提供某些结构,如“LIKE”/“SUBSTRING”,当在 Oracle 上用于 TEXT 列时,它们可以映射到某种形式的东西,但是它们可能只是使用标准SQL。没有符合标准的方式来坚持这一点

于 2011-10-19T13:06:24.453 回答
2

我刚刚为 openjpa 编写了一个 OracleTextDictionary,当参数以“神奇”标记为前缀时,它将普通的“like”运算符转换为“contains”运算符。

通过这种方式,可以将 QueryDSL 或 Criteria Language(或 JPQL)与 Oracle 文本一起使用。

字典在参数中检测带有魔术标记的 LIKE 语句,并重写 SQL 以使用 CTX CONTAINS 调用。

一个缺点是不能以简单的方式获得分数,但是可以通过分数来增强驱动程序的排序。随意编辑代码:-)

我想可以移植到休眠状态,假设有一种类似的机制可以将数据库查询调整到特定的数据库。

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



}

示例:使用参数调用以下(显然是人为的)输入生成:

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

附带说明:QueryDsl 实际上确实有一个“包含”运算符,据说是用于 Lucene 后端,jpa 和 sql 后端为此生成一个“like”语句。

我还没有想出重载 contains 运算符的方法,以便可以使用它。(除了重写代码,因为我使用的是与 WebSphere 捆绑的版本,所以我不能这样做。)

所以,我使用了一个小的静态方法来使它在使用 QuertyDSL 时看起来不错。

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

如果 jpql 可以为全文搜索引擎提供一些抽象(或插件),那就更好了...

于 2011-11-28T00:30:55.630 回答