Spring Data 擴充套件 (Spring Data Extensions)

本節文件描述了一組 Spring Data 擴充套件,這些擴充套件使 Spring Data 能夠在各種情境中使用。目前,大多數整合都針對 Spring MVC。(This section documents a set of Spring Data extensions that enable Spring Data usage in a variety of contexts. Currently, most of the integration is targeted towards Spring MVC.)

Querydsl 擴充套件 (Querydsl Extension)

Querydsl 是一個框架,它透過其流暢的 API 能夠建構靜態類型的類 SQL 查詢。(Querydsl is a framework that enables the construction of statically typed SQL-like queries through its fluent API.)

多個 Spring Data 模組透過 QuerydslPredicateExecutor 提供與 Querydsl 的整合,如下列範例所示 (Several Spring Data modules offer integration with Querydsl through QuerydslPredicateExecutor, as the following example shows)

QuerydslPredicateExecutor 介面 (QuerydslPredicateExecutor interface)
public interface QuerydslPredicateExecutor<T> {

  Optional<T> findById(Predicate predicate);  (1)

  Iterable<T> findAll(Predicate predicate);   (2)

  long count(Predicate predicate);            (3)

  boolean exists(Predicate predicate);        (4)

  // … more functionality omitted.
}
1 尋找並回傳符合 Predicate 的單一實體。(Finds and returns a single entity matching the Predicate.)
2 尋找並回傳所有符合 Predicate 的實體。(Finds and returns all entities matching the Predicate.)
3 回傳符合 Predicate 的實體數量。(Returns the number of entities matching the Predicate.)
4 回傳是否存在符合 Predicate 的實體。(Returns whether an entity that matches the Predicate exists.)

若要使用 Querydsl 支援,請在您的 repository 介面上擴充 QuerydslPredicateExecutor,如下列範例所示 (To use the Querydsl support, extend QuerydslPredicateExecutor on your repository interface, as the following example shows)

repositories 上的 Querydsl 整合 (Querydsl integration on repositories)
interface UserRepository extends CrudRepository<User, Long>, QuerydslPredicateExecutor<User> {
}

先前的範例讓您可以使用 Querydsl Predicate 實例來撰寫型別安全的查詢,如下列範例所示 (The preceding example lets you write type-safe queries by using Querydsl Predicate instances, as the following example shows)

Predicate predicate = user.firstname.equalsIgnoreCase("dave")
	.and(user.lastname.startsWithIgnoreCase("mathews"));

userRepository.findAll(predicate);

Web 支援 (Web support)

支援 repository 程式設計模型的 Spring Data 模組附帶各種 Web 支援。Web 相關組件需要 Spring MVC JAR 檔案位於類別路徑上。其中一些甚至提供與 Spring HATEOAS 的整合。一般而言,整合支援是透過在您的 JavaConfig 組態類別中使用 @EnableSpringDataWebSupport 註解來啟用,如下列範例所示 (Spring Data modules that support the repository programming model ship with a variety of web support. The web related components require Spring MVC JARs to be on the classpath. Some of them even provide integration with Spring HATEOAS. In general, the integration support is enabled by using the @EnableSpringDataWebSupport annotation in your JavaConfig configuration class, as the following example shows)

啟用 Spring Data Web 支援 (Enabling Spring Data web support)
  • Java

  • XML

@Configuration
@EnableWebMvc
@EnableSpringDataWebSupport
class WebConfiguration {}
<bean class="org.springframework.data.web.config.SpringDataWebConfiguration" />

<!-- If you use Spring HATEOAS, register this one *instead* of the former -->
<bean class="org.springframework.data.web.config.HateoasAwareSpringDataWebConfiguration" />

@EnableSpringDataWebSupport 註解註冊了一些組件。我們稍後在本節中討論這些組件。它也會偵測類別路徑上的 Spring HATEOAS,並註冊整合組件(如果存在)。(The @EnableSpringDataWebSupport annotation registers a few components. We discuss those later in this section. It also detects Spring HATEOAS on the classpath and registers integration components (if present) for it as well.)

基本 Web 支援 (Basic Web Support)

在 XML 中啟用 Spring Data Web 支援 (Enabling Spring Data web support in XML)

