198
阿裏雲
技術社區[雲棲]
Spring Data 官方文檔》4.7 Spring Data擴展
4.7 Spring Data擴展
這部分說明Spring Data一係列的擴展功能,可以使Spring Dta使用多樣的上下文.目前大部分集成是針對Spring MVC.
4.7.1 Querydsl擴展
Querydsl是一個框架,通過它的流式API構建靜態類型的SQL類查詢。多個Spring Data模塊通過QueryDslPredicateExecutor與Querydsl集成。
例29 QueryDslPredicateExecutor接口
1 |
public interface QueryDslPredicateExecutor<T> {
|
2 |
T findOne(Predicate predicate); ①
|
3 |
Iterable<T> findAll(Predicate predicate); ②
|
4 |
long count(Predicate predicate); ③
|
5 |
boolean exists(Predicate predicate); ④
|
6 |
// … more functionality omitted.
|
① 查詢並返回一個匹配Predicate的單例實體
②查詢並返回所有匹配Predicate的實體
③ 返回匹配Predicate的實體數量
④ 返回是否存在一個匹配Predicate的實體
為了簡單的使用Querydsl功能,在你的倉庫接口繼承QueryDslPredicateExecutor.
例30 在倉庫集成QueryDsl
1 |
interface UserRepository extends CrudRepository<User, Long>,
|
2 |
QueryDslPredicateExecutor<User> { |
像上麵這樣就可以使用Querydsl的Predicate書寫類型安全的查詢
1 |
Predicate predicate = user.firstname.equalsIgnoreCase( "dave" ).and(user.lastname.startsWithIgnoreCase( "mathews" ));
|
2 |
userRepository.findAll(predicate); |
4.7.2 Web支持
注意
本節包含Spring Data Web支持的文檔是在1.6範圍內的Spring Data Commons實現的.因為支持新引入的內容改變了很多東西,我們保留了舊行為的文檔在”遺留Web支持”部分.
如果模塊支持倉庫編程模型,那麼Spring Data模塊附帶了各種Web模塊支持.Web關聯的東西需要Spring MVC的JAR包位於classpath路徑下,它們中有些甚至提供了Spring HATEOAS集成.一般情況,集成方式支持使用@EnableSpringDataWebSupport注解在你的JavaConfig配置類.
例31 啟用Spring Data web支持
3 |
@EnableSpringDataWebSupport |
4 |
class WebConfiguration {}
|
@EnableSpringDataWebSupport注解注冊了一些組件,我們將在稍後討論.注解還將在類路徑上檢測Spring HATEOAS,如果才在將為其注冊集成組件.
作為可選項,如果你使用XML配置,注冊SpringDataWebSupport或者HateoasWareSpringDataWebSupport作為Spring bean:
例32 用XML啟用Spring Data web支持
1 |
< bean class = "org.springframework.data.web.config.SpringDataWebConfiguration" />
|
2 |
<!-- If you're using Spring HATEOAS as well register this one *instead* of the former --> |
3 |
< bean class = "org.springframework.data.web.config.HateoasAwareSpringDataWebConfiguration" />
|
基本Web支持
上麵展示的的配置設置將注冊幾個基本組件:
- 一個DomainClassConverter啟用Spring MVC來根據請求參數或路徑變量管理倉例實體類的實例
- HandlerMethodArgumentResolver實現讓Spring MVC從請求參數解析Pageable和Sort實例
實體類轉換
DomainClassConverter允許你在Spring MVC控製器方法簽名中直接使用實體類型,因此你不必手動的通過倉庫查詢實例:
例33 一個Spring MVC控製器在方法簽名中使用實體類型
2 |
@RequestMapping ( "/users" )
|
3 |
public class UserController {
|
4 |
@RequestMapping ( "/{id}" )
|
5 |
public String showUserForm( @PathVariable ( "id" ) User user, Model model) {
|
6 |
model.addAttribute( "user" , user);
|
如你所見,方法直接接收一個User實例並沒有更進一步的查詢是否必要.實例可以通過Spring MVC將路徑變量轉換為實體類的id類型並最終通過在實體類型注冊的倉庫實例上調用findOne(…)訪問實例轉換得到.
注意
當前的倉庫必須實現CrudRepository做好準備被發現來進行轉換.
為了分頁和排序分解方法參數
上麵的配置片段還注冊了一個PageableHandlerMethodArgumentResolver和一個SortHandlerMethodArgumentResolver實例.注冊使得Pageable和Sort成為有效的控製器方法參數.
例34 使用Pageable作為控製器方法參數
02 |
@RequestMapping ( "/users" )
|
03 |
public class UserController {
|
04 |
@Autowired UserRepository repository;
|
06 |
public String showUsers(Model model, Pageable pageable) {
|
07 |
model.addAttribute( "users" , repository.findAll(pageable));
|
這個方法簽名將使Spring MVC嚐試使用下麵的默認配置從請求參數中轉換一個Pageable實例:
表1 請求參數轉換Pageable實例

為了定製行為,可以繼承SpringDataWebConfiguration或者啟用等效的HATEOAS並覆蓋pageableResolver()或sortResolver()方法並導入你的自定義配置文件替代@Enable-注解.
有一種情況你需要多個Pageable或Sort實例從請求轉換(例如處理多個表單),你可以使用Spring的@Qualifier注解來互相區別.請求參數必須以${qualifier}為前綴.這樣一個方法的簽名像這樣:
1 |
public String showUsers(Model model,
|
2 |
@Qualifier ( "foo" )Pagebale first,
|
3 |
@Qualifier ( "bar" ) Pageable second) {
|
你必須填充foo_page和bar_page等.
默認的Pageable在方法中處理等價於一個new PageRequest(0, 20),但是可以使用@PageableDefaults注解在Pageable參數上定製.
Hypermedia支持分頁
Spring HATEOAS包裝了一個代表模型的類PageResources ,它可以使用Page實例包裝必要的Page元數據內容作為連接讓客戶端導航頁麵.一個頁麵到一個PageResources的轉換被Spring HATEOAS的ResourceAssembler接口實現PagedResourcesAssembler來完成.
例35 使用一個PagedResourcesAssembler作為控製器方法參數
02 |
class PersonController {
|
03 |
@Autowired PersonRepository repository;
|
04 |
@RequestMapping (value = "/persons" , method = RequestMethod.GET)
|
05 |
HttpEntity<PagedResources<Person>> persons(Pageable pageable,
|
06 |
PagedResourcesAssembler assembler) {
|
07 |
Page<Person> persons = repository.findAll(pageable);
|
08 |
return new ResponseEntity<>(assembler.toResources(persons), HttpStatus.OK);
|
像上麵這樣配置將允許PageResourcesAssembler作為控製器方法的一個參數.在這調用toResources(…)方法有以下作用:
- Page的內容將PageResources實例的內容
- PageResources將獲得PageMetadata實例,該實例由Page和基礎的PageRequest中的信息填充
- PageResources獲得prev和next連接,添加這些依賴在頁麵.這些鏈接將指向uri方法的調用映射.頁碼參數根據PageableHandlerMethodArgumentResolver添加到參數以在後麵被轉換
假設我們有30個Person實例在數據庫.你現在可以觸發一個GET請求 https://localhost:8080/persons, 你將可以看到類似下麵的內容:
01 |
{ "links" : [ { "rel" : "next", |
05 |
… // 20 Person instances rendered here |
你可以看到編譯生成了正確的URI,並且還會提取默認配置轉換參數到即將到來的請求中的Pageable.這意味著,如果你改變配置,鏈接也將自動跟隨改變.默認情況下,編譯指向控製器執行的方法,但是這可以被一個自定義鏈接作為基本構建來構成分頁的Link重載PagedResourcesAssembler.toResource(…)方法定製.
Querydsl web 支持
那些整合了QueryDSL的存儲可能從Request查詢字符串中的屬性驅動查詢.
這意味著前麵例子的查詢字符串可以給出User的對象
1 |
?firstname=Dave&lastname=Matthews |
可以被轉換為
1 |
QUser.user.firstname.eq( "Dave" ).and(QUser.user.lastname.eq( "Matthews" ))
|
使用QuerydslPredicateArgumentResolver.
注意
當在類路徑上找到Querydsl時,該功能將在@EnableSpringDataWebSupport注解中自動啟用
添加一個@QuerydslPredicate到一個方法簽名將提供一個就緒的Predicate,可以通過QueryDslPredicateExecutor執行.
提示
類型信息通常從返回方法上解析.由於這些信息不一定匹配實體類型,使用QuerydslPredicate的root屬性可能是個好主意.
02 |
class UserController {
|
03 |
@Autowired UserRepository repository;
|
04 |
@RequestMapping (value = "/" , method = RequestMethod.GET)
|
05 |
String index(Model model, @QuerydslPredicate (root = User. class ) Predicate predicate, ①
|
06 |
Pageable pageable, @RequestParam MultiValueMap<String, String>
|
08 |
model.addAttribute( "users" , repository.findAll(predicate, pageable));
|
①為User轉換匹配查詢字符串參數的Predicate
默認的綁定規則如下:
- Object在簡單屬性上如同eq
- Object在集合作為屬性如同contains
- Collection在簡單屬性上如同in
這些綁定可以通過@QuerydslPredicate的bindings屬性定製或者使用Java8default methods給倉庫接口添加QuerydslBinderCustomizer
01 |
interface UserReposotory extends CurdRepository<User, String>,
|
02 |
QueryDslPredicateExecutor<User>, ①
|
03 |
QuerydslBinderCustomizer<QUser> { ②
|
05 |
default public void customize(QuerydslBindings bindings, QUser user) {
|
06 |
bindings.bind(user.username).first((path, value) -> path.contains(value)); ③
|
07 |
bindings.bind(String. class ).first((StringPath path, String value) -> path.containsIgnoreCase(value)); ④
|
08 |
bindings.excluding(user.password); ⑤
|
① QueryDslPredicateExecutor為Predicate提供特殊的查詢方法提供入口
② 在倉庫接口定義QuerydslBinderCustomizer將自動注解@QuerydslPredicate(bindings=…)
③ 為username屬性定義綁定,綁定到一個簡單集合
④ 為String屬性定義默認綁定到一個不區分大小寫的集合
⑤ 從Predicate移除密碼屬性
倉庫填充
如果你使用Spring JDBC模塊,你可能熟悉在DataSource使用SQL腳本來填充.一個類似的抽象在倉庫級別可以使用,盡管它不是使用SQL作為數據定義語言,因為它必須由存儲決定.填充根據倉庫支持XML(通過Spring的OXM抽象)和JSON(通過Jackson)定義數據.
假設你有一個文件data.json內容如下:
例36 JSON定義的數據
1 |
[ { "_class" : "com.acme.Person", |
3 |
"lastname" : "Matthews" },
|
4 |
{ "_class" : "com.acme.Person",
|
5 |
"firstname" : "Carter",
|
6 |
"lastname" : "Beauford" } ]
|
你可以容易的根據Spring Data Commons提供倉庫的命名空間填充元素填充你的倉庫.為了填充前麵的數據到你的PersonRepository,像下麵這樣配置:
例37 聲明一個Jackson倉庫填充
01 |
<? xml version = "1.0" encoding = "UTF-8" ?>
|
09 |
< repository:jackson2-populator locations = "classpath:data.json" />
|
這樣的聲明可以讓data.json文件可以被一個Jackson的ObjectMpper讀取和反序列化.
JSON將要解析的對象類型由檢查JSON文檔的_class屬性決定.基本組件將最終選擇合適的倉庫去處理反序列化的對象.
要使用XML定義數據填充倉庫,你可以使用unmarshaller-populator元素.你配置它使用Spring OXM提供給你的XML裝配選項.在Spring reference documentation查看更多細節.
例38 聲明一個裝配倉庫填充器(使用JAXB)
01 |
<? xml version = "1.0" encoding = "UTF-8" ?>
|
12 |
< repository:unmarshaller-populator locations = "classpath:data.json"
|
13 |
unmarshaller-ref = "unmarshaller" />
|
14 |
< oxm:jaxb2-marshaller contextPath = "com.acme" />
|
遺留web支持
Spring MVC的實體類綁定
如果正在開發Spring MVC web應用,你通常必須從URL中解析實體類的id.默認的,你的任務是轉化請求參數或URL參數到實體類並將它移交給下麵或直接在實體上操作業務邏輯.這看起來像下麵這樣:
02 |
@RequestMapping ( "/users" )
|
03 |
public class UserController {
|
04 |
private final UserRepository userRepository;
|
07 |
public UserController(UserRepository userRepository) {
|
08 |
Assert.notNull(repository, "Repository must not be null!" );
|
09 |
this .userRepository = userRepository;
|
12 |
@RequestMapping ( "/{id}" )
|
13 |
public String showUserForm( @PathVariable ( "id" ) Long id, Model model) {
|
14 |
// Do null check for id
|
15 |
User user = userRepository.findOne(id);
|
16 |
// Do null check for user
|
17 |
model.addAttribute( "user" , user);
|
首先你為每個控製器定義一個依賴的倉庫來查找它們分別管理的實體.查詢實體也是樣板,因為它總是一個findOne(…)調用.幸運的Spring提供了方法來注冊自定義組件,允許一個String值轉換到一個屬性類型.
屬性編輯
Spring3.0之前JavaPropertyEditors被使用.為了集成這些,Spring Data提出一個DomainClassPropertyEditorRegistrar來查詢所有注冊到ApplicatonContext的Spring Data倉庫和一個定製的PropertyEditor來管理實體類.
01 |
< bean class = "….web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" >
|
02 |
< property name = "webBindingInitializer" >
|
03 |
< bean class = "….web.bind.support.ConfigurableWebBindingInitializer" >
|
04 |
< property name = "propertyEditorRegistrars" >
|
06 |
"org.springframework.data.repository.support.DomainClassPropertyEditorRegistrar" />
|
如果你已經像上麵這樣配置Spring MVC,你可以向下麵這樣配置你的控製器,從而減少不清晰和樣板式的代碼
2 |
@RequestMapping ( "/users" )
|
3 |
public class UserController {
|
4 |
@RequestMapping ( "/{id}" )
|
5 |
public String showUserForm( @PathVariable ( "id" ) User user, Model model) {
|
6 |
model.addAttribute( "user" , user);
|
最後更新:2017-05-19 11:01:49