MyBatis的运行过程,分两步:
1、读取配置文件,缓存到Configuration对象,用以创建SqlSessionFactory。
2、SqlSession的执行过程。
掌握源码中的技巧、设计和开发模式,对我们大有裨益。
构建SqlSessionFactory过程:
MyBatis采用Builder模式,去创建SqlSessionFactory。
实际,通过SqlSessionFactoryBuilder去构建:
1、通过XMLConfigBuilder解析配置的XML文件,而后存入Configuration类对象。
ps:Configuration是个单例模式。
2、使用Configuration创建SqlSessionFactory。它是一个接口。
ps:MyBatis提供了默认的实现类DefaultSqlSessionFactory
上述就是所谓的Builder模式,对于复杂对象,使用一个类(Configuration)作为统领,一步一步地构建最终对象(SqlSessionFactory)。
源码分析:
1、从XMLConfigBuilder源码:配置的typeHandler会被注册到typeHandlerRegistry对象中去。
2、typeHandlerRegistry对象,存放在XMLConfigBuilder的父类BaseBuilder中去。
3、typeHandlerRegistry其实就是Configuration单例的一个属性。
4、因此,可通过Configuration单例,拿到typeHandlerRegistry对象,从而拿到typeHandler。
构建Configuration:
Configuration的作用:
1、读取基础和映射器XML;2、初始化MyBatis的别名等;3、提供单例,为后续SessionFactory服务;4、执行一些重要对象的初始化方法。
最重要的解析内容是,映射器。
构建映射器的内部组成:
当XMLConfigBuilder解析XML时,会将每一个SQL和其配置的内容保存起来。
一条SQL和它相关的配置信息,由3个部分组成的,分别为MappedStatement、SqlSource、BoundSql。
MappedStatement:是个类,包括了配置的SQL、SQL的id、resultMap等,还包括了sqlSource属性。
SqlSource:是提供BoundSql对象的地方。它是一个接口,有DynamicSqlSource等实现类。拥有唯一的接口方法,getBoundSqlSource();
BoundSql:是一个结果对象。通过它便可以拿到,要执行的SQL和参数。
在插件中,往往需要拿到,BoundSql类,进而可以拿到当前运行的SQL和参数,从而对运行过程做出必要的修改。
BoundSql的3个主要属性:
parameterObject的功能:
1、传递简单对象,例如把int转化Integer来传递。
2、传递POJO或者Map;传递多个参数,没有@Param注解的话,ParameterObject将变为一个Map<String,Object>对象。因此,可通过#{1}、或者#{param1}。
3、有@Param注解,ParameterObject也将变为一个Map<String,Object>对象。只是把数字的键值置换为了@param注解的键值。
parameterMappings的功能:
它是个list,每个元素都是parameterMapping对象,该对象会描述参数,如名称、jdbcType、typeHandler等。通过它能实现参数与SQL的结合,以便PreparedStatement能够通过它,找到parameterObject对象的属性,设置参数。
sql属性的功能:
就是一条,被SqlSource解析后的SQL。
构建SqlSessionFactory:
真正的难点是,构建Configuration对象。
SqlSession运行过程:
sqlSession.getMapper的调用,运用了Configuration对象的getMapper方法。
Configuration又运用了,映射器的注册器Mapperregistry来获取对应的接口对象。
mapperRegistry.getMapper方法内:
首先判断是否注册了一个Mapper,若没有抛出异常,则启用工厂生成一个代理实例。
MapperProxyFactory类:
在newInstance方法中,可看到,动态代理对接口的绑定,它的作用就是生成动态代理对象(占位);
而代理的方法,则被放到了MapperProxy类中。
MapperProxy类源码:
若Mapper是一个JDK动态代理对象,在invoke方法内,执行execute方法,把SqlSession和当前运行的参数传递进去。
execute方法:
executeForMany方法:
通过源码可知:
它最后通过SqlSession对象去运行对象的SQL而已。MyBatis可以根据全路径和方法名,将接口和代理对象绑定起来。通过动态代理,让这个接口运行起来,而后采用命令模式,最后使用SqlSession接口的方法使得它能够执行对应的SQL。
SqlSession下的四大对象:
底层框架的四大对象:
Executor:代表执行器,StatementHandler:四大对象的核心,许多插件通过拦截它来实现。
ParameterHandler:处理SQL参数,ResultSetHandler:进行数据集的封装返回处理。
Executor:
SqlSession只是门面,真正干活的是,Executor。
有3种执行器,可在settings元素中配置:
SIMPLE(简易执行器)、REUSE(执行重用预处理语句)、BATCH(执行器重用语句和批量更新)
插件的原理:在调度真实的Executor方法之前,执行配置插件的代码。
在SimpleExecutor的doQuery方法中:
在prepareStatement方法中:
MyBatis根据Configuration来构建StatementHandler。用StatementHandler的prepare,进行预编译和基础设置,用parameterize来设置参数,用query时,将参数ResultHandler传递进去,使用它组织结果。
StatementHandler-数据库会话器:
创建的真实对象是一个RoutingStatementHandler对象,但不是真实的服务对象,它通过适配模式来找到对应的StatementHanlder来执行。
RoutingStatementHandler一共有3种:SimpleStatementHandler、PreparedStatementHandler、CallableStatementHandler;
RoutingStatementHandler源码中,定义了一个delegate的对象适配器,可根据配置来配适对应的StatementHandler对象。
Executor执行查询时,会执行,StatementHandler的prepare、parameterize和query方法。
在prepare方法中:
instantiateStatement方法,对SQL进行了预编译,然后做一些超时、获取最大行数等基础配置。
在parameterize方法中:
它是调用ParameterHandler来完成的。
在PreparedStatementHandler的query方法中:
在执行前参数和SQL都被prepare方法预编译,在parameterize方法中设置参数,只要执行SQL,就能返回结果了。
ParameterHandler-参数处理器:
ParameterHandler的实现类,DefaultParameterHandler,setParameters的实现:
可在parameterObject对象中取到参数,也选择使用typeHandler转换参数,typeHandler在MyBatis初始化时,注册在Configuration里。
ResultSetHanlder-结果处理器:
一般默认使用DefaultResultSetHandler的实现类。
SqlSession运行总结:
SqlSession通过Executor调度StatementHandler,后经过3步,prepared预编译SQL、parameterize设置参数、执行SQL。
parameterize可通过parameterHandler方法设置,参数根据typeHandler处理的。
执行SQL通过ResultSetHandler进行处理结果封装,update返回int,其它可通过typeHandler处理结果类型。
最后用ObjectFactory提供的规则,组装对象,返回给调用者。