勿以浮沙筑高台
config解析过程见文章:
Mybatis源码解析(一)newconfiguration构造过程
mapper-resultmap解析过程见文章:
Mybatis源码解析(二)mapper-resultmap详解构造过程
private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
if (namespace != null && !namespace.equals("")) {
this.builderAssistant.setCurrentNamespace(namespace);
this.cacheRefElement(context.evalNode("cache-ref"));
this.cacheElement(context.evalNode("cache"));
this.parameterMapElement(context.evalNodes("/mapper/parameterMap"));
this.resultMapElements(context.evalNodes("/mapper/resultMap"));
// 链接:AH-01
this.sqlElement(context.evalNodes("/mapper/sql"));
// 链接:AH-04
this.buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} else {
throw new BuilderException("Mapper's namespace cannot be empty");
}
} catch (Exception var3) {
throw new BuilderException("Error parsing Mapper XML. The XML location is '" + this.resource + "'. Cause: " + var3, var3);
}
}
AH-01:org.apache.ibatis.builder.xml.XMLMapperBuilder#sqlElement(java.util.List<org.apache.ibatis.parsing.XNode>)
private void sqlElement(List<XNode> list) {
//判断配置文件中的databaseid不为0
if (this.configuration.getDatabaseId() != null) {
//链接AH-02
this.sqlElement(list, this.configuration.getDatabaseId());
}
//链接AH-02
//不同的是第二个参数为0
//存入了2次,第一次是唯一标识的,第二次为id的
this.sqlElement(list, (String)null);
//return AH-01
}
AH-02:org.apache.ibatis.builder.xml.XMLMapperBuilder#sqlElement(java.util.List<org.apache.ibatis.parsing.XNode>, java.lang.String)
//<sql id="BaseColumns">
// `user_name`, `user_birthday`, `user_gender`, `user_state`, `user_height`, `user_describe`
// </sql>
//这里的list传入的多个sql节点信息
private void sqlElement(List<XNode> list, String requiredDatabaseId) {
Iterator var3 = list.iterator();
while(var3.hasNext()) {
XNode context = (XNode)var3.next();
//获取id和databaseid的属性
String databaseId = context.getStringAttribute("databaseId");
String id = context.getStringAttribute("id");
//给当前sql节点一个唯一的标识,id=namespace+"."+id
id = this.builderAssistant.applyCurrentNamespace(id, false);
//检查当前beaseid和requiredDatabaseId是否相同
//链接:AH-03
if (this.databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) {
//将当前唯一的ID和xnode进行映射<id,xnode>放入sqlFragments集合
this.sqlFragments.put(id, context);
//return AH-02
}
}
}
AH-03:org.apache.ibatis.builder.xml.XMLMapperBuilder#databaseIdMatchesCurrent
private boolean databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId) {
if (requiredDatabaseId != null) {
//当不一致时返回false
if (!requiredDatabaseId.equals(databaseId)) {
return false;
}
} else {
//当为空时返回false
if (databaseId != null) {
return false;
}
//这里主要是判断当前id是否重复,是的话返回false报错
//如果我们缓存的对象里包含了安全id
if (this.sqlFragments.containsKey(id)) {
//直接将Xnode节点拿出来
XNode context = (XNode)this.sqlFragments.get(id);
//判断xnode节点不等于null
if (context.getStringAttribute("databaseId") != null) {
return false;
}
}
}
//return AH-03
return true;
}
上面完成了唯一标识id和xnode的缓存构建
AH-04:org.apache.ibatis.builder.xml.XMLMapperBuilder#buildStatementFromContext(java.util.List<org.apache.ibatis.parsing.XNode>)
private void buildStatementFromContext(List<XNode> list) {
if (this.configuration.getDatabaseId() != null) {
//链接:AH-05
this.buildStatementFromContext(list, this.configuration.getDatabaseId());
}
this.buildStatementFromContext(list, (String)null);
}
AH-05:org.apache.ibatis.builder.xml.XMLMapperBuilder#buildStatementFromContext(java.util.List<org.apache.ibatis.parsing.XNode>)
//list=select|insert|update|delete
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
//遍历xnode节点
Iterator var3 = list.iterator();
while(var3.hasNext()) {
XNode context = (XNode)var3.next();
//创建解析类statementParser
XMLStatementBuilder statementParser = new XMLStatementBuilder(this.configuration, this.builderAssistant, context, requiredDatabaseId);
try {
//开始解析statem节点
//链接:parseStatementNode
statementParser.parseStatementNode();
} catch (IncompleteElementException var7) {
//如果解析失败放入不完整的节点缓存当中
this.configuration.addIncompleteStatement(statementParser);
}
}
}
解析SQL语句
parseStatementNode:org.apache.ibatis.builder.xml.XMLStatementBuilder#parseStatementNode
public void parseStatementNode() {
//获取当前statement类中的id和databaseid
String id = this.context.getStringAttribute("id");
String databaseId = this.context.getStringAttribute("databaseId");
//判断是否一致
if (this.databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
//获取节点的各种属性
Integer fetchSize = this.context.getIntAttribute("fetchSize");
Integer timeout = this.context.getIntAttribute("timeout");
String parameterMap = this.context.getStringAttribute("parameterMap");
String parameterType = this.context.getStringAttribute("parameterType");
Class<?> parameterTypeClass = this.resolveClass(parameterType);
String resultMap = this.context.getStringAttribute("resultMap");
String resultType = this.context.getStringAttribute("resultType");
String lang = this.context.getStringAttribute("lang");
LanguageDriver langDriver = this.getLanguageDriver(lang);
//通过别名解析resultype对应的类型
Class<?> resultTypeClass = this.resolveClass(resultType);
String resultSetType = this.context.getStringAttribute("resultSetType");
//解析当前statementtype的类型
StatementType statementType = StatementType.valueOf(this.context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
//根据节点名称解析 sqlcommandtype类型
ResultSetType resultSetTypeEnum = this.resolveResultSetType(resultSetType);
String nodeName = this.context.getNode().getNodeName();
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
//查询节点,为
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
boolean flushCache = this.context.getBooleanAttribute("flushCache", !isSelect);
boolean useCache = this.context.getBooleanAttribute("useCache", isSelect);
boolean resultOrdered = this.context.getBooleanAttribute("resultOrdered", false);
//解析<include>节点
// <sql id="BaseColumns"> `user_name`, `user_birthday`, `user_gender`, `user_state`, `user_height`, `user_describe`
// </sql>
// <insert id="insertUser" parameterType="User">INSERT INTO `t_user`(<include refid="BaseColumns" />) VALUES (#{userName}, #{userBirthday}, #{userGender}, #{userState}, #{userHeight},#{userDescribe})
//</insert>
//哪一段有include的标签会将标签的内容替换为sqlid的内容进行编译
//将构建助手和配置文件保留下来
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(this.configuration, this.builderAssistant);
//递归查询
//链接:AH-06
includeParser.applyIncludes(this.context.getNode());
//解析<selectkey>节点
this.processSelectKeyNodes(id, parameterTypeClass, langDriver);
//解析sql语句
//链接:AH-10
SqlSource sqlSource = langDriver.createSqlSource(this.configuration, this.context, parameterTypeClass);
String resultSets = this.context.getStringAttribute("resultSets");
String keyProperty = this.context.getStringAttribute("keyProperty");
String keyColumn = this.context.getStringAttribute("keyColumn");
String keyStatementId = id + "!selectKey";
keyStatementId = this.builderAssistant.applyCurrentNamespace(keyStatementId, true);
Object keyGenerator;
if (this.configuration.hasKeyGenerator(keyStatementId)) {
keyGenerator = this.configuration.getKeyGenerator(keyStatementId);
} else {
keyGenerator = this.context.getBooleanAttribute("useGeneratedKeys", this.configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType)) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
}
//将解析好的包装类对象放进configuration的mappedStatements
//链接:AH-08
this.builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, (KeyGenerator)keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
//return AH-06
}
AH-06:org.apache.ibatis.builder.xml.XMLIncludeTransformer#applyIncludes(org.w3c.dom.Node)
public void applyIncludes(Node source) {
//构建一个属性集合
Properties variablesContext = new Properties();
//从配置文件中将额外的属性集合拿出来
Properties configurationVariables = this.configuration.getVariables();
if (configurationVariables != null) {
//如果不等于空,则将所有集合添加进构建的属性集合中
//主要是链接数据库相关的属性
//url
//username
//password
//driverclass
variablesContext.putAll(configurationVariables);
}
//链接:AH-07
this.applyIncludes(source, variablesContext, false);
//return AH-06
}
AH-07:org.apache.ibatis.builder.xml.XMLIncludeTransformer#applyIncludes(org.w3c.dom.Node, java.util.Properties, boolean)
private void applyIncludes(Node source, Properties variablesContext, boolean included) {
//判断是不是include标签
if (source.getNodeName().equals("include")) {
//找出refid标签内的上下文对象
Node toInclude = this.findSqlFragment(this.getStringAttribute(source, "refid"), variablesContext);
//得到一个上下文文档对象
Properties toIncludeContext = this.getVariablesContext(source, variablesContext);
this.applyIncludes(toInclude, toIncludeContext, true);
//只要2个上下文对象不等
if (toInclude.getOwnerDocument() != source.getOwnerDocument()) {
//则将当前节点node引用属性设置为true
toInclude = source.getOwnerDocument().importNode(toInclude, true);
}
//将include子节点替换为true
source.getParentNode().replaceChild(toInclude, source);
//将每个子节点最前插入当前节点
while(toInclude.hasChildNodes()) {
toInclude.getParentNode().insertBefore(toInclude.getFirstChild(), toInclude);
}
toInclude.getParentNode().removeChild(toInclude);
//判断是不是元素(select,insert......)标签
} else if (source.getNodeType() == 1) {
int i;
if (included && !variablesContext.isEmpty()) {
NamedNodeMap attributes = source.getAttributes();
//setvalue值
for(i = 0; i < attributes.getLength(); ++i) {
Node attr = attributes.item(i);
attr.setNodeValue(PropertyParser.parse(attr.getNodeValue(), variablesContext));
}
}
//拿到当前节点下的子节点
NodeList children = source.getChildNodes();
//遍历一次将key和value值放进去
//key=标签属性
//value=body内容
for(i = 0; i < children.getLength(); ++i) {
//递归
//链接:AH-07
this.applyIncludes(children.item(i), variablesContext, included);
}
} else if (included && source.getNodeType() == 3 && !variablesContext.isEmpty()) {
source.setNodeValue(PropertyParser.parse(source.getNodeValue(), variablesContext));
}
// return AH-07
}
上面解析SQL语句内的include标签
AH-08:org.apache.ibatis.builder.MapperBuilderAssistant#addMappedStatement(java.lang.String, org.apache.ibatis.mapping.SqlSource, org.apache.ibatis.mapping.StatementType, org.apache.ibatis.mapping.SqlCommandType, java.lang.Integer, java.lang.Integer, java.lang.String, java.lang.Class<?>, java.lang.String, java.lang.Class<?>, org.apache.ibatis.mapping.ResultSetType, boolean, boolean, boolean, org.apache.ibatis.executor.keygen.KeyGenerator, java.lang.String, java.lang.String, java.lang.String, org.apache.ibatis.scripting.LanguageDriver, java.lang.String)
//将之前生成的包装类对象都传进来
//source=sql语句+参数映射+配置中心
//sqlcommandsql=执行命令类型
public MappedStatement addMappedStatement(String id, SqlSource sqlSource, StatementType statementType, SqlCommandType sqlCommandType, Integer fetchSize, Integer timeout, String parameterMap, Class<?> parameterType, String resultMap, Class<?> resultType, ResultSetType resultSetType, boolean flushCache, boolean useCache, boolean resultOrdered, KeyGenerator keyGenerator, String keyProperty, String keyColumn, String databaseId, LanguageDriver lang, String resultSets) {
if (this.unresolvedCacheRef) {
throw new IncompleteElementException("Cache-ref not yet resolved");
} else {
id = this.applyCurrentNamespace(id, false);
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
org.apache.ibatis.mapping.MappedStatement.Builder statementBuilder = (new org.apache.ibatis.mapping.MappedStatement.Builder(this.configuration, id, sqlSource, sqlCommandType)).resource(this.resource).fetchSize(fetchSize).timeout(timeout).statementType(statementType).keyGenerator(keyGenerator).keyProperty(keyProperty).keyColumn(keyColumn).databaseId(databaseId).lang(lang).resultOrdered(resultOrdered).resultSets(resultSets).resultMaps(
//链接:AH-09
this.getStatementResultMaps(resultMap, resultType, id)).resultSetType(resultSetType).flushCacheRequired((Boolean)this.valueOrDefault(flushCache, !isSelect)).useCache((Boolean)this.valueOrDefault(useCache, isSelect)).cache(this.currentCache);
ParameterMap statementParameterMap = this.getStatementParameterMap(parameterMap, parameterType, id);
if (statementParameterMap != null) {
statementBuilder.parameterMap(statementParameterMap);
}
//通过build方法创建statement方法
MappedStatement statement = statementBuilder.build();
//创建好将statement对象存储到configuration对象当中
//ID为key,值为value
this.configuration.addMappedStatement(statement);
return statement;
}
}
AH-09:org.apache.ibatis.builder.MapperBuilderAssistant#getStatementResultMaps
//resultMap=缓存里唯一resultmap的Id
private List<ResultMap> getStatementResultMaps(String resultMap, Class<?> resultType, String statementId) {
//通过resultmap唯一的ID拿到resultmap对象
resultMap = this.applyCurrentNamespace(resultMap, true);
List<ResultMap> resultMaps = new ArrayList();
if (resultMap != null) {
//对name进行分割
String[] resultMapNames = resultMap.split(",");
String[] var6 = resultMapNames;
int var7 = resultMapNames.length;
//遍历name数组
for(int var8 = 0; var8 < var7; ++var8) {
String resultMapName = var6[var8];
try {
//去掉空格将文件添加进集合
resultMaps.add(this.configuration.getResultMap(resultMapName.trim()));
} catch (IllegalArgumentException var11) {
throw new IncompleteElementException("Could not find result map " + resultMapName, var11);
}
}
} else if (resultType != null) {
ResultMap inlineResultMap = (new org.apache.ibatis.mapping.ResultMap.Builder(this.configuration, statementId + "-Inline", resultType, new ArrayList(), (Boolean)null)).build();
resultMaps.add(inlineResultMap);
}
//返回一个resultmap的映射集合
return resultMaps;
//return AH-09
}
AH-10:构建SQLorg.apache.ibatis.scripting.xmltags.XMLLanguageDriver#createSqlSource(org.apache.ibatis.session.Configuration, org.apache.ibatis.parsing.XNode, java.lang.Class<?>)
//构建SQL
//配置中心configuration
//参数类型parameterType
public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
//构建一个脚本解析器
//链接:AH-11
XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
//开始解析SQL语句
//链接:AH-12
return builder.parseScriptNode();
//return AH-10
}
AH-11:org.apache.ibatis.scripting.xmltags.XMLScriptBuilder#XMLScriptBuilder(org.apache.ibatis.session.Configuration, org.apache.ibatis.parsing.XNode, java.lang.Class<?>)
//构建节点handelmapper
public XMLScriptBuilder(Configuration configuration, XNode context, Class<?> parameterType) {
//重新刷新configuration
super(configuration);
this.nodeHandlerMap = new HashMap();
this.context = context;
this.parameterType = parameterType;
//构建解析器
this.initNodeHandlerMap();
//return AH-11
}
//构建解析器
private void initNodeHandlerMap() {
this.nodeHandlerMap.put("trim", new XMLScriptBuilder.TrimHandler());
this.nodeHandlerMap.put("where", new XMLScriptBuilder.WhereHandler());
this.nodeHandlerMap.put("set", new XMLScriptBuilder.SetHandler());
this.nodeHandlerMap.put("foreach", new XMLScriptBuilder.ForEachHandler());
this.nodeHandlerMap.put("if", new XMLScriptBuilder.IfHandler());
this.nodeHandlerMap.put("choose", new XMLScriptBuilder.ChooseHandler());
this.nodeHandlerMap.put("when", new XMLScriptBuilder.IfHandler());
this.nodeHandlerMap.put("otherwise", new XMLScriptBuilder.OtherwiseHandler());
this.nodeHandlerMap.put("bind", new XMLScriptBuilder.BindHandler());
}
AH-12:org.apache.ibatis.scripting.xmltags.XMLScriptBuilder#parseScriptNode
public SqlSource parseScriptNode() {
//解析静态标签
//链接:AH-13
MixedSqlNode rootSqlNode = this.parseDynamicTags(this.context);
SqlSource sqlSource = null;
//判断是不是动态节点
if (this.isDynamic) {
sqlSource = new DynamicSqlSource(this.configuration, rootSqlNode);
} else {
//链接:AH-17
sqlSource = new RawSqlSource(this.configuration, rootSqlNode, this.parameterType);
}
//return AH-12
return (SqlSource)sqlSource;
}
AH-13:org.apache.ibatis.scripting.xmltags.XMLScriptBuilder#parseDynamicTags
//这里的xnode就是insert语句
protected MixedSqlNode parseDynamicTags(XNode node) {
//创建了一个sqlnode的集合
/*sqlnode数据结构:
public interface SqlNode {
//代表应用动态的上下文
//DynamicContext 中有append,bind,getsql等基础方法
boolean apply(DynamicContext var1);
}
*/
List<SqlNode> contents = new ArrayList();
//得到一个子节点集合
NodeList children = node.getNode().getChildNodes();
for(int i = 0; i < children.getLength(); ++i) {
//newXNode将我们的子节点转换为一个xnode对象。
XNode child = node.newXNode(children.item(i));
String nodeName;
//判断当前节点是不是文本节点
if (child.getNode().getNodeType() != 4 && child.getNode().getNodeType() != 3) {
if (child.getNode().getNodeType() == 1) {
//拿到节点的名称
nodeName = child.getNode().getNodeName();
//通过节点的名称构建一个handler处理对象
XMLScriptBuilder.NodeHandler handler = (XMLScriptBuilder.NodeHandler)this.nodeHandlerMap.get(nodeName);
if (handler == null) {
throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
}
handler.handleNode(child, contents);
this.isDynamic = true;
}
} else {
//获取node名字
nodeName = child.getStringBody("");
//构建一个文本节点
TextSqlNode textSqlNode = new TextSqlNode(nodeName);
//判断当前文本节点是否是动态节点
//怎么样才是动态节点呢?
//链接:AH-14
if (textSqlNode.isDynamic()) {
contents.add(textSqlNode);
this.isDynamic = true;
} else {
contents.add(new StaticTextSqlNode(nodeName));
}
}
}
return new MixedSqlNode(contents);
}
AH-14:org.apache.ibatis.scripting.xmltags.TextSqlNode#isDynamic
public boolean isDynamic() {
TextSqlNode.DynamicCheckerTokenParser checker = new TextSqlNode.DynamicCheckerTokenParser();
//链接:AH-15
GenericTokenParser parser = this.createParser(checker);
//通过构造的handler和开闭对象进行解析
//链接:AH-16
parser.parse(this.text);
//return AH-14
return checker.isDynamic();
}
AH-15:org.apache.ibatis.scripting.xmltags.TextSqlNode#createParser
private GenericTokenParser createParser(TokenHandler handler) {
//构建一个开{一个闭},一个处理器
return new GenericTokenParser("${", "}", handler);
//return AH-15
}
AH-16:判断是否是包含${和}
解读SQL语句
org.apache.ibatis.parsing.GenericTokenParser#parse
public String parse(String text) {
//这里面解析是否包含${},eg:${name}
//和拼接SQL语句
if (text != null && !text.isEmpty()) {
//判断有没有#{
int start = text.indexOf(this.openToken);
if (start == -1) {
return text;
} else {
//找到后将文本转为字符数组
char[] src = text.toCharArray();
int offset = 0;
//构建一个builde对象
StringBuilder builder = new StringBuilder();
//构建一个StringBuilder 解析器
//大于-1,因为没有找到就是-1
for(StringBuilder expression = null; start > -1; start = text.indexOf(this.openToken, offset)) {
//发现了他的开启open
if (start > 0 && src[start - 1] == '\\') {
builder.append(src, offset, start - offset - 1).append(this.openToken);
offset = start + this.openToken.length();
} else {
//构建一个拼接的对象
if (expression == null) {
expression = new StringBuilder();
} else {
expression.setLength(0);
}
//src是当前源数组的字符串
//offset=0
//意思是追加整个sql语句进去
builder.append(src, offset, start - offset);
//这是找的发现的第一个#号大括号的前面一节
//即这样的一节UPDATE `t_user` SET `user_name` = #{
//offset 等于开始长度+#{的偏移量
//start = text.indexOf(this.openToken);
offset = start + this.openToken.length();
//下面开始寻找关闭的
int end;
//end = text.indexOf(this.closeToken, offset)
//参数长度=结束位置-开始位置
for(end = text.indexOf(this.closeToken, offset); end > -1; end = text.indexOf(this.closeToken, offset)) {
if (end <= offset || src[end - 1] != '\\') {
expression.append(src, offset, end - offset);
int var10000 = end + this.closeToken.length();
break;
}
//将条件参数添加进去
expression.append(src, offset, end - offset - 1).append(this.closeToken);
//重新计算偏移量
offset = end + this.closeToken.length();
}
if (end == -1) {
builder.append(src, start, src.length - start);
offset = src.length;
} else {
//this.handler.handleToken
//这里追加问号,代表有一个表达式就应该有一个问号占位符、
//另外将expression参数名放进handel对象中进行保存
//链接:AH-22
builder.append(this.handler.handleToken(expression.toString()));
offset = end + this.closeToken.length();
}
}
}
if (offset < src.length) {
builder.append(src, offset, src.length - offset);
}
//return AH-16-A
return builder.toString();
}
} else {
return "";
}
}
AH-17:org.apache.ibatis.scripting.defaults.RawSqlSource#RawSqlSource(org.apache.ibatis.session.Configuration, java.lang.String, java.lang.Class<?>)
public RawSqlSource(Configuration configuration, SqlNode rootSqlNode, Class<?> parameterType) {
//在mapper中一行就是就mappernode节点
//这里的getSql方法会整合集合,拼接成一条完整的sql语句
//链接:AH-18
this(configuration, getSql(configuration, rootSqlNode), parameterType);
}
//开始构建SQL语句
public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) {
//注意这里的sql语句没有做任何的处理只是将内容解析出来了。意思还是包含占位符#{name}
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
//获取返回实体类型,不是参数类型,是实体类型
Class<?> clazz = parameterType == null ? Object.class : parameterType;
//开始用解析器对象,解析关系,组成参数,sql语句的对象
//链接:AH-20
this.sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap());
//return AH-17
}
AH-18:org.apache.ibatis.scripting.defaults.RawSqlSource#getSql
private static String getSql(Configuration configuration, SqlNode rootSqlNode) {
//构建一个动态的上下文对象
//链接:AH-19
DynamicContext context = new DynamicContext(configuration, (Object)null);
//将node追加进对象,拼接sql
rootSqlNode.apply(context);
//获得sql语句
return context.getSql();
//return AH-18
}
AH-19:org.apache.ibatis.scripting.xmltags.DynamicContext#DynamicContext
public DynamicContext(Configuration configuration, Object parameterObject) {
//判断参数是否为空
if (parameterObject != null && !(parameterObject instanceof Map)) {
//不为空将参数构建为一个元数据对象
//元数据可以构建类对象+值,就可以构建实例对象
MetaObject metaObject = configuration.newMetaObject(parameterObject);
//将元数据对象放入集合
this.bindings = new DynamicContext.ContextMap(metaObject);
} else {
//如果为空,直接创建一个上下文集合
this.bindings = new DynamicContext.ContextMap((MetaObject)null);
}
//将参数添加进缓存
this.bindings.put("_parameter", parameterObject);
//将baseid加入进去
this.bindings.put("_databaseId", configuration.getDatabaseId());
//return AH-19
}
AH-20:org.apache.ibatis.builder.SqlSourceBuilder#parse
public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
//构建参数的元数据对象
//链接:AH-21
SqlSourceBuilder.ParameterMappingTokenHandler handler = new SqlSourceBuilder.ParameterMappingTokenHandler(this.configuration, parameterType, additionalParameters);
//动态处理的对象
GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
//开始解读SQL,将参数放入进去
//链接:AH-16-A
String sql = parser.parse(originalSql);
//将sql语句,configuration对象和sql语句对应的参数绑定的集合放入一个对象当中
//return AH-20
return new StaticSqlSource(this.configuration, sql, handler.getParameterMappings());
}
AH-21:org.apache.ibatis.builder.SqlSourceBuilder.ParameterMappingTokenHandler#ParameterMappingTokenHandler
public ParameterMappingTokenHandler(Configuration configuration, Class<?> parameterType, Map<String, Object> additionalParameters) {
super(configuration);
//赋值返回参数的类型
this.parameterType = parameterType;
//将需要赋值的参数构建为元数据对象,需要时反射调用
this.metaParameters = configuration.newMetaObject(additionalParameters);
}
AH-22:org.apache.ibatis.builder.SqlSourceBuilder.ParameterMappingTokenHandler#handleToken
//返回?号占位符
public String handleToken(String content) {
//链接
this.parameterMappings.add(this.buildParameterMapping(content));
return "?";
//return AH-22
}
private ParameterMapping buildParameterMapping(String content) {
//构建一个参数的映射关系
Map<String, String> propertiesMap = this.parseParameterMapping(content);
//获取属性对象
String property = (String)propertiesMap.get("property");
Class propertyType;
//判断是否有getter方法
if (this.metaParameters.hasGetter(property)) {
//拿去参数的值
propertyType = this.metaParameters.getGetterType(property);
} else if (this.typeHandlerRegistry.hasTypeHandler(this.parameterType)) {
//复制参数类型
propertyType = this.parameterType;
} else if
(JdbcType.CURSOR.name().equals(propertiesMap.get("jdbcType"))) {
propertyType = ResultSet.class;
} else if (property != null && !Map.class.isAssignableFrom(this.parameterType)) {
MetaClass metaClass = MetaClass.forClass(this.parameterType, this.configuration.getReflectorFactory());
if (metaClass.hasGetter(property)) {
propertyType = metaClass.getGetterType(property);
} else {
propertyType = Object.class;
}
} else {
propertyType = Object.class;
}
//以上都是在确定参数的类型
//构建参数builder对象
Builder builder = new Builder(this.configuration, property, propertyType);
Class<?> javaType = propertyType;
String typeHandlerAlias = null;
Iterator var8 = propertiesMap.entrySet().iterator();
//遍历集合
//对build
while(var8.hasNext()) {
Entry<String, String> entry = (Entry)var8.next();
String name = (String)entry.getKey();
String value = (String)entry.getValue();
if ("javaType".equals(name)) {
javaType = this.resolveClass(value);
builder.javaType(javaType);
} else if ("jdbcType".equals(name)) {
builder.jdbcType(this.resolveJdbcType(value));
} else if ("mode".equals(name)) {
builder.mode(this.resolveParameterMode(value));
} else if ("numericScale".equals(name)) {
builder.numericScale(Integer.valueOf(value));
} else if ("resultMap".equals(name)) {
builder.resultMapId(value);
} else if ("typeHandler".equals(name)) {
typeHandlerAlias = value;
} else if ("jdbcTypeName".equals(name)) {
builder.jdbcTypeName(value);
} else if (!"property".equals(name)) {
if ("expression".equals(name)) {
throw new BuilderException("Expression based parameters are not supported yet");
}
throw new BuilderException("An invalid property '" + name + "' was found in mapping #{" + content + "}. Valid properties are " + "javaType,jdbcType,mode,numericScale,resultMap,typeHandler,jdbcTypeName");
}
}
if (typeHandlerAlias != null) {
builder.typeHandler(this.resolveTypeHandler(javaType, typeHandlerAlias));
}
return builder.build();
}
总结:
1.他会去寻找对应sql中的body
2.通过元素标签,找到对应的sqlcommandtype的类型
3.解析元素标签的body,判断有没有${}这样的标识符,如果有则是动态语句,需要进行替换。
4.判断完成后递归解析include的标签,将include的内容替换掉。
5.开始截取字符串#{opentoken之前的拼接为sql,中间参数生成参数解析器对象。在元sql中拼接?问号占位符。
6.将sql,express,config封装为sqlsource资源对象进行返回。