1.前言2.测试案例
2.1. `Mapper` 接口2.2. `Mapper.xml` 3.`Mapper.xml` 文件的解析
3.1.`parseStatementNode()`3.2.`addMappedStatement()`3.3.`addMappedStatement()` 4.总结
1.前言今天在翻阅有关 MyBatis 专题的知识点时,看到了这样一道面试题:MyBatis 内的 Mapper接口方法为什么不能重载,对于这个问题,幸好前几天查漏补缺了一下 MyBatis 专题,毋庸置疑它考的是你对 MyBatis 源码的熟悉程度,少说多做看源码
2.测试案例 2.1. Mapper 接口package org.example.dao;
@Mapper
public interface UserMapper {
int deleteByPrimaryKey(Integer id);
int insert(User record);
int insertSelective(User record);
User selectByPrimaryKey(Integer id);
int updateByPrimaryKeySelective(User record);
int updateByPrimaryKey(User record);
int batchAddUser(List list);
}
2.2. Mapper.xml
注意当前的 namespace 为
3.Mapper.xml 文件的解析id , username, password, nickname insert into shiro_user (username, password, nickname) values ......(#{item.username},#{item.password},#{item.nickname})
要知道 Mapper 接口方法为什么不能重载,首先就要知道 Mapper 接口和 Mapper.xml 文件在何时被解析的,鉴于文章篇幅的原因,本文只说重点源码,详细源码 在这里
3.1.parseStatementNode()我们首先看 XMLStatementBuilder 类中的 parseStatementNode() 方法,也就是 这篇文章 的 2.5 节点的 parseStatementNode() ,如下
public void parseStatementNode() {
String id = context.getStringAttribute("id");
String databaseId = context.getStringAttribute("databaseId");
if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
return;
}
// 省略......
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
3.2.addMappedStatement()
MapperBuilderAssistant 类中的 addMappedStatement() 方法
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");
}
// 1.给 id 填充上 namespace
id = applyCurrentNamespace(id, false);
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
// 2.使用参数构建 MappedStatement.Builder
MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
.resource(resource)
.fetchSize(fetchSize)
.timeout(timeout)
.statementType(statementType)
.keyGenerator(keyGenerator)
.keyProperty(keyProperty)
.keyColumn(keyColumn)
.databaseId(databaseId)
.lang(lang)
.resultOrdered(resultOrdered)
.resultSets(resultSets)
.resultMaps(getStatementResultMaps(resultMap, resultType, id))
.resultSetType(resultSetType)
.flushCacheRequired(valueOrDefault(flushCache, !isSelect))
.useCache(valueOrDefault(useCache, isSelect))
.cache(currentCache);
ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
if (statementParameterMap != null) {
statementBuilder.parameterMap(statementParameterMap);
}
// 3.使用 MappedStatement.Builder 构建 MappedStatement
MappedStatement statement = statementBuilder.build();
// 4.将 MappedStatement 添加到缓存
configuration.addMappedStatement(statement);
return statement;
}
id = applyCurrentNamespace(id, false):给这个段打上断点,Debug 运行看它填充的是什么 namespace
我们 Step Over 一下,再看 id 结果,显然是给原来的方法名加上了全限类名
applyCurrentNamespace() 源码如下
3.3.addMappedStatement()跟进 addMappedStatement() 方法,来到了 Configuration 类中的 addMappedStatement() 方法,源码如下
public void addMappedStatement(MappedStatement ms) {
// StrictMap 的 put() 方法,key 是 id,value 是 ms
mappedStatements.put(ms.getId(), ms);
}
// mappedStatements 是一个 StrictMap
protected final Map mappedStatements = new StrictMap("Mapped Statements collection");
StrictMap 的 put() 方法,key = ms.getId(),value = ms。这个 StrictMap 不允许有重复的 key,而存入的 key 就是 statement 中的 id。因此 Mapper 接口中的方法不能重载
statement 中的 id 值如下
4.总结在同一个 namespace 中写了多个相同的 id 映射,且在 Mapper 接口中有对应的重载方法,由于 MyBatis 在构建 MappedStatement 缓存时,是通过当前的 namespace + 方法名 作为 key 添加进 StrictMap 缓存的,此时就出现了 key 的不唯一性,导致出错同理,在不同的 namespace 中写了多个相同的 id 映射,且在不同的 Mapper 接口中有对应的同名方法,此时是不会发生错误的,当然这样就不是方法重载了



