【mybatis和mybatis-plus】源码分析

2023-09-20 10:38:04

mybatis

核心类和接口说明

Environment

环境配置,包含id、TransactionFactory(事务工厂)、DataSource

在这里插入图片描述

TransactionFactory有三个实现类,我们与spring整合,默认使用第三个事务工厂
在这里插入图片描述

TypeAliasRegistry

别名映射

在这里插入图片描述
比如

全限定名:parameterType="java.lang.Integer"
<select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
别名:parameterType="integer"
<select id="selectByPrimaryKey" parameterType="integer" resultMap="BaseResultMap">

TypeHandlerRegistry

jdbc与java的类型映射处理器

在这里插入图片描述
比如StringTypeHandler,映射java的String,对应数据库VARCHAR
在这里插入图片描述

BaseBuilder

抽象类
字段包括:Configuration、TypeAliasRegistry、TypeHandlerRegistry

在这里插入图片描述

XMLMapperBuilder

mapper构造器,一个mapper对应一个XMLMapperBuilder类,继承父类BaseBuilder
变量包括XPathParser、MapperBuilderAssistant、sqlFragments、mapper文件resource

在这里插入图片描述
在构造方法中,初始化了XPathParser、MapperBuilderAssistant(mapper缓存处理类)

在这里插入图片描述

MapperBuilderAssistant

mapper构造器助手,主要用来处理二级缓存的逻辑、构造MappedStatement对象,继承父类BaseBuilder
变量包括:当前namespace、文件resource、Cache(当前二级缓存)、unresolvedCacheRef(二级缓存CacheRef),其它属性是通用配置

在这里插入图片描述
变量包括:当前namespace、文件resource、当前二级缓存、unresolvedCacheRef,其它属性是通用配置
在这里插入图片描述

XMLStatementBuilder

Statment构造器,用于解析继承父类BaseBuilder

XMLScriptBuilder

xml中表达式构造器,用于添加动态sql对应的处理器、生成SqlSource对象

在这里插入图片描述
在这里插入图片描述

XMLLanguageDriver

这里是引用

XPathParser

基于sax解析xml并封装document对象

在这里插入图片描述

SqlSessionTemplate

基于jdk动态代理,在构造方法中创建SqlSession的代理类,代理主要是为了统一处理sqlSession方法执行前后的事务功能

在这里插入图片描述

构造方法
在这里插入图片描述

SqlSessionInterceptor

作为SqlSessionTemplate的内部类,提供了SqlSessionTemplate动态代理SqlSession的InvocationHandler实现

在这里插入图片描述

源码

SqlSessionFactoryBean -> buildSqlSessionFactory -> XMLMapperBuilder.parse()

解析mapper文件资源

在这里插入图片描述
在这里插入图片描述

configurationElement

传入参数为XNode,由parser.evalNode(“/mapper”)方法返回结果
在这里插入图片描述

buildStatementFromContext(context.evalNodes(“select|insert|update|delete”))

在这里插入图片描述
解析出来的集合内容如下,包含我们写的sql 原始内容
在这里插入图片描述
遍历XNode,通过XMLStatementBuilder.parseStatementNode()解析每一个XNode
在这里插入图片描述

XMLStatementBuilder.parseStatementNode()

在这里插入图片描述

LanguageDriver.createSqlSource(configuration, context, parameterTypeClass);

通过XMLLanguageDriver.createSqlSource,返回SqlSource。核心由XMLScriptBuilder来代替实现
在这里插入图片描述
XMLScriptBuilder.parseScriptNode

  1. 先通过parseDynamicTags用于解析XNode的内容,通过不同的类型(动态标签,普通文本等)返回sqlNode集合
    在这里插入图片描述
    通过缓存的nodeHandlerMap,获取动态标签的值对应的处理器(如 => WhereHandler )
    在这里插入图片描述
    将处理结果添加到集合中
    在这里插入图片描述
    最终返回的集合包含了StaticTextSqlNode、WhereSqlNode,将集合封装到MixedSqlNode并返回
    原始sql
 <select id="normalList" resultType="org.hopehomi.cloud.entity.Orders" parameterType="integer">
        select * from orders
        <where>
            id = #{count}
        </where>
    </select>

