https://www.ibm.com/developerworks/cn/opensource/os-cn-spring-jpa/
Spring Data JPA 開發(fā)指南
本文由淺入深地講述了使用 Spring Data JPA 需要關注的各個方面敌完,為讀者了解和使用該框架提供了指導,可以作為 Spring Data JPA 的學習指南。
從一個簡單的 JPA 示例開始
本文主要講述 Spring Data JPA椎镣,但是為了不至于給
JPA 和 Spring 的初學者造成較大的學習曲線,我們首先從 JPA 開始刷后,簡單介紹一個 JPA 示例蔑滓;接著重構(gòu)該示例憨闰,并引入 Spring
框架,這兩部分不會涉及過多的篇幅确封,如果希望能夠深入學習 Spring 和 JPA除呵,可以根據(jù)本文最后提供的參考資料進一步學習。
自
JPA 伴隨 Java EE 5 發(fā)布以來爪喘,受到了各大廠商及開源社區(qū)的追捧颜曾,各種商用的和開源的 JPA
框架如雨后春筍般出現(xiàn),為開發(fā)者提供了豐富的選擇秉剑。它一改之前 EJB 2.x 中實體 Bean
笨重且難以使用的形象泛豪,充分吸收了在開源社區(qū)已經(jīng)相對成熟的 ORM 思想。另外侦鹏,它并不依賴于 EJB
容器诡曙,可以作為一個獨立的持久層技術而存在。目前比較成熟的 JPA 框架主要包括 Jboss 的 Hibernate
EntityManager略水、Oracle 捐獻給 Eclipse 社區(qū)的 EclipseLink价卤、Apache 的 OpenJPA 等。
本
文的示例代碼基于 Hibernate EntityManager 開發(fā)渊涝,但是讀者幾乎不用修改任何代碼荠雕,便可以非常容易地切換到其他 JPA
框架,因為代碼中使用到的都是 JPA 規(guī)范提供的接口 /
類驶赏,并沒有使用到框架本身的私有特性。示例主要涉及七個文件既鞠,但是很清晰:業(yè)務層包含一個接口和一個實現(xiàn)煤傍;持久層包含一個接口、一個實現(xiàn)嘱蛋、一個實體類蚯姆;另
外加上一個 JPA 配置文件和一個測試類。相關類 / 接口代碼如下:
清單 1. 實體類 AccountInfo.java
@Entity
@Table(name = "t_accountinfo")
public class AccountInfo implements Serializable {
private Long accountId;
private Integer balance;
// 此處省略 getter 和 setter 方法斑粱。
}
清單 2. 業(yè)務層接口 UserService.java
public interface UserService {
public AccountInfo createNewAccount(String user, String pwd, Integer init);
}
清單 3. 業(yè)務層的實現(xiàn)類 UserServiceImpl.java
public class UserServiceImpl implements UserService {
private UserDao userDao = new UserDaoImpl();
public AccountInfo createNewAccount(String user, String pwd, Integer init){
// 封裝域?qū)ο?/p>
AccountInfo accountInfo = new AccountInfo();
UserInfo userInfo = new UserInfo();
userInfo.setUsername(username);
userInfo.setPassword(password);
accountInfo.setBalance(initBalance);
accountInfo.setUserInfo(userInfo);
// 調(diào)用持久層割卖,完成數(shù)據(jù)的保存
return userDao.save(accountInfo);
}
}
清單 4. 持久層接口
public interface UserDao {
public AccountInfo save(AccountInfo accountInfo);
}
清單 5. 持久層的實現(xiàn)類
public class UserDaoImpl implements UserDao {
public AccountInfo save(AccountInfo accountInfo) {
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("SimplePU");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
em.persist(accountInfo);
em.getTransaction().commit();
emf.close();
return accountInfo;
}
}
清單 6. JPA 標準配置文件 persistence.xml
org.hibernate.ejb.HibernatePersistence
footmark.springdata.jpa.domain.UserInfo
footmark.springdata.jpa.domain.AccountInfo
value="com.mysql.jdbc.Driver"/>
value="jdbc:mysql://10.40.74.197:3306/zhangjp"/>
value="org.hibernate.dialect.MySQL5Dialect"/>
清單 7. 本文使用如下的 main 方法進行開發(fā)者測試
public class SimpleSpringJpaDemo {
public static void main(String[] args) {
new UserServiceImpl().createNewAccount("ZhangJianPing", "123456", 1);
}
}
簡述 Spring 框架對 JPA 的支持
接
下來我們引入 Spring短蜕,以展示 Spring 框架對 JPA 的支持。業(yè)務層接口 UserService
保持不變郭毕,UserServiceImpl 中增加了三個注解,以讓 Spring 完成依賴注入函荣,因此不再需要使用 new 操作符創(chuàng)建
UserDaoImpl 對象了显押。同時我們還使用了 Spring 的聲明式事務:
清單 8. 配置為 Spring Bean 的業(yè)務層實現(xiàn)
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Transactional
public AccountInfo createNewAccount(
String name, String pwd, Integer init) { …… }
}
對于持久層扳肛,UserDao 接口也不需要修改,只需修改 UserDaoImpl 實現(xiàn)乘碑,修改后的代碼如下:
清單 9. 配置為 Spring Bean 的持久層實現(xiàn)
@Repository("userDao")
public class UserDaoImpl implements UserDao {
@PersistenceContext
private EntityManager em;
@Transactional
public Long save(AccountInfo accountInfo) {
em.persist(accountInfo);
return accountInfo.getAccountId();
}
}
清單 10. Spring 配置文件
class="org.springframework.orm.jpa.JpaTransactionManager">
"org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
清單 11. 改造后的基于 Spring 的開發(fā)者測試代碼
public class SimpleSpringJpaDemo{
public static void main(String[] args){
ClassPathXmlApplicationContext ctx =
new ClassPathXmlApplicationContext("spring-demo-cfg.xml");
UserDao userDao = ctx.getBean("userDao", UserDao.class);
userDao.createNewAccount("ZhangJianPing", "123456", 1);
}
}
通過對比重構(gòu)前后的代碼挖息,可以發(fā)現(xiàn) Spring 對 JPA 的簡化已經(jīng)非常出色了,我們可以大致總結(jié)一下 Spring 框架對 JPA 提供的支持主要體現(xiàn)在如下幾個方面:
首先兽肤,它使得 JPA 配置變得更加靈活套腹。JPA 規(guī)范要求,配置文件必須命名為 persistence.xml资铡,并存在于類路徑下的
META-INF 目錄中电禀。該文件通常包含了初始化 JPA 引擎所需的全部信息。Spring 提供的
LocalContainerEntityManagerFactoryBean 提供了非常靈活的配置害驹,persistence.xml
中的信息都可以在此以屬性注入的方式提供鞭呕。
其次,Spring 實現(xiàn)了部分在 EJB 容器環(huán)境下才具有的功能宛官,比如對 @PersistenceContext葫松、@PersistenceUnit 的容器注入支持。
第三底洗,也是最具意義的腋么,Spring 將 EntityManager
的創(chuàng)建與銷毀、事務管理等代碼抽取出來亥揖,并由其統(tǒng)一管理珊擂,開發(fā)者不需要關心這些,如前面的代碼所示费变,業(yè)務方法中只剩下操作領域?qū)ο蟮拇a摧扇,事務管理和
EntityManager 創(chuàng)建、銷毀的代碼都不再需要開發(fā)者關心了挚歧。
更進一步:Spring Data JPA 讓一切近乎完美
通
過前面的分析可以看出扛稽,Spring 對 JPA 的支持已經(jīng)非常強大,開發(fā)者只需關心核心業(yè)務邏輯的實現(xiàn)代碼滑负,無需過多關注
EntityManager 的創(chuàng)建在张、事務處理等 JPA 相關的處理,這基本上也是作為一個開發(fā)框架而言所能做到的極限了矮慕。然而帮匾,Spring
開發(fā)小組并沒有止步,他們再接再厲痴鳄,于最近推出了 Spring Data JPA 框架瘟斜,主要針對的就是 Spring
唯一沒有簡化到的業(yè)務邏輯代碼,至此,開發(fā)者連僅剩的實現(xiàn)持久層業(yè)務邏輯的工作都省了哼转,唯一要做的明未,就只是聲明持久層的接口,其他都交給 Spring
Data JPA 來幫你完成壹蔓!
至此趟妥,讀者可能會存在一個疑問,框架怎么可能代替開發(fā)者實現(xiàn)業(yè)務邏輯呢佣蓉?畢竟披摄,每一個應用的持久層業(yè)務甚至
領域?qū)ο蠖疾槐M相同,框架是怎么做到的呢勇凭?其實這背后的思想并不復雜疚膊,比如,當你看到 UserDao.findUserById()
這樣一個方法聲明虾标,大致應該能判斷出這是根據(jù)給定條件的 ID 查詢出滿足條件的 User 對象寓盗。Spring Data JPA
做的便是規(guī)范方法的名字,根據(jù)符合規(guī)范的名字來確定方法需要實現(xiàn)什么樣的邏輯璧函。
接下來我們針對前面的例子進行改造傀蚌,讓 Spring Data JPA 來幫助我們完成業(yè)務邏輯。在著手寫代碼之前蘸吓,開發(fā)者需要先下載Spring Data JPA 的發(fā)布包(需要同時下載 Spring Data Commons 和 Spring Data JPA 兩個發(fā)布包善炫,Commons 是 Spring Data 的公共基礎包),并把相關的依賴 JAR 文件加入到 CLASSPATH 中库继。
首先箩艺,讓持久層接口 UserDao 繼承 Repository 接口。該接口使用了泛型宪萄,需要為其提供兩個類型:第一個為該接口處理的域?qū)ο箢愋鸵兆唬诙€為該域?qū)ο蟮闹麈I類型。修改后的 UserDao 如下:
清單 12. Spring Data JPA 風格的持久層接口
public interface UserDao extends Repository {
public AccountInfo save(AccountInfo accountInfo);
}
然后刪除 UserDaoImpl 類拜英,因為我們前面說過静汤,框架會為我們完成業(yè)務邏輯。最后聊记,我們需要在 Spring 配置文件中增加如下配置,以使 Spring 識別出需要為其實現(xiàn)的持久層接口:
清單 13. 在 Spring 配置文件中啟用掃描并自動創(chuàng)建代理的功能
<-- 需要在 標簽中增加對 jpa 命名空間的引用 -->
entity-manager-factory-ref="entityManagerFactory"
transaction-manager-ref="transactionManager"/>
至此便大功告成了恢暖!執(zhí)行一下測試代碼排监,然后看一下數(shù)據(jù)庫,新的數(shù)據(jù)已經(jīng)如我們預期的添加到表中了杰捂。如果要再增加新的持久層業(yè)務舆床,比如希望查詢出給 ID 的 AccountInfo 對象,該怎么辦呢?很簡單挨队,在 UserDao 接口中增加一行代碼即可:
清單 14. 修改后的持久層接口谷暮,增加一個方法聲明
public interface UserDao extends Repository {
public AccountInfo save(AccountInfo accountInfo);
// 你需要做的,僅僅是新增如下一行方法聲明
public AccountInfo findByAccountId(Long accountId);
}
下面總結(jié)一下使用 Spring Data JPA 進行持久層開發(fā)大致需要的三個步驟:
聲明持久層的接口盛垦,該接口繼承 Repository湿弦,Repository 是一個標記型接口,它不包含任何方法腾夯,當然如果有需要颊埃,Spring Data 也提供了若干 Repository 子接口,其中定義了一些常用的增刪改查蝶俱,以及分頁相關的方法班利。
在接口中聲明需要的業(yè)務方法。Spring Data 將根據(jù)給定的策略(具體策略稍后講解)來為其生成實現(xiàn)代碼榨呆。
在 Spring 配置文件中增加一行聲明罗标,讓 Spring 為聲明的接口創(chuàng)建代理對象。配置了
后积蜻,Spring 初始化容器時將會掃描 base-package 指定的包目錄及其子目錄闯割,為繼承 Repository
或其子接口的接口創(chuàng)建代理對象,并將代理對象注冊為 Spring Bean浅侨,業(yè)務層便可以通過 Spring 自動封裝的特性來直接使用該對象纽谒。
此外, 還提供了一些屬性和子標簽如输,便于做更細粒度的控制鼓黔。可以在 內(nèi)部使用 不见、 來過濾掉一些不希望被掃描到的接口澳化。具體的使用方法見Spring參考文檔。
應該繼承哪個接口稳吮?
前
面提到缎谷,持久層接口繼承 Repository 并不是唯一選擇。Repository 接口是 Spring Data
的一個核心接口灶似,它不提供任何方法列林,開發(fā)者需要在自己定義的接口中聲明需要的方法。與繼承 Repository
等價的一種方式酪惭,就是在持久層接口上使用 @RepositoryDefinition 注解希痴,并為其指定 domainClass 和 idClass
屬性。如下兩種方式是完全等價的:
清單 15. 兩種等價的繼承接口方式示例
public interface UserDao extends Repository { …… }
@RepositoryDefinition(domainClass = AccountInfo.class, idClass = Long.class)
public interface UserDao { …… }
如果持久層接口較多春感,且每一個接口都需要聲明相似的增
刪改查方法砌创,直接繼承 Repository 就顯得有些啰嗦虏缸,這時可以繼承
CrudRepository,它會自動為域?qū)ο髣?chuàng)建增刪改查方法嫩实,供業(yè)務層直接使用刽辙。開發(fā)者只是多寫了 "Crud"
四個字母,即刻便為域?qū)ο筇峁┝碎_箱即用的十個增刪改查方法甲献。
但是宰缤,使用 CrudRepository
也有副作用,它可能暴露了你不希望暴露給業(yè)務層的方法竟纳。比如某些接口你只希望提供增加的操作而不希望提供刪除的方法撵溃。針對這種情況,開發(fā)者只能退回到
Repository 接口锥累,然后到 CrudRepository 中把希望保留的方法聲明復制到自定義的接口中即可缘挑。
分頁查詢和排序是
持久層常用的功能,Spring Data 為此提供了 PagingAndSortingRepository 接口桶略,它繼承自
CrudRepository 接口语淘,在 CrudRepository
基礎上新增了兩個與分頁有關的方法。但是际歼,我們很少會將自定義的持久層接口直接繼承自
PagingAndSortingRepository惶翻,而是在繼承 Repository 或 CrudRepository
的基礎上,在自己聲明的方法參數(shù)列表最后增加一個 Pageable 或 Sort 類型的參數(shù)鹅心,用于指定分頁或排序信息即可吕粗,這比直接使用
PagingAndSortingRepository 提供了更大的靈活性。
JpaRepository 是繼承自
PagingAndSortingRepository 的針對 JPA 技術提供的接口旭愧,它在父接口的基礎上颅筋,提供了其他一些方法,比如
flush()输枯,saveAndFlush()议泵,deleteInBatch() 等。如果有這樣的需求桃熄,則可以繼承該接口先口。
上述四個接
口,開發(fā)者到底該如何選擇瞳收?其實依據(jù)很簡單碉京,根據(jù)具體的業(yè)務需求,選擇其中之一螟深。筆者建議在通常情況下優(yōu)先選擇 Repository 接口谐宙。因為
Repository 接口已經(jīng)能滿足日常需求,其他接口能做到的在 Repository 中也能做到血崭,彼此之間并不存在功能強弱的問題卧惜。只是
Repository 需要顯示聲明需要的方法,而其他則可能已經(jīng)提供了相關的方法夹纫,不需要再顯式聲明咽瓷,但如果對 Spring Data JPA
不熟悉,別人在檢視代碼或者接手相關代碼時會有疑惑舰讹,他們不明白為什么明明在持久層接口中聲明了三個方法茅姜,而在業(yè)務層使用該接口時,卻發(fā)現(xiàn)有七八個方法可
用月匣,從這個角度而言钻洒,應該優(yōu)先考慮使用 Repository 接口。
前面提到锄开,Spring Data JPA 在后臺為持久層接口創(chuàng)建代理對象時素标,會解析方法名字,并實現(xiàn)相應的功能萍悴。除了通過方法名字以外头遭,它還可以通過如下兩種方式指定查詢語句:
Spring Data JPA 可以訪問 JPA 命名查詢語句。開發(fā)者只需要在定義命名查詢語句時癣诱,為其指定一個符合給定格式的名字计维,Spring Data JPA 便會在創(chuàng)建代理對象時,使用該命名查詢語句來實現(xiàn)其功能撕予。
開發(fā)者還可以直接在聲明的方法上面使用 @Query 注解鲫惶,并提供一個查詢語句作為參數(shù),Spring Data JPA 在創(chuàng)建代理對象時实抡,便以提供的查詢語句來實現(xiàn)其功能欠母。
下面我們分別講述三種創(chuàng)建查詢的方式。
通過解析方法名創(chuàng)建查詢
通
過前面的例子澜术,讀者基本上對解析方法名創(chuàng)建查詢的方式有了一個大致的了解艺蝴,這也是 Spring Data JPA
吸引開發(fā)者的一個很重要的因素。該功能其實并非 Spring Data JPA 首創(chuàng)鸟废,而是源自一個開源的 JPA 框架 Hades猜敢,該框架的作者
Oliver Gierke 本身又是 Spring Data JPA 項目的 Leader,所以把 Hades 的優(yōu)勢引入到 Spring
Data JPA 也就是順理成章的了盒延。
框架在進行方法名解析時缩擂,會先把方法名多余的前綴截取掉,比如
find添寺、findBy胯盯、read、readBy计露、get博脑、getBy憎乙,然后對剩下部分進行解析。并且如果方法的最后一個參數(shù)是 Sort 或者
Pageable 類型叉趣,也會提取相關的信息泞边,以便按規(guī)則進行排序或者分頁查詢。
在創(chuàng)建查詢時疗杉,我們通過在方法名中使用屬性名稱來表達阵谚,比如 findByUserAddressZip ()⊙叹撸框架在解析該方法時梢什,首先剔除 findBy,然后對剩下的屬性進行解析朝聋,詳細規(guī)則如下(此處假設該方法針對的域?qū)ο鬄?AccountInfo 類型):
先判斷 userAddressZip (根據(jù) POJO 規(guī)范嗡午,首字母變?yōu)樾懀峦┦欠駷?AccountInfo 的一個屬性冀痕,如果是翼馆,則表示根據(jù)該屬性進行查詢;如果沒有該屬性金度,繼續(xù)第二步应媚;
從右往左截取第一個大寫字母開頭的字符串(此處為 Zip),然后檢查剩下的字符串是否為 AccountInfo
的一個屬性猜极,如果是中姜,則表示根據(jù)該屬性進行查詢;如果沒有該屬性跟伏,則重復第二步丢胚,繼續(xù)從右往左截取受扳;最后假設 user 為 AccountInfo
的一個屬性携龟;
接著處理剩下部分( AddressZip ),先判斷 user 所對應的類型是否有 addressZip 屬性勘高,如果有峡蟋,則表示該方法最終是根據(jù)
"AccountInfo.user.addressZip" 的取值進行查詢;否則繼續(xù)按照步驟 2 的規(guī)則從右往左截取华望,最終表示根據(jù)
"AccountInfo.user.address.zip" 的值進行查詢蕊蝗。
可能會存在一種特殊情況,比如
AccountInfo 包含一個 user 的屬性赖舟,也有一個 userAddress 屬性蓬戚,此時會存在混淆。讀者可以明確在屬性之間加上 "_"
以顯式表達意圖宾抓,比如 "findByUser_AddressZip()" 或者 "findByUserAddress_Zip()"子漩。
在查詢時豫喧,通常需要同時根據(jù)多個屬性進行查詢,且查詢的條件也格式各樣(大于某個值幢泼、在某個范圍等等)嘿棘,Spring Data JPA 為此提供了一些表達條件查詢的關鍵字,大致如下:
And --- 等價于 SQL 中的 and 關鍵字旭绒,比如 findByUsernameAndPassword(String user, Striang pwd);
Or --- 等價于 SQL 中的 or 關鍵字焦人,比如 findByUsernameOrAddress(String user, String addr)挥吵;
Between --- 等價于 SQL 中的 between 關鍵字,比如 findBySalaryBetween(int max, int min)花椭;
LessThan --- 等價于 SQL 中的 "<"忽匈,比如 findBySalaryLessThan(int max);
GreaterThan --- 等價于 SQL 中的">"矿辽,比如 findBySalaryGreaterThan(int min)丹允;
IsNull --- 等價于 SQL 中的 "is null",比如 findByUsernameIsNull()袋倔;
IsNotNull --- 等價于 SQL 中的 "is not null"雕蔽,比如 findByUsernameIsNotNull();
NotNull --- 與 IsNotNull 等價宾娜;
Like --- 等價于 SQL 中的 "like"批狐,比如 findByUsernameLike(String user);
NotLike --- 等價于 SQL 中的 "not like"前塔,比如 findByUsernameNotLike(String user)嚣艇;
OrderBy --- 等價于 SQL 中的 "order by",比如 findByUsernameOrderBySalaryAsc(String user)华弓;
Not --- 等價于 SQL 中的 "食零! =",比如 findByUsernameNot(String user)寂屏;
In --- 等價于 SQL 中的 "in"贰谣,比如 findByUsernameIn(Collection userList) ,方法的參數(shù)可以是 Collection 類型迁霎,也可以是數(shù)組或者不定長參數(shù)冈爹;
NotIn --- 等價于 SQL 中的 "not in",比如 findByUsernameNotIn(Collection userList) 欧引,方法的參數(shù)可以是 Collection 類型频伤,也可以是數(shù)組或者不定長參數(shù);
使用 @Query 創(chuàng)建查詢
@Query 注解的使用非常簡單芝此,只需在聲明的方法上面標注該注解憋肖,同時提供一個 JP QL 查詢語句即可因痛,如下所示:
清單 16. 使用 @Query 提供自定義查詢語句示例
public interface UserDao extends Repository {
@Query("select a from AccountInfo a where a.accountId = ?1")
public AccountInfo findByAccountId(Long accountId);
@Query("select a from AccountInfo a where a.balance > ?1")
public Page findByBalanceGreaterThan(
Integer balance,Pageable pageable);
}
很多開發(fā)者在創(chuàng)建 JP QL 時喜歡使用命名參數(shù)來代替位置編號,@Query 也對此提供了支持岸更。JP QL 語句中通過": 變量"的格式來指定參數(shù)鸵膏,同時在方法的參數(shù)前面使用 @Param 將方法參數(shù)與 JP QL 中的命名參數(shù)對應,示例如下:
清單 17. @Query 支持命名參數(shù)示例
public interface UserDao extends Repository {
public AccountInfo save(AccountInfo accountInfo);
@Query("from AccountInfo a where a.accountId = :id")
public AccountInfo findByAccountId(@Param("id")Long accountId);
@Query("from AccountInfo a where a.balance > :balance")
public Page findByBalanceGreaterThan(
@Param("balance")Integer balance,Pageable pageable);
}
此外怎炊,開發(fā)者也可以通過使用 @Query 來執(zhí)行一個更新操作谭企,為此,我們需要在使用 @Query 的同時评肆,用 @Modifying 來將該操作標識為修改查詢债查,這樣框架最終會生成一個更新的操作,而非查詢瓜挽。如下所示:
清單 18. 使用 @Modifying 將查詢標識為修改查詢
@Modifying
@Query("update AccountInfo a set a.salary = ?1 where a.salary < ?2")
public int increaseSalary(int after, int before);
通過調(diào)用 JPA 命名查詢語句創(chuàng)建查詢
命
名查詢是 JPA 提供的一種將查詢語句從方法體中獨立出來盹廷,以供多個方法共用的功能。Spring Data JPA
對命名查詢也提供了很好的支持久橙。用戶只需要按照 JPA 規(guī)范在 orm.xml 文件或者在代碼中使用 @NamedQuery(或
@NamedNativeQuery)定義好查詢語句俄占,唯一要做的就是為該語句命名時,需要滿足”DomainClass.methodName()”的
命名規(guī)則淆衷。假設定義了如下接口:
清單 19. 使用 JPA 命名查詢時缸榄,聲明接口及方法時不需要什么特殊處理
public interface UserDao extends Repository {
......
public List findTop5();
}
如果希望為 findTop5()
創(chuàng)建命名查詢,并與之關聯(lián)祝拯,我們只需要在適當?shù)奈恢枚x命名查詢語句碰凶,并將其命名為
"AccountInfo.findTop5",框架在創(chuàng)建代理類的過程中鹿驼,解析到該方法時欲低,優(yōu)先查找名為
"AccountInfo.findTop5" 的命名查詢定義,如果沒有找到畜晰,則嘗試解析方法名砾莱,根據(jù)方法名字創(chuàng)建查詢。
創(chuàng)建查詢的順序
Spring
Data JPA
在為接口創(chuàng)建代理對象時凄鼻,如果發(fā)現(xiàn)同時存在多種上述情況可用腊瑟,它該優(yōu)先采用哪種策略呢?為此块蚌, 提供了
query-lookup-strategy 屬性闰非,用以指定查找的順序。它有如下三個取值:
create --- 通過解析方法名字來創(chuàng)建查詢峭范。即使有符合的命名查詢财松,或者方法通過 @Query 指定的查詢語句,都將會被忽略。
create-if-not-found --- 如果方法通過 @Query
指定了查詢語句辆毡,則使用該語句實現(xiàn)查詢菜秦;如果沒有,則查找是否定義了符合條件的命名查詢舶掖,如果找到球昨,則使用該命名查詢;如果兩者都沒有找到眨攘,則通過解析方
法名字來創(chuàng)建查詢主慰。這是 query-lookup-strategy 屬性的默認值。
use-declared-query --- 如果方法通過 @Query 指定了查詢語句鲫售,則使用該語句實現(xiàn)查詢共螺;如果沒有,則查找是否定義了符合條件的命名查詢龟虎,如果找到,則使用該命名查詢沙庐;如果兩者都沒有找到鲤妥,則拋出異常。
Spring Data JPA 對事務的支持
默
認情況下拱雏,Spring Data JPA 實現(xiàn)的方法都是使用事務的棉安。針對查詢類型的方法,其等價于
@Transactional(readOnly=true)铸抑;增刪改類型的方法贡耽,等價于
@Transactional∪笛矗可以看出蒲赂,除了將查詢的方法設為只讀事務外,其他事務屬性均采用默認值刁憋。
如果用戶覺得有必要滥嘴,可以在接口方法上使用 @Transactional 顯式指定事務屬性,該值覆蓋 Spring Data JPA 提供的默認值至耻。同時若皱,開發(fā)者也可以在業(yè)務層方法上使用 @Transactional 指定事務屬性,這主要針對一個業(yè)務層方法多次調(diào)用持久層方法的情況尘颓。持久層的事務會根據(jù)設置的事務傳播行為來決定是掛起業(yè)務層事務還是加入業(yè)務層的事務走触。具體 @Transactional 的使用,請參考Spring的參考文檔疤苹。
為接口中的部分方法提供自定義實現(xiàn)
有些時候互广,開發(fā)者可能需要在某些方法中做一些特殊的處理,此時自動生成的代理對象不能完全滿足要求卧土。為了享受 Spring Data JPA 帶給我們的便利兜辞,同時又能夠為部分方法提供自定義實現(xiàn)迎瞧,我們可以采用如下的方法:
將需要開發(fā)者手動實現(xiàn)的方法從持久層接口(假設為 AccountDao )中抽取出來,獨立成一個新的接口(假設為 AccountDaoPlus )逸吵,并讓 AccountDao 繼承 AccountDaoPlus凶硅;
為 AccountDaoPlus 提供自定義實現(xiàn)(假設為 AccountDaoPlusImpl );
將 AccountDaoPlusImpl 配置為 Spring Bean扫皱;
在 中按清單 19 的方式進行配置足绅。
清單 20. 指定自定義實現(xiàn)類
此外, 提供了一個 repository-impl-postfix 屬性韩脑,用以指定實現(xiàn)類的后綴氢妈。假設做了如下配置:
清單 21. 設置自動查找時默認的自定義實現(xiàn)類命名規(guī)則
repository-impl-postfix="Impl"/>
則在框架掃描到 AccountDao 接口時,它將嘗試在相同的包目錄下查找 AccountDaoImpl.java段多,如果找到首量,便將其中的實現(xiàn)方法作為最終生成的代理類中相應方法的實現(xiàn)。
結(jié)束語
本文主要介紹了 Spring Data JPA 的使用进苍,以及它與 Spring 框架的無縫集成加缘。Spring Data JPA 其實并不依賴于 Spring 框架,有興趣的讀者可以參考本文最后的"參考資源"進一步學習觉啊。