前一節 中顯示的組態註冊了一些基本組件 (The configuration shown in the previous section registers a few basic components)

  • 一個 使用 DomainClassConverter 類別,讓 Spring MVC 從請求參數或路徑變數解析 repository 管理的網域類別的實例。(A Using the DomainClassConverter Class to let Spring MVC resolve instances of repository-managed domain classes from request parameters or path variables.)

  • HandlerMethodArgumentResolver 實作,讓 Spring MVC 從請求參數解析 PageableSort 實例。(HandlerMethodArgumentResolver implementations to let Spring MVC resolve Pageable and Sort instances from request parameters.)

  • Jackson 模組,用於反序列化/序列化類型,例如 PointDistance,或儲存特定類型,取決於使用的 Spring Data 模組。(Jackson Modules to de-/serialize types like Point and Distance, or store specific ones, depending on the Spring Data Module used.)

使用 DomainClassConverter 類別 (Using the DomainClassConverter Class)

DomainClassConverter 類別讓您直接在 Spring MVC 控制器方法簽章中使用網域類型,因此您無需透過 repository 手動查閱實例,如下列範例所示 (The DomainClassConverter class lets you use domain types in your Spring MVC controller method signatures directly so that you need not manually lookup the instances through the repository, as the following example shows)

在方法簽章中使用網域類型的 Spring MVC 控制器 (A Spring MVC controller using domain types in method signatures)
@Controller
@RequestMapping("/users")
class UserController {

  @RequestMapping("/{id}")
  String showUserForm(@PathVariable("id") User user, Model model) {

    model.addAttribute("user", user);
    return "userForm";
  }
}

該方法直接接收 User 實例,無需進一步查閱。實例可以透過讓 Spring MVC 首先將路徑變數轉換為網域類別的 id 類型來解析,最終透過在為網域類型註冊的 repository 實例上呼叫 findById(…) 來存取實例。(The method receives a User instance directly, and no further lookup is necessary. The instance can be resolved by letting Spring MVC convert the path variable into the id type of the domain class first and eventually access the instance through calling findById(…) on the repository instance registered for the domain type.)

目前,repository 必須實作 CrudRepository 才能符合轉換的發現資格。(Currently, the repository has to implement CrudRepository to be eligible to be discovered for conversion.)

Pageable 和 Sort 的 HandlerMethodArgumentResolvers (HandlerMethodArgumentResolvers for Pageable and Sort)

前一節 中顯示的組態片段也註冊了 PageableHandlerMethodArgumentResolver 以及 SortHandlerMethodArgumentResolver 的實例。註冊啟用 PageableSort 作為有效的控制器方法引數,如下列範例所示 (The configuration snippet shown in the previous section also registers a PageableHandlerMethodArgumentResolver as well as an instance of SortHandlerMethodArgumentResolver. The registration enables Pageable and Sort as valid controller method arguments, as the following example shows)

使用 Pageable 作為控制器方法引數 (Using Pageable as a controller method argument)
@Controller
@RequestMapping("/users")
class UserController {

  private final UserRepository repository;

  UserController(UserRepository repository) {
    this.repository = repository;
  }

  @RequestMapping
  String showUsers(Model model, Pageable pageable) {

    model.addAttribute("users", repository.findAll(pageable));
    return "users";
  }
}

先前的方法簽章導致 Spring MVC 嘗試使用下列預設組態從請求參數衍生 Pageable 實例 (The preceding method signature causes Spring MVC try to derive a Pageable instance from the request parameters by using the following default configuration)

表 1. 評估 Pageable 實例的請求參數 (Table 1. Request parameters evaluated for Pageable instances)

page

您想要檢索的頁面。0-indexed 且預設為 0。(Page you want to retrieve. 0-indexed and defaults to 0.)

size

您想要檢索的頁面大小。預設為 20。(Size of the page you want to retrieve. Defaults to 20.)

sort

