目录

  • 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<User> list);
}

2.2. Mapper.xml

注意当前的 namespace<mapper namespace="org.example.dao.UserMapper">

<?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="org.example.dao.UserMapper"><resultMap id="BaseResultMap" type="org.example.pojo.User"><id column="id" jdbcType="INTEGER" property="id"/><result column="username" jdbcType="VARCHAR" property="username"/><result column="password" jdbcType="VARCHAR" property="password"/><result column="nickname" jdbcType="VARCHAR" property="nickname"/></resultMap><sql id="Base_Column_List">id, username, password, nickname</sql><!--foreach批量插入--><insert id="batchAddUser" parameterType="java.util.List">insert into shiro_user (username, password, nickname) values<foreach collection="list" item="item" index="index" separator=",">(#{item.username},#{item.password},#{item.nickname})</foreach></insert>......
</mapper>

3.Mapper.xml 文件的解析

要知道 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 填充上 namespaceid = applyCurrentNamespace(id, false);boolean isSelect = sqlCommandType == SqlCommandType.SELECT;// 2.使用参数构建 MappedStatement.BuilderMappedStatement.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 构建 MappedStatementMappedStatement 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 是 msmappedStatements.put(ms.getId(), ms);
}// mappedStatements 是一个 StrictMap
protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
  • StrictMapput() 方法,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 接口中有对应的同名方法,此时是不会发生错误的,当然这样就不是方法重载了

MyBatis内的Mapper接口方法为什么不能重载相关推荐

  1. mybatis如何根据mapper接口生成其实现类

    SpringBoot集成mybatis mybatis的statement的解析与加载 mybatis如何根据mapper接口生成其实现类 mybatis的mapper返回map结果集 mybatis ...

  2. Spring整合MyBatis原理之Mapper接口和xml文件的解析

    目录 1. 前言 2. 类 `SqlSessionFactoryBean` 2.1. 实现了 `FactoryBean` 接口的 `getObject()` 2.2. `buildSqlSession ...

  3. 解决spring mybatis 整合后mapper接口注入失败

    spring整合mybatis,在dao层我们只写一个接口,配置相应的*mapper.xml文件, 报如下错误: 1 org.springframework.beans.factory.Unsatis ...

  4. Mybatis中mapper接口里方法重载的实现

    看了网上的很多文章,说mapper接口里不能写重载方法,感觉这种说法不对,mapper接口是可以实现重载方法的. 实现方法 例如: package mapper;import pojo.User;im ...

  5. mybatis接口中的方法重载_MyBatis的Mapper接口以及Example的实例函数及详解

    一.mapper接口中的方法解析 mapper接口中的函数及方法 方法 功能说明 int countByExample(UserExample example) thorws SQLException ...

  6. 一个mapper接口有多个mapper.xml 文件_MyBatis 源码解析:映射文件的加载与解析(上)

    上一篇我们分析了配置文件的加载与解析过程,本文将继续对映射文件的加载与解析实现进行分析.MyBatis 的映射文件用于配置 SQL 语句.二级缓存,以及结果集映射等,是区别于其它 ORM 框架的主要特 ...

  7. Mapper 接口无法注入或Invalid bound statement (not found)

    我们在使用MyBatis 的时候可能会遇到Mapper 接口无法注入,或者mapperstatement id 跟Mapper 接口方法无法绑定的情况.基于绑定的要求或者说规范,我们可以从这些地方去检 ...

  8. Mybatis-Dao层开发之Mapper接口

    Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法. Mapper接口开发 ...

  9. mapper接口原理

    mapper的实现原理是动态代理 那什么是动态代理呢?动态代理就是在程序运行期间由jvm通过反射等机制动态生成的,所以不会存在代理类的字节码文件,故我们在mybatis中使用mapper接口的时候没有 ...

  10. Mybatis接口Mapper内的方法为啥不能重载?

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 来源:https://my.oschina.net/zud ...

最新文章

  1. html流动模型,javascript的事件流模型都有什么?
  2. 【c语言】蓝桥杯算法训练 薪水计算
  3. 让AI个性化而且功耗更低 IBM研发新型神经网络芯片
  4. pca主成分分析结果解释_SKLEARN中的PCA(Principal Component Analysis)主成分分析法
  5. mysql force index报错_新特性解读 | MySQL 8.0 索引特性4-不可见索引
  6. mysql 字段操作_Mysql:数据库操作、数据表操作、字段操作整理
  7. 谷歌浏览器F12快速定位网页上组件信息
  8. 【论文+推导】Predictability and Prediction of Human Mobility Based on Application-Collected Location Data
  9. 检验杜宾 瓦森检验法R语言_2018年9-11月高级计量经济学主要授课内容概要
  10. jmeter+ant+jenkins接口自动化测试框架
  11. 上海公交投诉电话:12319
  12. 解决git push报错问题
  13. 【读万卷书】《挪威的森林》
  14. Web 开发最有用的50款 jQuery 插件集锦——《图片特效篇》
  15. BACnet/IP网关如何采集楼宇集中控制系统数据
  16. Kubernetes Pod 网络精髓:pause 容器详解
  17. 82岁高龄的高德纳仍在写《计算机程序设计艺术》,那是他未完成的人生目标...
  18. JS中 new FormData() - FormData对象的作用及用法
  19. String类-统计子串在字符串中出现的次数
  20. 神经网络Python实现(9行代码)

热门文章

  1. 容器技术Docker K8s 30 容器服务ACK基础与进阶-弹性伸缩
  2. The Basic Knowledge of Graph(图的基本知识)
  3. sizeof计算结构体时的内存对齐问题
  4. 归类问题:简单的代价函数和梯度下降----吴恩达机器学习
  5. 译林 五年级上 单词_译林版小学英语五上Unit 4 HobbiesStory time公开课优质课件教案视频教案...
  6. antd vue form 手动校验_vue测试模板与jsonSchema自动生成elment组件
  7. 【生信进阶练习1000days】day10-vcf format
  8. python 学习小结(1)
  9. android加载图片+背景,Android开发中ImageLoder加载网络图片时将图片设置为ImageView背景的方法...
  10. (博主可帮找错)Servlet.service() for servlet [dispatcherServlet] path [] threw exception feign.Feig,可截图私聊博主