手写MyBatis第36弹:MyBatis执行流程中SQL命令类型解析

发布于:2025-08-29 ⋅ 阅读:(16) ⋅ 点赞:(0)

🥂(❁´◡`❁)您的点赞👍➕评论📝➕收藏⭐是作者创作的最大动力🤞

💖📕🎉🔥 支持我:点赞👍+收藏⭐️+留言📝欢迎留言讨论

🔥🔥🔥(源码 + 调试运行 + 问题答疑)

🔥🔥🔥  有兴趣可以联系我。

我们常常在当下感到时间慢,觉得未来遥远,但一旦回头看,时间已经悄然流逝。对于未来,尽管如此,也应该保持一种从容的态度,相信未来仍有许多可能性等待着我们。 

目录

1 SQL命令类型概述与重要性

2 XML配置方式解析SQL命令类型

2.1 解析过程与关键代码

2.2 XML配置示例

3 注解方式解析SQL命令类型

3.1 注解解析过程

3.2 注解配置示例

4 MappedStatement的创建与SQL命令类型的设置

5 SQL命令类型在Executor中的使用

5.1 执行路由决策

5.2 其他影响

6 总结


MyBatis SQL命令类型解析:从注解与XML到MappedStatement的奥秘

  1. 《MyBatis SQL命令类型解析:揭秘@Select/@Insert与XML节点的执行决策依据》

  2. 《深入MyBatis内核:SQL操作类型如何影响Executor的行为路径》

  3. 《手写MyBatis(七):解析SQL命令类型,奠定执行决策基石》

  4. 《从注解/XML到MappedStatement:MyBatis如何确定你的SQL要做什么?》

  5. 《SQL命令类型的奥秘:MyBatis执行流程中的关键枚举值》


MyBatis作为一个优秀的持久层框架,其核心功能之一便是将开发者编写的SQL语句(无论是通过XML还是注解方式)转化为对数据库的实际操作。在这个过程中,准确识别SQL语句的类型(SELECT, INSERT, UPDATE, DELETE)至关重要,因为它直接决定了MyBatis底层执行引擎的行为路径。本文将深入剖析MyBatis如何解析并确定SQL命令类型,并探讨这一信息在后续执行流程中的关键作用。

1 SQL命令类型概述与重要性

在MyBatis中,SqlCommandType是一个枚举类型,它定义了以下几种SQL操作类型:

  • SELECT:对应查询操作,用于从数据库检索数据。

  • INSERT:对应插入操作,用于向数据库插入新数据。

  • UPDATE:对应更新操作,用于更新数据库中的现有数据。

  • DELETE:对应删除操作,用于从数据库中删除数据。

  • (还有其他如FLUSH等较少用的类型)

MappedStatement 对象是 MyBatis 框架的核心类之一,它存储了一个 SQL 对应的所有信息。其 sqlCommandType 属性记录了该 SQL 语句的操作类型。明确记录每条SQL语句的操作类型,是执行时决策(如调用Executor.query还是Executor.update)的依据。

SQL命令类型的重要性主要体现在以下几个方面:

  1. 执行路由决策:MyBatis的Executor会根据sqlCommandType来决定是调用doUpdate方法还是doQuery方法。

  2. 缓存行为控制:例如,SELECT语句通常会查询缓存,而INSERTUPDATEDELETE语句则可能刷新缓存(根据配置)。

  3. 主键生成处理INSERT操作可能需要处理自增主键或序列主键的获取。

  4. 事务语义理解:虽然事务通常由外部管理,但了解操作类型有助于理解数据变更的边界。

2 XML配置方式解析SQL命令类型

当MyBatis解析XML映射文件时,会根据SQL语句所在的XML节点名称来确定其命令类型。

2.1 解析过程与关键代码

MyBatis通过XMLStatementBuilder类来解析XML中的SQL节点。解析过程中,会提取节点的名称,并将其转换为对应的SqlCommandType

 public class XMLStatementBuilder {
     public void parseStatementNode() {
         // 获取XML节点的名称,如 "select", "insert", "update", "delete"
         String nodeName = context.getNode().getNodeName();
         // 将节点名称转换为大写,并匹配到SqlCommandType枚举值
         SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
         // ... 后续构建MappedStatement时,会使用这个sqlCommandType
     }
 }

2.2 XML配置示例

以下是一个XML映射文件的示例,展示了不同类型的SQL语句:

 <mapper namespace="com.example.MyMapper">
     <select id="selectUser" resultType="User">
         SELECT * FROM user WHERE id = #{id}
     </select>
     
     <insert id="insertUser" parameterType="User" useGeneratedKeys="true" keyProperty="id">
         INSERT INTO user (name, email) VALUES (#{name}, #{email})
     </insert>
     
     <update id="updateUser" parameterType="User">
         UPDATE user SET name = #{name}, email = #{email} WHERE id = #{id}
     </update>
     
     <delete id="deleteUser" parameterType="int">
         DELETE FROM user WHERE id = #{id}
     </delete>
 </mapper>

MyBatis在解析上述XML文件时,会根据<select><insert><update><delete>这些节点名称,分别创建SqlCommandTypeSELECTINSERTUPDATEDELETEMappedStatement对象。

3 注解方式解析SQL命令类型

对于使用注解配置的SQL语句,MyBatis通过MapperAnnotationBuilder来解析方法上的注解,并确定其SQL命令类型。

3.1 注解解析过程

MyBatis支持的SQL注解包括@Select@Insert@Update@Delete等。解析器会检查方法上的注解,并根据注解类型来确定SQL命令类型。

 public class MapperAnnotationBuilder {
     private void parseStatement(Method method) {
         // 获取方法上的注解
         Annotation annotation = findAnnotation(method, Select.class, Insert.class, Update.class, Delete.class, ...);
         SqlCommandType sqlCommandType;
         
         if (annotation instanceof Select) {
             sqlCommandType = SqlCommandType.SELECT;
         } else if (annotation instanceof Insert) {
             sqlCommandType = SqlCommandType.INSERT;
         } else if (annotation instanceof Update) {
             sqlCommandType = SqlCommandType.UPDATE;
         } else if (annotation instanceof Delete) {
             sqlCommandType = SqlCommandType.DELETE;
         } else {
             // 处理其他情况或报错
         }
         
         // ... 使用sqlCommandType构建MappedStatement
     }
 }

3.2 注解配置示例

public interface UserMapper {
     @Select("SELECT * FROM users WHERE id = #{id}")
     User selectUserById(int id);
 ​
     @Insert("INSERT INTO users (name, email) VALUES (#{name}, #{email})")
     @Options(useGeneratedKeys = true, keyProperty = "id")
     int insertUser(User user);
 ​
     @Update("UPDATE users SET name = #{name}, email = #{email} WHERE id = #{id}")
     int updateUser(User user);
 ​
     @Delete("DELETE FROM users WHERE id = #{id}")
     int deleteUser(int id);
 }

当MyBatis解析UserMapper接口时,它会读取每个方法上的注解,并根据注解类型(@Select, @Insert, @Update, @Delete)为每个方法对应的MappedStatement设置相应的SqlCommandType

4 MappedStatement的创建与SQL命令类型的设置

无论是通过XML还是注解方式解析,最终都会构建一个MappedStatement对象,其中包含了该SQL语句的所有配置信息,包括sqlCommandType

MappedStatement 对象通过 Builder 模式创建,并在构建过程中设置其属性,包括 sqlCommandType

MappedStatement 的关键属性包括

  • id:通常由命名空间和方法名组成,唯一标识一个MappedStatement。

  • sqlSource:表示解析出来的SQL。

  • sqlCommandType:SQL命令类型(SELECT, INSERT, UPDATE, DELETE等)。

  • parameterType:参数类型。

  • resultType/resultMap:结果类型或结果映射。

  • useCacheflushCache等缓存相关属性。

5 SQL命令类型在Executor中的使用

SQL命令类型(sqlCommandType)信息在Executor的执行方法中被使用,它是决定调用Executorquery方法还是update方法的关键依据。

5.1 执行路由决策

当通过SqlSession调用一个Mapper方法时,MyBatis会找到对应的MappedStatement,并根据其sqlCommandType来决定执行路径:

 public class CachingExecutor implements Executor {
     @Override
     public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
         // ... 处理查询操作(SELECT语句)
     }
 ​
     @Override
     public int update(MappedStatement ms, Object parameterObject) throws SQLException {
         // ... 处理更新操作(INSERT, UPDATE, DELETE语句)
     }
 }

DefaultSqlSession中,你可以看到根据sqlCommandType进行路由的逻辑:

public class DefaultSqlSession implements SqlSession {
     @Override
     public int insert(String statement, Object parameter) {
         return update(statement, parameter);
     }
     
     @Override
     public int update(String statement, Object parameter) {
         // 对于INSERT, UPDATE, DELETE语句,最终都调用update方法
         MappedStatement ms = configuration.getMappedStatement(statement);
         return executor.update(ms, wrapCollection(parameter));
     }
     
     @Override
     public <E> List<E> selectList(String statement, Object parameter) {
         // 对于SELECT语句,调用query方法
         MappedStatement ms = configuration.getMappedStatement(statement);
         return executor.query(ms, wrapCollection(parameter), RowBounds.DEFAULT, null);
     }
 }

值得注意的是,虽然SqlSession提供了insert, update, delete等不同方法,但在MyBatis底层,INSERT, UPDATE, DELETE操作最终都调用Executor.update方法,而SELECT操作则调用Executor.query方法。这是因为从数据库的角度来看,INSERTUPDATEDELETE都是写操作,会改变数据库状态,而SELECT是读操作。

5.2 其他影响

除了决定调用query还是update方法外,sqlCommandType还会影响:

  1. 缓存行为SELECT语句的结果可能会被缓存(根据配置),而写操作通常会刷新缓存。

  2. 事务管理:虽然事务通常由外部管理,但了解操作类型有助于理解数据变更的边界。

  3. 主键生成:对于INSERT语句,MyBatis可能会在处理后获取自增主键值并设置回参数对象。

6 总结

SQL命令类型的解析是MyBatis执行流程中的基础环节,它架起了开发者编写的SQL语句与底层数据库操作之间的桥梁。通过深入了解SqlCommandType的解析过程和作用机制,我们不仅能更好地理解MyBatis的内部工作原理,也能在遇到问题时更快地定位和解决。

无论是XML配置还是注解配置,MyBatis都提供了一致的机制来解析和确定SQL命令类型,并将其封装在MappedStatement对象中。最终,这一信息在Executor的执行方法中被关键性地使用,决定了SQL语句的执行路径和行为,确保了MyBatis能够正确、高效地处理各种数据库操作。


💖学习知识需费心,
📕整理归纳更费神。
🎉源码免费人人喜,
🔥码农福利等你领!

💖常来我家多看看,
📕我是程序员扣棣,
🎉感谢支持常陪伴,
🔥点赞关注别忘记!

💖山高路远坑又深,
📕大军纵横任驰奔,
🎉谁敢横刀立马行?
🔥唯有点赞+关注成!