搶先一步
VMware 提供培訓和認證,助您加速發展。
瞭解更多我們剛剛宣佈了名為 Dijkstra 的 Spring Data 發行火車 GA 版本的可用性。我想借此機會向您介紹在此版本中新增的一些功能。
該版本包含的第一個重要特性是向發行火車添加了 5 個模組。其中大多數已經存在了一段時間,但從今往後,我們將與其它模組同步釋出它們。新新增的模組包括 Spring Data Elasticsearch、Cassandra、Couchbase、Gemfire 和 Redis。
發行火車的許多改進通常會落入 Commons 模組,以便各個儲存模組都可以從新新增的功能中受益。以下是 Dijkstra 中最重要的幾項改進:
對於宣告返回單個域型別例項的 Spring Data 儲存庫方法,如果無法獲得結果,儲存庫會返回 null
。然而,如果我們從頭開始構建 API,而不是直接返回例項,我們可能更傾向於使用 Optional
來確保客戶端不會意外忘記進行 null
檢查。
Optional
是當今許多 Java 庫提供的一種型別。Google Guava 提供了它,更重要的是,JDK 8 也提供了一個。因此,透過 Dijkstra 發行火車,我們提供了使用這些型別作為返回值包裝器的可能性,並讓 Spring Data 儲存庫基礎設施自動為您包裝 null
值。
interface CustomerRepository extends Repository<Customer, Long> {
Optional<Customer> findOne(Long id);
Optional<Customer> findByEmailAddress(EmailAddress emailAddress);
}
您在此處看到的第一個方法是 CrudRepository.findOne(…)
的一個變體。請注意,儲存庫介面不繼承 CrudRepository
,因為這會導致編譯錯誤,因為我們無法重新宣告 findOne(…)
方法並更改返回型別。因此,如果您想改變 findOne(…)
的行為,我們建議您簡單地建立自己的基礎儲存庫介面(如參考文件中所述)。
第二個方法是一個簡單的查詢方法,它使用查詢派生(query derivation)讓 Spring Data 基礎設施從方法名派生出一個查詢。我們在這裡也檢測到 Optional
作為包裝型別,執行查詢並自動將結果包裝成 Optional
例項。
與此相關,我們開始向儲存庫方法新增非同步執行能力。查詢方法現在可以返回 Future<T>
,如果它用 @Async
進行註解,這將導致其非同步執行。
interface CustomerRepository extends Repository<Customer, Long> {
@Async
@Query(" … long running query declaration … ")
Future<List<Customer>> findByLongRunningQuery(String lastname);
}
然後,執行是基於 Spring 對非同步方法呼叫的支援。我們還在研究更高階的非同步執行模型,例如承諾(可能基於Project Reactor),以便在未來的 Spring Data 版本中新增。
MongoDB 模組(以及其他模組)一直支援 Mongo 的地理空間操作——無論是在 MongoTemplate
中還是在儲存庫抽象中。在當前版本中,我們將核心值型別(例如 Shape
, Point
, Box
, Distance
等)移到了 Spring Data Commons 模組中。這將允許您普遍地引用地理型別並與所有支援地理空間功能的 Spring Data 模組互動,而無需將這些型別相互對映。檢視 JavaDoc 瞭解詳情。
Spring Data 在儲存庫程式設計模型中一直支援分頁功能。它允許您按頁訪問資料以迭代大型資料集。除了純頁面內容,Page
介面還暴露了 API 以查詢總元素數和總頁數。
計算這個數字可能非常消耗資源(因為通常需要執行額外的查詢),而且很多時候,關於頁面的唯一有趣元資訊是它是否還有下一頁,以便客戶端可以繼續檢索更多資料。例如,您可以在 Facebook 時間線中看到這種模式。
Dijkstra 版本現在引入了 Page
的簡化版本,稱為 Slice
,它允許您瞭解當前切片後面是否還有更多內容。
interface BlogPostRepository extends Repository<BlogPost, Long> {
Slice<BlogPost> findByAuthorOrderByDateDesc(Author author, Pageable pageable);
}
您現在可以使用 Pageable
來表示您想要獲取的頁碼和大小,Spring Data 基礎設施將比請求多讀取一個專案,並使用它的存在或不存在來指示下一個切片是否可用。
查詢方法派生功能現在支援 deleteBy…
和 removeBy…
方法字首,以便根據給定的條件派生用於刪除受管域型別的查詢。
interface BlogPostRepository extends Repository<BlogPost, Long> {
int deleteByDateBefore(Date date);
}
此功能已在 Dijkstra 的 JPA、MongoDB 和 Solr 模組中實現,未來將新增到其他儲存模組中。
在 Dijkstra 的 JPA 模組開發期間,一個核心主題是對 JavaEE 7 / JPA 2.1 功能的支援。我們在本次釋出中解決的核心領域是對查詢執行中的實體圖(entity graphs)支援以及儲存過程的執行。
假設我們有以下域型別定義
@Entity
@NamedEntityGraph(name = "summary", attributeNodes = {
@NamedAttributeNode("firstname"),
@NamedAttributeNode("lastname")})
class Customer {
// properties ommitted
}
我們現在可以透過 @EntityGraph
註解引用命名實體圖,以指示我們希望將此圖應用於正在執行的查詢。
interface CustomerRepository extends Repository<Customer, Long> {
@EntityGraph("summary")
Optional<Customer> findByEmailAddress(EmailAddress emailAddress);
}
這將導致只有 firstname
和 lastname
屬性被急切載入,而其他所有屬性都準備在訪問時延遲載入。
JPA 2.1 添加了透過 EntityManager
API 執行儲存過程的能力。與上面的示例類似,儲存過程的元資料可以在域型別上宣告。假設您想要觸發一個為客戶隨機建立密碼的儲存過程
@Entity
@NamedStoredProcedureQuery(name = "Customer.generateNewPassword",
procedureName = "generateNewPassword", parameters = {
@StoredProcedureParameter(
mode = ParameterMode.IN, name = "username", type = String.class),
@StoredProcedureParameter(
mode = ParameterMode.OUT, name = "password", type = String.class)})
class Customer {
// properties ommitted
}
儲存過程可以使用這樣的儲存庫查詢方法執行
interface CustomerRepository extends Repository<Customer, Long> {
@Procedure
String generateNewPassword(@Param("username") String username);
}
預設情況下,我們將使用透過著名的 DomainType.methodName
模式找到的儲存過程元資料,並將其與方法宣告匹配。對於像此處所示的非常簡單的過程對映,您甚至可以省略元資料宣告,因為所有元資料都可以從方法名派生。在參考文件中查詢有關儲存過程支援的更多資訊。
Redis 模組的最新版本添加了累積一組可批次執行操作的功能。為了實現這一點,RedisTemplate
現在可以透過將 enableTransactionSupport
屬性配置為 true
來與 Spring 的事務同步整合(預設為 false
)。
啟用此功能將導致 RedisConnection
繫結到當前 Thread
併發出 MULTI
命令,從而允許底層 Redis 驅動程式潛在地執行命令排隊。如果事務沒有錯誤地完成,將發出 EXEC
命令;如果失敗,則使用 DISCARD
命令放棄累積的命令。
一旦啟用,連線將繫結到當前執行緒,確保每個寫入操作都透過同一個連線傳送並排隊等待周圍事務完成。讀取操作(例如 KEYS
命令)仍將透過使用新的、非執行緒繫結的連線立即執行。
@Bean
public StringRedisTemplate redisTemplate() {
StringRedisTemplate template =
new StringRedisTemplate(redisConnectionFactory());
// Enable transaction synchronization support
template.setEnableTransactionSupport(true);
return template;
}
像這樣配置的 RedisTemplate
然後可以與以下語義一起使用
// Executed on thread bound connection
template.opsForValue().set("foo", "bar");
// Read operation executed on separate connection
template.keys("*");
// Returns null as values set within transaction are not visible
// prior to transaction flush
template.opsForValue().get("foo");
使用 Spring Data Solr 的 criteria API 建立更復雜的查詢的需求已經存在一段時間了。因此,我們決定重寫部分實現,同時保持 API 與之前版本相容。
基本上,我們從相當扁平的表示形式轉向了樹狀模型,同時保留了我們習慣的流暢 API 風格。
Solr 查詢 q=name:solr OR (type:spring AND category:data)
現在可以表示為
new SimpleQuery(
where("name").is("solr").or(
where("type").is("spring").and("category").is("data")));
Spring Data REST 暴露的 REST 資源的一個非常常見的需求是能夠建立自定義表示。這意味著,使用者希望減少響應中呈現的屬性數量,或者內聯關聯實體以節省伺服器往返。在 Spring Data REST 2.1 中,我們現在提供了在伺服器端定義自定義投影的可能性。為此,您宣告一個介面來包含您想要暴露的屬性
@Projection(name = "summary", types = Order.class)
interface OrderSummary {
LocalDate getOrderedDate();
CustomerSummary getCustomer();
@Value("#{@shop.calculateTotal(target)}")
Money getTotal();
}
此介面可以放在 Order
所在的同一包中(或子包中),並將由 Spring Data REST 自動檢測到。這將導致所有暴露單個訂單或訂單集合的資源在 URI 模板中攜帶一個額外的引數,以指示投影能力。
{ _links : {
orders : { href : "…/orders{?projection}", templated : true }
}
}
如果客戶端現在使用 summary
擴充套件模板中的 projection
引數,我們將在伺服器端建立一個代理,該代理將交給 Jackson 進行編組。每個 getter 都將被轉發到實際目標類(在此示例中為 Order
)上的屬性查詢。
在上面的示例中,getCustomer()
指的是一個相關實體,在非投影場景下它只會被暴露為一個連結。透過使用投影,我們檢測到方法的返回型別不是 Customer
。這反過來會導致建立一個投影代理,以便您可以完全控制暴露的屬性。投影介面當然可以攜帶 Jackson 註解來進一步定製呈現的表示。
對於高階用例,您甚至可以使用 @Value
註解修飾投影方法,以便將 SpEL 表示式的結果返回給編組器。在我們這裡的示例中,我們呼叫一個名為 shop
的 Spring Bean 上的方法,並將代理目標例項傳遞給它來計算訂單總額,這可能考慮了折扣、稅費等。
希望透過這些精選示例,我能夠激發您探索 Dijkstra 發行火車中所包含模組的好奇心。我們現在將透過啟動下一個名為 Evans 的發行火車,繼續我們的使命,以簡化資料訪問層的實現。