簡體   English   中英

錯誤的 SQL 語法 - Spring 數據 R2DBC 與 Oracle

[英]Bad SQL grammar - Spring data R2DBC with Oracle

我正在使用 Spring 數據 R2DBC 和 Oracle 11g,使用findByIdR2dbcCrudRepository方法出現以下錯誤

executeMany; bad SQL grammar [SELECT GAME_PHASE.* FROM GAME_PHASE WHERE GAME_PHASE.ID = :P0_id FETCH FIRST 2 ROWS ONLY]

這是存儲庫聲明

public interface ReactiveGamePhaseRepository extends R2dbcRepository<GamePhase, Long> {
}

我不明白為什么FETCH FIRST 2 ROWS ONLY被添加到查詢中,這是問題的原因。

我在使用 R2dbcEntityTemplate 編寫查詢時遇到了同樣的問題,如下所示:

r2dbcEntityTemplate.selectOne(query(where("id").is(id)), GamePhase.class);

這些是使用的依賴項:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-r2dbc</artifactId>
    <version>2.5.12</version>
</dependency>

<dependency>
    <groupId>com.oracle.database.r2dbc</groupId>
    <artifactId>oracle-r2dbc</artifactId>
    <version>1.0.0</version>
</dependency>

我對您標記的產品一無所知(Oracle 除外),所以我不知道這是否有幫助。 希望它會)。


FETCH FIRST 2 ROWS ONLY添加到查詢中,這是問題的原因。

那是因為 - 我想 - 您的 Oracle 數據庫版本不支持該語法。 我猜它是11g或更低。 查看演示:

Oracle 11g:

SQL> select banner from v$version;

BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
PL/SQL Release 11.2.0.4.0 - Production
CORE    11.2.0.4.0      Production
TNS for Linux: Version 11.2.0.4.0 - Production
NLSRTL Version 11.2.0.4.0 - Production

SQL> SELECT *
  2    FROM emp
  3   WHERE deptno = 10
  4   FETCH FIRST 2 ROWS ONLY;
 FETCH FIRST 2 ROWS ONLY
 *
ERROR at line 4:
ORA-00933: SQL command not properly ended         --> that's your "bad grammar"

Oracle 18c:

SQL> select banner from v$version;

BANNER
--------------------------------------------------------------------------------
Oracle Database 18c Express Edition Release 18.0.0.0.0 - Production

SQL> SELECT *
  2    FROM emp
  3   WHERE deptno = 10
  4   FETCH FIRST 2 ROWS ONLY;

     EMPNO ENAME      JOB              MGR HIREDATE        SAL       COMM     DEPTNO
---------- ---------- --------- ---------- -------- ---------- ---------- ----------
      7782 CLARK      MANAGER         7839 09.06.81       2450                    10
      7839 KING       PRESIDENT            17.11.81       5000                    10

SQL>

所以,你可以做什么? 重寫查詢(如果可能)。 例如:

SQL> WITH
  2     temp
  3     AS
  4        (SELECT e.*, ROW_NUMBER () OVER (ORDER BY NULL) rn
  5           FROM emp e
  6          WHERE deptno = 10)
  7  SELECT *
  8    FROM temp
  9   WHERE rn <= 2;

     EMPNO ENAME      JOB              MGR HIREDATE        SAL       COMM     DEPTNO         RN
---------- ---------- --------- ---------- -------- ---------- ---------- ---------- ----------
      7782 CLARK      MANAGER         7839 09.06.81       2818          1         10          1
      7839 KING       PRESIDENT            17.11.81       5750          1         10          2

SQL>

(請注意,如果沒有ORDER BY子句,您將無法真正知道將返回哪些行。如果您對任何兩行感到滿意,那么可以。如果不滿意,請包括ORDER BY )。

我認為 Littlefoot 做得很好,解釋了為什么這會給你帶來問題。 讓我先解釋一下為什么該條款存在。

selectOne有以下合同

執行 SELECT 查詢並將結果項轉換為實體,以確保只有一個結果。

這意味着它需要嘗試獲取至少 2 個元素以確保實際上沒有第二個元素。 這可以通過選擇所有匹配的行來完成。 但它永遠不會查看第二行以外的任何內容,因此所有其他行都將被徒勞選擇。 可能更糟糕的是,當數據庫嘗試加載所有行與僅嘗試獲取前幾行時,執行計划可能會有很大差異。

因此限制應用於查詢。

它使用適用於 Oracle 更高版本的語法,因為這些是 Spring 數據 JDBC 支持的唯一語法。

您可以通過創建和注冊自己的方言來避免該問題,該方言不支持任何類型的限制或分頁,並將 ist 替換為 noop。 當然,您需要確保不要嘗試在應用程序中使用這些功能,因為它們顯然不起作用。 由於舊 Oracle 版本需要有趣的語法來正確實現這一點,因此很難正確實現舊 Oracle 版本的分頁和限制。

更好的選擇是遷移到 Oracle 的最新版本,盡管眾所周知,由於各種原因,這通常並不容易。

當然,您始終可以選擇使用@Query注釋或自定義方法在代碼中創建 SQL 語句。

If you're willing to use a third party library, you could translate the Oracle 12c compatible SQL using FETCH FIRST to Oracle 11g compatible SQL using jOOQ's ParsingConnection , which acts like an R2DBC Connection proxy, translating all incoming SQL to the target dialect. 在 Oracle 11g 上,將通過ROWNUM過濾生成FETCH子句

您顯然也可以通過 jOOQ API 使用替換您的 Spring 數據使用,以自動獲得正確的行為。 jOOQ 開箱即用地支持 R2DBC

如果這不是一個選項,那么您將不得不手動編寫 SQL,而不是依賴 Spring 數據或其他第三方的 DSL API。

免責聲明:我在 jOOQ 背后的公司工作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM