spring data項目旨在對不同數據源提供一組相似的、較一致的具有spring style的交互方法。目的方便用戶對不同數據源(關係數據庫、非關係性數據等)的api操作,並簡化系統持久層的代碼邏輯結構。spring data項目包含多個子項目,不同數據源有不同的子項目對應。詳見spring data官方文檔 。
文章目錄
項目中的問題以及本文目的
在項目開發過程中由於前期沒有很好規劃,mongoDB操作邏輯代碼編碼冗餘重複;存在大量collection和屬性名硬編碼;mongoDB操作邏輯同業務邏輯混在一起,閱讀困難;業務邏輯中存在大量複雜指標計算,業務邏輯複雜;這些導致系統後期需求開發緩慢,新人難以上手,維護難度大。基於此,本文想基於spring data的理念,淺談一下面向mongoDB的數據持久層(dao層)設計,並給出設計示例以及spring data mongoDB的操作方法和使用步驟。
設計理念
- 藉助Spring data封裝的mongoDB操作方法,以及Spring data基於方法名稱的操作範式簡化原來基於MongoTemplate和MongoOperations實現的具體代碼邏輯。
- 持久層與業務邏輯層分離。
- 以擴展的方式實現對不同collection的特殊業務邏輯。作爲對Spring data公共方法的補充。
spring data mongoDB操作方法和使用步驟
創建spring boot項目
創建一個spring boot項目並實現對mongoDB的連接,具體可參見博文:Spring boot連接和操作mongoDB 。
本文基於yml配置。
1.在pom中引入相關包:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
<version>2.1.6.RELEASE</version>
</dependency>
2.基於application.yml的連接配置
spring:
data:
mongodb:
database: 數據庫名稱
authentication-database: admin
uri: mongodb://用戶名:密碼@ip:端口號
Spring data mongoDB部分源碼概覽
在正確配置完yml後,spring容器啓動後將自動實例化對mongoDB的連接,在service類中通過簡單的@Autowire MongoTemplate即可編寫操作mongoDB代碼邏輯。本文希望通過Spring data的封裝的公共方法,簡化操作邏輯。org.springframework.data.repository.Repository接口及派生接口提供對部分mongoDB操作的封裝。
- Repository接口是根接口,@Indexed註解修飾,容器在編譯時會自動快速實例化實現或繼承了該接口的子孫代的接口或類(非@NoRepositoryBean修飾),起到“入口”作用。
- CrudRepository封裝基礎的增刪改查操作
- PagingAndSortingRepository在CrudRepository基礎上擴展了分頁和排序功能
- MongoRepository在上述基礎上擴展了insert方法,但是官方仍然推薦使用save方法。
- BaseMongoDao爲本文自定義接口,作爲mongoDB業務操作的base接口。
- SimpleMongoRepository是官方提供的MongoRepository接口的具體實現。
@NoRepositoryBean註解避免了容器在啓動時被作爲一個bean實例創建。可以通過自定義接口繼承MongoRepository使用這些方法。
如下截圖展示了MongoRepository,PagingAndSortingRepository,CrudRepository封裝的mongoDB公共操作方法,基本覆蓋了mongoDB的常規操作。
#### Spring Data統一數據庫操作範式
Spring Data提供基於方法名稱的統一操作範式,只需在接口層定義相應方法即可完成具體操作邏輯,**這一點非常有利於面向Nosql的操作,可極大簡化冗餘代碼操作邏輯。**我們知道在面向關係型數據庫的持久層設計中,大名鼎鼎的mybatis框架以xml文件的方式避免了sql腳本對業務邏輯代碼的侵入性,提供統一操作接口供sevice層調用。對於mongoDB需要掌握大量json操作命令的數據庫交互方式來說。Spring Data基於接口方法名稱的統一操作範式極大的降低了mongoDB的查詢難度。
基於接口方法名稱的統一操作典型範式如下:
- find…By, read…By, query…By
- count…By
- get…By
更多範式請參見官方文檔
interface PersonRepository extends Repository<User, Long> {
List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);
// 通過lastname或firstname屬性從Person表(@Document聲明的對應mongoDB collection)查詢記錄並去重
List<Person> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);
List<Person> findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname);
// 通過lastname從Person表(@Document聲明的對應mongoDB collection)查詢記錄忽略lastname大小寫
List<Person> findByLastnameIgnoreCase(String lastname);
// 通過lastname和firstname屬性從Person表(@Document聲明的對應mongoDB collection)查詢記錄忽略全部大小寫
List<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);
// 查詢書結果並排序
List<Person> findByLastnameOrderByFirstnameAsc(String lastname);
List<Person> findByLastnameOrderByFirstnameDesc(String lastname);
}
也可在範式的基礎上指定一些額外的規則
//查詢結果後根據age倒序排序
@Query(sort = "{ age : -1 }")
List<Person> findByFirstname(String firstname);
@Query(sort = "{ age : -1 }")
List<Person> findByLastname(String lastname, Sort sort);
更多關於範式的查詢請參見:Spring data官方文檔
基於Spring Data封裝的方法以及基於方法名稱的範式,基本可覆蓋80%的mongoDB常規操作。同學們可以定義接口根據需求進行不同的定義。如果還有其他複雜查詢,例如aggregaet,可以定義詳細實現類作爲擴展。
基於spring data mongoDB的數據持久層設計實例
持久層(dao)結構如下:
- BaseMongoDao繼承MongoRepository作爲項目的根接口,並預留響應擴展空間。
- BaseMongoDaoExtend作爲mongoDB複雜操作的基類並提供相應公共方法。
- LatestDeviceInfoDao接口繼承BaseMongoDao,繼承所有MongoRespostory方法,並提供基於方法名稱的範式方法聲明,根據需求對mongoDB的 LatestDeviceInfo表進行操作。
- LatestDeviceInfoDaoExtend繼承BaseMongoDaoExtend擴展了操作方法,編寫具體邏輯完成對mongoDB的 LatestDeviceInfo表的複雜操作。
代碼示例如下:
T爲實體類泛型,ID爲實體類對應表的主鍵類型泛型。
BaseMongoDaoExtend提供複雜操作的封裝,並對傳參進行重載:
LatestDeviceInfoDaoExtend繼承BaseMongoDaoExtend,完成一些具體操作實現,供service層調用。