Mybatis 作為國內開發中常用到的半自動 orm 框架,相信大家都很熟悉,它提供了簡單靈活的xml映射配置,方便開發人員編寫簡單、複雜SQL,在國內互聯網公司使用眾多。 本文針對筆者日常開發中對 Mybatis 占位符 #{} 和 ${} 使用時機結合源碼,思考總結而來 Mybatis 版本 3 ...
Mybatis 作為國內開發中常用到的半自動 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類