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

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

Spring Data JPA 允許使用 @Query 註解手動定義由 Repository 方法執行的查詢。遺憾的是,JPQL 中的引數繫結相當有限,只允許設定值並提供一些型別轉換。Evans 釋出列車的最新 Spring Data JPA M1 版本透過新增 SpEL 表示式支援來緩解這一痛點,以便在 @Query 註解語句中使用動態繫結引數,這在手動定義查詢時提供了額外的靈活性。在本篇博文中,我將向您介紹此功能的能力。

方法引數表示式

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,並使用它們準備用於評估 @Query 中定義的 SpEL 表示式的 EvaluationContext

到位的擴充套件現在將讓您充分利用 Spring Security SpEL 函式的全部功能。想象一個 Repository 查詢方法,它應返回當前使用者擁有的 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 Repository 中找到此處所示程式碼片段的實際示例。

複雜用例

新特性常常能實現以前認為不可能的事情——例如在原生查詢中進行分頁。由於該機制也暴露了 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 社群所有即將到來的活動。

檢視全部