mybatis 执行sql_彻底弄清为什么Mybatis只是实现了接口就可以执行sql?

21a0e0bdb1b3e1de11b3e7e836a55f1a.png

前言

6bef060ea74a35d97105413f2e23952d.png

这是Mybatis的整体架构图,可以看出它是由几个主要组件组成,分别为Configuration、Sql映射、Mapper、MappedStatements组成,Configuration包含了所有启动时的配置信息,包括mapper中方法映射SQL,数据源信息、对象工厂ObjectFactory和一些参数配置例如是否懒加载、是否开启缓存等等一系列信息,在接下来的动作或多或少都会用到Configuration里的数据。

c0b67ab154f2bdfced06ef0f3a12cd52.png

Mybatis的组件分三大类

第一类SqlSession为主要调配者,主要工作为调度工作,类似委派模式的委派者。

第二类为调配者分发下去主要工作的类。例如Parsing负责解析各种配置文件、SQL Parsing负责解析SQL语句、ParameterMapping负责SQL语句参数的配置、Executor为SQL语句执行者,负责执行SQL语句、ResultSetMapping负责去映射结果集成POJO、Plugins负责拦截SQL语句,对SQL进行操作。

第三类为底层用到的技术或是辅助的配置信息。例如反射,在动态代理Mapper类时经常用到。例如Binding,其辅助Mapper的动态代理创建,以及Mapper与方法对应的SQL的绑定工作。例如类型转换,返回结果字段映射到指定POJO时也会用到。资源加载资源解析Parsing就是去加载配置解析配置,等等等等就不多赘述了。

深入底层分析mybatis的实现原理

public static void main(String[] args) throws FileNotFoundException { TestMapper testMapper = getSqlSession().getMapper(TestMapper.class); testMapper.selectByPrimaryKey(1);}

我们看一下SqlSession获取Mapper,再到Mapper执行一个接口方法到底底层做了什么。首先抛出一个问题,TestMapper是一个Mapper接口,并没有写具体的实现类,为什么可以执行接口的方法?带着问题看下去。

001542c5461f3754c62845227eca1162.png

可以看到,SqlSession调用了他的Configuration去getMapper,实际是委派了Configuration去做这个工作。进入Configuration。

d0b60397b0764cdbe06bb697a3e1429d.png

而Configuration又从mapperRegistry中去getMapper,看看mapperRegistry里有什么?

7361781d94c5b54779b2246a8bc7fa46.png

Registry里的knownMappers中存放了两个Mapper的信息,key为namespace,value为一个MapperProxyFactory,可以猜想,在启动时这个Map就已经初始化好了,帮我们装配好了所有的Mapper信息,在Configuration的getMapper时就去这里取Mapper。继续进入MapperRegistry看看吧。

8bbc868c2b1fd8d06cdf117de00b13c0.png

这里可以看到,就像上面说的,去map根据接口类对象取出需要的MapperProxyFactory,此时如果你的Mapper文件没有注册到Configuration,就取不到对应的mapperProxyFactory,就会抛出找不到的异常。拿到这个proxyFactory之后,再调用它的newInstance方法去初始化一个mapper,继续进入。

697120c09604b5a6f71d682158bd1a37.png

到这里应该恍然大悟了,这个newInstance方法实际上做的是一个动态代理,传入实际mapper接口类对象,和一个mapperProxy,对mapper接口做一个代理,代理人为mapperProxy(需要有一定动态代理基础),来看看mapperProxy到底怎么去代理mapper?

public class MapperProxy implements InvocationHandler, Serializable { private static final long serialVersionUID = -6424540398559729838L; private final SqlSession sqlSession; private final Class mapperInterface; private final Map methodCache; public MapperProxy(SqlSession sqlSession, Class mapperInterface, Map methodCache) { this.sqlSession = sqlSession; this.mapperInterface = mapperInterface; this.methodCache = methodCache; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { MapperMethod mapperMethod = this.cachedMapperMethod(method); return mapperMethod.execute(this.sqlSession, args); }}

这里只保留了一些关键方法,可以看到MapperProxy持有一个sqlSession、一个mapper接口类对象,在invoke方法中,调用了mapperMethod的execute方法。

 public Object execute(SqlSession sqlSession, Object[] args) { Object param; Object result; switch(this.command.getType()) { case INSERT: param = this.method.convertArgsToSqlCommandParam(args); result = this.rowCountResult(sqlSession.insert(this.command.getName(), param)); break; case SELECT: param = this.method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(this.command.getName(), param); break; }}

这里删掉了很多无关的东西,注意看这里,在执行时会判断SQL类型,为SELECT时sqlSession就会执行selectOne方法,到这里应该明白了,实际上SqlSession委托了Configuration去拿到Mapper信息,然后再用动态代理用MapperProxy类给mapper接口做代理,其每个方法都会用command判断SQL类型,例如SELECT就会回到SqlSession去做一个selectOne方法,归根结底还是回到了SqlSession,mapper只是提供了一个接口去做一个动态代理,携带SQL语句和参数回到SqlSession去执行查询。

SimpleExecutor

78c558d3bf7e8587d589583f9d4184c0.png

在执行完语句之后,返回的结果集又交给resultSerHandler做结果的映射处理。resultSetHandler的工作有点复杂,大致就是用一个ObjectFactory去根据mapper.xml的reusltMap或者resultType去实例化一个空的POJO,然后一一判断类型,填充到POJO里,实现结果映射POJO。

总结

SqlSession为主要的调配者,持有Configuration与Executor,先是创建Mapper委托Configuration去以MapperProxy给Mapper接口做动态代理,底层查询方法根据mapper.xml的查询类型执行SqlSession的查询方法,而SqlSession在查询时又委托Executor去做实际的查询,Executor会使用Statement查询结果集,然后使用ResultSetmapping做结果集的映射POJO,然后返回给SqlSession,因为动态代理,所以mapper的方法实际是SqlSession执行的查询方法,所以这时候SqlSession返回给方法查询结果,表面看起来像是Mapper的方法返回的结果,实际上却是SqlSession在做事情。

综上所述,底层原理与前面提到的组件概述图结合起来看,SqlSession就像是一个调度者(委派者),Configuration与Executor是实际工作者,这三个大类为主要组件,这里提供给我们接下来手写一个简单的Mybatis框架的一个思路,可以围绕这三个主要类展开,先写出一个简单版本的1.0,再慢慢扩展其功能与组件。

4c4ea2f1a7e8dc3a972087a39136a25d.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值