spring-data-jpa 中文文檔(1)
- 簡介
爲了讓Spring Data的版本保持一致,可以使用maven提供的
dependencyManagement
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-releasetrain</artifactId> <version>${release-train}</version> <scope>import</scope> <type>pom</type> </dependency> </dependencies> </dependencyManagement>
Spring Boot依賴管理
- Spring Boot 會選擇一個較新的版本,但是假使你想升級到一個更新的版本,你可以只配置
spring-data-releasetrain.version
屬性爲下列屬性值中的一個.
BUILD-SNAPSHOT - current snapshots
M1, M2 etc. - milestones
RC1, RC2 etc. - release candidates
RELEASE - GA release
SR1, SR2 etc. - service releases
- Spring Boot 會選擇一個較新的版本,但是假使你想升級到一個更新的版本,你可以只配置
開始使用Spring Data Repositories
.
- 核心概念 核心接口是
Repository
.它以domain
和domain的id類型
作爲參數進行管理 .CrudRepository
接口提供了CRUD功能.
public interface Repository<T, ID extends Serializable> {
}
public interface CrudRepository<T, ID extends Serializable>
extends Repository<T, ID> {
<S extends T> S save(S entity);
T findOne(ID primaryKey);
Iterable<T> findAll();
Long count();
void delete(T entity);
boolean exists(ID primaryKey);
// … more functionality omitted.
}
Spring Data 也提供持久化的具體抽象接口 比如說JpaRepository
和MongoRepository
這些接口擴展CrudRepository
並暴露出底層的持久化技術,但是CrudRepository
等類似的比較通用的持久性與具體技術無關(沒有直接的實現)的接口並不包含在內.其只提供要實現的方法.
接着CrudRepository
有一個PagingAndSortingRepository
的抽象接口.其有一些分頁相關的功能.
public interface PagingAndSortingRepository<T, ID extends Serializable>
extends CrudRepository<T, ID> {
Iterable<T> findAll(Sort sort);
Page<T> findAll(Pageable pageable);
}
獲取一個每頁20條第二頁的User
信息,你可以只是簡單的如此做:
PagingAndSortingRepository<User, Long> repository = // … get access to a bean
Page<User> users = repository.findAll(new PageRequest(1, 20));
除查詢的方法,查詢數量和刪除的語句也可以用這樣的方式實現.
public interface UserRepository extends CrudRepository<User, Long> {
Long countByLastname(String lastname);
}
public interface UserRepository extends CrudRepository<User, Long> {
Long deleteByLastname(String lastname);
List<User> removeByLastname(String lastname);
}
查詢方法(Query Methods)
標準的CRUD功能倉庫實現的查詢比較底層.用Spring Data,定義這些查詢變成了四步:
定義一實現了
Repository
接口或者它的子接口的接口,並且它將會綁定輸入domain類
和domain類的ID類型
.interface PersonRepository extends Repository<Person, Long> { List<Person> findByLastname(String lastname); }
定義查詢的方法
interface PersonRepository extends Repository<Person, Long> { List<Person> findByLastname(String lastname); }
讓
spring
爲這些接口創建代理實例import org.springframework.data.jpa.repository.config.EnableJpaRepositories; @EnableJpaRepositories class Config {}
獲得
repository
實例並且使用public class SomeClient { @Autowired private PersonRepository repository; public void doSomething() { List<Person> persons = repository.findByLastname("Matthews"); } }
接下來詳細解釋每一步
定義repository接口
第一部中你定義一個特定domain
類型的repository接口.這個接口你必須繼承Repository
接口並且定義domain類
和ID類型
.如果你想暴露CRUD方法,你可以繼承CrudRepository
.讓Repository定義的更有規則
通常,你的repository接口會繼承Repository
,CrudRepository
或者PagingAndSortingRepository
.如果你不想繼承Spring Data interfaces
你也可以用@RepositoryDefinition
自己定義repository接口.繼承CrudRepository
暴露了完整的管理你的實體的方法,如果你更喜歡自己定義哪些方法需要去暴露,只需要把要暴露的方法從CrudRepository
中複製出來就可以了.@NoRepositoryBean interface MyBaseRepository<T, ID extends Serializable> extends Repository<T, ID> { T findOne(ID id); T save(T entity); } interface UserRepository extends MyBaseRepository<User, Long> { User findByEmailAddress(EmailAddress emailAddress); }
你需要確保你自己定義的repository接口有@NoRepositoryBean註解.這樣可以保證Spring Data可以實例化它
利用
multiple Spring Data modules
來使用Repositories
使用應用程序中的一個獨特的Spring Data Module
讓事情變得很簡單.因此在定義範圍內的所有repository接口都會綁定到Spring Data Module
.有時候,應用程序需要多個Spring Data Module
,這種情況下,它需要用持久化技術來區分不同的repository.Spring Data Module
進入strict repository mode
,因爲它檢測到在類路徑上有多個資源庫的工廠.strict repository mode
要求在repository或者domain的細節來決定一個repository
定義的Spring Data module
綁定:- 如果
repository
定義 繼承the module-specific repository
如果domain被
the module-specific type annotation
註解.例如JPA’s@Entity
,或者說Spring Data MongoDb/Spring Data Elasticsearch的@Document
.interface MyRepository extends JpaRepository<User, Long> { } @NoRepositoryBean interface MyBaseRepository<T, ID extends Serializable> extends JpaRepository<T, ID> { … } interface UserRepository extends MyBaseRepository<User, Long> { … }
MyRepository 和UserRepository 繼承了JpaRepository 在他們的類型結構中,這是有效的.
interface AmbiguousRepository extends Repository<User, Long> { … } @NoRepositoryBean interface MyBaseRepository<T, ID extends Serializable> extends CrudRepository<T, ID> { … } interface AmbiguousUserRepository extends MyBaseRepository<User, Long> { … }
AmbiguousRepository 和AmbiguousUserRepository 分別繼承Repository 和CrudRepository在他們的類型結構中,雖然這也是非常好的,
multiple modules
不能區分哪一個特定的Spring Data
是repositories要綁定的.interface PersonRepository extends Repository<Person, Long> { … } @Entity public class Person { … } interface UserRepository extends Repository<User, Long> { … } @Document public class User { … }
domain類被第三方(如@Entity或者@Document)註解註解了的.
- 如果
定義查詢方法
SpringData通過方法名有兩種方式去解析出用戶的查詢意圖:一種是直接通過方法的命名規則去解析,第二種是通過Query去解析,那麼當同時存在幾種方式時,SpringData怎麼去選擇這兩
種方式呢?好了,SpringData有一個策略去決定到底使用哪種方式:查詢策略
接下來我們將介紹策略的信息,你可以通過配置<repository>
的query-lookup-strategy
屬性來決定。或者通過Java config的Enable${store}Repositories
註解的queryLookupStrategy
屬性來指定:- CREATE 通過解析方法名字來創建查詢。這個策略是刪除方法中固定的前綴,然後再來解析其餘的部分。
- USE_DECLARED_QUERY 它會根據已經定義好的語句去查詢,如果找不到,則會拋出異常信息。這個語句可以在某個註解或者方法上定義。根據給定的規範來查找可用選項,如果在方法被調用時沒有找到定義的查
詢,那麼會拋出異常。 - CREATE_IF_NOT_FOUND 這個策略結合了以上兩個策略。他會優先查詢是否有定義好的查詢語句,如果沒有,就根據方法的名字去構建查詢。這是一個默認策略,如果不特別指定其他策略,那麼這個策略會在項目
中沿用。
構建查詢
查詢構造器是內置在SpringData中的,他是非常強大的,這個構造器會從方法名中剔除掉類似find…By, read…By, 或者get…By的前綴,然後開始解析其餘的名字。你可以在方法名中加入更多的表達式,例如你需要Distinct的約束,那麼你可以在方法名中加入Distinct即可。在方法中,第一個By表示着查詢語句的開始,你也可以用And或者Or來關聯多個條件。public interface PersonRepository extends Repository<User, Long> { List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname); // 需要在語句中使用Distinct 關鍵字,你需要做的是如下 List<Person> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname); List<Person> findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname); // 如果你需要忽略大小寫,那麼你要用IgnoreCase 關鍵字,你需要做的是如下 List<Person> findByLastnameIgnoreCase(String lastname); // 所有屬性都忽略大小寫呢?AllIgnoreCase 可以幫到您 List<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname); // 同樣的,如果需要排序的話,那你需要:OrderBy List<Person> findByLastnameOrderByFirstnameAsc(String lastname); List<Person> findByLastnameOrderByFirstnameDesc(String lastname); }
根據方法名解析的查詢結果跟數據庫是相關,但是,還有幾個問題需要注意:
多個屬性的查詢可以通過連接操作來完成,例如And,Or
。當然還有其他的,例如Between,LessThan,GreaterThan,Like
。這些操作時跟數據庫相關的,當然你還需要看看相關的數據庫文檔是否支持這些操作。
你可以使用IngoreCase
來忽略被標記的屬性的大小寫,也可以使用AllIgnoreCase
來忽略全部的屬性,當然這個也是需要數據庫支持才允許的。
你可以使用OrderBy
來進行排序查詢,排序的方向是Asc跟Desc,如果需要動態排序,請看後面的章節。屬性表達式
具體的方法名解析查詢需要怎樣的規則呢?這種方法名查詢只能用在被管理的實體類上,就好像之前的案例。假設一個類Person
中有個Address
,並且Address
還有ZipCode
,那麼根據ZipCode來查詢這個Person
需要怎麼做呢?List<Person> findByAddressZipCode(ZipCode zipCode);
在上面的例子中,我們用
x.address.zipCode
去檢索屬性,這種解析算法會在方法名中先找出實體屬性的完整部分(AddressZipCode
),檢查這部分是不是實體類的屬性,如果解析成功,則按
照駝峯式從右到左去解析屬性,如:AddressZipCode
將分爲AddressZip
跟Code
,在這個時候,我們的屬性解析不出Code屬性,則會在此用同樣的方式切割,分爲Address
跟ZipCode
(如果
第一次分割不能匹配,解析器會向左移動分割點),並繼續解析。
爲了避免這種解析的問題,你可以用“_”
去區分,如下所示:List<Person> findByAddress_ZipCode(ZipCode zipCode);
特殊參數處理
上面的例子已經展示了綁定簡單的參數,那麼除此之外,我們還可以綁定一些指定的參數,如Pageable
和Sort
來動態的添加分頁、排序查詢。Page<User> findByLastname(String lastname, Pageable pageable); List<User> findByLastname(String lastname, Sort sort); List<User> findByLastname(String lastname, Pageable pageable);
第一個方法通過傳遞
org.springframework.data.domain.Pageable
來實現分頁功能,排序也綁定在裏面。如果需要排序功能,那麼需要添加參數org.springframework.data.domain.Sort
,如第二行中,返回的對象可以是List
,當然也可以是Page
類型的。限制查詢結果
查詢結果可以通過first
或者top
來進行限制,first
或者top
是可以替換的.可以用一個數字追加在top/first
後邊以指定返回結果的條數.如果這個數字在first/top
左邊,則返回結果大小爲1
.User findFirstByOrderByLastnameAsc(); User findTopByOrderByAgeDesc(); Page<User> queryFirst10ByLastname(String lastname, Pageable pageable); Slice<User> findTop3ByLastname(String lastname, Pageable pageable); List<User> findFirst10ByLastname(String lastname, Sort sort); List<User> findTop10ByLastname(String lastname, Pageable pageable);
表達的限制條件也可以使用
Distinct
關鍵字將查詢結果放入到
Stream
中
查詢結果可以用Java 8Stream<T>
作爲返回結果類型進行處理.@Query("select u from User u") Stream<User> findAllByCustomQueryAndStream(); Stream<User> readAllByFirstnameNotNull(); @Query("select u from User u") Stream<User> streamAllPaged(Pageable pageable);
但並不是所有的 Spring Data modules都能正確的支持
Stream<T>
作爲返回結果類型.異步查詢結果
Repository查詢可以通過Spring的異步處理方法 異步執行.這意味着查詢方法會立即返回.真是的查詢會被作爲一個task放入到Spring TaskExecutor
中.@Async Future<User> findByFirstname(String firstname); @Async CompletableFuture<User> findOneByFirstname(String firstname); @Async ListenableFuture<User> findOneByLastname(String lastname); Use java.util.concurrent.Future as return type. Use a Java 8 java.util.concurrent.CompletableFuture as return type. Use a org.springframework.util.concurrent.ListenableFuture as return type.
創建Repository實體
創建已定義的Repository接口,最簡單的方式就是使用Spring配置文件,當然,需要JPA的命名空間。XML配置
你可以使用JPA命名空間裏面的repositories去自動檢索路徑下的repositories元素:<?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/data/jpa" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"> <repositories base-package="com.acme.repositories" /> </beans:beans>
在本例中,Spring能夠通過
base-package
檢測出指定路徑下所有繼承Repository或者其子接口的接口(有點繞口)。每找到一個接口的時候,FactoryBean
就會創建一個合適的代理去處理以及調用裏面的查詢方法。每個註冊的Bean的名稱都是源於接口名稱,例如:UserRepository將會被註冊爲userRepository。base-package
允許使用通配符作爲掃描格式。使用過濾器
在默認的設置中,將使用全路徑掃描的方式去檢索接口,當然,你在業務上可能需要更細緻的操作,這時候,你可以在<repositories>
中使用<include-filter>
或者<exclude-filter>
。這樣的話,
你可以指定掃描的路徑包含或者不包含指定的路徑。<repositories base-package="com.acme.repositories"> <context:exclude-filter type="regex" expression=".*SomeRepository" /> </repositories>
這個例子中,我們排除了所有以
SomeRepository
結尾的接口。
JavaConfig
可以通過在一個JavaConfig 類上用@Enable${store}Repositories
註解來觸發.@Configuration @EnableJpaRepositories("com.acme.repositories") class ApplicationConfiguration { @Bean public EntityManagerFactory entityManagerFactory() { // … } }
獨立使用
你可以不在Spring容器裏面使用repository。但是你還需要Spring的依賴包在你的classpath
中,你需要使用RepositoryFactory
來實現,代碼如下:RepositoryFactorySupport factory = ... // 初始化 UserRepository repository = factory.getRepository(UserRepository. class);
自定義Repository實現
我們可以自己實現repository的方法。在repository中添加自定義方法
自定義接口:
interface UserRepositoryCustom { public void someCustomMethod(User user); }
自定義接口的實現類
class UserRepositoryImpl implements UserRepositoryCustom { public void someCustomMethod(User user) { // Your custom implementation } }
擴展CRUDRepository
interface UserRepository extends CrudRepository<User, Long>, UserRepositoryCustom { // Declare query methods here }
這樣的話,就能夠在常用的Repository中實現自己的方法。
配置
在XML的配置裏面,框架會自動搜索base-package裏面的實現類,這些實現類的後綴必須滿足repository-impl-postfix中指定的命名規則,默認的規則是:Impl<repositories base- package ="com.acme.repository" /> <repositories base- package ="com.acme.repository" repository-impl-postfix="FooBar" />
第一個配置我們將找到com.acme.repository.UserRepositoryImpl,而第二個配置我們將找到com.acme.repository.UserRepositoryFooBar。
- 人工裝配
前面的代碼中,我們使用了註釋以及配置去自動裝載。如果你自己定義的實現類需要特殊的裝載,那麼你可以跟普通bean一樣聲明出來就可以了,框架會手工的裝載起來,而不是創建本身。
<repositories base-package="com.acme.repository"/> <beans:bean id="userRepositoryImpl" class="…"> </beans:bean>
爲所有的repository添加自定義方法
假如你要爲所有的repository添加一個方法,那麼前面的方法都不可行。你可以這樣做:你需要先聲明一箇中間接口,然後讓你的接口來繼承這個中間接口而不是Repository接口,代碼如下:
中間接口:@NoRepositoryBean public interface MyRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> { void sharedCustomMethod(ID id); }
這時候,我們需要創建我們的實現類,這個實現類是基於Repository中的基類的,這個類會作爲Repository代理的自定義類來執行。
public class MyRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements MyRepository<T, ID> { private final EntityManager entityManager; public MyRepositoryImpl(JpaEntityInformation entityInformation, EntityManager entityManager) { super(entityInformation, entityManager); // Keep the EntityManager around to used from the newly introduced methods. this.entityManager = entityManager; } public void sharedCustomMethod(ID id) { // implementation goes here } }
配置自定義Repository的
base class
用JavaConfig類
@Configuration @EnableJpaRepositories(repositoryBaseClass = MyRepositoryImpl.class) class ApplicationConfiguration { … }
用XML
<repositories base-package="com.acme.repository" repository-base-class="….MyRepositoryImpl" />
Spring Data 擴展
這部分我們將會把SpringData擴展到其他框架中,目前我們繼承的目標是SpringMVC。- Querydsl擴展
public interface QueryDslPredicateExecutor<T> { T findOne(Predicate predicate); Iterable<T> findAll(Predicate predicate); long count(Predicate predicate); boolean exists(Predicate predicate); // … more functionality omitted. }
Finds and returns a single entity matching the
Predicate
.
Finds and returns all entities matching thePredicate
.
Returns the number of entities matching thePredicate
.
Returns if an entity that matches thePredicate
exists.利用
Querydsl
,在你的Repository接口上繼承QueryDslPredicateExecutor
interface UserRepository extends CrudRepository<User, Long>, QueryDslPredicateExecutor<User> {}
然後可以用Querydsl
Predicate
寫類型安全的查詢Predicate predicate = user.firstname.equalsIgnoreCase("dave") .and(user.lastname.startsWithIgnoreCase("mathews")); userRepository.findAll(predicate);
Web支持
SpringData支持很多web功能。當然你的應用也要有SpringMVC的Jar包,有的還需要繼承Spring HATEOAS。
通常來說,你可以在你的JavaConfig配置類中加入@EnableSpringDataWebSupport即可:@Configuration @EnableWebMvc @EnableSpringDataWebSupport class WebConfiguration { }
這個註解註冊了幾個功能,我們稍後會說,他也能檢測Spring HATEOAS,並且註冊他們。
如果你用XML配置的話,那麼你可以用下面的配置:
在xml中配置:<bean class="org.springframework.data.web.config.SpringDataWebConfiguration" /> <!-- If you're using Spring HATEOAS as well register this one *instead* of the former --> <bean class="org.springframework.data.web.config.HateoasAwareSpringDataWebConfiguration" />
- 基本的web支持
上面的配置註冊了以下的幾個功能:
1. DomainClassConverter將會讓SpringMVC能從請求參數或者路徑參數中解析出來。
2. HandlerMethodArgumentResolver 能讓SpringMVC從請求參數中解析出Pageable(分頁)與Sort(排序)。 DomainClassConverter
這個類允許你在SpringMVC控制層的方法中直接使用你的領域類型(Domain types),如下:@Controller @RequestMapping("/users") public class UserController { @RequestMapping("/{id}") public String showUserForm(@PathVariable("id") User user, Model model) { model.addAttribute("user", user); return "userForm"; } }
正如你所見,上面的方法直接接收了一個User對象,你不需要做任何的搜索操作,這個轉換器自動的設id的值進去對象中,並且最終調用了
findOne
方法查詢出實體。(注:當前的Repository
必須實現CrudRepository)HandlerMethodArgumentResolver分頁排序
這個配置項同時註冊了PageableHandlerMethodArgumentResolver 和 SortHandlerMethodArgumentResolver,使得Pageable跟Sort能作爲控制層的參數使用:@Controller @RequestMapping("/users") public class UserController { @Autowired UserRepository repository; @RequestMapping public String showUsers(Model model, Pageable pageable) { model.addAttribute("users", repository.findAll(pageable)); return "users"; } }
這個配置會讓SpringMVC傳遞一個Pageable實體參數,下面是默認的參數:
參數名 說明 page 你要獲取的頁數 size 一頁中最大的數據量 sort 需要被排序的屬性(格式:屬性1,屬性2(ASC/DESC)),默認是ASC,使用多個字段排序,你可以使用sort=first&sort=last,asc 如果你需要對多個表寫多個分頁或排序,那麼你需要用@Qualifier來區分,請求參數的前綴是${qualifire}_,那麼你的方法可能變成這樣:
public String showUsers(Model model, @Qualifier("foo") Pageable first, @Qualifier("bar") Pageable second) { … }
你需要填寫foo_page和bar_page等。
默認的Pageable相當於new PageRequest(0,20),你可以用@PageableDefaults註解來放在Pageable上。超媒體分頁
Spring HATEOAS有一個PagedResources類,他豐富了Page實體以及一些讓用戶更容易導航到資源的請求方式。Page轉換到PagedResources是由一個實現了Spring HATEOAS
ResourceAssembler接口的實現類:PagedResourcesAssembler提供轉換的。@Controller class PersonController { @Autowired PersonRepository repository; @RequestMapping(value = "/persons", method = RequestMethod.GET) HttpEntity<PagedResources<Person>> persons(Pageable pageable, PagedResourcesAssembler assembler) { Page<Person> persons = repository.findAll(pageable); return new ResponseEntity<>(assembler.toResources(persons), HttpStatus.OK); } }
上面的toResources方法會執行以下的幾個步驟:
- Page對象的內容會轉換成爲PagedResources對象。
- PagedResources會的到一個PageMetadata的實體附加,包含Page跟PageRequest。
- PagedResources會根據狀態得到prev跟next鏈接,這些鏈接指向URI所匹配的方法中。分頁參數會根據PageableHandlerMethodArgumentResolver配置,以讓其在後面的方法中
- 解析使用。
假使我們現在有30個Person實例在數據庫中,你可以通過GET
http://localhost:8080/persons 並且 你會得到像下邊這樣的一些反饋:
{ "links" : [ { "rel" : "next", "href" : "http://localhost:8080/persons?page=1&size=20 } ], "content" : [ … // 20 Person instances rendered here ], "pageMetadata" : { "size" : 20, "totalElements" : 30, "totalPages" : 2, "number" : 0 } }
Querydsl web支持
它可以從Request
的query string中提取出一些屬性,並轉換成Querydsl樣式.
這意味着它可以把?firstname=Dave&lastname=Matthews
這樣的query string 解析成:
QUser.user.firstname.eq("Dave").and(QUser.user.lastname.eq("Matthews"))
使用
QuerydslPredicateArgumentResolver
.
在未來當Querydsl在classpath中被發現時,僅僅使用@EnableSpringDataWebSupport
就可以激活
在方法上增加一個@QuerydslPredicate
將會提供一個可以通過QueryDslPredicateExecutor
來執行的Predicate
使用QueryDslPredicateExecutor
中的root
屬性來確定@QuerydslPredicate
的返回值類型@Controller class UserController { @Autowired UserRepository repository; @RequestMapping(value = "/", method = RequestMethod.GET) String index(Model model, @QuerydslPredicate(root = User.class) Predicate predicate, Pageable pageable, @RequestParam MultiValueMap<String, String> parameters) { model.addAttribute("users", repository.findAll(predicate, pageable)); return "index"; } }
爲User解析query string來匹配
Predicate
默認的綁定方式如下:Object
on simple properties aseq
.Object
on collection like properties ascontains
.Collection
on simple properties asin
.
這樣的綁定可以通過@QuerydslPredicate
的bingdings
屬性來定製,也可以使用Java 8 的default methods
在Repository接口上追加QuerydslBinderCustomizer
interface UserRepository extends CrudRepository<User, String>, QueryDslPredicateExecutor<User>, QuerydslBinderCustomizer<QUser> { @Override default public void customize(QuerydslBindings bindings, QUser user) { bindings.bind(user.username).first((path, value) -> path.contains(value)) bindings.bind(String.class) .first((StringPath path, String value) -> path.containsIgnoreCase(value)); bindings.excluding(user.password); } }
QueryDslPredicateExecutor
給Predicate
提供具體的查找方法.- 在Repository 接口中
QuerydslBinderCustomizer
的定義和@QuerydslPredicate(bindings=…)
的快捷方式將會被自動抓取 username
屬性的綁定就是一個簡單的contains
綁定.- 默認的
String
屬性綁定是不區分大小寫的匹配contains
- Exclude the password property from Predicate resolution.
- 基本的web支持
Repository填充
如果你用過Spring JDBC,那麼你肯定很熟悉使用SQL去填寫數據源(DataSource),在這裏,我們可以使用XML或者Json去填寫數據,而不再使用SQL填充。
假如你有一個data.json的文件,如下:[ { "_class" : "com.acme.Person", "firstname" : "Dave", "lastname" : "Matthews" }, { "_class" : "com.acme.Person", "firstname" : "Carter", "lastname" : "Beauford" } ]
要PersonRepository填充這些數據進去,你需要做如下的聲明:
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/data/repository http://www.springframework.org/schema/data/repository/spring-repository.xsd"> <repository:jackson2-populator locations="classpath:data.json" /> </beans>
這個聲明使得data.json能夠通過Jackson ObjectMapper被其他地方讀取,反序列化。
Legacy Web(傳統web)支持
在SpringMVC中綁定領域類(Domain class)
你在開發web項目的時候,你經常需要從URL或者請求參數中解析領域類中的ID,你可能是這麼做得:@Controller @RequestMapping("/users") public class UserController { private final UserRepository userRepository; @Autowired public UserController(UserRepository userRepository) { Assert.notNull(repository, "Repository must not be null!"); this.userRepository = userRepository; } @RequestMapping("/{id}") public String showUserForm(@PathVariable("id") Long id, Model model) { // Do null check for id User user = userRepository.findOne(id); // Do null check for user model.addAttribute("user", user); return "user"; } }
首先你要注入一個UserRepository ,然後通過findOne查詢出結果。幸運的是,Spring提供了自定義組件允許你從String類型到任意類型的轉換。
PropertyEditors(屬性編輯器)
在Spring3.0之前,Java的PropertyEditor已經被使用。現在我們要集成它,SpringData提供了一個DomainClassPropertyEditorRegistrar類,他能在ApplicationContext中查找SpringData的
Repositories,並且註冊自定義的PropertyEditor。<bean class="….web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="webBindingInitializer"> <bean class="….web.bind.support.ConfigurableWebBindingInitializer"> <property name="propertyEditorRegistrars"> <bean class="org.springframework.data.repository.support.DomainClassPropertyEditorRegistrar" /> </property> </bean> </property> </bean>
如果你做了上面的工作,那麼你在前面的例子中,會大大減少工作量:
@Controller @RequestMapping("/users") public class UserController { @RequestMapping("/{id}") public String showUserForm(@PathVariable("id") User user, Model model) { model.addAttribute("user", user); return "userForm"; } }
轉換服務
在Spring3以後,PropertyEditor已經被轉換服務取代了,SpringData現在用DomainClassConverter模仿
DomainClassPropertyEditorRegistrar中的實現。你可以使用如下的配置:<mvc:annotation-driven conversion-service="conversionService"/> <bean class="org.springframework.data.repository.support.DomainClassConverter"> <constructor-arg ref="conversionService"/>
如果你是用JavaConfig,你可以集成SpringMVC的WebMvcConfigurationSupport並且處理FormatingConversionService,那麼你可以這麼做:
class WebConfiguration extends WebMvcConfigurationSupport { // 省略其他配置 @Bean public DomainClassConverter<?> domainClassConverter() { return new DomainClassConverter<FormattingConversionService>(mvcConversionService()); } }
- Querydsl擴展