Mybatis源码解析(三)mapper-sql详解构造过程

勿以浮沙筑高台


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资源对象进行返回。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值