MyBatis 源码解析:MapperMethod 与 SqlCommand 解析

摘要

MapperMethodSqlCommand 是 MyBatis 中的核心组件,负责将 Mapper 接口方法映射为 SQL 语句并执行操作。本文将通过自定义实现模拟这些组件,解析其工作原理,帮助开发者理解如何将接口方法转换为 SQL 语句,并解析源码以加深对这一机制的理解。


前言

MyBatis 通过 MapperMethodSqlCommand 实现了接口方法到 SQL 操作的动态映射。开发者定义的 Mapper 接口没有具体实现,而是在运行时动态生成代理类并执行数据库操作。这种设计极大简化了数据库操作的编写工作,同时提高了代码的可维护性。

本文将通过详细的源码解析,展示 MapperMethodSqlCommand 的内部实现逻辑,并通过自定义实现展示如何将接口方法与 SQL 操作关联。


自定义实现:模拟 MapperMethod 和 SqlCommand 的映射机制

目标与功能

通过自定义实现一个简化的 MapperMethodSqlCommand 类,完成以下功能:

  1. 解析接口方法:通过 MapperMethod 解析接口方法的调用。
  2. SQL 操作映射:通过 SqlCommand 解析 SQL 的类型及生成 SQL 语句。
  3. 执行 SQL 操作:根据方法名执行相应的 SQL 语句,并返回模拟结果。

实现步骤

  1. 定义 Mapper 接口:创建一个 UserMapper 接口,用于模拟查询和插入操作。
  2. 创建 SqlCommand:负责解析 SQL 操作类型,并生成 SQL 语句。
  3. 创建 MapperMethod:负责执行映射的 SqlCommand,并返回结果。

1. 定义 Mapper 接口

我们首先定义一个 UserMapper 接口,包含常见的查询用户信息和插入用户的操作。

public interface UserMapper {
    String getUserById(int id);
    void insertUser(String name, int age);
}
  • 功能getUserById 用于查询用户信息,insertUser 用于插入新用户。
  • 说明:这些方法没有具体实现,MyBatis 会在运行时将其映射为 SQL 并执行。

2. 创建 SqlCommand

SqlCommand 负责解析 Mapper 方法的 SQL 操作类型(如 SELECTINSERT 等),并将其映射为具体 SQL 语句。

public class SqlCommand {
    private final String name;
    private final String sql;
    private final CommandType commandType;

    public enum CommandType {
        SELECT, INSERT, UPDATE, DELETE
    }

    public SqlCommand(String name, CommandType commandType, String sql) {
        this.name = name;
        this.commandType = commandType;
        this.sql = sql;
    }

    public String getSql() {
        return sql;
    }

    public CommandType getCommandType() {
        return commandType;
    }
}
实现细节
  • CommandType:用于区分 SQL 操作的类型(如 SELECTINSERT)。
  • SqlCommand:存储方法名、SQL 类型及对应的 SQL 语句。

3. 创建 MapperMethod

MapperMethod 负责将 Mapper 接口的方法映射为 SqlCommand,并在运行时执行相应的 SQL 操作。

import java.util.HashMap;
import java.util.Map;

public class MapperMethod {
    private final Map<String, SqlCommand> methodSqlMap = new HashMap<>();

    public MapperMethod() {
        methodSqlMap.put("getUserById", new SqlCommand("getUserById", SqlCommand.CommandType.SELECT, "SELECT * FROM users WHERE id = ?"));
        methodSqlMap.put("insertUser", new SqlCommand("insertUser", SqlCommand.CommandType.INSERT, "INSERT INTO users (name, age) VALUES (?, ?)"));
    }

    public Object execute(String methodName, Object[] args) {
        SqlCommand command = methodSqlMap.get(methodName);

        if (command != null) {
            switch (command.getCommandType()) {
                case SELECT:
                    System.out.println("执行SQL:" + command.getSql() + " 参数:" + args[0]);
                    return "User" + args[0];
                case INSERT:
                    System.out.println("执行SQL:" + command.getSql() + " 参数:" + args[0] + ", " + args[1]);
                    return null;
                default:
                    throw new UnsupportedOperationException("不支持的命令类型");
            }
        }
        throw new IllegalArgumentException("未找到 SQL 映射: " + methodName);
    }
}
实现细节
  • methodSqlMap:将 Mapper 接口方法与 SqlCommand 映射。
  • execute 方法:根据方法名执行对应的 SQL 操作,并返回结果。