應依格式 property,property(,ASC|DESC)(,IgnoreCase) 排序的屬性。預設排序方向為區分大小寫的升序。如果您想要切換方向或區分大小寫,請使用多個 sort 參數 — 例如,?sort=firstname&sort=lastname,asc&sort=city,ignorecase。(Properties that should be sorted by in the format property,property(,ASC|DESC)(,IgnoreCase). The default sort direction is case-sensitive ascending. Use multiple sort parameters if you want to switch direction or case sensitivity — for example, ?sort=firstname&sort=lastname,asc&sort=city,ignorecase.)

若要自訂此行為,請分別註冊實作 PageableHandlerMethodArgumentResolverCustomizer 介面或 SortHandlerMethodArgumentResolverCustomizer 介面的 bean。將會呼叫其 customize() 方法,讓您變更設定,如下列範例所示 (To customize this behavior, register a bean that implements the PageableHandlerMethodArgumentResolverCustomizer interface or the SortHandlerMethodArgumentResolverCustomizer interface, respectively. Its customize() method gets called, letting you change settings, as the following example shows)

@Bean SortHandlerMethodArgumentResolverCustomizer sortCustomizer() {
    return s -> s.setPropertyDelimiter("<-->");
}

如果設定現有 MethodArgumentResolver 的屬性不足以達到您的目的,請擴充 SpringDataWebConfiguration 或啟用 HATEOAS 的對等項目,覆寫 pageableResolver()sortResolver() 方法,並匯入您的自訂組態檔,而不是使用 @Enable 註解。(If setting the properties of an existing MethodArgumentResolver is not sufficient for your purpose, extend either SpringDataWebConfiguration or the HATEOAS-enabled equivalent, override the pageableResolver() or sortResolver() methods, and import your customized configuration file instead of using the @Enable annotation.)

如果您需要從請求解析多個 PageableSort 實例(例如,用於多個表格),您可以使用 Spring 的 @Qualifier 註解來區分彼此。然後請求參數必須以 ${qualifier}_ 作為前綴。下列範例顯示產生的方法簽章 (If you need multiple Pageable or Sort instances to be resolved from the request (for multiple tables, for example), you can use Spring’s @Qualifier annotation to distinguish one from another. The request parameters then have to be prefixed with ${qualifier}_. The following example shows the resulting method signature)

String showUsers(Model model,
      @Qualifier("thing1") Pageable first,
      @Qualifier("thing2") Pageable second) { … }

您必須填入 thing1_pagething2_page 等。(You have to populate thing1_page, thing2_page, and so on.)

傳遞到方法中的預設 Pageable 等同於 PageRequest.of(0, 20),但您可以使用 Pageable 參數上的 @PageableDefault 註解來自訂它。(The default Pageable passed into the method is equivalent to a PageRequest.of(0, 20), but you can customize it by using the @PageableDefault annotation on the Pageable parameter.)

Page 建立 JSON 表示法 (Creating JSON representations for Page)

Spring MVC 控制器通常會嘗試最終將 Spring Data page 的表示法呈現給用戶端。雖然可以簡單地從處理程式方法回傳 Page 實例,讓 Jackson 原樣呈現它們,但我們強烈建議不要這樣做,因為底層實作類別 PageImpl 是一個網域類型。這表示我們可能想要或必須基於不相關的原因變更其 API,而此類變更可能會以破壞性的方式更改產生的 JSON 表示法。(It’s common for Spring MVC controllers to try to ultimately render a representation of a Spring Data page to clients. While one could simply return Page instances from handler methods to let Jackson render them as is, we strongly recommend against this as the underlying implementation class PageImpl is a domain type. This means we might want or have to change its API for unrelated reasons, and such changes might alter the resulting JSON representation in a breaking way.)

從 Spring Data 3.1 開始,我們開始透過發出警告記錄來暗示此問題。我們仍然最終建議利用 與 Spring HATEOAS 的整合,以獲得完全穩定且啟用超媒體的方式來呈現頁面,讓用戶端可以輕鬆地導航它們。但是從 3.3 版 Spring Data 開始,提供了一種方便使用但不需要包含 Spring HATEOAS 的頁面渲染機制。(With Spring Data 3.1, we started hinting at the problem by issuing a warning log describing the problem. We still ultimately recommend to leverage the integration with Spring HATEOAS for a fully stable and hypermedia-enabled way of rendering pages that easily allow clients to navigate them. But as of version 3.3 Spring Data ships a page rendering mechanism that is convenient to use but does not require the inclusion of Spring HATEOAS.)

使用 Spring Data 的 PagedModel (Using Spring Data' PagedModel)

在核心方面,支援包括 Spring HATEOAS 的簡化版本 PagedModel(位於 org.springframework.data.web 套件中的 Spring Data 版本)。它可以用來包裝 Page 實例,並產生簡化的表示法,該表示法反映了 Spring HATEOAS 建立的結構,但省略了導航連結。(At its core, the support consists of a simplified version of Spring HATEOAS' PagedModel (the Spring Data one located in the org.springframework.data.web package). It can be used to wrap Page instances and result in a simplified representation that reflects the structure established by Spring HATEOAS but omits the navigation links.)

import org.springframework.data.web.PagedModel;

@Controller
class MyController {

  private final MyRepository repository;

  // Constructor ommitted

  @GetMapping("/page")
  PagedModel<?> page(Pageable pageable) {
    return new PagedModel<>(repository.findAll(pageable)); (1)
  }
}
1 Page 實例包裝到 PagedModel 中。(Wraps the Page instance into a PagedModel.)

這將產生如下所示的 JSON 結構 (This will result in a JSON structure looking like this)

{
  "content" : [
     … // Page content rendered here
  ],
  "page" : {
    "size" : 20,
    "totalElements" : 30,
    "totalPages" : 2,
    "number" : 0
  }
}

請注意,文件如何包含一個 page 欄位,該欄位公開了必要的分頁元數據。(Note how the document contains a page field exposing the essential pagination metadata.)

全域啟用簡化的 Page 渲染 (Globally enabling simplified Page rendering)

如果您不想變更所有現有的控制器來新增對應步驟,以回傳 PagedModel 而不是 Page,您可以透過如下調整 @EnableSpringDataWebSupport 來啟用 PageImpl 實例自動轉換為 PagedModel (If you don’t want to change all your existing controllers to add the mapping step to return PagedModel instead of Page you can enable the automatic translation of PageImpl instances into PagedModel by tweaking @EnableSpringDataWebSupport as follows)

@EnableSpringDataWebSupport(pageSerializationMode = VIA_DTO)
class MyConfiguration { }

這將允許您的控制器仍然回傳 Page 實例,並且它們將自動渲染為簡化的表示法 (This will allow your controller to still return Page instances and they will automatically be rendered into the simplified representation)

@Controller
class MyController {

  private final MyRepository repository;

  // Constructor ommitted

  @GetMapping("/page")
  Page<?> page(Pageable pageable) {
    return repository.findAll(pageable);
  }
}

PageSlice 的超媒體支援 (Hypermedia Support for Page and Slice)

Spring HATEOAS 附帶一個表示法模型類別 (PagedModel/SlicedModel),該類別允許使用必要的 Page/Slice 元數據以及連結來豐富 PageSlice 實例的內容,以讓用戶端輕鬆導航頁面。PagePagedModel 的轉換是由 Spring HATEOAS RepresentationModelAssembler 介面的實作完成的,稱為 PagedResourcesAssembler。同樣,可以使用 SlicedResourcesAssemblerSlice 實例轉換為 SlicedModel。下列範例顯示如何將 PagedResourcesAssembler 用作控制器方法引數,因為 SlicedResourcesAssembler 的工作方式完全相同 (Spring HATEOAS ships with a representation model class (PagedModel/SlicedModel) that allows enriching the content of a Page or Slice instance with the necessary Page/Slice metadata as well as links to let the clients easily navigate the pages. The conversion of a Page to a PagedModel is done by an implementation of the Spring HATEOAS RepresentationModelAssembler interface, called the PagedResourcesAssembler. Similarly Slice instances can be converted to a SlicedModel using a SlicedResourcesAssembler. The following example shows how to use a PagedResourcesAssembler as a controller method argument, as the SlicedResourcesAssembler works exactly the same)

使用 PagedResourcesAssembler 作為控制器方法引數 (Using a PagedResourcesAssembler as controller method argument)
@Controller
class PersonController {

  private final PersonRepository repository;

  // Constructor omitted

  @GetMapping("/people")
  HttpEntity<PagedModel<Person>> people(Pageable pageable,
    PagedResourcesAssembler assembler) {

    Page<Person> people = repository.findAll(pageable);
    return ResponseEntity.ok(assembler.toModel(people));
  }
}

啟用組態,如先前的範例所示,讓 PagedResourcesAssembler 可以用作控制器方法引數。在其上呼叫 toModel(…) 具有以下效果 (Enabling the configuration, as shown in the preceding example, lets the PagedResourcesAssembler be used as a controller method argument. Calling toModel(…) on it has the following effects)

  • Page 的內容成為 PagedModel 實例的內容。(The content of the Page becomes the content of the PagedModel instance.)

  • PagedModel 物件會附加一個 PageMetadata 實例,並使用來自 Page 和底層 Pageable 的資訊填入。(The PagedModel object gets a PageMetadata instance attached, and it is populated with information from the Page and the underlying Pageable.)

  • PagedModel 可能會附加 prevnext 連結,具體取決於頁面的狀態。連結指向方法對應的 URI。新增至方法的分頁參數與 PageableHandlerMethodArgumentResolver 的設定相符,以確保連結稍後可以解析。(The PagedModel may get prev and next links attached, depending on the page’s state. The links point to the URI to which the method maps. The pagination parameters added to the method match the setup of the PageableHandlerMethodArgumentResolver to make sure the links can be resolved later.)

假設我們在資料庫中有 30 個 Person 實例。您現在可以觸發請求 (GET localhost:8080/people) 並查看類似於以下的輸出 (Assume we have 30 Person instances in the database. You can now trigger a request (GET localhost:8080/people) and see output similar to the following)

{ "links" : [
    { "rel" : "next", "href" : "https://127.0.0.1:8080/persons?page=1&size=20" }
  ],
  "content" : [
     … // 20 Person instances rendered here
  ],
  "page" : {
    "size" : 20,
    "totalElements" : 30,
    "totalPages" : 2,
    "number" : 0
  }
}
此處顯示的 JSON 信封格式不遵循任何正式指定的結構,並且不保證穩定,我們可能會隨時更改它。強烈建議啟用渲染為超媒體啟用的官方媒體類型,Spring HATEOAS 支援,例如 HAL。這些可以透過使用其 @EnableHypermediaSupport 註解來啟用。在 Spring HATEOAS 參考文件 中找到更多資訊。(The JSON envelope format shown here doesn’t follow any formally specified structure and it’s not guaranteed stable and we might change it at any time. It’s highly recommended to enable the rendering as a hypermedia-enabled, official media type, supported by Spring HATEOAS, like HAL. Those can be activated by using its @EnableHypermediaSupport annotation. Find more information in the Spring HATEOAS reference documentation.)

組裝器產生了正確的 URI,並且還採用了預設組態來將參數解析為即將到來的請求的 Pageable。這表示,如果您變更該組態,連結會自動遵守變更。預設情況下,組裝器指向調用它的控制器方法,但是您可以透過傳遞自訂 Link 作為基礎來建構分頁連結來自訂它,這會重載 PagedResourcesAssembler.toModel(…) 方法。(The assembler produced the correct URI and also picked up the default configuration to resolve the parameters into a Pageable for an upcoming request. This means that, if you change that configuration, the links automatically adhere to the change. By default, the assembler points to the controller method it was invoked in, but you can customize that by passing a custom Link to be used as base to build the pagination links, which overloads the PagedResourcesAssembler.toModel(…) method.)

Spring Data Jackson 模組 (Spring Data Jackson Modules)

核心模組和某些商店特定的模組,附帶一組用於類型的 Jackson 模組,例如 org.springframework.data.geo.Distanceorg.springframework.data.geo.Point,Spring Data 網域使用。(The core module, and some of the store specific ones, ship with a set of Jackson Modules for types, like org.springframework.data.geo.Distance and org.springframework.data.geo.Point, used by the Spring Data domain.)
一旦 啟用 Web 支援com.fasterxml.jackson.databind.ObjectMapper 可用,這些模組就會匯入。(Those Modules are imported once web support is enabled and com.fasterxml.jackson.databind.ObjectMapper is available.)

在初始化期間,SpringDataJacksonModules,例如 SpringDataJacksonConfiguration,會被基礎結構拾取,以便將宣告的 com.fasterxml.jackson.databind.Module 提供給 Jackson ObjectMapper。(During initialization SpringDataJacksonModules, like the SpringDataJacksonConfiguration, get picked up by the infrastructure, so that the declared com.fasterxml.jackson.databind.Modules are made available to the Jackson ObjectMapper.)

通用基礎結構註冊了以下網域類型的資料繫結 mixin。(Data binding mixins for the following domain types are registered by the common infrastructure.)

org.springframework.data.geo.Distance
org.springframework.data.geo.Point
org.springframework.data.geo.Box
org.springframework.data.geo.Circle
org.springframework.data.geo.Polygon

個別模組可能會提供額外的 SpringDataJacksonModules。(The individual module may provide additional SpringDataJacksonModules.)
請參閱商店特定章節以了解更多詳細資訊。(Please refer to the store specific section for more details.)

Web 資料繫結支援 (Web Databinding Support)

您可以使用 Spring Data 投影(在 投影 中描述)透過使用 JSONPath 表示式(需要 Jayway JsonPath)或 XPath 表示式(需要 XmlBeam)來繫結傳入的請求有效負載,如下列範例所示 (You can use Spring Data projections (described in Projections) to bind incoming request payloads by using either JSONPath expressions (requires Jayway JsonPath) or XPath expressions (requires XmlBeam), as the following example shows)

使用 JSONPath 或 XPath 表示式的 HTTP 有效負載繫結 (HTTP payload binding using JSONPath or XPath expressions)
@ProjectedPayload
public interface UserPayload {

  @XBRead("//firstname")
  @JsonPath("$..firstname")
  String getFirstname();

  @XBRead("/lastname")
  @JsonPath({ "$.lastname", "$.user.lastname" })
  String getLastname();
}

您可以使用先前範例中顯示的類型作為 Spring MVC 處理程式方法引數,或透過在 RestTemplate 的方法之一上使用 ParameterizedTypeReference。先前的方法宣告將嘗試在給定文件中尋找任何位置的 firstnamelastname XML 查閱在傳入文件的頂層執行。該 JSON 變體首先嘗試頂層 lastname,但也嘗試巢狀在 user 子文件中的 lastname(如果前者未傳回值)。這樣,可以輕鬆地減輕來源文件結構的變更,而無需讓用戶端呼叫公開的方法(通常是基於類別的有效負載繫結的缺點)。(You can use the type shown in the preceding example as a Spring MVC handler method argument or by using ParameterizedTypeReference on one of methods of the RestTemplate. The preceding method declarations would try to find firstname anywhere in the given document. The lastname XML lookup is performed on the top-level of the incoming document. The JSON variant of that tries a top-level lastname first but also tries lastname nested in a user sub-document if the former does not return a value. That way, changes in the structure of the source document can be mitigated easily without having clients calling the exposed methods (usually a drawback of class-based payload binding).)

支援巢狀投影,如 投影 中所述。如果方法回傳複雜的非介面類型,則使用 Jackson ObjectMapper 來對應最終值。(Nested projections are supported as described in Projections. If the method returns a complex, non-interface type, a Jackson ObjectMapper is used to map the final value.)

對於 Spring MVC,一旦 @EnableSpringDataWebSupport 處於活動狀態且所需的依賴項在類別路徑上可用,必要的轉換器就會自動註冊。對於與 RestTemplate 一起使用,請手動註冊 ProjectingJackson2HttpMessageConverter (JSON) 或 XmlBeamHttpMessageConverter。(For Spring MVC, the necessary converters are registered automatically as soon as @EnableSpringDataWebSupport is active and the required dependencies are available on the classpath. For usage with RestTemplate, register a ProjectingJackson2HttpMessageConverter (JSON) or XmlBeamHttpMessageConverter manually.)

有關更多資訊,請參閱正規 Spring Data Examples repository 中的 web projection example。(For more information, see the web projection example in the canonical Spring Data Examples repository.)

Querydsl Web 支援 (Querydsl Web Support)

對於那些具有 QueryDSL 整合的商店,您可以從 Request 查詢字串中包含的屬性衍生查詢。(For those stores that have QueryDSL integration, you can derive queries from the attributes contained in a Request query string.)

考慮以下查詢字串 (Consider the following query string)

?firstname=Dave&lastname=Matthews

給定先前範例中的 User 物件,您可以使用 QuerydslPredicateArgumentResolver 將查詢字串解析為以下值,如下所示 (Given the User object from the previous examples, you can resolve a query string to the following value by using the QuerydslPredicateArgumentResolver, as follows)

QUser.user.firstname.eq("Dave").and(QUser.user.lastname.eq("Matthews"))
當在類別路徑上找到 Querydsl 時,此功能會與 @EnableSpringDataWebSupport 一起自動啟用。(The feature is automatically enabled, along with @EnableSpringDataWebSupport, when Querydsl is found on the classpath.)

@QuerydslPredicate 新增至方法簽章會提供一個隨即可用的 Predicate,您可以使用 QuerydslPredicateExecutor 執行它。(Adding a @QuerydslPredicate to the method signature provides a ready-to-use Predicate, which you can run by using the QuerydslPredicateExecutor.)

類型資訊通常從方法的回傳類型解析。由於該資訊不一定與網域類型相符,因此使用 QuerydslPredicateroot 屬性可能是個好主意。(Type information is typically resolved from the method’s return type. Since that information does not necessarily match the domain type, it might be a good idea to use the root attribute of QuerydslPredicate.)

下列範例顯示如何在方法簽章中使用 @QuerydslPredicate (The following example shows how to use @QuerydslPredicate in a method signature)

@Controller
class UserController {

  @Autowired UserRepository repository;

  @RequestMapping(value = "/", method = RequestMethod.GET)
  String index(Model model, @QuerydslPredicate(root = User.class) Predicate predicate,    (1)
          Pageable pageable, @RequestParam MultiValueMap<String, String> parameters) {

    model.addAttribute("users", repository.findAll(predicate, pageable));

    return "index";
  }
}
1 將查詢字串引數解析為 User 的相符 Predicate。(Resolve query string arguments to matching Predicate for User.)

預設繫結如下 (The default binding is as follows)

  • 簡單屬性上的 Object 作為 eq。(Object on simple properties as eq.)

  • 類集合屬性上的 Object 作為 contains。(Object on collection like properties as contains.)

  • 簡單屬性上的 Collection 作為 in。(Collection on simple properties as in.)

您可以透過 @QuerydslPredicatebindings 屬性來自訂這些繫結,或透過使用 Java 8 default methods 並將 QuerydslBinderCustomizer 方法新增至 repository 介面,如下所示 (You can customize those bindings through the bindings attribute of @QuerydslPredicate or by making use of Java 8 default methods and adding the QuerydslBinderCustomizer method to the repository interface, as follows)

interface UserRepository extends CrudRepository<User, String>,
                                 QuerydslPredicateExecutor<User>,                (1)
                                 QuerydslBinderCustomizer<QUser> {               (2)

  @Override
  default void customize(QuerydslBindings bindings, QUser user) {

    bindings.bind(user.username).first((path, value) -> path.contains(value))    (3)
    bindings.bind(String.class)
      .first((StringPath path, String value) -> path.containsIgnoreCase(value)); (4)
    bindings.excluding(user.password);                                           (5)
  }
}
1 QuerydslPredicateExecutor 提供對 Predicate 特定尋找器方法的存取。(QuerydslPredicateExecutor provides access to specific finder methods for Predicate.)
2 在 repository 介面上定義的 QuerydslBinderCustomizer 會自動被拾取,並簡化 @QuerydslPredicate(bindings=…​)。(QuerydslBinderCustomizer defined on the repository interface is automatically picked up and shortcuts @QuerydslPredicate(bindings=…​).)
3 定義 username 屬性的繫結為簡單的 contains 繫結。(Define the binding for the username property to be a simple contains binding.)
4 定義 String 屬性的預設繫結為不區分大小寫的 contains 比對。(Define the default binding for String properties to be a case-insensitive contains match.)
5 Predicate 解析中排除 password 屬性。(Exclude the password property from Predicate resolution.)
您可以註冊一個 QuerydslBinderCustomizerDefaults bean,該 bean 在應用來自 repository 或 @QuerydslPredicate 的特定繫結之前,持有預設的 Querydsl 繫結。(You can register a QuerydslBinderCustomizerDefaults bean holding default Querydsl bindings before applying specific bindings from the repository or @QuerydslPredicate.)

Repository 資料填充器 (Repository Populators)

如果您使用 Spring JDBC 模組,您可能熟悉使用 SQL 腳本填充 DataSource 的支援。repository 層級也提供類似的抽象,儘管它不使用 SQL 作為資料定義語言,因為它必須是商店獨立的。因此,資料填充器支援 XML(透過 Spring 的 OXM 抽象)和 JSON(透過 Jackson)來定義用於填充 repository 的資料。(If you work with the Spring JDBC module, you are probably familiar with the support for populating a DataSource with SQL scripts. A similar abstraction is available on the repositories level, although it does not use SQL as the data definition language because it must be store-independent. Thus, the populators support XML (through Spring’s OXM abstraction) and JSON (through Jackson) to define data with which to populate the repositories.)

假設您有一個名為 data.json 的檔案,其內容如下 (Assume you have a file called data.json with the following content)

以 JSON 定義的資料 (Data defined in JSON)
[ { "_class" : "com.acme.Person",
 "firstname" : "Dave",
  "lastname" : "Matthews" },
  { "_class" : "com.acme.Person",
 "firstname" : "Carter",
  "lastname" : "Beauford" } ]

您可以使用 Spring Data Commons 中提供的 repository 命名空間的 populator 元素來填充您的 repository。若要將先前的資料填充到您的 PersonRepository,請宣告一個類似於以下的 populator (You can populate your repositories by using the populator elements of the repository namespace provided in Spring Data Commons. To populate the preceding data to your PersonRepository, declare a populator similar to the following)

宣告 Jackson repository 資料填充器 (Declaring a Jackson repository populator)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:repository="http://www.springframework.org/schema/data/repository"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/data/repository
    https://www.springframework.org/schema/data/repository/spring-repository.xsd">

  <repository:jackson2-populator locations="classpath:data.json" />

</beans>

先前的宣告導致讀取 data.json 檔案並由 Jackson ObjectMapper 反序列化。(The preceding declaration causes the data.json file to be read and deserialized by a Jackson ObjectMapper.)

要將 JSON 物件解組成的類型,是透過檢查 JSON 文件的 _class 屬性來決定的。基礎結構最終會選擇適當的 repository 來處理反序列化的物件。(The type to which the JSON object is unmarshalled is determined by inspecting the _class attribute of the JSON document. The infrastructure eventually selects the appropriate repository to handle the object that was deserialized.)

若要改為使用 XML 來定義應填充 repository 的資料,您可以使用 unmarshaller-populator 元素。您將其組態為使用 Spring OXM 中可用的 XML marshaller 選項之一。有關詳細資訊,請參閱 Spring 參考文件。下列範例顯示如何使用 JAXB 解組 repository 資料填充器 (To instead use XML to define the data the repositories should be populated with, you can use the unmarshaller-populator element. You configure it to use one of the XML marshaller options available in Spring OXM. See the Spring reference documentation for details. The following example shows how to unmarshall a repository populator with JAXB)

宣告 unmarshalling repository 資料填充器 (使用 JAXB) (Declaring an unmarshalling repository populator (using JAXB))
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:repository="http://www.springframework.org/schema/data/repository"
  xmlns:oxm="http://www.springframework.org/schema/oxm"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/data/repository
    https://www.springframework.org/schema/data/repository/spring-repository.xsd
    http://www.springframework.org/schema/oxm
    https://www.springframework.org/schema/oxm/spring-oxm.xsd">

  <repository:unmarshaller-populator locations="classpath:data.json"
    unmarshaller-ref="unmarshaller" />

  <oxm:jaxb2-marshaller contextPath="com.acme" />

</beans>