相关推荐
这样写代码,比直接使用 MyBatis 效率提高了 100 倍 !
2024-11-10 15:06

作者:乔伊酱 链接:https://juejin.cn/post/7027733039299952676

这样写代码,比直接使用 MyBatis 效率提高了 100 倍 !

对一个 Java 后端程序员来说,、、 等都是我们常用的 ORM 框架。它们有时候很好用,比如简单的 CRUD,事务的支持都非常棒。

但有时候用起来也非常繁琐,比如接下来我们要聊到的一个常见的开发需求,而对这类需求,本文会给出一个比直接使用这些 ORM 开发效率至少会提高 100 倍的方法(绝无夸张)。

用户表(user):(简单起见,假设只有 4 个字段)

字段名 类型 含义 id bitint 用户 ID name varchar(45) 用户名 age int 年龄 role_id int 角色 ID

角色表(role):(简单起见,假设只有 2 个字段)

字段名 类型 含义 id int 角色 ID name varchar(45) 角色名

这个查询有点复杂,它的要求如下:

  • 可按用户名

    字段查询,要求:

    • 可精确匹配(等于某个值)
    • 可全模糊匹配(包含给定的值)
    • 可后模糊查询(以...开头)
    • 可前模糊查询(以.. 结尾)
    • 可指定以上四种匹配是否可以忽略大小写
  • 可按年龄

    字段查询,要求:

    • 可精确匹配(等于某个年龄)
    • 可大于匹配(大于某个值)
    • 可小于匹配(小于某个值)
    • 可区间匹配(某个区间范围)
  • 可按查询,要求:精确匹配

  • 可按查询,要求:同字段

  • 可指定只输出哪些列(例如,只查询 与 列)

  • 支持分页(每次查询后,页面都要显示满足条件的用户总数)

  • 查询时可选择按 、、 等任意字段排序

试想一下,对于这种要求的查询,后端接口里的代码如果用 、、 直接来写的话,100 行代码 能实现吗?

反正我是没这个信心,算了,我还是直接坦白,面对这种需求后端如何 只用一行代码搞定 吧(有兴趣的同学可以 MyBatis 等写个试试,最后可以对比一下)

首先,重点人物出场啦:Bean Searcher, 它就是专门来对付这种列表检索的,无论简单的还是复杂的,统统一行代码搞定!而且它还非常轻量,Jar 包体积仅不到 100KB,无第三方依赖。

假设我们项目使用的框架是 Spring Boot(当然 Bean Searcher 对框架没有要求,但在 Spring Boot 中使用更加方便)

Spring Boot 基础就不介绍了,推荐下这个实战教程: https://github.com/javastacks/spring-boot-best-practice

Maven :


Gradle :



接口路径就叫 /user/index 吧:


上述代码中的 是 Bean Searcher 提供的一个工具类, 只是为了把前端传来的请求参数统一收集起来,然后剩下的,就全部交给 检索器了。

(1)无参请求

  • GET /user/index
  • 返回结果:

(2)分页请求(page | size)

  • GET /user/index? page=2 & size=10
  • 返回结果:结构同 (1)(只是每页 10 条,返回第 2 页)

参数名 和 可自定义, 默认从 开始,同样可自定义,并且可与其它参数组合使用

(3)数据排序(sort | order)

  • GET /user/index? sort=age & order=desc
  • 返回结果:结构同 (1)(只是 dataList 数据列表以 age 字段降序输出)

参数名 和 可自定义,可与其它参数组合使用

(4)指定(排除)字段(onlySelect | selectExclude)

  • GET /user/index? onlySelect=id,name,role
  • GET /user/index? selectExclude=age,roleId
  • 返回结果:( 列表只含 id,name 与 role 三个字段)

参数名 和 可自定义,可与其它参数组合使用

(5)字段过滤(op = eq)

  • GET /user/index? age=20
  • GET /user/index? age=20 & age-op=eq
  • 返回结果:结构同 (1)(但只返回 age = 20 的数据)

参数 表示 的 字段运算符 是 ( 的缩写),表示参数 与参数值 之间的关系是 ,由于 是一个默认的关系,所以 也可以省略

参数名 的后缀 可自定义,且可与其它字段参数 和 上文所列的参数(分页、排序、指定字段)组合使用,下文所列的字段参数也是一样,不再复述。

(6)字段过滤(op = ne)

  • GET /user/index? age=20 & age-op=ne
  • 返回结果:结构同 (1)(但只返回 age != 20 的数据, 是 的缩写)

(7)字段过滤(op = ge)

  • GET /user/index? age=20 & age-op=ge
  • 返回结果:结构同 (1)(但只返回 age >= 20 的数据, 是 的缩写)

