作者 | 稻草江南

来源 | https://juejin.cn/post/6883081187103866894

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

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

入口类:MybatisSqlSessionFactoryBuilder

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

public class MybatisSqlSessionFactoryBuilder extends SqlSessionFactoryBuilder {public SqlSessionFactory build(Configuration configuration) {// ... 省略若干行if (globalConfig.isEnableSqlRunner()) {new SqlRunnerInjector().inject(configuration);}// ... 省略若干行return sqlSessionFactory;}
}

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

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

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

如果您正在学习Spring Boot,那么推荐一个连载多年还在继续更新的免费教程:http://blog.didispace.com/spring-boot-learning-2x/

MybatisConfiguration类

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

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

其中有3个重载方法addMapper实现了注册MP动态脚本的功能。推荐一个 Spring Boot 基础教程及实战示例:https://github.com/javastacks/spring-boot-best-practice

public class MybatisConfiguration extends Configuration {/*** Mapper 注册*/protected final MybatisMapperRegistry mybatisMapperRegistry = new MybatisMapperRegistry(this);// ..../*** 初始化调用*/public MybatisConfiguration() {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>*/@Overridepublic void addMappedStatement(MappedStatement ms) {// ...}// ... 省略若干行/*** 使用自己的 MybatisMapperRegistry*/@Overridepublic <T> void addMapper(Class<T> type) {mybatisMapperRegistry.addMapper(type);}// .... 省略若干行
}

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

public class MybatisMapperRegistry extends MapperRegistry {@Overridepublic <T> void addMapper(Class<T> type) {// ... 省略若干行MybatisMapperAnnotationBuilder parser = new MybatisMapperAnnotationBuilder(config, type);parser.parse();// ... 省略若干行}
}

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

MybatisMapperAnnotationBuilder动态构造

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

public class MybatisMapperAnnotationBuilder extends MapperAnnotationBuilder {@Overridepublic void parse() {//... 省略若干行for (Method method : 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);}//... 省略若干行}@Overridepublic void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {Class<?> modelClass = extractModelClass(mapperClass);//... 省略若干行List<AbstractMethod> methodList = this.getMethodList(mapperClass);TableInfo tableInfo = TableInfoHelper.initTableInfo(builderAssistant, modelClass);// 循环注入自定义方法methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo));mapperRegistryCache.add(className);}
}
public class DefaultSqlInjector extends AbstractSqlInjector {@Overridepublic List<AbstractMethod> getMethodList(Class<?> mapperClass) {return Stream.of(new Insert(),//... 省略若干行new SelectPage()).collect(toList());}
}

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

如果您正在学习Spring Cloud,推荐一个经典教程(含Spring Cloud Alibaba):https://blog.didispace.com/spring-cloud-learning/

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

以 SelectById 这个类为例说明下

/*** 根据ID 查询一条数据*/
public class SelectById extends AbstractMethod {@Overridepublic MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {/** 定义 mybatis xml method id, 对应 <id="xyz"> **/SqlMethod sqlMethod = SqlMethod.SELECT_BY_ID;/** 构造id对应的具体xml片段 **/SqlSource sqlSource = new RawSqlSource(configuration, String.format(sqlMethod.getSql(),sqlSelectColumns(tableInfo, false),tableInfo.getTableName(), tableInfo.getKeyColumn(), tableInfo.getKeyProperty(),tableInfo.getLogicDeleteSql(true, true)), Object.class);/** 将xml method方法添加到mybatis的MappedStatement中 **/return this.addSelectMappedStatementForTable(mapperClass, getMethod(sqlMethod), sqlSource, tableInfo);}
}

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

总结一下

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

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

public class YourSqlSessionFactoryBean extends SqlSessionFactoryBean implements ApplicationContextAware {private Resource[] mapperLocations;@Overridepublic void setMapperLocations(Resource... mapperLocations) {super.setMapperLocations(mapperLocations);/** 暂存使用mybatis原生定义的mapper xml文件路径**/this.mapperLocations = mapperLocations;}/*** {@inheritDoc}*/@Overridepublic void afterPropertiesSet() throws Exception {ConfigurableListableBeanFactory beanFactory = getBeanFactory();/** 只需要通过将自定义的方法构造成xml resource和原生定义的Resource一起注入到mybatis中即可, 这样就可以实现MP的自定义动态SQL和原生SQL的共生关系**/this.setMapperLocations(InjectMapper.getMapperResource(this.dbType, beanFactory, this.mapperLocations));super.afterPropertiesSet();}
}

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

往期推荐

IDEA 2021.3 正式发布:支持远程开发、故障排查、Java和Spring等多项优化改进

如何防止你的 jar 被反编译?

Jetbrains推出新一代编辑器:Fleet,网友:VS Code迎来劲敌JB Code!

免费开源、功能完善、暗黑风格,你会拒绝这款SSH工具吗?

那个当上非洲酋长的交大才子,如今怎么样了?

技术交流群

最近有很多人问,有没有读者交流群,想知道怎么加入。加入方式很简单,有兴趣的同学,只需要点击下方卡片,回复“加群“,即可免费加入我们的高质量技术交流群!

点击阅读原文,送你免费Spring Boot教程!

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

