标签:ndt from keygen tostring detail trim 返回 nta eterm
本文接 《精尽 MyBatis 源码分析 —— MyBatis 初始化(二)之加载 Mapper 映射配置文件》 一文,来分享 MyBatis 初始化的第三步,加载 Statement 配置。而这个步骤的入口是 XMLStatementBuilder 。下面,我们一起来看看它的代码实现。
在 《精尽 MyBatis 源码分析 —— MyBatis 初始化(二)之加载 Mapper 映射配置文件》 的 「2.3.5 buildStatementFromContext」 中,我们已经看到对 XMLStatementBuilder 的调用代码。代码如下:
// XMLMapperBuilder.java
|
org.apache.ibatis.builder.xml.XMLStatementBuilder
,继承 BaseBuilder 抽象类,Statement XML 配置构建器,主要负责解析 Statement 配置,即 <select />
、<insert />
、<update />
、<delete />
标签。
// XMLStatementBuilder.java
|
#parseStatementNode()
方法,执行 Statement 解析。代码如下:
// XMLStatementBuilder.java
|
<1>
处,获得 id
属性,编号。<2>
处,获得 databaseId
属性,并调用 #databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId)
方法,判断 databaseId
是否匹配。详细解析,见 「2.3 databaseIdMatchesCurrent」 。<3>
处,获得各种属性。<4>
处,调用 #getLanguageDriver(String lang)
方法,获得 lang
对应的 LanguageDriver 对象。详细解析,见 「2.4 getLanguageDriver」 。<5>
处,获得 resultType
对应的类。<6>
处,获得 resultSet
对应的枚举值。关于 org.apache.ibatis.mapping.ResultSetType
枚举类,点击查看。一般情况下,不会设置该值。它是基于 java.sql.ResultSet
结果集的几种模式,感兴趣的话,可以看看 《ResultSet 的 Type 属性》 。<7>
处,获得 statementType
对应的枚举值。关于 org.apache.ibatis.mapping.StatementType
枚举类,点击查看。<8>
处,获得 SQL 对应的 SqlCommandType 枚举值。<9>
处,获得各种属性。<10>
处,创建 XMLIncludeTransformer 对象,并调用 XMLIncludeTransformer#applyIncludes(Node source)
方法,替换 <include />
标签相关的内容。详细解析,见 「3. XMLIncludeTransformer」 。<11>
处,调用 #processSelectKeyNodes(String id, Class<?> parameterTypeClass, LanguageDriver langDriver)
方法,解析 <selectKey />
标签。详细解析,见 「2.5 processSelectKeyNodes」 。<12>
处,调用 LanguageDriver#createSqlSource(Configuration configuration, XNode script, Class<?> parameterType)
方法,创建 SqlSource 对象。详细解析,见后续文章。<13>
处,获得 KeyGenerator 对象。分成 <13.1>
和 <13.2>
两种情况。具体的,胖友耐心看下代码注释。<14>
处,调用 MapperBuilderAssistant#addMappedStatement(...)
方法,创建 MappedStatement 对象。详细解析,见 「4.1 addMappedStatement」 中。#databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId)
方法,判断 databaseId
是否匹配。代码如下:
// XMLStatementBuilder.java
|
#databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId)
方法是一致的。#getLanguageDriver(String lang)
方法,获得 lang
对应的 LanguageDriver 对象。代码如下:
// XMLStatementBuilder.java
|
调用 MapperBuilderAssistant#getLanguageDriver(lass<? extends LanguageDriver> langClass)
方法,获得 LanguageDriver 对象。代码如下:
// MapperBuilderAssistant.java
|
org.apache.ibatis.scripting.LanguageDriverRegistry
类,我们在后续的文章,详细解析。#processSelectKeyNodes(String id, Class<?> parameterTypeClass, LanguageDriver langDriver)
方法,解析 <selectKey />
标签。代码如下:
// XMLStatementBuilder.java
|
<1>
处,获得 <selectKey />
节点们。<2>
处,调用 #parseSelectKeyNodes(...)
方法,执行解析 <selectKey />
节点们。详细解析,见 「2.5.1 parseSelectKeyNodes」 。<3>
处,调用 #removeSelectKeyNodes(List<XNode> selectKeyNodes)
方法,移除 <selectKey />
节点们。代码如下:
// XMLStatementBuilder.java
|
#parseSelectKeyNodes(String parentId, List<XNode> list, Class<?> parameterTypeClass, LanguageDriver langDriver, String skRequiredDatabaseId)
方法,执行解析 <selectKey />
子节点们。代码如下:
// XMLStatementBuilder.java
|
<1>
处,遍历 <selectKey />
节点们,逐个处理。<2>
处,获得完整 id
编号,格式为 ${id}!selectKey
。这里很重要,最终解析的 <selectKey />
节点,会创建成一个 MappedStatement 对象。而该对象的编号,就是 id
。<3>
处,获得 databaseId
,并调用 #databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId)
方法,判断 databaseId
是否匹配。?? 通过此处,我们可以看到,即使有多个 <selectionKey />
节点,但是最终只会有一个节点被解析,就是符合的 databaseId
对应的。因为不同的数据库实现不同,对于获取主键的方式也会不同。<4>
处,调用 #parseSelectKeyNode(...)
方法,执行解析单个 <selectKey />
节点。详细解析,见 「2.5.2 parseSelectKeyNode」 。#parseSelectKeyNode(...)
方法,执行解析单个 <selectKey />
节点。代码如下:
// XMLStatementBuilder.java
|
<1.1>
处理,获得各种属性和对应的类。<1.2>
处理,创建 MappedStatement 需要用到的默认值。<1.3>
处理,调用 LanguageDriver#createSqlSource(Configuration configuration, XNode script, Class<?> parameterType)
方法,创建 SqlSource 对象。详细解析,见后续文章。<1.4>
处理,调用 MapperBuilderAssistant#addMappedStatement(...)
方法,创建 MappedStatement 对象。<2.1>
处理,获得 SelectKeyGenerator 的编号,格式为 ${namespace}.${id}
。<2.2>
处理,获得 MappedStatement 对象。该对象,实际就是 <1.4>
处创建的 MappedStatement 对象。<2.3>
处理,调用 Configuration#addKeyGenerator(String id, KeyGenerator keyGenerator)
方法,创建 SelectKeyGenerator 对象,并添加到 configuration
中。代码如下:
// Configuration.java
|
org.apache.ibatis.builder.xml.XMLIncludeTransformer
,XML <include />
标签的转换器,负责将 SQL 中的 <include />
标签转换成对应的 <sql />
的内容。
// XMLIncludeTransformer.java
|
#applyIncludes(Node source)
方法,将 <include />
标签,替换成引用的 <sql />
。代码如下:
// XMLIncludeTransformer.java
|
<1>
处,创建 variablesContext
,并将 configurationVariables
添加到其中。这里的目的是,避免 configurationVariables
被下面使用时候,可能被修改。实际上,从下面的实现上,不存在这个情况。<2>
处,调用 #applyIncludes(Node source, final Properties variablesContext, boolean included)
方法,处理 <include />
。#applyIncludes(Node source, final Properties variablesContext, boolean included)
方法,使用递归的方式,将 <include />
标签,替换成引用的 <sql />
。代码如下:
// XMLIncludeTransformer.java
|
这是个有自递归逻辑的方法,所以理解起来会有点绕,实际上还是蛮简单的。为了更好的解释,我们假设示例如下:
// mybatis-config.xml
|
included
,是否正在处理 <include />
标签中。?? 一脸懵逼?不要方,继续往下看。<select />
节点进入这个方法时,会首先进入 <2>
这块逻辑。
<2.1>
处,因为 不满足 included
条件,初始传入是 false
,所以跳过。<2.2>
处,遍历子节点,递归调用 #applyIncludes(...)
方法,继续替换。如图所示:
[0]
和 [2]
,执行该方法时,不满足 <1>
、<2>
、<3>
任一一种情况,所以可以忽略。虽然说,满足 <3>
的节点类型为 Node.TEXT_NODE
,但是 included
此时为 false
,所以不满足。[1]
,执行该方法时,满足 <1>
的情况,所以走起。[1]
,即 <include />
节点进入 <1>
这块逻辑:
<1.1>
处,调用 #findSqlFragment(String refid, Properties variables)
方法,获得 <sql />
对应的节点,即上述示例看到的,<sql id="123" lang="${cpu}"> ... </>
。详细解析,见 「3.3 findSqlFragment」 。<1.2>
处,调用 #getVariablesContext(Node node, Properties inheritedVariablesContext)
方法,获得包含 <include />
标签内的属性 Properties 对象。详细解析,见 「3.4 getVariablesContext」 。<1.3>
处,递归调用 #applyIncludes(...)
方法,继续替换。注意,此处是 <sql />
对应的节点,并且 included
参数为 true
。详细的结果,见 ?????? 处。<1.4>
处,将处理好的 <sql />
节点,替换掉 <include />
节点。逻辑有丢丢绕,胖友耐心看下注释,好好思考。<sql />
节点,会进入 <2>
这块逻辑:
<2.1>
处,因为 included
为 true
,所以能满足这块逻辑,会进行执行。如 <sql id="123" lang="${cpu}">
的情况,lang
属性是可以被替换的。<2.2>
处,遍历子节点,递归调用 #applyIncludes(...)
方法,继续替换。如图所示:
[0]
,执行该方法时,满足 <3>
的情况,所以可以使用变量 Properteis 对象,进行替换,并修改原节点。其实,整理一下,逻辑也不会很绕。耐心耐心耐心。
#findSqlFragment(String refid, Properties variables)
方法,获得对应的 <sql />
节点。代码如下:
// XMLIncludeTransformer.java
|
#getVariablesContext(Node node, Properties inheritedVariablesContext)
方法,获得包含 <include />
标签内的属性 Properties 对象。代码如下:
// XMLIncludeTransformer.java
|
如下是 <include />
标签内有属性的示例:
<sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql>
|
#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)
方法,构建 MappedStatement 对象。代码如下:
// MapperBuilderAssistant.java
|
<1>
处,如果只想的 Cache 未解析,抛出 IncompleteElementException 异常。<2>
处,获得 id
编号,格式为 ${namespace}.${id}
。<3>
处,创建 MappedStatement.Builder 对象。详细解析,见 「4.1.3 MappedStatement」 。
<3.1>
处,调用 #getStatementResultMaps(...)
方法,获得 ResultMap 集合。详细解析,见 「4.1.3 getStatementResultMaps」 。<3.2>
处,调用 #getStatementParameterMap(...)
方法,获得 ParameterMap ,并设置到 MappedStatement.Builder 中。详细解析,见 4.1.4 getStatementResultMaps」 。<4>
处,创建 MappedStatement 对象。详细解析,见 「4.1.1 MappedStatement」 。<5>
处,调用 Configuration#addMappedStatement(statement)
方法,添加到 configuration
中。代码如下:
// Configuration.java
|
org.apache.ibatis.mapping.MappedStatement
,映射的语句,每个 <select />
、<insert />
、<update />
、<delete />
对应一个 MappedStatement 对象。代码比较简单,但是有点略长,胖友直接点击 链接 查看,已经添加了完整的注释。
另外,比较特殊的是,<selectKey />
解析后,也会对应一个 MappedStatement 对象。
在另外,MappedStatement 有一个非常重要的方法 #getBoundSql(Object parameterObject)
方法,获得 BoundSql 对象。代码如下:
// MappedStatement.java
|
org.apache.ibatis.mapping.ParameterMap
,参数集合,对应 paramType=""
或 paramMap=""
标签属性。代码比较简单,但是有点略长,胖友直接点击 链接 查看,已经添加了完整的注释。
#getStatementResultMaps(...)
方法,获得 ResultMap 集合。代码如下:
// MapperBuilderAssistant.java
|
resultMap
存在使用逗号分隔的情况。这个出现在使用存储过程的时候,参见 《mybatis调用存储过程返回多个结果集》 。#getStatementParameterMap(...)
方法,获得 ParameterMap 对象。代码如下:
// MapperBuilderAssistant.java
|
<1>
处,如果 parameterTypeClass
非空,则创建 ParameterMap 对象。<2>
处,MyBatis 官方不建议使用 parameterMap
的方式标签:ndt from keygen tostring detail trim 返回 nta eterm
原文地址:https://www.cnblogs.com/siye1989/p/11622204.html