← 返回题目列表

MyBatis 的 MappedStatement 和 Configuration 是什么?一条 SQL 在 MyBatis 里怎么被表示?

困难 第 24 / 24 题 更新于 2026/07/28
MappedStatementConfigurationSqlSource核心数据结构

简化版

**MappedStatement 是 MyBatis 里「一条 SQL 语句的完整表示」——你在 Mapper XML 里写的每一个 <select>/<insert>/<update>/<delete>,或每一个 @Select 注解,都会被解析成一个 MappedStatement 对象。**它封装了这条 SQL 的一切信息:SQL 语句本身(SqlSource)、SQL 类型(增删改查)、入参类型、返回结果映射(resultMap)、缓存配置、超时、主键生成策略等。而 Configuration 是 MyBatis 的「全局配置中心」——它持有所有的 MappedStatement(一个 Map,key 是「namespace.方法名」)、所有的 TypeHandler、所有的插件、所有的设置,是整个 MyBatis 的「大脑」和「注册表」。类比理解MappedStatement 之于 MyBatis,就像 BeanDefinition 之于 Spring——都是「把配置解析成的、描述’一个东西该怎么用’的元数据对象」。一条 SQL 执行时,MyBatis 就是拿「statement id」去 Configuration 里找到对应的 MappedStatement,按它的描述去执行 SQL、绑参数、映射结果。

详细版

MappedStatement 封装的信息

字段含义
id唯一标识(namespace.方法名,如 UserMapper.selectById)
sqlSourceSQL 来源(能生成最终 SQL 和参数绑定)
sqlCommandTypeSQL 类型:SELECT/INSERT/UPDATE/DELETE
parameterMap入参映射
resultMaps结果映射(怎么把结果集变成对象)
cache二级缓存配置
keyGenerator主键生成策略(自增/序列)
timeout / fetchSize / statementType超时/抓取大小/Statement 类型

Configuration 持有的核心

Configuration(全局配置中心)内部:
  Map<String, MappedStatement> mappedStatements   ← 所有 SQL 语句
  Map<String, ResultMap> resultMaps               ← 所有结果映射
  TypeHandlerRegistry typeHandlerRegistry         ← 所有类型处理器
  TypeAliasRegistry typeAliasRegistry             ← 所有类型别名
  InterceptorChain interceptorChain               ← 所有插件
  各种 settings(缓存、驼峰映射、懒加载等开关)
  MapperRegistry mapperRegistry                   ← 所有 Mapper 接口
一条 SQL 执行时找 MappedStatement 的过程:
  userMapper.selectById(1)
    → Mapper 代理拦截,拼出 statement id = "com.x.UserMapper.selectById"
    → configuration.getMappedStatement("com.x.UserMapper.selectById")
    → 拿到 MappedStatement
    → Executor 按它的 SqlSource 生成 SQL、绑参数、执行、按 resultMap 映射结果

⚠️ 理解 MappedStatementConfiguration 是「读懂 MyBatis 源码和高级机制」的钥匙,和 Spring 的 BeanDefinition/ApplicationContext 是完全对应的思想:把「配置」解析成「统一的元数据对象」,运行时按元数据干活。很多 MyBatis 的「高级操作」都是在动这两个对象:MyBatis-PlusBaseMapper 单表 CRUD,本质就是启动时动态往 Configuration 里注册(addMappedStatement)一批 MappedStatement(帮你「凭空」造出 insert/select 等 SQL 语句),这和 Spring Boot「动态注册 BeanDefinition」是同一套路。理解「一条 SQL = 一个 MappedStatement,全都注册在 Configuration 的 Map 里,执行时按 id 查找」,MyBatis 的执行流程、插件机制、MP 增强就都能串起来了。

完整版教学

一、MappedStatement:一条 SQL 的完整表示

先理解 MappedStatement 承载了什么:

你在 Mapper XML 里写:
  <select id="selectById" parameterType="long" resultType="User">
    SELECT * FROM user WHERE id = #{id}
  </select>

MyBatis 解析后,这个 <select> 变成一个 MappedStatement 对象,里面记着:
  - id:UserMapper.selectById(namespace + 方法名)
  - sqlCommandType:SELECT
  - sqlSource:能生成 "SELECT * FROM user WHERE id = ?" 和参数绑定
  - resultMaps:结果按 User 映射
  - parameterType:入参是 long
  - 缓存、超时、主键策略等其他配置

所以 MappedStatement = "一条 SQL 语句的所有信息的载体"
  执行这条 SQL 需要知道的一切,都在这个对象里
  (SQL 是什么、什么类型、参数怎么绑、结果怎么映射、缓存怎么用...)