在这里插入图片描述
2. 接下来判断XNode是否包含动态sql标签
如果包含动态sql标签,封装成DynamicSqlSource并返回
在这里插入图片描述

MapperBuilderAssistant.addMappedStatement(…)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

通过建造者模式,构造出MappedStatement,缓存到Configuration中
key为MappedStatement的id(namespace + “.” + id)

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

bindMapperForNamespace

在这里插入图片描述
通过namespace获取到mapper接口,执行configuration.addMapper(boundType);
在这里插入图片描述
由MapperRegistry来执行

MapperRegistry.addMapper

在这里插入图片描述

添加Mapper注解映射,比如@Select、@Update

MapperAnnotationBuilder的构造方法

在这里插入图片描述

在这里插入图片描述

MybatisMapperAnnotationBuilder.parse()

判断方法是否通过注解的方式执行
在这里插入图片描述
在这里插入图片描述

SqlSessionFactoryBean -> buildSqlSessionFactory -> this.sqlSessionFactoryBuilder.build(targetConfiguration)

在这里插入图片描述
返回SqlSessionFactory的默认实现DefaultSqlSessionFactory

在这里插入图片描述
在这里插入图片描述

SqlSessionTemplate创建

初始化SqlSessionTemplate
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
构造方法中创建SqlSession的代理对象
在这里插入图片描述

mybatis-plus

核心类和接口的说明

MybatisPlusProperties

MP的配置注入,如configLocation(mybatis的config文件)、mapperLocations(mapper文件路径)、MybatisConfiguration、globalConfig等

在这里插入图片描述
在这里插入图片描述

MybatisConfiguration

继承自mybatis的Configuration,重写了一些方法
在这里插入图片描述

GlobalConfig

MP的全局配置,如banner、MetaObjectHandler(元对象字段填充控制器)、IdentifierGenerator(主键生成器)等

在这里插入图片描述

MybatisPlusInterceptor

实现了Mybatis的Interceptor接口,用于执行过程的拦截

在这里插入图片描述
在intercept方法中,会解析mybatis的入参invocation,将解析完的参数传入到各个InnerInterceptor扩展的查询/修改方法中
在这里插入图片描述

InnerInterceptor接口

Mp的自定义扩展拦截器
在这里插入图片描述

自定义扩展InnerInterceptor

官方提供了几个拦截器,我们可以在最前面先添加我们自己的拦截器,在执行的时候会最高优先级执行,如果是作为根模块来封装,可以通过责任链模式来构造一个我们自己的拦截器链,业务项目继承封装的接口即可
在这里插入图片描述

RootMybatisInterceptor自定义拦截器链

AbstractMybatisInterceptor

public abstract class AbstractMybatisInterceptor implements InnerInterceptor {

    /**
     * 查询拦截
     * @param executor
     * @param ms
     * @param parameter
     * @param rowBounds
     * @param resultHandler
     * @param boundSql
     * @param chain
     * @return
     * @throws SQLException
     */
    public abstract boolean queryIntercept(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql, InterceptorChain chain) throws SQLException;

    /**
     * 修改拦截
     * @param executor
     * @param ms
     * @param parameter
     * @param chain
     * @return
     * @throws SQLException
     */
    public abstract boolean updateIntercept(Executor executor, MappedStatement ms, Object parameter, InterceptorChain chain) throws SQLException;

    public int priority() {
        return 0;
    }
}

RootMybatisInterceptor

public class RootMybatisInterceptor extends AbstractMybatisInterceptor {

    private final AbstractMybatisInterceptor[] interceptors;

    public RootMybatisInterceptor(AbstractMybatisInterceptor[] interceptorChain) {
        this.interceptors = interceptorChain;
    }

