Spring Data Gosling 版本的新功能?

工程 | Christoph Strobl | 2015 年 9 月 4 日 | ...

超過 12 個專案修正了 300 多個問題,使得很難追蹤自上次發布以來發生的事情。因此,以下是我們在上次迭代期間一直在開發的一些新功能的更詳細摘錄。

Ad-hoc JPA 提取圖。

自 Dijkstra 發布系列以來,我們已經能夠透過 JPA 後端儲存庫中的 @EntityGraph 註釋引用在實體上宣告的具名實體圖。在下面的範例中,這強制firstName 和 lastName 被急切載入,而所有其他屬性保持延遲載入。

@Entity
@NamedEntityGraphs(
  @NamedEntityGraph(name = "with-tags",
    attributeNodes = { @NamedAttributeNode("tags") }))
class Product {

  @ManyToMany
  Set<Tag> tags;

  // other properties omitted
}

interface ProductRepository extends Repository<Customer, Long> {

  @EntityGraph("with-tags")
  Product findOneById(Long id);
}

Gosling 版本現在將我們的 JPA 2.1 故事向前推進一步,將其擴展到 ad-hoc 提取圖定義。透過在查詢方法上透過 @EntityGraph(attributePaths = …) 顯式指定屬性,您無需在實體上擁有 NamedEntityGraph 註釋。

@Entity
class Product {

  @ManyToMany
  Set<Tag> tags;

  // other properties omitted
}

interface ProductRepository extends Repository<Customer, Long> {

  @EntityGraph(attributePaths = {"tags"})
  Product findOneById(Long id);
}

Querydsl Web 支援

Spring Data Web 支援已經允許您在控制器處理常式方法中宣告 Pageable 類型的參數。新引入的 Querydsl 整合擴展了該功能,使您可以接收直接從 HTTP 請求的查詢字串衍生的現成可用的 Predicate。當配置 @EnableSpringDataWebSupport 並且在類別路徑上找到 Querydsl 時,該功能會自動啟用。

如果 Predicate 在沒有進一步配置的情況下使用,我們將嘗試從該方法的回傳類型解析 Predicate 解析的根類型,儘管在大多數情況下,透過 @QuerydslPredicate(root = …) 顯式宣告所需的類型引用可能更好。有了這個設定,查詢字串屬性將綁定到該類型的匹配屬性,從而建立例如

QUser.user.firstname.eq("Dave").and(QUser.user.lastname.eq("Matthews")) 

從使用預設屬性類型相依綁定的 ?firstname=Dave&lastname=Matthews

@SpringBootApplication
public class Application {

  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }

  @Controller
  @RequiredArgsConstructor(onConstructor = @__(@Autowired))
  static class UserController {

    private final UserRepository repository;

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

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

現在,使用預設(等於)綁定並不總是有意義的,而是每個屬性的專用綁定或特定類型的綁定。為了實現這一點,只需透過提供一個可以透過 @QuerydslPredicate(bindings = …) 註冊或簡單地由儲存庫實現的 QuerydslBinderCustomizer 來覆寫預設值。

interface UserRepository extends CrudRepository<User, String>,
    QueryDslPredicateExecutor<User>,
    QuerydslBinderCustomizer<QUser> {

  // Query methods go here

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

    bindings.bind(user.nationality).first(
      (path, value) -> path.equalsIgnoreCase(value)); // 1
    bindings.bind(String.class).first(
      (StringPath path, String value) -> path.containsIgnoreCase(value)); // 2
    bindings.excluding(user.password);
  }
}

正如您所看到的,我們利用 Java 8 lambda 以及 Querydsl 的類型安全屬性引用來定義特定屬性的綁定 (1) 或給定類型的所有屬性的綁定 (2)。使用 QuerydslBindings.excluding 允許您從查詢中移除路徑。

Spring Data 範例儲存庫 中找到一個完整的工作範例,並查閱 參考文件 以了解詳細資訊。

Spring Data REST

Querydsl 支援

Spring Data Commons 中引入的 Querydsl 支援(參見上方)已整合到 Spring Data REST 中。這表示您可以透過將簡單的屬性查詢參數附加到請求 URI 來過濾您的集合資源。

在公開 Starbucks 商店位置的 Spring Data REST 範例中,請注意 StoreRepository 現在如何同時實現 QueryDslPredicateExecutorQuerydslBinderCustomizer<QStore>,就像上面描述的那樣。

Spring Data REST 公開的商店集合資源將允許您發出如下請求

$ http :8080/api/stores?address.city=York