  1. MyBatis学习 之 三、动态SQL语句

    2019独角兽企业重金招聘Python工程师标准>>> 有些时候,sql语句where条件中,需要一些安全判断,例如按某一条件查询时如果传入的参数是空,此时查询出的结果很可能是空的, ...

  2. Mybatis注解-注解方式的动态SQL语句

    基于手写SQL语句的注解CRUD 把手写SQL语句的注解CRUD,修改为动态SQL语句方式. CategoryDynaSqlProvider 新增CategoryDynaSqlProvider,提供C ...

  3. MyBatis使用@Select注解写动态SQL语句

    这里有三张表,teacher,organization,sysuser 目的:使用分页查询三表联查,并且对username的模糊查询,和对tecauditstate进行判断查询 teacher表字段( ...

  4. 动态SQL语句--mybatis

    转自:http://www.blogjava.net/Steven-bot/articles/363460.html 动态SQL语句--mybatis 动态SQL语句         有些时候,sql ...

  5. Mybatis映射文件动态SQL语句-01

    因为在很多业务逻辑复杂的项目中,往往不是简单的sql语句就能查询出来自己想要的数据,所有mybatis引入了动态sql语句, UserMapper.xml <?xml version=" ...

  6. MyBatis——动态SQL语句——if标签和where标签复合使用

    功能需求 根据性别和名字查询用户 官方文档 MyBatis--动态 SQL SQL语句 SELECT id, username, birthday, sex, address FROM `user` ...

  7. Mybatis 动态Sql语句《常用》

    MyBatis 的强大特性之一便是它的动态 SQL.如果你有使用 JDBC 或其他类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句有多么痛苦.拼接的时候要确保不能忘了必要的空格,还要注意省掉 ...

  8. 【转】mybatis实战教程(mybatis in action)之八:mybatis 动态sql语句

    转自:除非申明,文章均为一号门原创,转载请注明本文地址,谢谢! 转载地址:http://blog.csdn.net/kutejava/article/details/9164353#t5 1. if ...

  9. mybatis动态SQL语句

    三.动态SQL语句 有些时候,sql语句where条件中,需要一些安全判断,例如按性别检索,如果传入的参数是空的,此时查询出的结果很可能是空的,也许我们需要参数为空时,是查出全部的信息.这是我们可以使 ...

最新文章

  1. 关于CSS样式浏览器兼容问题的一些注意事项
  2. JavaScript 闭包解析
  3. linux下查找文件及内容 grep
  4. K-means算法Java实现
  5. 云端TensorFlow读取数据IO的高效方式
  6. ai中如何插入签名_如何在PDF中插入一个或多个空白页?
  7. 8051系列单片机汇编语言指令速查表(汇编语言的111条指
  8. Verilog定义计算位宽的函数clogb2
  9. 【ArcGIS风暴】ArcGIS创建栅格数据集色彩映射表案例--以GlobeLand30土地覆盖数据为例
  10. 服务器不稳定 如何让百度重新收录网站,教你如何让百度重新收录首页
  11. 视频盒子APP视频播放源代码安卓+IOS双端源码
  12. rabbitmq实战_RabbitMQ实战(四) - RabbitMQ amp; Spring整合开发
  13. CCF201809-4 再卖菜
  14. [Java] 蓝桥杯 BASIC-2 基础练习 01字串
  15. dom控制html元素编号,JavaScript DOM对象控制HTML元素详解
  16. 简易的Python小游戏
  17. swf导出html代码,将SWF轻松导出HTML5
  18. android如何用真机测试,android studio如何使用真机测试app
  19. 怎么在电脑中更新statsmodels.stats. diagnostic
  20. <2021SC@SDUSC>博客(9)山东大学软件工程应用与实践Jpress代码分析(8)

热门文章

  1. 【原】为DevExpress的ChartControl添加Y轴控制 和 GridControl中指定列添加超级链接
  2. SQL 2014新特性- Delayed durability
  3. 学习鸟哥的Linux私房菜笔记(13)——用户管理
  4. JDK1.6安装与环境变量设置详细图解
  5. ADempiere TableDirect类型的reference分析
  6. 树形控件CTreeCtrl的使用详解(一)
  7. python 3.10 新增 switch-case 简介
  8. golang 字符串 去除最后一个字符
  9. photoshop ps 拉长裙子 方法
  10. python3 pyinstaller 打包后执行文件运行错误 No such file or directory 和 Cannot load native module 解决方法