当前位置: 首页 > 后端技术 > Java

面试官问:MybatisPlus是如何实现动态SQL语句的?原理你懂吗?

时间:2023-04-01 20:26:43 Java

来源:juejin.cn/post/6883081187103866894Mybatis-Plus(简称MP)是一款Mybatis增强工具,那么它是如何增强的呢?其实它已经封装了一些crud方法,所以开发不需要写xml,直接调用这些方法即可,类似JPA。那么在这篇文章中,我们就来看看下面MP的具体实现,看看是如何实现这些增强的。入口类:MybatisSqlSessionFactoryBuilder在入口类MybatisSqlSessionFactoryBuilder#build方法中,当应用程序启动时,将mybatisplus(简称MP)自定义的动态配置xml文件注入到Mybatis中。publicclassMybatisSqlSessionFactoryBuilderextendsSqlSessionFactoryBuilder{publicSqlSessionFactorybuild(Configurationconfiguration){//...省略一些行if(globalConfig.isEnableSqlRunner()){newSqlRunnerInjector().inject(configuration);}//...省略一些行returnsqlSessionFactory;}}这里涉及到继承自Mybatis的MybatisConfiguration类扩展的2个MP2功能类:MP动态脚本构建、注册等逻辑判断。SqlRunnerInjector:MP默认为一些动态方法插入xml脚本方法。MybatisConfiguration类这里重点分析MybatisConfiguration类。在MybatisConfiguration中,MP初始化了自己的MybatisMapperRegistry,MybatisMapperRegistry是MP加载自定义SQL方法的注册器。MybatisConfiguration中的很多方法都是使用MybatisMapperRegistry重写的。其中,实现注册MP动态脚本功能的重载方法addMapper有3个。publicclassMybatisConfigurationextendsConfiguration{/***Mapper注册*/protectedfinalMybatisMapperRegistrymybatisMapperRegistry=newMybatisMapperRegistry(this);//..../***初始化调用*/publicMybatisConfiguration(){super();这。mapUnderscoreToCamelCase=true;languageRegistry.setDefaultDriverClass(MybatisXMLLanguageDriver.class);}/***MybatisPlus加载SQL顺序:*

1.在XML中加载SQL

*

2.在SqlProvider中加载SQL

*

3.XmlSql和SqlProvider不能包含相同的SQL

*

调整后的SQL优先级:XmlSql>sqlProvider>CurdSql

*/@OverridepublicvoidaddMappedStatement(MappedStatementms){//...}//...省略几行/***使用你自己的MybatisMapperRegistry*/@OverridepublicvoidaddMapper(Classtype){mybatisMapperRegistry.addMapper(type);}//....省略几行}MybatisMapperRegistry中MP将mybatis的MapperAnnotationBuilder替换为MP自己的MybatisMapperAnnotationBuilderpublicclassMybatisMapperRegistryextendsMapperRegistry{@OverridepublicvoidaddMapper(Classtype){//...省略几行MybatisMapperAnnotationBuilder(配置,类型);解析器.parse();//...省略几行}}在MybatisMapperRegistry类的addMapper方法中,真正进入了MP的核心类MybatisMapperAnnotationBuilder,它是MP实现动态脚本的关键类MybatisMapperAnnotationBuilder是在MP的核心类MybatisMapperAnnotationBuilder的parser方法中动态构建的。MP逐一遍历要加载的Mapper类。加载方法包括如下publicclassMybatisMapperAnnotationBuilderextendsMapperAnnotationBuilder{@Overridepublicvoidparse(){//...省略几行for(Methodmethod:type.getMethods()){/**for循环代码,MP判断是否method方法为mybatis注解方法如@Select@Insert**/parseStatement(method);InterceptorIgnoreHelper.initSqlParserInfoCache(缓存,mapperName,方法);SqlParserHelper.initSqlParserInfoCache(mapperName,方法);}/**这两行代码,MP注入默认方法列表**/if(GlobalConfigUtils.isSupperMapperChildren(configuration,type)){GlobalConfigUtils.getSqlInjector(configuration).inspectInject(助手,类型);}//...省略几行}@OverridepublicvoidinspectInject(MapperBuilderAssistantbuilderAssistant,ClassmapperClass){ClassmodelClass=extractModelC拉斯(映射器类);//...省略几行ListmethodList=this.getMethodList(mapperClass);TableInfotableInfo=TableInfoHelper.initTableInfo(builderAssistant,modelClass);//循环注入自定义方法methodList.forEach(m->m.inject(builderAssistant,mapperClass,modelClass,tableInfo));mapperRegistryCache.add(className);}}publicclassDefaultSqlInjectorextendsAbstractSqlInjector{@OverridepublicListgetMethodList(ClassmapperClass){返回流。of(newInsert(),//...省略几行newSelectPage()).collect(toList());}}在MybatisMapperAnnotationBuilder中,MP实际上是将框架自定义的动态SQL语句注册到Mybatis引擎中,而不是AbstractMethod实现具体方法的SQL语句构造。具体AbstractMethod实例类,以SelectById类为例构造具体方法SQL语句/***根据ID查询一条数据*/publicclassSelectByIdextendsAbstractMethod{@OverridepublicMappedStatementinjectMappedStatement(ClassmapperClass,ClassmodelClass,TableInfotableInfo){/**定义mybatisxml方法id,对应**/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);/**将xml方法方法添加到mybatis的MappedStatement中**/returnthis.addSelectMappedStatementForTable(mapperClass,getMethod(sqlMethod),sqlSource,tableInfo);}}至此,MP在启动时完成了加载自定义方法xml配置的过程,接下来是mybatis${variable}#{variable}的动态替换预编译,进入了mybatis自身的功能。综上所述,MP总共对mybatis的十多个类进行了改写和替换,主要如下图所示:总体来说,MP实现了对mybatis的增强,手段略显繁琐,不够直观。其实自定义方法的xml文件就是根据MybatisMapperAnnotationBuilder构建的。要将其转化为MybatisResource资源,只能继承并重写一个Mybatis类:SqlSessionFactoryBean例如:publicclassYourSqlSessionFactoryBeanextendsSqlSessionFactoryBeanimplementsApplicationContextAware{privateResource[]mapperLocations;@OverridepublicvoidsetMapperLocations(Resourcesmapper...).setMapperLocations(mapperLocations);/**暂存mybatis原生定义的mapperxml文件路径**/this.mapperLocations=mapperLocations;}/***{@inheritDoc}*/@OverridepublicvoidafterPropertiesSet()throwsException{ConfigurableListableBeanFactorybeanFactory=getBeanFactory();/**只需要在xml资源中注入自定义方法,在mybatis中注入原生定义的Resource,就可以实现MP的自定义动态SQL和原生SQL的共生关系**/this.setMapperLocations(InjectMapper.getMapperResource(this.dbType,beanFactory,this.mapperLocations));超级后rPropertiesSet();}}在这篇文章中,我简单介绍了MP实现动态语句的实现过程,并给出了一种可能更方便的方法。近期热点文章推荐:1.1,000+Java面试题及答案(2021最新版)2.别再满脑子if/else了,试试策略模式,真香!!3.操!Java中xx≠null的新语法是什么?4、SpringBoot2.6正式发布,一大波新特性。.5.《Java开发手册(嵩山版)》最新发布,赶快下载吧!感觉不错,别忘了点赞+转发!