二次封装 Spring Data JPA/MongoDB,打造更易用的数据访问层


二次封装 Spring Data JPA/MongoDB,打造更易用的数据访问层

文章插图
 
最近我在做一个新项目,由于我们项目组一直使用的是 MongoDB 数据库,所以新项目我就打算上 Spring Data MongoDB 尝试一下,虽然我早就用过了 Spring Data JPA,对 Spring Data 的相关 CRUD 和 动态查询的封装也比较熟悉,但是自带的封装显然不能很好的满足我们的需求,本篇带大家讲述我所遇到的问题以及解决方案 。
注: MongoRepository / JPARepository 都继承自
PagingAndSortingRepository,除了对应的数据库不同之外,功能都基本相同,所以本文的二次封装也可以用于 JPARepository 上 。
1. 我遇到的问题问题一
在 Spring Data 中可以通过继承 MongoRepository / JPARepository 接口的方式获得 CRUD 和 分页的能力,但是这种能力也仅仅满足基础的 CRUD 操作和 分页,对于极其常用的两个操作比如:针对数据库某个字段进行更新 和 多条件查询,这个接口并没有提供 。
准确的来说,多条件查询的能力是提供了,但是非常不宜用,它必须使用你的类做为查询条件,这个类的变量名还必须和数据库表中的字段名保持一致,这可以非常简单的让我们想到使用 PO 类当作这个查询条件 。
但是在有些规范中,PO 类应该是一个拥有全参构造器的不可变类,这使得先创建这个类然后对应的查询字段进行赋值的操作变得不可行,这里我举一个简单的例子,我拥有一个数据表的映射对象:User,这就是俗称的 PO 。
@Document("user")class User (@Idval id : String,?val account : String,?val pwd : String,?val name : String,)复制代码然后我如果想要单独更新 name 这个字段时,我需要拥有整个 User 对象中的所有属性,因为 Repository 接口所提供的能力是把新增操作和更新操作放在一起的 (save 方法),每次更新都是所有字段的更新,这是我不愿意看到的,也是极其麻烦的 。
接着就是多条件查询的问题,我们先来看下如果我想要使用多条件查询,它的参数是什么:
二次封装 Spring Data JPA/MongoDB,打造更易用的数据访问层

文章插图
 
可以明显看到是一个叫 Example 的对象,如果我想使用,它应该是这样的:
fun test() {val user = cssUser()user.name = "我要查询的参数具体值"?userRepository.findAll(Example.of(user))}复制代码这里我定义了一个 CssUser 去当它的查询条件的类,而且这个类和 User 类的内容几乎一样,因为我的 User 类是一个全参构造器没办法直接创建一个空对象进行赋值,所以我不得不创建一个 CssUser 去当查询条件的类,对于程序员来讲,这很烦 。
我想要的效果是什么样的呢?是这样的:
fun test() {?userRepository.listAll(Criteria.where("account").`is`("admin").and("name").`is`("你的名字"))?}复制代码通过 lambda 的方式直接获取到某个属性的名字,然后作为查询变量,然后跟着链式调用可以随便在里面加上各样的查询条件,例子中的 Criteria 类是 Spring 已经为我们做好的,但是 Repository 接口并没有提供它,所以我们需要一层封装 。
问题二
从上面的例子中我们可以看到在组装查询条件时,需要硬编码进去字段名,这对于程序员来说,是很烦的 。
所以我们应该使用 lambda 的特性,帮助我们去获取某一个类的字段名,通常是 PO,因为它和数据库属性是一一对应的,整体要达到的有点像 MyBatis-PLus 的效果,大概是这样:
fun test() {?userRepository.listAll(Criteria.where(CssUser::account.mongoFiled()).`is`("admin").and(CssUser::name.mongoFiled()).`is`("你的名字"))?}复制代码当然我的这个效果还没有 Mybatis-PLus 的效果好,它可以直接省略 .mongoFiled() 这个操作,这是因为我只加了三四行代码就能达到这个效果,对我而言够用了,而 Mybatis-PLus 则是有一套相关支持 。
虽然我这是 Kotlin 示例,但随后也会给出 JAVA 语法中的相关思路 。
2. Repository 接口封装先来谈谈对 CRUD 的增强,正常情况下,我们只需要使用一个接口继承 MongoRepository 接口,然后 Spring Data 就会帮我们生成一个动态代理类,并声明为 Bean,直接注入就可以使用了,就像这样(代码中的 :语法是继承的意思):
interface UserMongoRepository : MongoRepository<User, String> {?}复制代码现在既然我们要对 Repository 进行增强,就需要再抽象出一个类,作为我们新的基类,之后的自己的业务类需要继承这个接口,而非原来的 MongoRepository 接口,当然,我们这个新的基类接口还会去继承 MongoRepository 接口,然后在接口中定义我们需要的新操作即可:


推荐阅读