    @Override
    public boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        InterceptorChain interceptorChain = new InterceptorChain(interceptors);
        return this.queryIntercept(executor, ms, parameter, rowBounds, resultHandler, boundSql, interceptorChain);
    }

    @Override
    public boolean willDoUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {
        InterceptorChain interceptorChain = new InterceptorChain(interceptors);
        return this.updateIntercept(executor, ms, parameter, interceptorChain);
    }

    @Override
    public boolean queryIntercept(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql, InterceptorChain chain) throws SQLException {
        return chain.next(executor, ms, parameter, rowBounds, resultHandler, boundSql, chain);
    }

    @Override
    public boolean updateIntercept(Executor executor, MappedStatement ms, Object parameter, InterceptorChain chain) throws SQLException {
        return chain.next(executor, ms, parameter, chain);
    }

    @Override
    public int priority() {
        return Ordered.HIGHEST_PRECEDENCE;
    }
}

InterceptorChain

public class InterceptorChain {

    private final List<AbstractMybatisInterceptor> chains;
    private final AtomicInteger index;

    public InterceptorChain(AbstractMybatisInterceptor[] interceptorChains) {
        List<AbstractMybatisInterceptor> chainList = Arrays.stream(interceptorChains).sorted(Comparator.comparingInt(AbstractMybatisInterceptor::priority)).collect(Collectors.toList());
        chains = new ArrayList<>();
        index = new AtomicInteger(0);
        chains.addAll(chainList);
    }

    public boolean next(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql, InterceptorChain chain) throws SQLException {
        int curIndex = index.getAndIncrement();
        if (curIndex == chains.size()) {
            return true;
        }
        AbstractMybatisInterceptor abstractMybatisInterceptor = chains.get(curIndex);
        return abstractMybatisInterceptor.queryIntercept(executor, ms, parameter, rowBounds, resultHandler, boundSql, chain);
    }

    public boolean next(Executor executor, MappedStatement ms, Object parameter, InterceptorChain chain) throws SQLException {
        int curIndex = index.getAndIncrement();
        if (curIndex == chains.size()) {
            return true;
        }
        AbstractMybatisInterceptor abstractMybatisInterceptor = chains.get(curIndex);
        return abstractMybatisInterceptor.updateIntercept(executor, ms, parameter, chain);
    }

    public List<AbstractMybatisInterceptor> getChains() {
        return Collections.unmodifiableList(chains);
    }


}

WriteReadMybatisIntercepts读写分离插件

@Slf4j
public class WriteReadMybatisIntercepts extends AbstractMybatisInterceptor {

    @Override
    public boolean queryIntercept(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql, InterceptorChain chain) throws SQLException {
        DynamicDataSourceHolder.putDataSource(DynamicDataSourceGlobalEnum.READ);
        return chain.next(executor, ms, parameter, rowBounds, resultHandler, boundSql, chain);
    }

    @Override
    public boolean updateIntercept(Executor executor, MappedStatement ms, Object parameter, InterceptorChain chain) throws SQLException {
        DynamicDataSourceHolder.putDataSource(DynamicDataSourceGlobalEnum.WRITE);
        return chain.next(executor, ms, parameter, chain);
    }
}

MybatisPlusAutoConfiguration

创建SqlSessionFactory

在这里插入图片描述
创建MybatisSqlSessionFactoryBean,将DataSource、MybatisPlusProperties、MybatisConfiguration、GlobalConfig等参数设置到MybatisSqlSessionFactoryBean中,调用MybatisSqlSessionFactoryBean.getObject()来构建实例

在这里插入图片描述

MybatisSqlSessionFactoryBean

拷贝自Mybatis的SqlSessionFactoryBean,基本内容和SqlSessionFactoryBean一致

基于MybatisSqlSessionFactoryBean,他是一个factoryBean
返回bean类型为SqlSessionFactory
在这里插入图片描述

getObject

先判断sqlSessionFactory是否有值,没有值会进行初始化

在这里插入图片描述
afterPropertiesSet
在这里插入图片描述

buildSqlSessionFactory

在这里插入图片描述