4. 测试自定义实现

通过调用 MapperMethodexecute 方法,测试 SQL 语句的映射和执行。

public class TestMapperMethod {
    public static void main(String[] args) {
        MapperMethod mapperMethod = new MapperMethod();
        
        // 测试 getUserById 方法
        Object result = mapperMethod.execute("getUserById", new Object[]{1});
        System.out.println("查询结果:" + result);
        
        // 测试 insertUser 方法
        mapperMethod.execute("insertUser", new Object[]{"Alice", 25});
    }
}

输出结果

执行SQL:SELECT * FROM users WHERE id = ? 参数:1
查询结果:User1
执行SQL:INSERT INTO users (name, age) VALUES (?, ?) 参数:Alice, 25
说明
  • getUserById 执行了查询 SQL,并返回了模拟查询结果。
  • insertUser 执行了插入 SQL,成功输出了 SQL 执行日志。

5. 类图

SqlCommand
+String name
+String sql
+CommandType commandType
+getSql()
+getCommandType()
MapperMethod
-Map<String, SqlCommand> methodSqlMap
+execute(String methodName, Object[] args)
UserMapper
+getUserById(int id)
+insertUser(String name, int age)
类图说明:
  • UserMapper 是用户定义的接口,没有实际的实现类。
  • MapperMethod 负责将接口方法与 SqlCommand 进行关联,并通过 execute 方法执行 SQL。
  • SqlCommand 保存了方法的 SQL 信息以及执行类型。

6. 流程图

接口调用
进入 MapperMethod 的 execute 方法
查找对应的 SqlCommand
执行 SQL
返回结果
流程图说明:
  • 当调用 UserMapper 接口方法时,进入 MapperMethodexecute 方法,查找对应的 SqlCommand,执行对应的 SQL,并返回结果。

源码解析:MapperMethod 与 SqlCommand

在 MyBatis 中,MapperMethodSqlCommand 负责将接口方法与 SQL 操作关联,并在运行时执行对应的 SQL。

1. MapperMethod

MapperMethod 结合 SqlCommand,将接口方法与 SQL 操作关联,并执行 SQL。源码中,MapperMethod 使用 SqlSession 来执行查询或更新操作。

public class MapperMethod {
    private final SqlCommand command;

    public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
        this.command = new SqlCommand(mapperInterface, method, config);
    }

    public Object execute(SqlSession sqlSession, Object[] args) {
        switch (command.getType()) {
            case INSERT:
                return sqlSession.insert(command.getName(), args);
            case SELECT:
                return sqlSession.selectOne(command.getName(), args);
            default:
                throw new BindingException("未知的执行方法");
        }
    }
}

2. SqlCommand

SqlCommand 负责解析 SQL 操作类型(如 SELECTINSERT 等),并映射为具体 SQL。

public class SqlCommand {
    private final String name;
    private final CommandType type;

    public SqlCommand(Class<?> mapperInterface, Method method, Configuration config) {
        this.name = mapperInterface.getName() + "." + method.getName();
        this.type = resolveType(method);
    }

    private CommandType resolveType(Method method) {
        if (method.isAnnotationPresent(Insert.class)) {
            return CommandType.INSERT;
        } else if (method.isAnnotationPresent(Select.class)) {
            return CommandType.SELECT;
        }
        throw new BindingException("未知的执行方法");
   

```java
    public String getName() {
        return name;
    }

    public CommandType getType() {
        return type;
    }
}

总结

本文通过自定义实现详细解析了 MyBatis 中 MapperMethodSqlCommand 的工作原理。MapperMethod 负责将接口方法映射为 SQL 操作,SqlCommand 负责处理 SQL 类型和映射逻辑。通过类图和流程图的结合,我们更清晰地理解了接口方法到 SQL 执行的过程。希望这篇文章能帮助你理解 MyBatis 的动态代理和 SQL 映射机制。

如果你觉得这篇文章对你有帮助,请点赞、收藏,并在评论区留言!

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

捕风捉你

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值