2011-10-18 37 views

Trả lời

2

Nghi ngờ điều đó. API có trên tất cả RDBMS và cung cấp một số cấu trúc như "LIKE"/"SUBSTRING" có thể được ánh xạ tới biểu mẫu đó khi được sử dụng trên Oracle cho cột TEXT, nhưng sau đó lại có thể chỉ sử dụng tiêu chuẩn SQL. Không có cách nào tuân thủ tiêu chuẩn để nhấn mạnh rằng

+1

Vì vậy, tôi đoán tôi phải sử dụng truy vấn gốc JPA bằng cách sử dụng chuỗi nối và an toàn loại bỏ. Ouch. – Ryan

2

Tôi vừa viết một OracleTextDictionary cho openjpa, chuyển đổi toán tử 'like' thành các toán tử 'contains', khi đối số được đặt trước bằng dấu "huyền diệu".

Bằng cách này, bạn có thể sử dụng QueryDSL hoặc Ngôn ngữ tiêu chí (hoặc JPQL) với văn bản Oracle.

Từ điển phát hiện các câu lệnh LIKE với một điểm đánh dấu ma thuật trong đối số và viết lại SQL để sử dụng lệnh gọi CTX CONTAINS.

Một nhược điểm là điểm không thể truy cập theo cách đơn giản, nhưng có thể tăng cường trình điều khiển theo thứ tự theo điểm số. Vui lòng chỉnh sửa mã :-)

Tôi cho rằng có thể chuyển sang chế độ ngủ đông, giả sử có cơ chế tương tự để điều chỉnh truy vấn cơ sở dữ liệu cho một db cụ thể.

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



} 

Ví dụ: Sau đây (rõ ràng là giả tạo) đầu vào sản xuất được gọi với các thông số:

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

Như một mặt lưu ý : QueryDsl thực sự có một toán tử ´contains ', được cho là phụ trợ Lucene, trong đó các chương trình phụ trợ của jpa và sql tạo ra một câu lệnh' like '.

Tôi chưa tìm ra cách để quá tải toán tử chứa, để nó có thể được sử dụng. (Khác với viết lại mã, mà tôi không thể làm được vì tôi đang sử dụng phiên bản đi kèm với WebSphere.)

Vì vậy, tôi sử dụng phương pháp tĩnh nhỏ để làm cho nó trông đẹp khi sử dụng QuertyDSL.

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

Nó sẽ càng đẹp hơn nếu JPQL có thể cung cấp một số trừu tượng (hoặc bổ sung) cho công cụ tìm kiếm toàn văn ...

10

Tiêu chuẩn hỗ trợ API function() cho phép một hàm cơ sở dữ liệu được gọi bằng Tên.

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

EclipseLink cũng hỗ trợ điều này trong JPQL bằng từ khóa FUNC.

+0

Điều này có vẻ đầy hứa hẹn. Tôi đã không thử nó mặc dù. – Ryan

+0

Điều đó mang lại cho tôi Lỗi SQL: ORA-29909: nhãn cho toán tử phụ trợ không phải là số chữ. Bất kỳ ý tưởng? – Tomasz

+0

Tìm thấy nó. query.setHint (QueryHints.BIND_PARAMETERS, HintValues.FALSE); – Tomasz

Các vấn đề liên quan