mybatis+spring开发过程中的保存对象源码详解

时间:2024-03-17 11:17:33

在web开发过程中持久化是每个web开发者都需要面对的问题,那么有的公司选择了jpa持久化,有的选择mybatis来完成对象的持久化,而今天我们就讨论一下如果你选择的框架和持久化框架分别是springmvc和mybatis的话,当你保存或者是查询的时候,你的sql源码是怎么完成这个操作的。我们以插入为列,来说明。

关于数据源和mybatis包扫描的配置我就不写了,这是最基本的,网上也到处都是。

我们都知道mybatis的实现需要dao对象,mapper接口对象和对应的mapper.xml文件来完成这波操作,我们今天要说的mybatis版本为3.3.1版本的。

首先我们在调用对象保存方法时,我们会执行mapper.insert(Dao dao)方法或者insertSelective(Dao dao)方法如下图,

mybatis+spring开发过程中的保存对象源码详解这个时候我们的代码接下来去了哪儿呢;

1)因为mybatis是通过动态代理来完成具体接口绑定的,而且面向方法的,所以我们找一个MapperProxy的类,他是个拦截器,在其中的invoke方法中加断点,然后下一步就进去了,如下图:

mybatis+spring开发过程中的保存对象源码详解

从图中我们可以看到要执行的method的值就是我们想要执行的真实方法。

而其中:

final MapperMethod mapperMethod = cachedMapperMethod(method);//查看是否已经缓存了方法,如果没有,则将其初始化并缓存
return mapperMethod.execute(sqlSession, args);//执行mapperMethod方法

2)我们去找一个MapperMethod类并给其中的execute方法打断点,然后下一步:

我们可以看到代码进入其中的execute方法,如下图所示:

mybatis+spring开发过程中的保存对象源码详解

从图中我们可以看出会根据command的type值来决定是执行插入,更新,删除还是查找等,因为我们是插入,所以在截图最后,可以看出type确实是INSERT.

3)在2)的截图中我们可以看到如果是插入的话,有两行代码会执行,其中第一行明显是param对象的封装(通过MapperMethod类中的方法)最后将得到我们要入库的对象如下图:

mybatis+spring开发过程中的保存对象源码详解

而第二句则比较关键根据方法名,明显是返回执行行数,所以关键就是sqlSession.insert方法了;而sqlSession会通过SqlSessionTemplate拦截器来获取;我们在SqlSessionTemplate中的invoke方法中加断点,执行下一步,然后得到如下图:

mybatis+spring开发过程中的保存对象源码详解

从截图中可以看到,这个类来自mybatis-spring-1.2.3.jar包,然后对sqlSession做了初始化;

4)从3)图中可以知道,接下来会执行sqlsession的方法,然后它实际上走的是DefaultSqlSession的方法,于是我们在其类中对应的update两个参数的方法中加断点,得到如下图:

mybatis+spring开发过程中的保存对象源码详解

5)从4)图中可以看出方法也不复杂,从配置文件中获取mapper映射,然后通过执行器执行,然后执行器类型比较多,首先是CachingExecutor和BaseExecutor,因为我开启了缓存配置,所以会先进入CatchingExecutor类中,清除缓存,所以我们打断点在其update方法中,下一步得到如下图:

mybatis+spring开发过程中的保存对象源码详解

从图中可以看出,首先根据得到的ms来flush了缓存,然后执行了delegate的方法,那么这个delegate就是我上文说到的BaseExecutor类;

6)在BaseExecutor类的update方法中打断点,并执行下一步,得到如下图:

mybatis+spring开发过程中的保存对象源码详解

从图中可以看出首先清空本地缓存,然后执行了一个doUpdate方法,这个时候你会发现这个类其实是抽象类,而他执行的方法一定是子类的;

7)然后我们看他的实现有,BatchExecutor,closedExecutor(未实现),ReuseExecutor,SimpleExecutor;从configuration的参数中可以看出我们的是SimpleExecutor类,在其中的doUpdate方法中加断点,然后下一步,得到如下图

mybatis+spring开发过程中的保存对象源码详解

然后方法中,从ms中得到配置信息,然后根据配置信息和参数得到handler来执行prepareStatement方法将连接赋值给handler并返回对应的statement,然后执行对应的handler的方法;

8)从StatementHandler的两个实现RoutingStatementHandler和BaseStatementHandler的名字来看一定会先去RoutingStatementHandler,所以在其中的prepare和BaseStatementHandler的prepare中加断点,并下一步得到如下图:

mybatis+spring开发过程中的保存对象源码详解

可以看出直接调用了delegate的prepare,我们直接下一步:

mybatis+spring开发过程中的保存对象源码详解

可以看到还是来到了baseStatementHandler实现类中。

9)从图中可以看出执行了依据instantiateStatement(connection)这句是关键,查看当前类,发现是抽象的,该方法也是,所以一定是调用的子类的方法,所以看其实现类,如下图:

mybatis+spring开发过程中的保存对象源码详解

已经很明显了,我们的肯定是PreparedStatementHandler了,所以在其中对应的方法中加断点下一步然后得到如下图:

mybatis+spring开发过程中的保存对象源码详解

已经很明显了,到这里就是通过执行connection.prepareStatement来与数据库执行实际的交互了。

10)接下来就是一路返回了,照着倒序的形式最终回到MapperProxy,然后回到你调用方法的地方,如下图;

mybatis+spring开发过程中的保存对象源码详解

到这里整个流程就结束了,如果你执行的是其他方法,那么你会在MapperMethod中通过不同的if判断进不同的执行逻辑,但是整体流程是不会变的。如果想看到具体的sql组装之类的,可以具体F8然后逐个方法查看就可以了。