{
    "_embedded": {
        "stores": [
            {
                "_links": {
                    …
                }, 
                "address": {
                    "city": "New York", 
                    "location": { "x": -73.938421, "y": 40.851 }, 
                    "street": "803 W 181st St", 
                    "zip": "10033-4516"
                }, 
                "name": "Washington Hgts/181st St"
            }, 
            {
                "_links": {
                    …
                }, 
                "address": {
                    "city": "New York", 
                    "location": { "x": -73.939822, "y": 40.84135 }, 
                    "street": "4001 Broadway", 
                    "zip": "10032-1508"
                }, 
                "name": "168th & Broadway"
            }, 
            …
        ]
    }, 
    "_links": {
        …
    }, 
    "page": {
        "number": 0, 
        "size": 20, 
        "totalElements": 209, 
        "totalPages": 11
    }
}

請注意,它只會回傳城市以 "York" 結尾的商店,就像在 StoresRepositoryQuerydslBinderCustomizer 的實作中所定義的那樣。

我們目前正在研究以更明顯的方式宣傳這種查詢機制的選項,例如使用範本變數,甚至提供進階的對應功能來自訂要使用的請求參數名稱。

自訂 HAL 瀏覽器

Spring Data REST 的 Gosling 版本附帶了一個額外的模組,該模組包裝了 Mike Kelly 的 HAL 瀏覽器 並對其進行了一些調整,以利用我們公開的 API 元資料。要將瀏覽器與您的應用程式一起使用,只需將 spring-data-rest-hal-browser 模組添加到您的專案中,您的 API 根目錄將為接受 text/html 的請求提供瀏覽器。當然,預設情況下或如果您使用基於 JSON 的 Accept 標頭,仍然會提供標準 HAL 回應。

