1.环境版本
- spring-boot 2.1.7.RELEASE
- mapper-spring-boot-starter 2.1.5
2.基本原理
MappedStatement
MappedStatement维护了一条<select|update|delete|insert>节点的封装
SqlSource
负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中。
BoundSql
示动态生成的SQL语句以及相应的参数信息
当调用SqlSource的getBoundSql方法,传入的就是parameterMappings相对应的参数,最终生成BoundSql对象,有了BoundSql就可以执行sql语句了。
在 MyBatis 中,使用@SelectProvider 这种方式定义的方法,最终会构造成 ProviderSqlSource,ProviderSqlSource 是一种处于中间的 SqlSource,它本身不能作为最终执行时使用的 SqlSource,但是他会根据指定方法返回的 SQL 去构造一个可用于最后执行的 StaticSqlSource,StaticSqlSource的特点就是静态 SQL,支持在 SQL 中使用#{param} 方式的参数,但是不支持
1 | public interface SelectMapper<T> { |
在 MyBatis 中,每一个方法(注解或 XML 方式)经过处理后,最终会构造成 MappedStatement 实例,这个对象包含了方法id(namespace+id)、结果映射、缓存配置、SqlSource 等信息,和 SQL 关系最紧密的是其中的 SqlSource,MyBatis 最终执行的 SQL 时就是通过这个接口的 getBoundSql 方法获取的。
针对不同的运行环境,需要用不同的方式去替换。当使用纯 MyBatis (没有Spring)方式运行时,替换很简单,因为会在系统中初始化 SqlSessionFactory,可以初始化的时候进行替换,这个时候也不会出现前面提到的问题。替换的方式也很简单,通过 SqlSessionFactory 可以得到 SqlSession,然后就能得到 Configuration,通过 configuration.getMappedStatements() 就能得到所有的 MappedStatement,循环判断其中的方法是否为通用接口提供的方法,如果是就按照前面的方式替换就可以了。
在使用 Spring 的情况下,以继承的方式重写了 MapperScannerConfigurer 和 MapperFactoryBean,在 Spring 调用 checkDaoConfig 的时候对 SqlSource 进行替换。在使用 Spring Boot 时,提供的 mapper-starter 中,直接注入 List
3.基于springboot版本的代码分析
tk.mybatis.spring.mapper.MapperFactoryBean为整个基于springboot加载的入口类。其继承的抽象类org.springframework.dao.support.DaoSupport实现了org.springframework.beans.factory.InitializingBean最终会执行到MapperFactoryBean的checkDaoConfig()方法。
1 | //位置tk.mybatis.spring.mapper.MapperFactoryBean,动态创建每个mapper的代理 |
匹配MappedStatement进行配置,类位置tk.mybatis.mapper.mapperhelper.MapperHelper
1 | public void processConfiguration(Configuration configuration, Class<?> mapperInterface) { |
继续处理,找到之前通过扫描缓存的MapperTemplate,所有@SelectProvider注解指定的自定义provider类都会继承MapperTemplate,后续缓存的信息进行和MapperTemplate进行设置处理,类位置tk.mybatis.mapper.mapperhelper.MapperHelper
1 | public void processMappedStatement(MappedStatement ms){ |
继续跟进设置,为每个MappedStatement替换其setSqlSource,类位置tk.mybatis.mapper.mapperhelper.MapperTemplate
1 | public void setSqlSource(MappedStatement ms) throws Exception { |
支持在spring容器启动完毕后,所有的MappedStatement的SqlSource都已经被替换完毕。后续的增删等直接调用spring和mybatis的原生接口即可实现。
v1.5.2