MappedStatement 是「一条 SQL 语句的完整表示」——你写的每个 <select>/@Select 都被解析成一个 MappedStatement,封装了这条 SQL 的一切(id、SQL 类型、SqlSource、结果映射、入参、缓存、主键策略等)。执行这条 SQL 需要的所有信息都在这个对象里。理解「MappedStatement 是一条 SQL 的完整表示、每个 /@Select 解析成一个,封装 id/SQL类型/SqlSource/resultMap/缓存/主键策略);SqlSource=根据入参生成最终 SQL(因动态 SQL 依赖参数不能存死字符串,#{}→?预编译);Configuration=全局配置中心+注册表(单例,持有所有 MappedStatement(Map,key=namespace.方法名)/TypeHandler/插件/settings,所有 SqlSession 共享);执行:Mapper 代理→statement id→Configuration 查 MappedStatement→Executor 执行;★类比 Spring:MappedStatement↔BeanDefinition、Configuration↔ApplicationContext(同一元数据驱动思想);MyBatis-Plus 的 BaseMapper=启动时动态 addMappedStatement(和 Spring Boot 注册 BeanDefinition 同套路)」。

七、常见误区与追问

  • 误区:MyBatis 直接存 SQL 字符串去执行。 存的是 MappedStatement(含 SqlSource),因为有动态 SQL(/),最终 SQL 依赖运行时参数、不能存死字符串;SqlSource.getBoundSql(参数) 才生成最终 SQL 和参数绑定。
  • 误区:MappedStatement 只是存了 SQL 语句。 它封装一条 SQL 的「一切」——id、SQL 类型、SqlSource、入参类型、结果映射 resultMap、缓存配置、主键生成策略、超时等;执行这条 SQL 需要的所有信息都在里面。
  • 误区:Configuration 是可有可无的配置类。 它是 MyBatis 的「大脑和注册表」——单例、持有所有 MappedStatement/TypeHandler/插件/settings,所有 SqlSession 共享;运行时所有查配置、找 SQL、找类型处理器都到它这里,没它 MyBatis 无法运行。
  • 误区:Mapper 接口方法和 XML 里的 SQL 是随意对应的。 靠 statement id 严格对应——Mapper 接口的「全限定名+方法名」必须和 XML 的「namespace+id」对上,MyBatis 才能用这个 id 从 Configuration 里找到对应的 MappedStatement;对不上会报 BindingException。
  • 追问:MyBatis-Plus 的 BaseMapper 单表 CRUD 底层是什么? MP 启动时扫描继承 BaseMapper 的接口,根据实体信息(@TableName、字段)拼出 insert/select 等 SQL,构造成 MappedStatement,调 configuration.addMappedStatement() 动态注册到 Configuration;于是这些「你没写过」的 SQL 语句就有了,调用时按 id 找到执行——和 Spring Boot 动态注册 BeanDefinition 同一套路。
  • 追问:MappedStatement 和 Spring 的 BeanDefinition 有什么共同点? 都是「把配置(XML/注解)解析成的、描述’一个东西该怎么用’的统一元数据对象」,集中注册在一个中心(Configuration/容器)里,运行时按元数据干活(执行 SQL/创建 Bean),且都能动态注册——是「元数据驱动」框架设计的同一范式。
  • 追问:一条动态 SQL(含 )的最终 SQL 是什么时候确定的? 运行时——MappedStatement 里的 SqlSource 是 DynamicSqlSource,执行时调 getBoundSql(参数),根据传入的参数判断 等动态标签,拼出最终的 SQL 字符串和参数绑定(BoundSql);所以同一个 MappedStatement 不同参数可能生成不同的最终 SQL。

八、加强记忆

MappedStatement 是 MyBatis 里「一条 SQL 语句的完整表示」——每个 <select>/<insert>/@Select 都被解析成一个 MappedStatement,封装这条 SQL 的一切:id(namespace.方法名)、SQL 类型、SqlSource(根据入参生成最终 SQL,因动态 SQL 依赖参数不能存死字符串,#{}? 预编译)、结果映射 resultMap、缓存、主键策略等。Configuration 是 MyBatis 的「全局配置中心 + 注册表」——单例(一个 SqlSessionFactory 持有一个、所有 SqlSession 共享),持有所有 MappedStatement(Map,key 是 statement id)、所有 TypeHandler、所有插件、所有 settings、数据源执行一条 SQL:Mapper 代理拦截 → 拼出 statement id → configuration.getMappedStatement(id) 查出 → Executor 用它的 SqlSource 生成 SQL、执行、按 resultMaps 映射结果(id 是接口方法和 SQL 关联的钥匙,namespace+id 必须对上否则 BindingException)。类比 SpringMappedStatementBeanDefinitionConfigurationApplicationContext——同一「元数据驱动」思想(配置解析成统一元数据、集中注册、运行时按元数据干活、可动态注册)。MyBatis-Plus 的 BaseMapper 就是启动时拼 SQL、构造 MappedStatementaddMappedStatement() 动态注册(和 Spring Boot 注册 BeanDefinition 同套路)。一句话「MappedStatement=一条 SQL 的完整元数据(id/SqlSource/resultMap/缓存),Configuration=持有所有 MappedStatement 的全局注册表(单例);执行按 statement id 查 MappedStatement;类比 BeanDefinition/ApplicationContext(元数据驱动);MP 的 BaseMapper=动态 addMappedStatement」。