mybatis mapper解析(4)
開篇
這篇文章的目的主要是想講清楚mapper標籤的解析過程,核心目的是想講清楚/mapper/resultMap、/mapper/sql、select|insert|update|delete 這三大類標籤的解析過程,以及生成最終的解析物件並儲存到Configuration當中。
解析過程時序圖
解析過程原始碼分析
mapper.xml檔案的常用格式
mapper.xml當中通用的幾大類標籤,分別是resultMap,sql,以及select相關語句,我們的解析就是針對這幾類標籤進行解析,當然千萬千萬不要遺漏了namespace這個標籤。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qbd.mapper.StudentMappers">
<select id="findbyid" parameterType="Integer" resultMap="StudentResult">
select *from student where id=#{id}
</select>
<select id="findbygradeid" parameterType="Integer" resultMap="StudentResult">
select *from student where gid=#{gid}
</select>
<resultMap type="Student" id="StudentResult">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<association property="address" column="addid" select="com.qbd.mapper.AddressMappers.findbyid">
</association>
<association property="grade" column="gid" select="com.qbd.mapper.GradeMappers.findbyid">
</association>
</resultMap>
</mapper>
mapper原始碼解析入口
僅關注mapperElement(root.evalNode("mappers")),因為mappers裡面包含了真正執行的SQL語句的定義。
<mappers>
<mapper resource="map/query.xml" />
<mapper resource="map/insert.xml" />
<mapper resource="map/update.xml" />
<mapper resource="map/delete.xml" />
</mappers>
---------------------XMLConfigBuilder.java--------------------------------------
//解析配置
private void parseConfiguration(XNode root) {
try {
//分步驟解析
//issue #117 read properties first
//1.properties
propertiesElement(root.evalNode("properties"));
//2.型別別名
typeAliasesElement(root.evalNode("typeAliases"));
//3.外掛
pluginElement(root.evalNode("plugins"));
//4.物件工廠
objectFactoryElement(root.evalNode("objectFactory"));
//5.物件包裝工廠
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
//6.設定
settingsElement(root.evalNode("settings"));
// read it after objectFactory and objectWrapperFactory issue #631
//7.環境
environmentsElement(root.evalNode("environments"));
//8.databaseIdProvider
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
//9.型別處理器
typeHandlerElement(root.evalNode("typeHandlers"));
//10.對映器
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
開始遍歷mappers標籤下的所有mapper逐一進行解析,一般我們通過resource標籤引入SQL定義的xml檔案,進而通過XMLMapperBuilder物件進行parse操作。
//10.對映器
// 10.1使用類路徑
// <mappers>
// <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
// <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
// <mapper resource="org/mybatis/builder/PostMapper.xml"/>
// </mappers>
//
// 10.2使用絕對url路徑
// <mappers>
// <mapper url="file:///var/mappers/AuthorMapper.xml"/>
// <mapper url="file:///var/mappers/BlogMapper.xml"/>
// <mapper url="file:///var/mappers/PostMapper.xml"/>
// </mappers>
//
// 10.3使用java類名
// <mappers>
// <mapper class="org.mybatis.builder.AuthorMapper"/>
// <mapper class="org.mybatis.builder.BlogMapper"/>
// <mapper class="org.mybatis.builder.PostMapper"/>
// </mappers>
//
// 10.4自動掃描包下所有對映器
// <mappers>
// <package name="org.mybatis.builder"/>
// </mappers>
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
//10.4自動掃描包下所有對映器
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) {
//10.1使用類路徑
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
//對映器比較複雜,呼叫XMLMapperBuilder
//注意在for迴圈裡每個mapper都重新new一個XMLMapperBuilder,來解析
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource,
configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
//10.2使用絕對url路徑
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
//對映器比較複雜,呼叫XMLMapperBuilder
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration,
url, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {
//10.3使用java類名
Class<?> mapperInterface = Resources.classForName(mapperClass);
//直接把這個對映加入配置
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
----------------------XMLMapperBuilder.java---------------------
public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource,
Map<String, XNode> sqlFragments) {
this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()),
configuration, resource, sqlFragments);
}
private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
super(configuration);
this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
this.parser = parser;
this.sqlFragments = sqlFragments;
this.resource = resource;
}
//解析
public void parse() {
//如果沒有載入過再載入,防止重複載入
if (!configuration.isResourceLoaded(resource)) {
//配置mapper
configurationElement(parser.evalNode("/mapper"));
//標記一下,已經載入過了
configuration.addLoadedResource(resource);
//繫結對映器到namespace
bindMapperForNamespace();
}
//還有沒解析完的東東這裡接著解析?
parsePendingResultMaps();
parsePendingChacheRefs();
parsePendingStatements();
}
解析SQL定義的xml檔案
核心標籤在於我們解析 /mapper/resultMap標籤,/mapper/sql標籤,select|insert|update|delete標籤。
-------------------XMLMapperBuilder.java-------------------
private void configurationElement(XNode context) {
try {
//1.配置namespace
String namespace = context.getStringAttribute("namespace");
if (namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
//2.配置cache-ref
cacheRefElement(context.evalNode("cache-ref"));
//3.配置cache
cacheElement(context.evalNode("cache"));
//4.配置parameterMap(已經廢棄,老式風格的引數對映)
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
//5.配置resultMap(高階功能)
resultMapElements(context.evalNodes("/mapper/resultMap"));
//6.配置sql(定義可重用的 SQL 程式碼段)
sqlElement(context.evalNodes("/mapper/sql"));
//7.配置select|insert|update|delete TODO
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
}
}
/mapper/resultMap解析
解析resultMap放置到Configuration當中,這裡把所有resultMap的標籤列表開始進行解析,configuration.addResultMap(resultMap)。
private void resultMapElements(List<XNode> list) throws Exception {
//基本上就是迴圈把resultMap加入到Configuration裡去,保持2份,一份縮略,一分全名
for (XNode resultMapNode : list) {
try {
//迴圈調resultMapElement
resultMapElement(resultMapNode);
} catch (IncompleteElementException e) {
// ignore, it will be retried
}
}
}
private ResultMap resultMapElement(XNode resultMapNode) throws Exception {
return resultMapElement(resultMapNode, Collections.<ResultMapping> emptyList());
}
//5.1 配置resultMap
private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception {
//錯誤上下文
//取得標示符 ("resultMap[userResultMap]")
// <resultMap id="userResultMap" type="User">
// <id property="id" column="user_id" />
// <result property="username" column="username"/>
// <result property="password" column="password"/>
// </resultMap>
ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
String id = resultMapNode.getStringAttribute("id",
resultMapNode.getValueBasedIdentifier());
//一般拿type就可以了,後面3個難道是相容老的程式碼?
String type = resultMapNode.getStringAttribute("type",
resultMapNode.getStringAttribute("ofType",
resultMapNode.getStringAttribute("resultType",
resultMapNode.getStringAttribute("javaType"))));
//高階功能,還支援繼承?
// <resultMap id="carResult" type="Car" extends="vehicleResult">
// <result property="doorCount" column="door_count" />
// </resultMap>
String extend = resultMapNode.getStringAttribute("extends");
//autoMapping
Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
Class<?> typeClass = resolveClass(type);
Discriminator discriminator = null;
List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
resultMappings.addAll(additionalResultMappings);
List<XNode> resultChildren = resultMapNode.getChildren();
//解析這個xml檔案當中所有的<resultMap></resultMap> 節點
for (XNode resultChild : resultChildren) {
if ("constructor".equals(resultChild.getName())) {
//解析result map的constructor
processConstructorElement(resultChild, typeClass, resultMappings);
} else if ("discriminator".equals(resultChild.getName())) {
//解析result map的discriminator
discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
} else {
List<ResultFlag> flags = new ArrayList<ResultFlag>();
if ("id".equals(resultChild.getName())) {
flags.add(ResultFlag.ID);
}
//調5.1.1 buildResultMappingFromContext,得到ResultMapping
resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
}
}
//最後再調ResultMapResolver得到ResultMap
ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass,
extend, discriminator, resultMappings, autoMapping);
try {
return resultMapResolver.resolve();
} catch (IncompleteElementException e) {
configuration.addIncompleteResultMap(resultMapResolver);
throw e;
}
}
private ResultMapping buildResultMappingFromContext(XNode context, Class<?> resultType,
List<ResultFlag> flags) throws Exception {
//<id property="id" column="author_id"/>
//<result property="username" column="author_username"/>
String property = context.getStringAttribute("property");
String column = context.getStringAttribute("column");
String javaType = context.getStringAttribute("javaType");
String jdbcType = context.getStringAttribute("jdbcType");
String nestedSelect = context.getStringAttribute("select");
//處理巢狀的result map
String nestedResultMap = context.getStringAttribute("resultMap",
processNestedResultMappings(context, Collections.<ResultMapping> emptyList()));
String notNullColumn = context.getStringAttribute("notNullColumn");
String columnPrefix = context.getStringAttribute("columnPrefix");
String typeHandler = context.getStringAttribute("typeHandler");
String resulSet = context.getStringAttribute("resultSet");
String foreignColumn = context.getStringAttribute("foreignColumn");
boolean lazy = "lazy".equals(context.getStringAttribute("fetchType",
configuration.isLazyLoadingEnabled() ? "lazy" : "eager"));
Class<?> javaTypeClass = resolveClass(javaType);
@SuppressWarnings("unchecked")
Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
//又去調builderAssistant.buildResultMapping
return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass,
jdbcTypeEnum, nestedSelect, nestedResultMap, notNullColumn,
columnPrefix, typeHandlerClass, flags, resulSet, foreignColumn, lazy);
}
public ResultMap resolve() {
//解析又去呼叫MapperBuilderAssistant.addResultMap
return assistant.addResultMap(this.id, this.type, this.extend, this.discriminator, this.resultMappings, this.autoMapping);
}
public ResultMap addResultMap(
String id,
Class<?> type,
String extend,
Discriminator discriminator,
List<ResultMapping> resultMappings,
Boolean autoMapping) {
id = applyCurrentNamespace(id, false);
extend = applyCurrentNamespace(extend, true);
//建造者模式
ResultMap.Builder resultMapBuilder = new ResultMap.Builder(configuration, id, type, resultMappings, autoMapping);
if (extend != null) {
if (!configuration.hasResultMap(extend)) {
throw new IncompleteElementException("Could not find a parent resultmap with id '" + extend + "'");
}
ResultMap resultMap = configuration.getResultMap(extend);
List<ResultMapping> extendedResultMappings = new ArrayList<ResultMapping>(resultMap.getResultMappings());
extendedResultMappings.removeAll(resultMappings);
// Remove parent constructor if this resultMap declares a constructor.
boolean declaresConstructor = false;
for (ResultMapping resultMapping : resultMappings) {
if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {
declaresConstructor = true;
break;
}
}
if (declaresConstructor) {
Iterator<ResultMapping> extendedResultMappingsIter = extendedResultMappings.iterator();
while (extendedResultMappingsIter.hasNext()) {
if (extendedResultMappingsIter.next().getFlags().contains(ResultFlag.CONSTRUCTOR)) {
extendedResultMappingsIter.remove();
}
}
}
resultMappings.addAll(extendedResultMappings);
}
resultMapBuilder.discriminator(discriminator);
ResultMap resultMap = resultMapBuilder.build();
configuration.addResultMap(resultMap);
return resultMap;
}
/mapper/sql片段解析
遍歷所有的sql標籤挨個進行解析,然後放置到sqlFragments的map當中,sqlFragments.put(id, context)。
//6 配置sql(定義可重用的 SQL 程式碼段)
private void sqlElement(List<XNode> list) throws Exception {
if (configuration.getDatabaseId() != null) {
sqlElement(list, configuration.getDatabaseId());
}
sqlElement(list, null);
}
//6.1 配置sql
//<sql id="userColumns"> id,username,password </sql>
private void sqlElement(List<XNode> list, String requiredDatabaseId) throws Exception {
for (XNode context : list) {
String databaseId = context.getStringAttribute("databaseId");
String id = context.getStringAttribute("id");
id = builderAssistant.applyCurrentNamespace(id, false);
//比較簡單,就是將sql片段放入hashmap,不過此時還沒有解析sql片段
if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) {
sqlFragments.put(id, context);
}
}
}
select|insert|update|delete語句解析
遍歷所有的select|insert|update|delete的標籤,挨個進行解析並儲存到configuration當中,configuration.addMappedStatement(statement)。後面使用的時候通過select等標籤的id直接獲取statement從而實現了interface和xml定義SQL語句的對映關係了。
核心的SQL替換在方法includeParser.applyIncludes(context.getNode())當中實現。
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);這裡的SqlSource有多個實現細節。
---------------------XMLMapperBuilder.java------------------------
//7.配置select|insert|update|delete
private void buildStatementFromContext(List<XNode> list) {
//呼叫7.1構建語句
if (configuration.getDatabaseId() != null) {
buildStatementFromContext(list, configuration.getDatabaseId());
}
buildStatementFromContext(list, null);
}
//7.1構建語句
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
for (XNode context : list) {
//構建所有語句,一個mapper下可以有很多select
//語句比較複雜,核心都在這裡面,所以呼叫XMLStatementBuilder
final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant,
context, requiredDatabaseId);
try {
//核心XMLStatementBuilder.parseStatementNode
statementParser.parseStatementNode();
} catch (IncompleteElementException e) {
//如果出現SQL語句不完整,把它記下來,塞到configuration去
configuration.addIncompleteStatement(statementParser);
}
}
}
------------------------XMLStatementBuilder.java-------------------------
public void parseStatementNode() {
String id = context.getStringAttribute("id");
String databaseId = context.getStringAttribute("databaseId");
//如果databaseId不匹配,退出
if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
return;
}
//暗示驅動程式每次批量返回的結果行數
Integer fetchSize = context.getIntAttribute("fetchSize");
//超時時間
Integer timeout = context.getIntAttribute("timeout");
//引用外部 parameterMap,已廢棄
String parameterMap = context.getStringAttribute("parameterMap");
//引數型別
String parameterType = context.getStringAttribute("parameterType");
Class<?> parameterTypeClass = resolveClass(parameterType);
//引用外部的 resultMap(高階功能)
String resultMap = context.getStringAttribute("resultMap");
//結果型別
String resultType = context.getStringAttribute("resultType");
//指令碼語言,mybatis3.2的新功能
String lang = context.getStringAttribute("lang");
//得到語言驅動
LanguageDriver langDriver = getLanguageDriver(lang);
Class<?> resultTypeClass = resolveClass(resultType);
//結果集型別,FORWARD_ONLY|SCROLL_SENSITIVE|SCROLL_INSENSITIVE 中的一種
String resultSetType = context.getStringAttribute("resultSetType");
//語句型別, STATEMENT|PREPARED|CALLABLE 的一種
StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType",
StatementType.PREPARED.toString()));
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
//獲取命令型別(select|insert|update|delete)
String nodeName = context.getNode().getNodeName();
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
//是否要快取select結果
boolean useCache = context.getBooleanAttribute("useCache", isSelect);
//僅針對巢狀結果 select 語句適用:如果為 true,就是假設包含了巢狀結果集或是分組了,
//這樣的話當返回一個主結果行的時候,就不會發生有對前面結果集的引用的情況。
//這就使得在獲取巢狀的結果集的時候不至於導致記憶體不夠用。預設值:false。
boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
// Include Fragments before parsing
//解析之前先解析<include>SQL片段
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
includeParser.applyIncludes(context.getNode());
// Parse selectKey after includes and remove them.
//解析之前先解析<selectKey>
processSelectKeyNodes(id, parameterTypeClass, langDriver);
// Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
//解析成SqlSource,一般是DynamicSqlSource
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
String resultSets = context.getStringAttribute("resultSets");
//(僅對 insert 有用) 標記一個屬性, MyBatis 會通過 getGeneratedKeys 或者通過 insert 語句的 selectKey 子元素設定它的值
String keyProperty = context.getStringAttribute("keyProperty");
//(僅對 insert 有用) 標記一個屬性, MyBatis 會通過 getGeneratedKeys 或者通過 insert 語句的 selectKey 子元素設定它的值
String keyColumn = context.getStringAttribute("keyColumn");
KeyGenerator keyGenerator;
String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
if (configuration.hasKeyGenerator(keyStatementId)) {
keyGenerator = configuration.getKeyGenerator(keyStatementId);
} else {
keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
? new Jdbc3KeyGenerator() : new NoKeyGenerator();
}
//又去調助手類
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
-------------------------XMLLanguageDriver.java---------------------
public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
//用XML指令碼構建器解析
XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
return builder.parseScriptNode();
}
public SqlSource parseScriptNode() {
List<SqlNode> contents = parseDynamicTags(context);
MixedSqlNode rootSqlNode = new MixedSqlNode(contents);
SqlSource sqlSource = null;
if (isDynamic) {
sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
} else {
sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
}
return sqlSource;
}
---------------------XMLIncludeTransformer.java-----------------------------------
//<select id="selectUsers" resultType="map">
// select <include refid="userColumns"/>
// from some_table
// where id = #{id}
//</select>
public void applyIncludes(Node source) {
if (source.getNodeName().equals("include")) {
//走到這裡,單獨解析<include refid="userColumns"/>
//拿到SQL片段
Node toInclude = findSqlFragment(getStringAttribute(source, "refid"));
//遞迴呼叫自己,應用上?
applyIncludes(toInclude);
//總之下面就是將字串拼接進來,看不懂。。。
if (toInclude.getOwnerDocument() != source.getOwnerDocument()) {
toInclude = source.getOwnerDocument().importNode(toInclude, true);
}
source.getParentNode().replaceChild(toInclude, source);
while (toInclude.hasChildNodes()) {
toInclude.getParentNode().insertBefore(toInclude.getFirstChild(), toInclude);
}
toInclude.getParentNode().removeChild(toInclude);
} else if (source.getNodeType() == Node.ELEMENT_NODE) {
//一開始會走這段,取得所有兒子
NodeList children = source.getChildNodes();
for (int i=0; i<children.getLength(); i++) {
//遞迴呼叫自己
applyIncludes(children.item(i));
}
}
}
------------------------------------MapperBuilderAssistant.java-----------------------------
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 (unresolvedCacheRef) {
throw new IncompleteElementException("Cache-ref not yet resolved");
}
//為id加上namespace字首
id = applyCurrentNamespace(id, false);
//是否是select語句
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
//又是建造者模式
MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType);
statementBuilder.resource(resource);
statementBuilder.fetchSize(fetchSize);
statementBuilder.statementType(statementType);
statementBuilder.keyGenerator(keyGenerator);
statementBuilder.keyProperty(keyProperty);
statementBuilder.keyColumn(keyColumn);
statementBuilder.databaseId(databaseId);
statementBuilder.lang(lang);
statementBuilder.resultOrdered(resultOrdered);
statementBuilder.resulSets(resultSets);
setStatementTimeout(timeout, statementBuilder);
//1.引數對映
setStatementParameterMap(parameterMap, parameterType, statementBuilder);
//2.結果對映
setStatementResultMap(resultMap, resultType, resultSetType, statementBuilder);
setStatementCache(isSelect, flushCache, useCache, currentCache, statementBuilder);
MappedStatement statement = statementBuilder.build();
//建造好呼叫configuration.addMappedStatement
configuration.addMappedStatement(statement);
return statement;
}
SqlSource的類圖
相關文章
- mybatis通用mapper原始碼解析(二)MyBatisAPP原始碼
- mybatis通用mapper原始碼解析(一)MyBatisAPP原始碼
- 《手寫Mybatis》第4章:Mapper XML的解析和註冊使用MyBatisAPPXML
- MyBatis外掛 - 通用mapperMyBatisAPP
- mybatis的外掛:mapperMyBatisAPP
- Mybatis原始碼解析4——SqlSessionMyBatis原始碼SQLSession
- MyBatis 原始dao與mapper代理MyBatisAPP
- MyBatis外掛使用–通用MapperMyBatisAPP
- mybatis中@Mapper使用介紹MyBatisAPP
- myBatis原始碼解析-反射篇(4)MyBatis原始碼反射
- mybatis-plus原始碼解析(三)----Mapper介面動態代理呼叫過程MyBatis原始碼APP
- 從零搭建Spring Boot腳手架(4):手寫Mybatis通用MapperSpring BootMyBatisAPP
- 自動生成Mybatis的Mapper檔案MyBatisAPP
- mybatis mapper.xml批次刪除操作MyBatisAPPXML
- mybatis-plus原始碼解析(二)----基於@MapperScan註解掃描載入MapperMyBatis原始碼APP
- 兩張圖徹底搞懂MyBatis的Mapper原理!MyBatisAPP
- Spring Boot整合MyBatis實現通用MapperSpring BootMyBatisAPP
- Mybatis1.1——使用mapper代理開發daoMyBatisAPP
- mybatis 中mapper 的namespace有什麼用?MyBatisAPPnamespace
- mybatis的mapper的特殊符號處理MyBatisAPP符號
- 【肥朝】圖解原始碼 | MyBatis的Mapper原理圖解原始碼MyBatisAPP
- Spring+Mybatis(一)無mapper.xml方式SpringMyBatisAPPXML
- 開發一個MyBatis通用Mapper的輪子MyBatisAPP
- MyBatis 配置解析MyBatis
- Mybatis配置解析MyBatis
- 剔除Intellij中Mybatis的Mapper自動注入警告IntelliJMyBatisAPP
- mybatis中mapper.xml檔案引數問題MyBatisAPPXML
- Mybatis中mapper檔案中的兩層迴圈MyBatisAPP
- Mybatis的Mapper中的方法為什麼不能過載?MyBatisAPP
- Spring整合Mybatis時Mapper代理報錯BindingExceptionSpringMyBatisAPPException
- MyBatis原始碼解析MyBatis原始碼
- MyBatis原理解析MyBatis
- Mybatis是如何將Mapper介面註冊到Spring IoC的MyBatisAPPSpring
- mybatis的mapper.xml用條件判斷做轉譯MyBatisAPPXML
- Mybatis的Mapper對映檔案中常用標籤及作用MyBatisAPP
- java.lang.NoSuchMethodException: tk.mybatis.mapper.provider.ExampleProvider.「init」()JavaExceptionMyBatisAPPIDE
- 【MyBatis原始碼解析】MyBatis一二級快取MyBatis原始碼快取
- mybatis plugin原始碼解析MyBatisPlugin原始碼