封装Mybatis的Configuration,数据来源自前面传入的成员变量,需要满足mybatis需要的配置数据
在这里插入图片描述
在这里插入图片描述
赋值targetConfiguration
在这里插入图片描述
在这里插入图片描述
初始化Environment,将SpringManagedTransactionFactory作为事务工厂
在这里插入图片描述
mapper文件解析,这段代码和mybatis原生一样
在这里插入图片描述
最终调用到MybatisMapperRegistry.addMapper方法
在这里插入图片描述
该方法与mybatis的MapperRegistry.addMapper区别是替换了一些类,大致是一样的
下图是mybatis的MapperRegistry
在这里插入图片描述

MybatisMapperAnnotationBuilder.parse()

与mybatis源码不同的是,加入了拦截策略的初始化,创建CRUD的mapperStatement加入到缓存中
区别如下两图所示

mybatis

mp

parserInjector()

由于我们自定义的Mapper继承了BaseMapper,在BaseMapper中有默认的增删改查方法

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  1. 获取Mapper上实体注解
  2. 基于实体封装TableInfo(表结构对象,包含表名、id字段、是否新增/修改填充字段等)
    在这里插入图片描述
  3. 返回所有MP内置CRUD方法(基于DefaultSqlInjector,可以参考来实现自己的内置方法)
    在这里插入图片描述
    在这里插入图片描述
  4. 循环注入自定义方法(基于第三步获取的所有方法,依次遍历添加到MappedStatement缓存中)
    在这里插入图片描述
    在这里插入图片描述
    内置方法都需要继承AbstractMethod,基于模板方法调用各自的injectMappedStatement方法
    下图实例Insert

    在这里插入图片描述
    injectMappedStatement最后一步addInsertMappedStatement,由父类AbstractMethod默认实现,最终调用addMappedStatement方法
    在这里插入图片描述
AbstractMethod.addMappedStatement

最终由MapperBuilderAssistant.addMappedStatement添加到mappedStatements中

在这里插入图片描述
insert入参
在这里插入图片描述
添加到mybatis
在这里插入图片描述

XMLMapperBuilder.parse()解析完毕后,获取SqlSessionFactory实现

在这里插入图片描述
mp会在这里初始化一些默认配置(如主键生成器),最后依然是调用myabtis的.build方法来构建并返回SqlSessionFactory的实现
在这里插入图片描述
这里myabtis直接返回了DefaultSqlSessionFactory
下图为mybatis的SqlSessionFactoryBuilder.build
在这里插入图片描述

SqlSessionTemplate的bean创建

MP

@InterceptorIgnore的拦截处理

初始化

MybatisMapperAnnotationBuilder.parse();
与mybatis不同的是,在这里插入执行了InterceptorIgnoreHelper.initSqlParserInfoCache(type)(根据Mapper类上注解来初始化)
在后面插入执行了InterceptorIgnoreHelper.initSqlParserInfoCache(ignoreStrategy, mapperName, method)(根据具体方法上注解来初始化)

mapper类上是否存在@InterceptorIgnore注解
在这里插入图片描述
在这里插入图片描述
方法上是否存在@InterceptorIgnore注解
在这里插入图片描述
在这里插入图片描述

构建IgnoreStrategy

在这里插入图片描述
在这里插入图片描述

缓存IgnoreStrategy

在这里插入图片描述
在这里插入图片描述

使用

更多推荐

AIGC生态,引领社会、行业、个体的翻天覆地变革

1879年,当爱迪生成功地实验出能够持续发光的灯丝时,他迎来了一个新的挑战:如何让更多人能够享受到电力的便利?经过艰难探索,直到1882年9月,在曼哈顿的珍珠街上,爱迪生才铺设了世界上第一张电力网络,为普罗大众提供了电力。正由此,第二次工业革命的幕布才得以徐徐打开。每当一项重大技术突破出现时,人类都不得不思考如何将其普

MT6785(Helio G95)安卓核心板_联发科4G高能低耗安卓主板开发板

MTK6785(HelioG95)安卓核心板采用八核CPU具有两个强大的ArmCortex-A76处理器内核,主频高达2.05GHz,外加六个Cortex-A55高效处理器。其强大的图形性能由ArmMali-G76MC4提供,速度可提升至900MHz。高达10GB的2133MHz的LPDDR4X提供了充足的带宽,同时支

