因为公司老系统是用hibernate3开发的,重构自然过渡到使用Spring Data JPA
。由于系统中对sql
的使用方式还是手动为主,这就造成在service
层存在大量的sql
拼接方式,后续维护比较困难。
因为怀念以往项目对sql
驱动的持久层开发用的mybatis
的时光,笔者参考b站up主Java灭霸詹的手写mybatis系列,为JPA
扩展了一个mybatis
风格的查询模块。
在大佬讲解实现的基础上完成了plugin
模块的编写以支持分页特性,同时扩展了mybatis
的xml文件中的sql
节点标签,让功能按照个人需求实现进一步的定制。
在整个代码实践过程中,笔者对mybatis
的实现原理也有了进一步的认识,同时通过编写这个JPA
的mybatis
风格查询的扩展模块,为后面做sql
的重构也铺平了道路。
文章目录
- 封装JDBC
- xml中编写sql的示例
- in查询标签
- 分页查询
- 分页功能的优化
封装JDBC
对于JPA
它的优势自然是ORM
框架所具有的对象关系映射,而实际业务中存在大量手写sql
的情况下,用JPA
自然不是最好的选择,如果是公司架构的要求,那对JPA
操作JDBC
的那部分代码做一层封装,让其具有了mybatis
的sql
处理能力,自然会是一件两全其美的事情。
这里封装JPA
的JDBC
操作,只是做了一层sql
映射的逻辑层的节点树结构,在实际执行查询时会执行节点树,通过上下文的串联,将每个sql
节点对应解析出来的sql
进行拼接,得到要执行的目标sql
。并在这个过程中完成sql
中参数占位符的处理,映射参数列表。
而sql
执行所处的环境、数据源连接、事务环境这些都托管给了Spring
管理。最底层只需要调用org.hibernate.Session
的session.doReturningWork()
方法操作jdbc
即可。
xml中编写sql的示例
这里给出笔者实现该模块的示例查询:
这里在实现mybatis
动态查询功能的基础上,做了一些扩展。比如这里的<in>
标签、用于分页查询优化的<sql>
标签。
in查询标签
mybatis
在处理列表形式的参数时使用的<foreach>
标签用起来比较麻烦,笔者在手写mybatis
实现基础功能的基础上扩展了原有标签的功能,实现了简单易用的<in>
标签。执行的效果和mybatis
的<foreach>
标签一样:
分页查询
对于mybatis
提供的plugin
功能,市面上有类似于PageHelper
这样的第三方插件来整合分页功能。笔者翻看其源码,体会到由于mybatis
在封装上的一些安全性考虑,并没有为Plugin
的拦截扩展功能开太多的口子,这就造成插件的第三方开发者需要做很多的设计和实现来适配。
而对于笔者手写mybatis
来说,要扩展插件就变得简单了。框子是笔者定的,为了更好的扩展,可以适当的把口子开大点,让扩展更好的适配。在实现分页拦截器时,笔者为框子底层的类开了类似切面的口子,方便扩展:
这样基于笔者手写的mybatis
,用短短20几行代码就实现了一个功能完备的分页插件:
分页功能的优化
目前市面上的分页插件对于分页的select count
查询,并没有实现对内部sql
的干预机制,更多的是在外面包一层select count(1) from (...) t
,而内层的sql诸如select *
的处理方式并没有屏蔽掉。所以count
查询的效率自然不敢恭维。而笔者在扩展mybatis
手写功能时,对这一块做了用户定制优化:
这里<sql>
标签包裹的内容是在分页列表查询时必要的,而count
查询将会被替换掉,如果指定了countSql
属性,就用属性值代替,否则替换为空。这样无论一个结构多复杂的sql
,用这种包裹和替换方式,都无需为了提高count
查询效率,而单独写一个查询sql
。执行结果正如我们所期望的那样: