Mybatis占位符#和$的区别?源码解读
本文针对笔者日常开发中对 MyBatis 占位符 #{} 和 ${} 使用时机结合源码,思考总结而来
- • Mybatis 版本 3.5.11
- • Spring boot 版本 3.0.2
- • mybatis-spring 版本 3.0.1
- • Github地址:https://github.com/wayn111,欢迎大家关注,点个star

文章插图
流程图
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类 。
回到主题,在 buildStatementFromContext(context.evalNodes("select|insert|update|delete")) 方法中会通过如下调用 。
buildStatementFromContext(List<XNode> list, String requiredDatabaseId) -> parseStatementNode()-> createSqlSource(Configuration configuration, XNode script, Class<?> parameterType)-> parseScriptNode()-> parseDynamicTags(context)
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Linux上使用Docker实现应用程序打包和分发
- 打造高质量Web应用程序:React 和 Vue 框架对比和实践经验总结
- 如何使用 Python 和 python-docx 库读取、写入和操作 Word 文件
- 养老金又有新消息!
- 个人养老金制度出炉!我们该如何解读和面对?
- 城市退休职工和农民养老金差距大,原因何在?
- 泰国椰青和海南椰青的区别 泰国椰青和海南椰青的区别在哪
- 蚕宝宝怎么养视频教程 蚕宝宝的养殖方法和注意事项
- 刘家昌发长文控诉前妻和儿子,每天找律师争夺财产,丝毫没有人性
- 新西兰属于哪个洲,澳大利亚和新西兰属于哪个洲