Spring Data JPA @Query 定義中的 SpEL 支援

工程 | Thomas Darimont | 2014 年 7 月 15 日 | ...

Spring Data JPA 允許使用 @Query 註解手動定義儲存庫方法要執行的查詢。不幸的是,JPQL 中的參數綁定非常有限,僅允許您設定值並提供一些類型轉換。最新的 Spring Data JPA M1 Evans 發布列車版本,透過在 @Query 註解中的語句內新增對使用 SpEL 表達式來動態綁定參數的支援,緩解了這種痛點,這在手動定義查詢時提供了額外的彈性。在這篇部落格文章中,我將向您介紹此功能的功能。

方法參數表達式

SpEL 支援提供對查詢方法引數的存取權。這允許您直接綁定參數,或在綁定前執行額外操作。

@Query("select u from User u where u.age = ?#{[0]}")
List<User> findUsersByAge(int age);

@Query("select u from User u where u.firstname = :#{#customer.firstname}")
List<User> findUsersByCustomersFirstname(@Param("customer") Customer customer);

參數透過索引存取 (第一個方法中的 [0]) 或透過使用 @Param 宣告的名稱公開。實際的 SpEL 表達式綁定由 ?#:# 觸發。我們同時支援這兩種型別,以使您與標準 JPQL 參數綁定保持一致,這些綁定也可能出現在查詢定義中。特殊型別的參數,例如 SortPageable,以其簡單的類別名稱作為變數公開。

進階 SpEL 表達式

雖然進階參數綁定是一個非常有用的功能,但 SpEL 的真正威力來自於表達式可以引用框架抽象或其他應用程式元件的事實。SpEL 的一個非常常見的場景是安全約束的定義。因此,如果我們可以限制查詢僅傳回與目前已驗證使用者相關的結果,那就太好了

@Query("select u from User u where u.emailAddress = ?#{principal.emailAddress}")
List<User> findCurrentUserWithCustomQuery();

如您所見,我們引用了 Spring Security 的 principal 的屬性。那麼 Spring Data SpEL 支援如何與 Spring Security 整合呢?

SpEL EvaluationContext 擴充模型

Spring Data 公開了一個擴充點 EvaluationContextExtension。此介面允許實作者以非常詳細的方式自訂 EvaluationContext,但為了方便起見,我們提供了一個 EvaluationContextExtensionSupport 基礎類別,方便您僅實作您感興趣的部分

class SecurityEvaluationContextExtension extends EvaluationContextExtensionSupport {

  @Override
  public String getExtensionId() {
    return "security";
  }

  @Override
  public SecurityExpressionRoot getRootObject() {
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    return new SecurityExpressionRoot(authentication) {};
  }
}

對於我們的 Spring Security 擴充,我們擴充了 EvaluationContextExtensionSupport 並覆寫了 getRootObject() 方法,並傳回一個新的 SecurityExpressionRoot 實例,該實例公開了您已經從 @PreAuthorize 中的用法中了解的所有安全性屬性和表達式。此步驟也使它們在我們的 @Query 註解中的 SpEL 表達式中可用。

我們需要採取的最後一步是將安全性擴充註冊為 bean

@Configuration
@EnableJpaRepositories
class SecurityConfiguration {

    @Bean
    EvaluationContextExtension securityExtension() {
        return new SecurityEvaluationContextExtension();
    }
}

Spring Data JPA 將會選取所有型別為 EvaluationContextExtension 的 bean,並使用它們來準備 EvaluationContext,以便用於評估在 @Query 中定義的 SpEL 表達式。

就位的擴充現在將讓您充分利用 Spring Security SpEL 功能的強大功能。想像一下,一個儲存庫查詢方法應傳回目前使用者擁有的 BusinessObject,如果目前使用者是管理員,則傳回所有 BusinessObject。查詢方法定義如下所示

interface SecureBusinessObjectRepository extends Repository<BusinessObject,Long>{

    @Query("select o from BusinessObject o where o.owner.emailAddress like "+
      "?#{hasRole('ROLE_ADMIN') ? '%' : principal.emailAddress}")
    List<BusinessObject> findBusinessObjectsForCurrentUser();
}

您可以在 Spring-Data-Examples 儲存庫中找到此處看到的程式碼片段的工作範例。

複雜的使用案例

新功能通常會啟用以前認為不可能的事情的實現方式 - 其中一個例子是在原生查詢中的分頁。由於此機制也公開了 SortPageable 等特殊參數類型,我們現在可以在原生查詢中使用分頁。此處可以找到一個範例 UserRepository

接下來是什麼?

目前,我們正在研究將 Spring Security 更緊密地整合到 Spring Data 中。我們也正在努力為其他 Spring Data 模組新增 SpEL 功能的支援。

現在輪到您了 - 請在下面的評論中告訴我們您的想法,或在我們的 JIRA 中提交功能請求。

SpringOne 2GX 2014

SpringOne 2GX 2014 即將到來。

如果您想了解有關 Spring Data 的更多資訊,請務必註冊今年的 SpringOne 會議。議程包含許多與資料相關的演講,向您介紹我們即將隨 Evans 發佈的最新功能。

取得 Spring 電子報

與 Spring 電子報保持聯繫

訂閱

領先一步

VMware 提供培訓和認證,以加速您的進展。

了解更多

取得支援

Tanzu Spring 在一個簡單的訂閱中提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位檔案。

了解更多

即將到來的活動

查看 Spring 社群中所有即將到來的活動。

查看所有