如何在orm框架如mybatis的基础上实现自己的sql拦截需求(分表等)

场景

针对一些业务中通用的sql拦截需求,比如数据库分表,让sql自动带上分表号,对业务完全透明,是最好的做法,这就涉及到拦截,这层拦截在应用这层做可以减小数据库的压力,并且减小对数据库的依赖,利于伸缩。所谓拦截,就是在方法的统一入口处进行控制,以下针对java中的一种orm框架mybatis进行具体描述。

实现原理

mybatis提供了interceptors机制,在其重要组件比如参数组装器、结果集组装器、statement组装器和sql执行器 executor上都提供了interceptor支持。我们的分表需求就可以在executor这一层做拦截,生成新的sql语句以及参数列表。

具体实现以及测试代码见:https://github.com/guzhangyu/midwares/tree/master/unitymob-batch

思路所需储备

1. 首先,简单介绍一下mybatis的组件以及sql执行过程

耳熟能详的SqlSession是通过executor执行的sql的:
这里写图片描述

顺藤摸瓜,executor和session是什么时候初始化的?
这里写图片描述

这里写图片描述

2. 接下来分析interceptor是怎么实现

普通的拦截器需要自己去控制拦截的方法,获取拦截的参数、执行相应的工作;mybatis这里有多个级别的拦截器,既要实现固定形式的拦截器编写(不用自己写太多拦截器签名等依赖于具体实现的代码),又要通用。mybatis的做法是:首先代理使用的java动态代理,其次定义了注解,用来标识所要拦截的方法。
这里写图片描述

思路来源
那么这个方案是怎么来的呢?一开始的确是想到过要在代码中间注入自己的一些逻辑,但是没有想到拦截器的方式,也没有想到框架本身提供了支持。
之前有过一个mybatis自动生成的mapper实现需要支持batch更新的需求,便是想着在executor执行之前加逻辑,但是mybatis支持的BatchExecutor,由于SqlSession被spring做了一层包装,每次执行sql都自动提交,起不到batch的效果;并且batchExecutor每次update后都返回一个负数的影响行数,这让我十分疑惑mybatis这个BatchExecutor的初始意图何在?
带着疑问,我看了BatchExecutor的实现,里面仅仅对statement做了一次addBatch,没有提交;这说明了它的意图是提供外部多次addBatch,再一次性flush之用,更新条数也就在flush的时候才能拿到。

然后就思索如何能够在中间换executor(从ReuseExecutor到BatchExecutor),将一次executor执行改为多次executor执行,然后一次性flush;然后就去寻找executor生成的地方,这个时候发现executor是每次开启事务的时候初始化的,并且还有一个transaction参数,直观感受在最开始的时候会生成executor(openSession)。接着看executor初始化的代码,看到plugin这个东西,直觉这个东西帮助很大,跟进去看。发现解决方案的关键;联想到mybatis的分页插件,一看它的实现方式,通过自己的尝试就得到了可行的拦截方案。

更加通用的干货

碰到问题,在解决不了的时候,可以看看它的原理,了解它的流程,在这个过程中集中思路。

猜你喜欢

转载自blog.csdn.net/guzhangyu12345/article/details/80935254