(8)字段过滤(op = le)

  • GET /user/index? age=20 & age-op=le
  • 返回结果:结构同 (1)(但只返回 age <= 20 的数据, 是 的缩写)

(9)字段过滤(op = gt)

  • GET /user/index? age=20 & age-op=gt
  • 返回结果:结构同 (1)(但只返回 age > 20 的数据, 是 的缩写)

(10)字段过滤(op = lt)

  • GET /user/index? age=20 & age-op=lt
  • 返回结果:结构同 (1)(但只返回 age < 20 的数据, 是 的缩写)

(11)字段过滤(op = bt)

  • GET /user/index? age-0=20 & age-1=30 & age-op=bt
  • GET /user/index? age=[20,30] & age-op=bt(简化版,[20,30] 需要 UrlEncode, 参考下文)
  • 返回结果:结构同 (1)(但只返回 20 <= age <= 30 的数据, 是 的缩写)

参数 表示 的第 0 个参数值是 。上述提到的 实际上是 的简写形式。另:参数名 与 中的连字符 可自定义。

(12)字段过滤(op = mv)

  • GET /user/index? age-0=20 & age-1=30 & age-2=40 & age-op=mv
  • GET /user/index? age=[20,30,40] & age-op=mv(简化版,[20,30,40] 需要 UrlEncode, 参考下文)
  • 返回结果:结构同 (1)(但只返回 age in (20, 30, 40) 的数据, 是 的缩写,表示有多个值的意思)

(13)字段过滤(op = in)

  • GET /user/index? name=Jack & name-op=in
  • 返回结果:结构同 (1)(但只返回 name 包含 Jack 的数据, 是 的缩写)

(14)字段过滤(op = sw)

  • GET /user/index? name=Jack & name-op=sw
  • 返回结果:结构同 (1)(但只返回 name 以 Jack 开头的数据, 是 的缩写)

(15)字段过滤(op = ew)

  • GET /user/index? name=Jack & name-op=ew
  • 返回结果:结构同 (1)(但只返回 name 以 Jack 结尾的数据, 是 的缩写)

(16)字段过滤(op = ey)

  • GET /user/index? name-op=ey
  • 返回结果:结构同 (1)(但只返回 name 为空 或为 null 的数据, 是 的缩写)

(17)字段过滤(op = ny)

  • GET /user/index? name-op=ny
  • 返回结果:结构同 (1)(但只返回 name 非空 的数据, 是 的缩写)

(18)忽略大小写(ic = true)

  • GET /user/index? name=Jack & name-ic=true
  • 返回结果:结构同 (1)(但只返回 name 等于 Jack (忽略大小写) 的数据, 是 的缩写)

参数名 中的后缀 可自定义,该参数可与其它的参数组合使用,比如这里检索的是 name 等于 Jack 时忽略大小写,但同样适用于检索 name 以 Jack 开头或结尾时忽略大小写。

当然,以上各种条件都可以组合,例如

查询 name 以 Jack (忽略大小写) 开头,且 roleId = 1,结果以 id 字段排序,每页加载 10 条,查询第 2 页:

  • GET /user/index? name=Jack & name-op=sw & name-ic=true & roleId=1 & sort=id & size=10 & page=2
  • 返回结果:结构同 (1)

OK,效果看完了, 接口里我们确实只写了一行代码,它便可以支持这么多种的检索方式,有没有觉得现在 你写的一行代码 就可以 干过别人的一百行 呢?

本例中,我们只使用了 Bean Searcher 提供的 检索器的一个检索方法,其实,它还有很多检索方法。

  • 查询指定条件下的数据 总条数
  • 查询指定条件下的 某字段统计值
  • 查询指定条件下的 多字段统计值
  • 分页 查询指定条件下数据 列表总条数
  • 同上 + 多字段 统计
  • 查询指定条件下的 第一条 数据
  • 分页 查询指定条件下数据 列表
  • 查询指定条件下 所有 数据 列表

另外,Bean Searcher 除了提供了 检索器外,还提供了 检索器,它同样拥有 所有的方法,只是它返回的单条数据不是 ,而是一个 泛型 对象。

另外,如果你是在 Service 里使用 Bean Searcher,那么直接使用 类型的参数可能不太优雅,为此, Bean Searcher 特意提供了一个参数构建工具。

例如,同样查询 name 以 Jack (忽略大小写) 开头,且 roleId = 1,结果以 id 字段排序,每页加载 10 条,加载第 2 页,使用参数构建器,代码可以这么写:


这里使用的是 检索器,以及它的 方法。

上文我们看到,Bean Searcher 对实体类中的每一个字段,都直接支持了很多的检索方式。

但某同学:哎呀!检索方式太多了,我根本不需要这么多,我的数据量几十亿,用户名字段的前模糊查询方式利用不到索引,万一把我的数据库查崩了怎么办呀?

好办,Bean Searcher 支持运算符的约束,实体类的用户名 字段只需要注解一下即可:


如上,通过 注解的 属性,指定这个用户名 只能适用与 精确匹配后模糊查询,其它检索方式它将直接忽略。

上面的代码是限制了 只能有两种检索方式,如果再严格一点,只允许 精确匹配,那其实有两种写法。

(1)还是使用运算符约束:

(2)在 Controller 的接口方法里把运算符参数覆盖:

该同学又:哎呀!我的数据量还是很大,age 字段没有索引,我不想让它参与 where 条件,不然很可能就出现慢 SQL 啊!

不急,Bean Searcher 还支持条件的约束,让这个字段直接不能作为条件:


如上,通过 注解的 属性, 就直接不允许 字段参与条件了,无论前端怎么传参,Bean Searcher 都不搭理。

该同学仍:哎呀!哎呀 ...

别怕! Bean Searcher 还支持配置全局参数过滤器,可自定义任何参数过滤规则,在 Spring Boot 项目中,只需要配置一个 Bean:


  1. 参数名是否奇怪,这其实看个人喜好,如果你不喜欢中划线 ,不喜欢 、 后缀,完全可以自定义,参考这篇文档:

searcher.ejlchina.com/guide/lates…

  1. 参数个数的多少,其实是和需求的复杂程度相关的。如果需求很简单,那么很多参数没必要让前端传,后端直接塞进去就好。比如: 只要求后模糊匹配, 只要求区间匹配,则可以:

这样前端就不用传 与 这两个参数了。

其实还有一种更简单的方法,那就是 运算符约束(当约束存在时,运算符默认就是 属性中指定的第一个值,前端可以省略不传):


  1. 对于 op=bt/mv 的多值参数传递,参数确实可以简化,例如:
  • 把 简化为 ,
  • 把 简化为 ,

简化方法:只需配置一个 (参数过滤器)即可,具体代码可以参考这里:

https://github.com/ejlchina/bean-searcher/issues/10

其实,Bean Searcher 的检索器只是需要一个 类型的参数,至于这个参数是怎么来的,和 Bean Searcher 并没有直接关系。前文之所以从 里取,只是因为这样代码看起来简洁,如果你喜欢声明参数,完全可以把代码写成这样:


上文所述的字段参数之间确是都是 "且" 的关系,至于 “或”,虽然这种使用场景不太多,但 Bean Searcher 也是支持的,详细可以参考这篇文章:

https://github.com/ejlchina/bean-searcher/issues/8

这里就不再复述了。

从本例其实可以看出,效率提升的程度依赖于检索需求的复杂度。需求越复杂,则效率提高倍数越多,反之则越少,如果需求超级复杂,则提高 1000 倍都有可能。

但即使我们日常开发中没有如此复杂的需求,开发效率只提升了 510 倍,那是不是也非常可观呢?

本文介绍了 Bean Searcher 在复杂列表检索领域的超强能力。它之所以可以极大提高这类需求的研发效率,根本上归功于它 独创动态字段运算符多表映射机制,这是传统 ORM 框架所没有的。但由于篇幅所限,它的特性本文不能尽述,比如它还:

  • 支持 聚合查询
  • 支持 Select|Where|From子查询
  • 支持 实体类嵌入参数
  • 支持 字段转换器
  • 支持 Sql 拦截器
  • 支持 数据库 Dialect 扩展
  • 支持 多数据源
  • 支持 自定义注解
  • 等等

Bean Searcher 是我在工作中总结封装出来的一个小工具,公司内部使用了 4 年,经历大小项目三四十个,只是最近才着手完善文档分享给大家,如果你喜欢,一定去点个 Star 哦 _

再奉上 Bean Searcher 的详细文档:searcher.ejlchina.com/

近期热文推荐:

1.1,000+ 道 Java面试题及答案整理(2021最新版)

2.别在再满屏的 if/ else 了,试试策略模式,真香!!

3.卧槽!Java 中的 xx ≠ null 是什么新语法?

4.Spring Boot 2.6 正式发布,一大波新特性。。

5.《Java开发手册(嵩山版)》最新发布,速速下载!

    以上就是本篇文章【这样写代码,比直接使用 MyBatis 效率提高了 100 倍 !】的全部内容了,欢迎阅览 ! 文章地址:http://ktsh.xhstdz.com/quote/720.html 
     栏目首页      相关文章      动态      同类文章      热门文章      网站地图      返回首页 物流园资讯移动站 http://ktsh.xhstdz.com/mobile/ , 查看更多   
发表评论
0评