Mybatis是一个半自动化的ORM框架,相对于Hibernate来说,是轻量级的,而且对于自定义sql有着非常友好的用户体验,使用也方便。现在我们来粗略探索下Mybatis是怎么帮助我们对数据库进行操作并映射的。
我们先来说说 Mybatis 的执行流程,先看使用的代码:
(图1)
我们就以这个来说明Mybatis的执行过程。Mybatis 的使用首先需要有4个关键对象,分别是SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession和对应需要操作的Mapper。简单的可以这么说, SqlSessionFactoryBuilder 可以生产出 SqlSessionFactory,SqlSessionFactory 可以获得SqlSession,而SqlSession可以得到对应的Mapper,具体的操作和生命周期可以看《Mybatis 之创建时的4个核心对象的生命周期》。接下来我们来进入源码看流程。
首先是看 SqlSessionFactory 如何被创建。进入 SqlSessionFactoryBuilder.build(InputStream inputStream) 方法的源码看到 它里边又调用了子集的一个builder方法(75行)(图2)
其中首先根据传入的配置文件(mybatis-config.xml)去创建一个XMLConfigBuilder,用于解析配置文件,解析的过程为78行的parser.parse() 方法
(图3)
如图所示,将配置文件里边的各个configuration下的标签一一解析。这里我们重点是看解析 mappers(120行)
(图4)
进入到了XMLConfigBuilder.mapperElement方法,源码会判断配置的mapper文件的类型是package类型还是其他类型,我们这里直接走 381行 的解析文件类型
(图5)
这里有两个关键的地方1和2,分别为解析和绑定。我们先进入1
(图6)
这里其实就是在配置我们自定义的xxxMapper.xml文件,里边的参数都是文件里边的配置,包括resultMap、sql、cache等等。我们直接去关键的部分121行
(图7)
这里进入127行的 buildStatementFromContext 方法,在131行又进入了134行的方法中,继续进看138行,进入里边的方法
(图8)
这个方法就是在解析mapper配置文件里边 增删改查标签 中的配置,直接看到方法最后的一句,这个是重点
(图9)
进入这个方法
(图10)
这里将会生成一个 MappedStatement ,里边就含有配置configuration/sql、返回对象、形参等,看该方法倒数第二行并进入
(图11)
(图12)
调用了Configuration里边的方法,存放了刚刚生成的 MappedStatement 到 mappedStatements,而 mappedStatements 又是 刚刚Configuration中的一个属性map。
到此已经把包装了sql、形参等信息的 MappedStatement 放到了 Configuration 中,这结论先记着,很重要
回到图5的2中
(图13)
获得了mapper的namespace,并调用了configuration的addMapper方法,传入了namespace对应的mapper的class类型,点进去
(图14)
图(15)
这里的操作就是将namespace对应的class类型(即mapper接口的class)和一个MapperProxyFactory绑定一起,放到knownMappers中,而这又是一个hashmap,而knownMappers是在MapperRegistry中,MapperRegistry在Configuration中,即最后还是放到Configuration中,至此,解析配置文件结束
回到图2的78行 最后是 return(parser.parse()) ,即返回一个解析后的Confuguration,点进去调用了下边的方法,返回一个DefaultSqlSessionFactory
(图15)
到这里 SqlSessionFactory 已经被创建出来了,并且持有已经解析好了的Configuration对象
然后就是sqlSessionFactory.openSession()方法获得SqlSession。我们进入DefaultSqlSessinFactory的openSession方法,最后是调用了自己的一个重载方法
(图16)
其中1创建了事务,2创建了执行器,3创建并返回了持有执行器对象 executor 的 DefaultSqlSession。创建执行器的过程如下:
(图17)
会根据类型去创建对应的执行器(BATCH/REUSE/SIMPLE),假如开启了缓存,会再包一层, 创建成CachingExecutor。
到这里SqlSession也创建出来了
然后就是获取Mapper对象 sqlSession.getMapper()。进入源码
(图18)
如图,调用了configuration.getMapper方法,而这个configuration就是前边解析配置文件后存放所有配置信息的configuration。继续跳进源码,一直跳到关键方法如下图
(图18)
有两个关键地方。1是从knownMappers中根据classType获得对应的MapperProxyFactory,而这个knownMappers就是我们在前边解析的时候数据存进去的(看图15)。然后2传入sqlSession调用了newInstance方法。
(图19)
接着来到了方法1,先创建一个MapperProxy,然后又调用了方法2,用jdk动态代理方式生成了MapperProxy的一个代理对象并返回,这个就是最后得到的Mapper对象(代理对象),这也是为什么Mapper接口不需要实现的原因,因为最后被实例出来的只是一个代理对象。
最后是调用Mapper的具体数据库操作的方法(如 mapper.getById())。由以上知道mapper只是拿到一个代理对象,故执行的时候将会进入 MapperProxy 的 invoke方法
(图20)
先判断调用的方法是否是Object本身(如toString/equals等),是的话直接反射调用,不是的话走85行。先走cachedInvoker(method)方法,去configuration中获得对象的sql信息,然后再调用invoke方法,点进去
(图21)
(图22)
这时就会根据上一步cachedInvoker(method)后得到的sql信息对应的走特定流程,例如走87行的selectOne(),就进入了DefaultSqlSession中
(图23)
继续点,最后来到了selectList方法
(图24)
这里可以看到,其实底层用到了Executor执行器去执行数据库操作,接下来到了 BaseExecutor中
(图25)
(图26)
(图27)
从这里开始,就开始进入具体执行器中的代码,例如SimpleExecutor
(图28)
首先将前边获得的sql等信息封窗成handler,然后封装参数,并继续调用query方法
(图29)
从这里开始,就是jdbc的执行操作了,mybatis层已经走到底了。然后就是执行sql并返回结果。以上就是mybatis的执行流程,整合Spring后,sqlSession就不再用DefaultSqlSession(因为DefaultSqlSession不是线程安全的),而是用了SqlSessionTemplate。后边会抽空再出Spring整合mybatis后mybatis的执行流程,会和以上有些许不同,如 Mapper 的注入和session的开启和关闭,都无需手动控制。最后再来几张流程图