HarmonyOS开发:那些开发中常见的问题汇总(一)

前言本来这篇文章需要讲述静态共享包如何实现远程依赖和上传以及关于静态共享包私服的搭建,非常遗憾的告诉大家,由于组织管理申请迟迟未通过,和部分文档官方权限暂未开放,关于这方面的讲解需要延后了,大概需要等到2024年第一季度,也就是来年,毕竟关于HarmonyOS的升级,舍弃AOSP,也是在2024年第一季度才会面向所有开

【HarmonyOS】元服务卡片router实现跳转到指定页面

【关键字】元服务卡片、router跳转不同页面【写在前面】本篇文章主要介绍开发元服务卡片时,如何实现从卡片中点击事件跳转到指定的应用内页面功能。此处以JSUI开发服务卡片为例,JS卡片支持组件设置action,包括router事件和message事件,其中router事件用于应用跳转,message事件用于卡片开发人员

【鸿蒙(HarmonyOS)】UI开发的两种范式:ArkTS、JS(以登录界面开发为例进行对比)

文章目录一、引言1、开发环境2、整体架构图二、认识ArkUI1、基本概念2、开发范式(附:案例)(1)ArkTS(2)JS三、附件一、引言1、开发环境之后关于HarmonyOS技术的分享,将会持续使用到以下版本HarmonyOS:3.1/4.0SDK:API9ReleaseNode.js:v14.20.1DevEcoS

Zookeeper应用场景和底层设计

一、什么是zookeeperZookeeper是一个开源的分布式协调服务框架,它是服务于其它集群式框架的框架。【简言之】有一个服务A,以集群的方式提供服务。只需要A专注于它提供的服务就可以,至于它如何以多台服务器协同完成任务的事情,交给Zookeeper来协调。【底层设计逻辑】1)基于观察者设计模式2)等价于文件系统+

开源项目的版本管理:Git的最佳实践

🌷🍁博主猫头虎带您GotoNewWorld.✨🍁🦄博客首页——猫头虎的博客🎐🐳《面试题大全专栏》文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺🌊《IDEA开发秘籍专栏》学会IDEA常用操作,工作效率翻倍~💐🌊《100天精通Golang(基础入门篇)》学会Golang语言,畅玩云原生,走遍大

【Rust 基础篇】Rust 模式:高效、安全和灵活的匹配工具

导言在编程中,经常需要对数据进行匹配和处理,例如从一个复杂的数据结构中提取特定的值,或者根据不同的情况执行不同的逻辑。Rust是一门现代的系统编程语言,它引入了一种称为"模式"(Pattern)的强大特性,使得数据的匹配和处理变得高效、安全和灵活。本篇博客将深入探讨Rust模式的各种用法,带您领略Rust的魅力。什么是

ES6如何声明一个类?类如何继承?

在JavaScript中,您可以使用关键字class来声明一个类。类是一种模板,用于创建对象的构造函数,其中包含了属性和方法。以下是声明一个类的基本语法:classClassName{constructor(){//构造函数,用于创建对象实例时初始化属性this.propertyName=value;}//方法定义me

stm32学习-芯片系列/选型

【03】STM32·HAL库开发-初识STM32|STM概念、芯片分类、命名规则、选型|STM32原理图设计、看数据手册、最小系统的组成、STM32IO分配_小浪宝宝的博客-CSDN博客STM32:ST是意法半导体,M是MCU/MPU,32是32位。ST累计推出了:5大类、18个系列、1000多个型号的Cortex内核

注入之mssql数据库(手工注入)

sa最高权限(可以获取系统权限)打开一个mssql数据库要拼接一个参数拼接这个参数?xxser=1检查是否是mssql数据库andexists(select*from%20sysobjects)为真是属于mssql查询当前数据库系统的用户名andsystem_user=0(由于版本问题谷歌不可以)可以去虚拟机,爆出系统

热文推荐