mybatis-plus mapper整理

mybatis-plus mapper整理

简介

提供了操作数据的框架,避免使用jdbc操作数据,加速开发效率,支持多种数据库(databaseId)。既支持sql,mapper的编写,也支持注解@Select等,同时提供了一二级缓存,以及BaseMapper接口以及IService、ServiceImpl这些接口,来提供模板化的方法。同时还支持逆向工程生成代码,是很好用的dao层框架。

用例

  • spring-boot-starter-jdbc
  • mysql-connector-java
  • druid
  • mybatis-plus-boot-starter

    解析

    依赖信息

     <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>
    

    使用过程

  • 我们使用时通常加入@mapperScan注解扫描一个basePackage, 或是 @Mapper注解指定一个mapper接口类
  • 而调用时,就是自动注入 mapper接口类 bean 然后调用bean方法,或是 使用session.getMapper() 进行方法调用。

    依据上面的使用过程解读代码

    解读1:扫描mapper接口类的bean定义

  • 容器启动,扫描bean定义,加载 mapper接口类注册成bean
    1. 容器启动时,扫描bean定义:invokeBeanFactoryPostProcessors(beanFactory);
    2. 代码中使用了@MapperScan,就会加载MapperScannerRegistrar
    3. MapperScannerRegistrar会加载MapperScannerConfigurer
    4. MapperScannerConfigurer引入ClassPathMapperScanner,进行basePackage扫描。 这里会区分:如果是 @MapperScan,那么basePackage就是里面的属性,且不会添加includeFilter;而如果使用@Mapper,那么basePackage就是启动类的包路径,添加@Mapper作为includeFilter。
    5. ClassPathMapperScanner扫描完成后,会执行processBeanDefinitions()修改bean定义,将beanClass设置为 MapperFactoryBean 实现了FactoryBean接口,作为工厂bean,实例化时会调用getObject方法来获取实际bean。而getObject会获取 MybatisConfiguration.getMapper(),也就是读取xml时放入mapper映射的位置(解读3)

    解读2,实例化mapper接口类时,加载sqlSessionFacory

  • 容器启动,实例化剩余bean,由于bean依赖sqlSessionFactory,触发加载
    1. 容器启动,实例化剩余bean:finishBeanFactoryInitialization
    2. 实例化mapper,依赖于sqlSessionFactory,就需要加载 com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration#sqlSessionFactory()
    3. 加载sqlSessionFacory会加载 容器中的实现了inteceptor接口的bean,放入到 InterceptorChain。后话:这个InterceptorChain后面在调用时会用Plugin工具代理 Executor、StatementHandler、ResultSetHandler、ParameterHandler等。
    4. 加载sqlSessionFacory会扫描 配置项定义的mybatis-plus.mapper-locations,作为xml存放的路径,作为resource扫描这个文件夹下的xml文件。(如果没有设置,就是用默认的/mapper/**/*.xml)。

    解读3,加载sqlSessionFacory时,扫描xml,生成mapper映射表和各个方法的mapperStatement

  • 扫描xml文件,解析各个标签,最终生成mapper到MybatisMapperProxyFactory的映射
    1. 解析 mapper标签,作为一个xml文件
    2. 解析里面的cache标签,生成cache链,PerpetualCache <- LruCache <- SerializedCache <- LoggingCache <- SynchronizedCache,并存起来等mapperStatement注册时用。
    3. 解析里面的parameterMap、resultMap、sql、select|insert|update|delete标签,其中sql语句解析时使用 mixSqlNode, 解析各个子标签foreach,trim等等。解析完成后将这个方法,使用 nameSpace+id将方法注册成一个 mapperStatement。这个mapperStatement会带上上面的 cache 数据。 所以有cache数据的mapperStatement就是使用了二级缓存的。
    4. 完成解析后,注册这个mapper:bindMapperForNamespace。注册 mapper 到 MybatisConfiguration,也就是 MapperFactoryBean.getObject之后会执行的 MybatisConfiguration.getMapper(解读1)。存放 clazz, MybatisMapperProxyFactory 的映射关系

    解读4,实例化mapper接口类时,创建代理类

  • 由 bean定义中的 MapperFactoryBean.getObject(),执行到 MybatisConfiguration .getMapper() 获得 存在的mapper映射关系得到 MybatisMapperProxyFactory。
  • 然后由 MybatisMapperProxyFactory.newProxyInstance(),使用 MapperProxy 作为拦截器。

    解读5,调用时被MybatisMapperProxy拦截,以及缓存读取

  • MapperProxy 就会拦截这个mapper下的方法,执行时会判断mapperstatement类型,如果是查询,就判断 mapperstatement上有没有带cache数据,如果没有说明没使用标签。如果有,就可以走 二级缓存存取。
  • 之后就开始查询,就需要 openSession().execute(), 这里就需要newExecutor(),而 newExeCutor()时,会使用 InterceptorChain, 创建代理类,形成拦截链。

    总结

    第一部分:

    加载mapper接口类,并注册bean为mapperFactoryBean。

    第二部分:

    加载sqlSessionFacory, 读取xml文件,作为mapper到mybatisMapperProxyFactory的映射表,存入到 MybatisConfiguration。

    第三部分:

    MapperFactoryBean.getObject(),会读取MybatisConfiguration.getBean(),从而获取到 mybatisMapperProxyFactory 映射表中的这个value,然后 使用这个 mybatisMapperProxyFactory 创建代理类,使用 mapperProxy 作为拦截器,用于拦截mapper方法。

    额外部分:

  • 读取xml时,获取cache链配置。
  • 加载sqlSessionFacory, 获取容器中的 Interceptor, 收集到 InterceptorChain。在newExecutor/StatementHandler/....时多层代理。

    感受

  • 一些重要方法没有特别好的标识,阅读时会容易轻视忽略掉。例如:
    1. 缓存链的生成(org.apache.ibatis.mapping.CacheBuilder#build)
    2. sqlSessionFactory的构建,含了xml数据的读取(com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean#buildSqlSessionFactory)
    3. sql标签的读取(org.apache.ibatis.scripting.xmltags.XMLLanguageDriver#createSqlSource(org.apache.ibatis.session.Configuration, org.apache.ibatis.parsing.XNode, java.lang.Class))
  • 使用了设计模式。例如:
    1. cache的责任链
    2. InterceptorChain, 使用plugin工具包装层层target。(org.apache.ibatis.plugin.InterceptorChain#pluginAll)
    3. MybatisMapperProxyFactory.newInstance(),内部还是使用的jdk动态代理,使用mapperProxy作为拦截器。(org.apache.ibatis.binding.MapperProxyFactory#newInstance(org.apache.ibatis.session.SqlSession))
  • 巧妙运用了spring-boot的一些加载机制
    1. 加载MapperScannerConfigurer的bean定义,再加载ClassPathMapperScanner,用于扫描Mapper,并修改bean定义为MapperFactoryBean。再触发getObject,拿到存储起来的 MybatisConfiguration的 knowmappers 和 mapperStatements等。
  • 对设计模式和类加载有挺好的参考价值。
  • posted @ 2024-05-16 23:37  林致礼  阅读(9)  评论(0编辑  收藏  举报