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层调用。