![HAL 瀏覽器](https://gist.githubusercontent.com/olivergierke/4d3683c12769211d97cb/raw/9d11209ea9840b42693854ba62f8a46bc9940f39/hal-browser.png)

圖 1. - HAL 瀏覽器(點擊放大)

雖然 Spring Data REST 模組可以輕鬆地將瀏覽器添加到您的應用程式中,但它也對瀏覽器進行了輕微調整。當您點擊按鈕以觸發非 GET 請求時,瀏覽器通常會開啟一個模態對話框,該對話框需要一些原始 JSON 輸入。當然,如果您知道自己在做什麼,這很好,但它有點容易出錯,而且不是很方便,因為您必須了解伺服器期望的資料結構。

Spring Data REST 透過利用 profile 連結關係公開系統公開的類型的 JSON 結構描述文件,這使得結構描述可以被泛型發現,而無需將發現邏輯綁定到 Spring Data REST 本身。我們提供的瀏覽器實例將查閱該結構描述元資料,如果它可以找到一些元資料,則會將其傳遞給 JSON Editor,以使用完全從 JSON 結構描述派生的表單替換預設對話框。

![基於 JSON Editor 的 POST 表單](https://gist.githubusercontent.com/olivergierke/4d3683c12769211d97cb/raw/9d11209ea9840b42693854ba62f8a46bc9940f39/json-editor-post-form.png)

圖 2. - 基於 JSON Editor 的 POST 表單(點擊放大)

請參閱表單如何允許新增行項目,因為結構描述將其公開為陣列。價格和訂單日期欄位被標記為唯讀,location 欄位允許從具有國際化值的列舉中選擇值。

範例專案可以在 GitHub 上找到。

正如您在上面的螢幕截圖中看到的,restbucks:orders 連結附帶了人類可讀的描述。描述是使用 _links.$rel.title 鍵從一個可選的資源捆綁包 rest-messages 中提取的,以定義一個可讀的值。該範例使用 rest-messages.properties 作為後備資源捆綁包,但也包含一個 rest-messages_de.properties,用於為發送設定為 deAccept-Language 標頭的客戶端回傳德語標籤。

相同的資源包可用於國際化列舉值,以便在客戶端以人類可讀的方式使用。為了不破壞現有的應用程式,必須透過 RepositoryRestConfiguration.setEnableEnumTranslation(…) 顯式啟用此功能。有關翻譯的詳細資訊可以在 EnumTranslationConfiguration 上配置。

Spring Data GemFire & Apache Geode

對 Pivotal GemFire 8.1 和 Apache Geode 的支援是 Spring Data GemFire 1.7 中最顯著的補充。Pivotal GemFire 早些時候已提交給 Apache Incubator,Spring Data 團隊迅速回應,將 支援 納入 Spring Data GemFire 中。

此外,還添加了幾個其他功能,以簡化使用 Spring 開發 GemFire 和 Apache Geode 應用程式。例如,開發人員現在可以使用註釋來定義特定於應用程式網域物件的到期策略。

@TimeToLiveExpiration(
  timeout = "@expirationSettings['spel.defined.timeout']" action="DESTROY")
@IdleTimeoutExpiration(
  timeout = "1800" action="${property.placeholder.defined.action}")
class ApplicationDomainObject { … }

基於到期的註釋支援 SpEL 和 Spring Property Placeholder 值。若要啟用基於註釋的到期策略,您只需在 GemFire 區域上配置 Spring Data GemFire 的 CustomExpiry 實作,AnnotationBasedExpiration,用於 TTL 和 TTI 中的任一或兩者。

<gfe:partitioned-region id="Example" persistent="false" …>
  <gfe:custom-entry-ttl>
    <bean class="….gemfire.support.AnnotationBasedExpiration" factory-method="forTimeToLive"/>
  </gfe:custom-entry-ttl>
  <gfe:custom-entry-tti ref="ttiExpiration"/>
</gfe:partitioned-region>

<bean id="ttiExpiration" class="….gemfire.support.AnnotationBasedExpiration" factory-method="forIdleTimeout">
  <constructor-arg ref="defaultExpirationAttributes"/>
</bean>

<bean id="defaultExpirationAttributes" class="….ExpirationAttributes">
  <constructor-arg value="600"/>
  <constructor-arg value="#{T(….ExpirationAction).DESTROY}"/>
</bean>

請參閱參考指南,以 了解更多資訊。接下來,添加了通過註釋支持儲存庫查詢方法 OQL 擴展。

interface CustomerRepository implements CrudRepository<Cutomer, Long> {

  @Trace
  @Limit(25)
  @Import("org.exmple.Customer")
  @Hint("CustomerLastNameIdx")
  List<Customer> findByLastNameOrderByLastNameAsc(String lastName);
}

@Trace 啟用個別 OQL 語句除錯。@Limit 限制查詢結果集中的結果數量,而 @Import 允許應用程式區分相似命名的物件類型。例如,您的應用程式可能同時定義 org.example.app.core.Customerorg.example.app.vendor.xyz.Customer 類型。有關更多詳細資訊,請參閱 GemFire 的 doc@Hint 啟用 OQL 提示的使用,以識別適用於查詢的索引。在此處瞭解有關 OQL 擴展的更多資訊。

最後,Spring Data GemFire 使用 Spring Data GemFire XML 資料命名空間提供對 GemFire 快取和區域快照 的支援。

<gfe:partitioned-region id="Example" persistent="false" … />

<gfe-data:snapshot-service id="exampleRegionSnapshotService" region-ref="Example">
  <gfe-data:snapshot-import location="/path/to/import/example.snapshot"/>
  <gfe-data:snapshot-export locator="/path/to/export/example.snapshot"/>
</gfe-data:snapshot-service>

您可以進一步了解 Spring Data GemFire 如何支援匯入上的 ZIP 檔案、使用 Spring ApplicationEvents 來觸發匯入和匯出快照,以及如何適當地篩選匯入和匯出的資料 在此

Spring Data KeyValue & 基於 Map 的儲存庫

自從我們被要求為 Spring Data 儲存庫提供一個非常簡單的基於 Map 的實作以來已經很長一段時間了,這主要是為了各種測試目的。這些要求最終以一種略有不同的方式重振了 KeyValue 模組,與它之前存在的狀態不同。

Spring Data KeyValue 現在包含一個基本的基於 Map 的儲存庫實作,預設情況下它將使用 Spring Expression Language (SpEL) 查詢值,並根據其集合模組提供排序、分頁和 Querydsl 整合。它還公開了專用的 API,允許鍵值儲存利用儲存中的儲存特定最佳化、檢索,最重要的是,如果需要,可以執行查詢。

Spring Data KeyValue 儲存庫使用的預設查詢機制基於 SpEL,並允許您定義和運行複雜的查詢。當在 COMPILED 模式 下運行時,此方法展示了它的真正威力,因為它有效地編譯了要在值上執行的過濾表達式。或者,您也可以使用 Querydsl 表達式進行類型安全的查詢。

@Configuration
@EnableMapRepositories("com.acme.repositories")
class AppConfig {}

@KeySpace("user")
class User {

  String @Id id;
  String firstname;
}

interface UserRepository extends CrudRepository<User, String> {
  List<String> findByFirstnameStartingWith(String firstname);
}

我們目前有針對 Ehcache、HazelcastAerospike 的 API 擴展正在開發中,並且期待評估選項以整合 Redis,並可能移植一些 Gemfire API 來使用它。

下一步是什麼?

接下來是 SpringOne2GX 在華盛頓特區 - 我們很高興在那裡見到您 - 這是與團隊聯繫、了解新功能並度過美好時光的最佳地點。同時,我們已經在為 Fowler Release Train 準備下一個服務版本,並開始為 Hopper Release Train 開發新功能(噓...我們將在 SpringOne 的 “Spring Data 的 2015 年新功能?”演講 中搶先了解 Hopper)。

取得 Spring 電子報

與 Spring 電子報保持聯繫

訂閱

搶先一步

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

了解更多

取得支援

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

了解更多

即將舉行的活動

查看 Spring 社群中所有即將舉行的活動。

查看全部