2011-10-18 13 views

答えて

2

APIはすべてのRDBMSに存在し、 "LIKE"/"SUBSTRING"のような構造を提供しています。これは、TEXT列に対してOracle上で使用されたときにその形式にマップできますが、再び標準 SQLを使用します。

+1

だから、私はJPAネイティブクエリーに文字列連結とforgoタイプの安全性を使用しなければならないと思います。ああ。 – Ryan

2

openjpa用のOracleTextDictionaryを作成しました。引数に「マジカル」マーカーを前置すると、通常の「like」演算子が「contains」演算子に変換されました。

このようにして、QueryDSLまたはCriteria Language(またはJPQL)をOracleテキストとともに使用することができます。

ディクショナリは、引数にマジックマーカーを含むLIKE文を検出し、SQLを書き換えてCTX CONTAINSコールを使用します。

1つの欠点は、簡単な方法でスコアにアクセスできないことですが、スコアで注文するようにドライバを強化することが可能です。

特定のデータベースへのデータベースクエリをチューニングするための同様のメカニズムがあると仮定して、私はそれを休止状態にすることは可能です。

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 = "@[email protected]"; 
    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 "@[email protected] near ponies" 
:2 "@[email protected]" 
:3 "@[email protected]%" 
:4 "abc1%"      <-- an ordinary like :-) 
:5 "@[email protected]%" 

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は実際にjpaとsqlのバックエンドが 'like'ステートメントを生成する、Luceneバックエンドのための'contains '演算子があります。

私はcontains演算子をオーバーロードする方法を理解していないので、それを使うことができます。 (WebSphereにバンドルされているバージョンを使用しているので、コードを書き直す以外にはできません。)

したがって、私はQuertyDSLを使用するときれいに見えるように小さな静的メソッドに頼っています。

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

JPQLは、いくつかの抽象化(またはプラグイン)を提供することができれば、それは、フルテキスト検索エンジンのためにも、よりよいだろう...

10

基準は、データベース機能がによって呼び出されることを可能にする機能()APIをサポートしています名。

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

また、EclipseLinkでは、FUNCキーワードを使用してJPQLでこれをサポートしています。

+0

これは有望そうです。私はそれを試していない。 – Ryan

+0

それは私にSQLエラーを与えます:ORA-29909:補助演算子のラベルはリテラル番号ではありません。何か案は? – Tomasz

+0

見つけました。 query.setHint(QueryHints.BIND_PARAMETERS、HintValues.FALSE); – Tomasz

関連する問題