Mybatis Plus是如何实现动态SQL语句的?原理你懂吗?

发布网友 发布时间:2024-09-27 06:21

我来回答

1个回答

热心网友 时间:2024-10-04 20:34

Mybatis-Plus(简称MP)是一个 Mybatis 的增强工具,那么它是怎么增强的呢?其实就是它已经封装好了一些crud方法,开发就不需要再写xml了,直接调用这些方法就行,就类似于JPA。

那么这篇文章就来阅读以下MP的具体实现,看看是怎样实现这些增强的。

入口类:MybatisSqlSessionFactoryBuilder

通过在入口类 MybatisSqlSessionFactoryBuilder#build方法中, 在应用启动时, 将MyBatis plus(简称MP)自定义的动态配置xml文件注入到Mybatis中。

publicclassMybatisSqlSessionFactoryBuilderextendsSqlSessionFactoryBuilder{publicSqlSessionFactorybuild(Configurationconfiguration){//...省略若干行if(globalConfig.isEnableSqlRunner()){newSqlRunnerInjector().inject(configuration);}//...省略若干行returnsqlSessionFactory;}}

这里涉及到2个MP2个功能类

扩展继承自Mybatis的MybatisConfiguration类: MP动态脚本构建,注册,及其它逻辑判断。

SqlRunnerInjector: MP默认插入一些动态方法的xml 脚本方法。

MybatisConfiguration类

这里我们重点剖析MybatisConfiguration类,在MybatisConfiguration中,MP初始化了其自身的MybatisMapperRegistry,而MybatisMapperRegistry是MP加载自定义的SQL方法的注册器。

MybatisConfiguration中很多方法是使用MybatisMapperRegistry进行重写实现

其中有3个重载方法addMapper实现了注册MP动态脚本的功能。

publicclassMybatisConfigurationextendsConfiguration{/***Mapper注册*/protectedfinalMybatisMapperRegistrymybatisMapperRegistry=newMybatisMapperRegistry(this);//..../***初始化调用*/publicMybatisConfiguration(){super();this.mapUnderscoreToCamelCase=true;languageRegistry.setDefaultDriverClass(MybatisXMLLanguageDriver.class);}/***MybatisPlus加载SQL顺序:*<p>1、加载XML中的SQL</p>*<p>2、加载SqlProvider中的SQL</p>*<p>3、XmlSql与SqlProvider不能包含相同的SQL</p>*<p>调整后的SQL优先级:XmlSql>sqlProvider>CurdSql</p>*/@OverridepublicvoidaddMappedStatement(MappedStatementms){//...}//...省略若干行/***使用自己的MybatisMapperRegistry*/@Overridepublic<T>voidaddMapper(Class<T>type){mybatisMapperRegistry.addMapper(type);}//....省略若干行}

在MybatisMapperRegistry中,MP将mybatis的MapperAnnotationBuilder替换为MP自己的MybatisMapperAnnotationBuilder

publicclassMybatisMapperRegistryextendsMapperRegistry{@Overridepublic<T>voidaddMapper(Class<T>type){//...省略若干行MybatisMapperAnnotationBuilderparser=newMybatisMapperAnnotationBuilder(config,type);parser.parse();//...省略若干行}}

在MybatisMapperRegistry类的addMapper方法中,真正进入到MP的核心类MybatisMapperAnnotationBuilder,MybatisMapperAnnotationBuilder这个类是MP实现动态脚本的关键类。

MybatisMapperAnnotationBuilder动态构造

在MP的核心类MybatisMapperAnnotationBuilder的parser方法中,MP逐一遍历要加载的Mapper类,加载的方法包括下面几个

publicclassMybatisMapperAnnotationBuilderextendsMapperAnnotationBuilder{@Overridepublicvoidparse(){//...省略若干行for(Methodmethod:type.getMethods()){/**for循环代码,MP判断method方法是否是@Select@Insert等mybatis注解方法**/parseStatement(method);InterceptorIgnoreHelper.initSqlParserInfoCache(cache,mapperName,method);SqlParserHelper.initSqlParserInfoCache(mapperName,method);}/**这2行代码,MP注入默认的方法列表**/if(GlobalConfigUtils.isSupperMapperChildren(configuration,type)){GlobalConfigUtils.getSqlInjector(configuration).inspectInject(assistant,type);}//...省略若干行}@OverridepublicvoidinspectInject(MapperBuilderAssistantbuilderAssistant,Class<?>mapperClass){Class<?>modelClass=extractModelClass(mapperClass);//...省略若干行List<AbstractMethod>methodList=this.getMethodList(mapperClass);TableInfotableInfo=TableInfoHelper.initTableInfo(builderAssistant,modelClass);//循环注入自定义方法methodList.forEach(m->m.inject(builderAssistant,mapperClass,modelClass,tableInfo));mapperRegistryCache.add(className);}}publicclassDefaultSqlInjectorextendsAbstractSqlInjector{@OverridepublicList<AbstractMethod>getMethodList(Class<?>mapperClass){returnStream.of(newInsert(),//...省略若干行newSelectPage()).collect(toList());}}

在MybatisMapperAnnotationBuilder中,MP真正将框架自定义的动态SQL语句注册到Mybatis引擎中。而AbstractMethod则履行了具体方法的SQL语句构造。

具体的AbstractMethod实例类,构造具体的方法SQL语句

以 SelectById 这个类为例说明下

/***根据ID查询一条数据*/publicclassSelectByIdextendsAbstractMethod{@OverridepublicMappedStatementinjectMappedStatement(Class<?>mapperClass,Class<?>modelClass,TableInfotableInfo){/**定义mybatisxmlmethodid,对应<id="xyz">**/SqlMethodsqlMethod=SqlMethod.SELECT_BY_ID;/**构造id对应的具体xml片段**/SqlSourcesqlSource=newRawSqlSource(configuration,String.format(sqlMethod.getSql(),sqlSelectColumns(tableInfo,false),tableInfo.getTableName(),tableInfo.getKeyColumn(),tableInfo.getKeyProperty(),tableInfo.getLogicDeleteSql(true,true)),Object.class);/**将xmlmethod方法添加到mybatis的MappedStatement中**/returnthis.addSelectMappedStatementForTable(mapperClass,getMethod(sqlMethod),sqlSource,tableInfo);}}

至此,MP完成了在启动时加载自定义的方法xml配置的过程,后面的就是mybatis ${变量} #{变量}的动态替换和预编译,已经进入mybatis自有功能。

总结一下

MP总共改写和替换了mybatis的十多个类,主要如下图所示:

总体上来说,MP实现mybatis的增强,手段略显繁琐和不够直观,其实根据MybatisMapperAnnotationBuilder构造出自定义方法的xml文件,将其转换为mybatis的Resource资源,可以只继承重写一个Mybatis类:SqlSessionFactoryBean 比如如下:

publicclassYourSqlSessionFactoryBeanextendsSqlSessionFactoryBeanimplementsApplicationContextAware{privateResource[]mapperLocations;@OverridepublicvoidsetMapperLocations(Resource...mapperLocations){super.setMapperLocations(mapperLocations);/**暂存使用mybatis原生定义的mapperxml文件路径**/this.mapperLocations=mapperLocations;}/***{@inheritDoc}*/@OverridepublicvoidafterPropertiesSet()throwsException{ConfigurableListableBeanFactorybeanFactory=getBeanFactory();/**只需要通过将自定义的方法构造成xmlresource和原生定义的Resource一起注入到mybatis中即可,这样就可以实现MP的自定义动态SQL和原生SQL的共生关系**/this.setMapperLocations(InjectMapper.getMapperResource(this.dbType,beanFactory,this.mapperLocations));super.afterPropertiesSet();}}

在这边文章中,简单介绍了MP实现动态语句的实现过程,并且给出一个可能的更便捷方法。

来源:juejin.cn/post/68830811871038664

热心网友 时间:2024-10-04 20:39

Mybatis-Plus(简称MP)是一个 Mybatis 的增强工具,那么它是怎么增强的呢?其实就是它已经封装好了一些crud方法,开发就不需要再写xml了,直接调用这些方法就行,就类似于JPA。

那么这篇文章就来阅读以下MP的具体实现,看看是怎样实现这些增强的。

入口类:MybatisSqlSessionFactoryBuilder

通过在入口类 MybatisSqlSessionFactoryBuilder#build方法中, 在应用启动时, 将MyBatis plus(简称MP)自定义的动态配置xml文件注入到Mybatis中。

publicclassMybatisSqlSessionFactoryBuilderextendsSqlSessionFactoryBuilder{publicSqlSessionFactorybuild(Configurationconfiguration){//...省略若干行if(globalConfig.isEnableSqlRunner()){newSqlRunnerInjector().inject(configuration);}//...省略若干行returnsqlSessionFactory;}}

这里涉及到2个MP2个功能类

扩展继承自Mybatis的MybatisConfiguration类: MP动态脚本构建,注册,及其它逻辑判断。

SqlRunnerInjector: MP默认插入一些动态方法的xml 脚本方法。

MybatisConfiguration类

这里我们重点剖析MybatisConfiguration类,在MybatisConfiguration中,MP初始化了其自身的MybatisMapperRegistry,而MybatisMapperRegistry是MP加载自定义的SQL方法的注册器。

MybatisConfiguration中很多方法是使用MybatisMapperRegistry进行重写实现

其中有3个重载方法addMapper实现了注册MP动态脚本的功能。

publicclassMybatisConfigurationextendsConfiguration{/***Mapper注册*/protectedfinalMybatisMapperRegistrymybatisMapperRegistry=newMybatisMapperRegistry(this);//..../***初始化调用*/publicMybatisConfiguration(){super();this.mapUnderscoreToCamelCase=true;languageRegistry.setDefaultDriverClass(MybatisXMLLanguageDriver.class);}/***MybatisPlus加载SQL顺序:*<p>1、加载XML中的SQL</p>*<p>2、加载SqlProvider中的SQL</p>*<p>3、XmlSql与SqlProvider不能包含相同的SQL</p>*<p>调整后的SQL优先级:XmlSql>sqlProvider>CurdSql</p>*/@OverridepublicvoidaddMappedStatement(MappedStatementms){//...}//...省略若干行/***使用自己的MybatisMapperRegistry*/@Overridepublic<T>voidaddMapper(Class<T>type){mybatisMapperRegistry.addMapper(type);}//....省略若干行}

在MybatisMapperRegistry中,MP将mybatis的MapperAnnotationBuilder替换为MP自己的MybatisMapperAnnotationBuilder

publicclassMybatisMapperRegistryextendsMapperRegistry{@Overridepublic<T>voidaddMapper(Class<T>type){//...省略若干行MybatisMapperAnnotationBuilderparser=newMybatisMapperAnnotationBuilder(config,type);parser.parse();//...省略若干行}}

在MybatisMapperRegistry类的addMapper方法中,真正进入到MP的核心类MybatisMapperAnnotationBuilder,MybatisMapperAnnotationBuilder这个类是MP实现动态脚本的关键类。

MybatisMapperAnnotationBuilder动态构造

在MP的核心类MybatisMapperAnnotationBuilder的parser方法中,MP逐一遍历要加载的Mapper类,加载的方法包括下面几个

publicclassMybatisMapperAnnotationBuilderextendsMapperAnnotationBuilder{@Overridepublicvoidparse(){//...省略若干行for(Methodmethod:type.getMethods()){/**for循环代码,MP判断method方法是否是@Select@Insert等mybatis注解方法**/parseStatement(method);InterceptorIgnoreHelper.initSqlParserInfoCache(cache,mapperName,method);SqlParserHelper.initSqlParserInfoCache(mapperName,method);}/**这2行代码,MP注入默认的方法列表**/if(GlobalConfigUtils.isSupperMapperChildren(configuration,type)){GlobalConfigUtils.getSqlInjector(configuration).inspectInject(assistant,type);}//...省略若干行}@OverridepublicvoidinspectInject(MapperBuilderAssistantbuilderAssistant,Class<?>mapperClass){Class<?>modelClass=extractModelClass(mapperClass);//...省略若干行List<AbstractMethod>methodList=this.getMethodList(mapperClass);TableInfotableInfo=TableInfoHelper.initTableInfo(builderAssistant,modelClass);//循环注入自定义方法methodList.forEach(m->m.inject(builderAssistant,mapperClass,modelClass,tableInfo));mapperRegistryCache.add(className);}}publicclassDefaultSqlInjectorextendsAbstractSqlInjector{@OverridepublicList<AbstractMethod>getMethodList(Class<?>mapperClass){returnStream.of(newInsert(),//...省略若干行newSelectPage()).collect(toList());}}

在MybatisMapperAnnotationBuilder中,MP真正将框架自定义的动态SQL语句注册到Mybatis引擎中。而AbstractMethod则履行了具体方法的SQL语句构造。

具体的AbstractMethod实例类,构造具体的方法SQL语句

以 SelectById 这个类为例说明下

/***根据ID查询一条数据*/publicclassSelectByIdextendsAbstractMethod{@OverridepublicMappedStatementinjectMappedStatement(Class<?>mapperClass,Class<?>modelClass,TableInfotableInfo){/**定义mybatisxmlmethodid,对应<id="xyz">**/SqlMethodsqlMethod=SqlMethod.SELECT_BY_ID;/**构造id对应的具体xml片段**/SqlSourcesqlSource=newRawSqlSource(configuration,String.format(sqlMethod.getSql(),sqlSelectColumns(tableInfo,false),tableInfo.getTableName(),tableInfo.getKeyColumn(),tableInfo.getKeyProperty(),tableInfo.getLogicDeleteSql(true,true)),Object.class);/**将xmlmethod方法添加到mybatis的MappedStatement中**/returnthis.addSelectMappedStatementForTable(mapperClass,getMethod(sqlMethod),sqlSource,tableInfo);}}

至此,MP完成了在启动时加载自定义的方法xml配置的过程,后面的就是mybatis ${变量} #{变量}的动态替换和预编译,已经进入mybatis自有功能。

总结一下

MP总共改写和替换了mybatis的十多个类,主要如下图所示:

总体上来说,MP实现mybatis的增强,手段略显繁琐和不够直观,其实根据MybatisMapperAnnotationBuilder构造出自定义方法的xml文件,将其转换为mybatis的Resource资源,可以只继承重写一个Mybatis类:SqlSessionFactoryBean 比如如下:

publicclassYourSqlSessionFactoryBeanextendsSqlSessionFactoryBeanimplementsApplicationContextAware{privateResource[]mapperLocations;@OverridepublicvoidsetMapperLocations(Resource...mapperLocations){super.setMapperLocations(mapperLocations);/**暂存使用mybatis原生定义的mapperxml文件路径**/this.mapperLocations=mapperLocations;}/***{@inheritDoc}*/@OverridepublicvoidafterPropertiesSet()throwsException{ConfigurableListableBeanFactorybeanFactory=getBeanFactory();/**只需要通过将自定义的方法构造成xmlresource和原生定义的Resource一起注入到mybatis中即可,这样就可以实现MP的自定义动态SQL和原生SQL的共生关系**/this.setMapperLocations(InjectMapper.getMapperResource(this.dbType,beanFactory,this.mapperLocations));super.afterPropertiesSet();}}

在这边文章中,简单介绍了MP实现动态语句的实现过程,并且给出一个可能的更便捷方法。

来源:juejin.cn/post/68830811871038664

声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com