mybatis-plus mapper整理
简介
提供了操作数据的框架,避免使用jdbc操作数据,加速开发效率,支持多种数据库(databaseId)。既支持sql,mapper的编写,也支持注解@Select等,同时提供了一二级缓存,以及BaseMapper接口以及IService、ServiceImpl这些接口,来提供模板化的方法。同时还支持逆向工程生成代码,是很好用的dao层框架。
用例
解析
依赖信息
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.13</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
使用过程
依据上面的使用过程解读代码
解读1:扫描mapper接口类的bean定义
- 容器启动时,扫描bean定义:invokeBeanFactoryPostProcessors(beanFactory);
- 代码中使用了@MapperScan,就会加载MapperScannerRegistrar
- MapperScannerRegistrar会加载MapperScannerConfigurer
- MapperScannerConfigurer引入ClassPathMapperScanner,进行basePackage扫描。 这里会区分:如果是 @MapperScan,那么basePackage就是里面的属性,且不会添加includeFilter;而如果使用@Mapper,那么basePackage就是启动类的包路径,添加@Mapper作为includeFilter。
- ClassPathMapperScanner扫描完成后,会执行processBeanDefinitions()修改bean定义,将beanClass设置为 MapperFactoryBean 实现了FactoryBean接口,作为工厂bean,实例化时会调用getObject方法来获取实际bean。而getObject会获取 MybatisConfiguration.getMapper(),也就是读取xml时放入mapper映射的位置(解读3)
解读2,实例化mapper接口类时,加载sqlSessionFacory
- 容器启动,实例化剩余bean:finishBeanFactoryInitialization
- 实例化mapper,依赖于sqlSessionFactory,就需要加载 com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration#sqlSessionFactory()
- 加载sqlSessionFacory会加载 容器中的实现了inteceptor接口的bean,放入到 InterceptorChain。后话:这个InterceptorChain后面在调用时会用Plugin工具代理 Executor、StatementHandler、ResultSetHandler、ParameterHandler等。
- 加载sqlSessionFacory会扫描 配置项定义的mybatis-plus.mapper-locations,作为xml存放的路径,作为resource扫描这个文件夹下的xml文件。(如果没有设置,就是用默认的/mapper/**/*.xml)。
解读3,加载sqlSessionFacory时,扫描xml,生成mapper映射表和各个方法的mapperStatement
- 解析 mapper标签,作为一个xml文件
- 解析里面的cache标签,生成cache链,PerpetualCache <- LruCache <- SerializedCache <- LoggingCache <- SynchronizedCache,并存起来等mapperStatement注册时用。
- 解析里面的parameterMap、resultMap、sql、select|insert|update|delete标签,其中sql语句解析时使用 mixSqlNode, 解析各个子标签foreach,trim等等。解析完成后将这个方法,使用 nameSpace+id将方法注册成一个 mapperStatement。这个mapperStatement会带上上面的 cache 数据。 所以有cache数据的mapperStatement就是使用了二级缓存的。
- 完成解析后,注册这个mapper:bindMapperForNamespace。注册 mapper 到 MybatisConfiguration,也就是 MapperFactoryBean.getObject之后会执行的 MybatisConfiguration.getMapper(解读1)。存放 clazz, MybatisMapperProxyFactory 的映射关系
解读4,实例化mapper接口类时,创建代理类
解读5,调用时被MybatisMapperProxy拦截,以及缓存读取
总结
第一部分:
加载mapper接口类,并注册bean为mapperFactoryBean。
第二部分:
加载sqlSessionFacory, 读取xml文件,作为mapper到mybatisMapperProxyFactory的映射表,存入到 MybatisConfiguration。
第三部分:
MapperFactoryBean.getObject(),会读取MybatisConfiguration.getBean(),从而获取到 mybatisMapperProxyFactory 映射表中的这个value,然后 使用这个 mybatisMapperProxyFactory 创建代理类,使用 mapperProxy 作为拦截器,用于拦截mapper方法。
额外部分:
图
感受
- 缓存链的生成(org.apache.ibatis.mapping.CacheBuilder#build)
- sqlSessionFactory的构建,含了xml数据的读取(com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean#buildSqlSessionFactory)
- sql标签的读取(org.apache.ibatis.scripting.xmltags.XMLLanguageDriver#createSqlSource(org.apache.ibatis.session.Configuration, org.apache.ibatis.parsing.XNode, java.lang.Class))
- cache的责任链
- InterceptorChain, 使用plugin工具包装层层target。(org.apache.ibatis.plugin.InterceptorChain#pluginAll)
- MybatisMapperProxyFactory.newInstance(),内部还是使用的jdk动态代理,使用mapperProxy作为拦截器。(org.apache.ibatis.binding.MapperProxyFactory#newInstance(org.apache.ibatis.session.SqlSession))
- 加载MapperScannerConfigurer的bean定义,再加载ClassPathMapperScanner,用于扫描Mapper,并修改bean定义为MapperFactoryBean。再触发getObject,拿到存储起来的 MybatisConfiguration的 knowmappers 和 mapperStatements等。