源码解读Mybatis占位符#和$的区别
wayn111 人气:0Mybatis 作为国内开发中常用到的半自动 orm 框架,相信大家都很熟悉,它提供了简单灵活的xml映射配置,方便开发人员编写简单、复杂SQL,在国内互联网公司使用众多。
本文针对笔者日常开发中对 Mybatis
占位符 #{}
和 ${}
使用时机结合源码,思考总结而来
Mybatis
版本 3.5.11Spring boot
版本 3.0.2mybatis-spring
版本 3.0.1- github地址:https://github.com/wayn111 欢迎大家关注,点个star
一. 启动时,mybatis-spring 解析xml文件流程图
Spring
项目启动时,mybatis-spring
自动初始化解析xml文件核心流程
Mybatis
在 buildSqlSessionFactory()
会遍历所有 mapperLocations(xml文件)
调用 xmlMapperBuilder.parse()
解析,源码如下
在 parse() 方法中, Mybatis
通过 configurationElement(parser.evalNode("/mapper"))
方法解析xml文件中的各个标签
public class XMLMapperBuilder extends BaseBuilder { ... private final MapperBuilderAssistant builderAssistant; private final Map<String, XNode> sqlFragments; ... public void parse() { if (!configuration.isResourceLoaded(resource)) { // xml文件解析逻辑 configurationElement(parser.evalNode("/mapper")); configuration.addLoadedResource(resource); bindMapperForNamespace(); } parsePendingResultMaps(); parsePendingCacheRefs(); parsePendingStatements(); } private void configurationElement(XNode context) { try { // 解析xml文件内的namespace、cache-ref、cache、parameterMap、resultMap、sql、select、insert、update、delete等各种标签 String namespace = context.getStringAttribute("namespace"); if (namespace == null || namespace.isEmpty()) { throw new BuilderException("Mapper's namespace cannot be empty"); } builderAssistant.setCurrentNamespace(namespace); cacheRefElement(context.evalNode("cache-ref")); cacheElement(context.evalNode("cache")); parameterMapElement(context.evalNodes("/mapper/parameterMap")); resultMapElements(context.evalNodes("/mapper/resultMap")); sqlElement(context.evalNodes("/mapper/sql")); buildStatementFromContext(context.evalNodes("select|insert|update|delete")); } catch (Exception e) { throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e); } } }
最后会把 namespace、cache-ref、cache、parameterMap、resultMap、select、insert、update、delete
等标签内容解析结果放到 builderAssistant 对象中,将sql标签解析结果放到sqlFragments对象中,其中 由于 builderAssistant 对象会保存select、insert、update、delete
标签内容解析结果我们对 builderAssistant 对象进行深入了解
public class MapperBuilderAssistant extends BaseBuilder { ... } public abstract class BaseBuilder { protected final Configuration configuration; ... } public class Configuration { ... protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection") .conflictMessageProducer((savedValue, targetValue) -> ". please check " + savedValue.getResource() + " and " + targetValue.getResource()); protected final Map<String, Cache> caches = new StrictMap<>("Caches collection"); protected final Map<String, ResultMap> resultMaps = new StrictMap<>("Result Maps collection"); protected final Map<String, ParameterMap> parameterMaps = new StrictMap<>("Parameter Maps collection"); protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<>("Key Generators collection"); protected final Set<String> loadedResources = new HashSet<>(); protected final Map<String, XNode> sqlFragments = new StrictMap<>("XML fragments parsed from previous mappers"); ... }
builderAssistant 对象继承至 BaseBuilder,BaseBuilder 类中包含一个 configuration 对象属性, configuration 对象中会保存xml文件标签解析结果至自身对应属性mappedStatements、caches、resultMaps、sqlFragments
。
这里有个问题上面提到的sql标签结果会放到 XMLMapperBuilder 类的 sqlFragments 对象中,为什么 Configuration 类中也有个 sqlFragments 属性?
这里回看上文 buildSqlSessionFactory()
方法最后
原来 XMLMapperBuilder 类中的 sqlFragments 属性就来自Configuration类
加载全部内容