MappedStatement 是 MyBatis 框架中一个极其核心且重要的对象。我们可以把它理解为一条 SQL 语句(包括 <select>, <insert>, <update>, <delete> 等标签定义的语句)在 MyBatis 内部的完整表示。它封装了执行这条 SQL 所需的所有静态信息。
MappedStatement 封装了哪些信息?
MappedStatement 包含的信息非常丰富,几乎涵盖了执行一个数据库操作所需的所有配置细节:
- ID (id): 语句的唯一标识符,通常是
namespace + "." + statementId(例如com.example.mapper.UserMapper.selectUserById)。MyBatis 通过这个 ID 来查找和定位要执行的 SQL 语句。 - SQL 源 (SqlSource): 这是最重要的部分之一。它不是简单的 SQL 字符串,而是一个能够根据传入参数动态生成最终可执行 SQL 的对象。
- 对于静态 SQL(没有
<if>,<foreach>等动态标签),它可能是StaticSqlSource。 - 对于包含动态标签的 SQL,它会是
DynamicSqlSource,在运行时解析动态标签并生成最终 SQL。
- 对于静态 SQL(没有
- 命令类型 (SqlCommandType): 标识 SQL 语句的类型,是
SELECT,INSERT,UPDATE,DELETE中的一种。这决定了 MyBatis 底层会调用 JDBC 的哪种执行方法(executeQuery,executeUpdate)。 - 参数类型 (parameterType): 定义了传入该 SQL 语句的参数对象的类型(例如
int,String,java.util.Map, 或某个自定义 Pojo 类)。MyBatis 会根据这个类型处理参数。 - 参数映射 (parameterMap/parameterMappings): 定义了如何将传入的 Java 对象属性映射到 SQL 语句中的占位符 (
#{},${})。parameterMappings是一个ParameterMapping对象的列表,包含了每个参数的属性名、Java 类型、JDBC 类型等信息。 - 结果类型 (resultType): 对于
SELECT语句,定义了期望返回结果的类型(例如int,String,java.util.Map, 或某个自定义 Pojo 类)。适用于简单结果映射,MyBatis 会尝试自动将列名匹配到对象属性名。 - 结果映射 (resultMap/resultMaps): 对于
SELECT语句,提供了更复杂的、自定义的结果映射规则。ResultMap对象包含了列名到 Java 对象属性名的详细映射关系、关联查询(association,collection)、鉴别器(discriminator)等高级特性。resultMaps是一个ResultMap对象的列表。 - 缓存 (Cache): 关联的二级缓存配置信息。
- 刷新缓存 (flushCacheRequired): 指示执行此语句后是否需要清空(刷新)二级缓存(通常
INSERT,UPDATE,DELETE会设置为 true)。 - 使用缓存 (useCache): 指示执行
SELECT语句时是否尝试从二级缓存中获取结果。 - 超时设置 (timeout): SQL 执行的超时时间(单位秒)。
- 获取大小 (fetchSize): JDBC 驱动程序每次批量从数据库获取的记录条数。
- 语句类型 (statementType):
STATEMENT,PREPARED,CALLABLE中的一种,指示 MyBatis 使用 JDBC 的哪种 Statement 类型执行 SQL(默认为PREPARED,即PreparedStatement)。 - 结果集类型 (resultSetType):
FORWARD_ONLY,SCROLL_SENSITIVE,SCROLL_INSENSITIVE中的一种,控制 JDBCResultSet的类型。 - 数据库厂商 ID (databaseId): 用于支持多数据库厂商的 SQL 语句。
- 语言驱动 (lang): 用于解析 SQL 语句的语言驱动,允许扩展(默认是处理 XML 标签的驱动)。
- 主键生成策略 (keyGenerator, keyProperties, keyColumn): 用于处理
INSERT语句后获取自动生成的主键。
MappedStatement 是如何被创建的?
MappedStatement 的创建发生在 MyBatis 初始化阶段,当 MyBatis 解析配置文件(mybatis-config.xml)和 Mapper 文件(XML 或注解)时:
- 解析入口: MyBatis 的
Configuration对象负责管理所有的配置信息。在构建SqlSessionFactory的过程中,会触发对 Mapper 文件(通过<mappers>标签或mapper-locations指定)或 Mapper 接口注解的解析。 - XML 解析: 对于 XML Mapper 文件,
XMLMapperBuilder会负责解析文件内容。当遇到<select>,<insert>,<update>,<delete>等 SQL 语句标签时,会调用XMLStatementBuilder。 - 注解解析: 对于使用注解的 Mapper 接口,
MapperAnnotationBuilder会解析接口及其方法上的@Select,@Insert等注解。 - 信息提取: 无论是
XMLStatementBuilder还是MapperAnnotationBuilder,它们都会从 XML 标签的属性或注解中提取上面列出的各种配置信息(ID、parameterType、resultType、SQL 内容等)。 - 创建 SqlSource: 根据 SQL 内容是否包含动态标签,创建相应的
SqlSource对象(StaticSqlSource或DynamicSqlSource)。 - 构建 MappedStatement: 使用
MappedStatement.Builder模式,将提取到的所有信息(包括创建好的SqlSource)设置到 Builder 中,最后调用build()方法创建一个不可变 (Immutable) 的MappedStatement对象。 - 注册到 Configuration: 创建好的
MappedStatement对象会被添加到Configuration对象的一个Map(mappedStatements)中,以其唯一的 ID 作为 Key 进行存储。
这个创建过程在应用程序启动时完成一次,之后 MappedStatement 对象就会常驻内存,供后续运行时使用。
MappedStatement 是如何被使用的?
MappedStatement 在运行时被用来执行实际的数据库操作:
- Mapper 接口调用: 当你调用一个 Mapper 接口的方法时(例如
userMapper.selectUserById(1)),MyBatis 的代理机制会拦截这个调用。 - 定位 MappedStatement: MyBatis 根据调用的接口方法名(结合接口的命名空间)生成
MappedStatement的 ID,然后从Configuration的mappedStatementsMap 中查找并获取对应的MappedStatement对象。 - 获取 SqlSession 和 Executor: 调用通常发生在
SqlSession的上下文中。SqlSession会获取一个Executor(执行器,如SimpleExecutor,ReuseExecutor,BatchExecutor)。 - 参数处理和 SQL 生成:
Executor会使用MappedStatement中的SqlSource和传入的参数对象,调用SqlSource.getBoundSql(parameterObject)方法。这一步会处理动态 SQL 标签,生成最终的可执行 SQL 字符串以及对应的参数映射信息,封装在BoundSql对象中。BoundSql包含了本次执行的具体 SQL 和参数值。 - 语句执行:
Executor根据MappedStatement中的SqlCommandType和statementType,使用 JDBC 连接和BoundSql中的信息,创建PreparedStatement(或其他类型 Statement),设置参数,并执行 SQL 语句(调用executeQuery或executeUpdate)。 - 结果处理 (对 SELECT): 如果是
SELECT语句,Executor会获取ResultSet。然后,ResultSetHandler会利用MappedStatement中定义的resultMap或resultType信息,将ResultSet中的数据映射成 Java 对象(或列表、Map)。 - 缓存处理: 在执行前后,
Executor会根据MappedStatement的缓存配置进行二级缓存的读写或刷新操作。 - 返回结果: 最终的处理结果(查询到的对象、影响的行数等)被返回给调用者(Mapper 接口的调用处)。
总结:
MappedStatement 是 MyBatis 中定义和存储单个 SQL 操作所有静态配置信息的蓝图或模板。它在 MyBatis 初始化时被解析和创建,并存储在全局的 Configuration 中。在运行时,MyBatis 根据方法调用找到对应的 MappedStatement,并结合传入的参数动态生成 SQL (BoundSql),然后利用 MappedStatement 中的配置信息(如结果映射、缓存策略等)来执行数据库操作和处理结果。
559

被折叠的 条评论
为什么被折叠?



