开发者

MyBatis-Plus基于MyBatis封装 BaseMapper的流程步骤

目录
  • 引言
  • 一、从调用开始:BaseMapper 的使用场景
  • 二、链路追踪:从接口调用到代理执行
    • 1. 获取 Mapper 代理对象
    • 2. 方法调用拦截
  • 三、SQL 注入:BaseMapper 方法的实现来源
    • 1. 启动时的 SQL 注入
    • 2. TableInfo 的作用
  • 四、链路总结:从调用到执行的全流程
    • 五、与 MyBATis 的协作与增强
      • 六、结论

        引言

        MyBatis-Plus(简称 MP)作为 MyBatis 的增强框架,通过 BaseMapper 提供了通用的 CRUD 操作,极大地提升了开发效率。为了更透彻地理解其封装机制,本文将采用链路追踪的思维,从开发者调用接口开始,逐步深入到 MyBatis-Plus 的核心实现,分析其如何基于 MyBatis 完成对 BaseMapper 的封装。

        一、从调用开始:BaseMapper 的使用场景

        假设我们有一个简单的实体类 User 和对应的 Mapper 接口:

        @TableName("t_user")
        public class User {
            @TableId
            private Long id;
            private String name;
            private Integer age;
            // getter 和 setter 省略
        }
        
        public interface UserMapper extends BaseMapper<User> {
        }
        

        开发者只需这样调用:

        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User user = userMapper.selectById(1L);
        

        表面上看,selectByIdBaseMapper 提供的方法,但其背后是如何实现的呢?让我们沿着调用链路逐步追踪。

        二、链路追踪:从接口调用到代理执行

        1. 获取 Mapper 代理对象

        当调用 sqlSe编程客栈ssion.getMapper(UserMapper.class) 时,MyBatis 的 SqlSession 会委托给 ConfigurationgetMapper 方法:

        // MyBatis: org.apache.ibatis.session.Configuration
        public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
            return mapperRegistry.getMapper(type, sqlSession);
        }
        

        这里的 mapperRegistry 是 MyBatis 的 MapperRegistry 类,但在 MyBatis-Plus 中被替换为 MybatisMapperRegistry。链路进入 MyBatis-Plus 的自定义实现:

        // MyBatis-Plus: com.baomidou.mybatisplus.core.MybatisMapperRegistry
        public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
            MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
            if (mapperProxyFactory == null) {
                throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
            }
            return mapperProxyFactory.newInstance(sqlSession);
        }
        
        • 关键点:MyBatis-Plus 重写了 MapperRegistry,并在启动时通过扫描将所有继承 BaseMapper 的接口注册到 knownMappers 中。
        • 代理生成newInstance 方法创建了一个 MapperProxy 对象,这是 JDK 动态代理的实现。

        2. 方法调用拦截

        当调用 userMapper.selectById(1L) 时,代理对象 MapperProxyinvoke 方法被触发:

        // MyBatis-Plus: com.baomidou.mybatisplus.core.override.MybatisMapperProxy
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            }
            MybatisMapperMethod mapperMethod = cachedMapperMethod(method);
            return mapperMethod.execute(sqlSession, args);
        }
        
        • 链路分叉cachedMapperMethod 会根据方法签名缓存并返回一个 MybatisMapperMethod 对象。
        • 执行逻辑execute 方法根据方法名和参数决定具体的执行路径。

        对于 selectById,链路进入 MybatisMapperMethod.execute

        // MyBatis-Plus: cohttp://www.devze.comm.baomidou.mybatisplus.core.override.MybatisMapperMethod
        public Object execute(SqlSession sqlSession, Object[] args) {
            if (SqlCommandType.SELECT == command.getType()) {
                if (method.getReturnTyp编程客栈e().isAssignableFrom(List.class)) {
                    return sqlSession.selectList(command.getName(), args);
                }
                return sqlSession.selectOne(command.getName(), args[0]);
            }
            // 其他类型如 INSERT、UPDATE 等略
        }
        
        • 关键点command.getName() 返回的是一个全局唯一的 SQL ID,例如 com.baomidou.mybatisplus.core.mapper.BaseMapper.selectById
        • 执行 SQL:最终调用 MyBatis 的 selectOne 方法执行查询。

        三、SQL 注入:BaseMapper 方法的实现来源

        问题来了:selectById 的 SQL 是从哪里来的?答案在于 MyBatis-Plus 的 SQL 注入机制。

        1. 启动时的 SQL 注入

        MyBatis-Plus 在 Spring 容器初始化时,通过 MapperScannerConfigurer 扫描 Mapper 接口,并调用 ISqlInjector 注入通用 SQL:

        // MyBatis-Plus: com.baomidou.mybatisplus.core.injector.DefaultSqlInjector
        public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
            List<AbstractMethod> methodList = getMethodList(mapperClass);
            for (AbstractMethod method : methodList) {
                method.inject(builderAssistant, mapperClass);
            }
        }
        
        • 方法列表getMethodList 返回 BaseMapper 中定义的所有方法(如 selectByIdinsert 等)的实现类,例如 SelectById
        • 注入过程:以 SelectById 为例:
        // MyBatis-Plus: com.baomidou.mybatisplus.core.i编程njector.methods.SelectById
        public void injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
            String sql = String.format("<script>SELECT %s FROM %s WHERE %s = #{id}</script>",
                sqlSelectColumns(), tableInfo.getTableName(), tableInfo.getKeyColumn());
            SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
            addSelectMappedStatement(mapperClass, "selectById", sqlSource, modelClass, tableInfo);
        }
        
        • SQL 生成:根据 TableInfo(通过反射解析实体类)动态生成 SELECT * FROM t_user WHERE id = #{id}
        • 注册:将生成的 MappedStatement 注册到 MyBatis 的 Configuration 中。

        2. TableInfo 的作用

        TableInfo 是 MyBatis-Plus 的核心元数据类,通过 TableInfoHelper 在启动时解析实体类:

        • @TableName("t_user") → 表名 t_user
        • @TableId → 主键字段 id
        • 字段映射 → 自动推断 nameage 等列名。

        这些信息为 SQL 注入提供了基础数据。

        四、链路总结:从调用到执行的全流程

        • 开发者调用userMapper.selectById(1L)
        • 代理拦截MybatisMapperProxy.invokeMybatisMapperMethod.execute
        • SQL 执行sqlSession.selectOne → MyBatis 执行预注入的 MappedStatement
        • 结果映射:MyBatis 将查询结果映射为 User 对象返回。

        背后支持:

        • 启动时:扫描 Mapper → 注入通用 SQL → 注册 MappedStatement
        • 运行时:动态代理 → 方法路由 → SQL 执行。

        五、与 MyBatis www.devze.com的协作与增强

        • 复用 MyBatis:动态代理、SqlSession、结果映射等核心机制完全继承自 MyBatis。
        • 增强点

          SQL 自动化:通过 ISqlInjector 注入通用 SQL。

          元数据管理TableInfo 实现实体与表的自动映射。

          条件构造Wrapper 扩展了动态查询能力。

        六、结论

        MyBatis-Plus 对 BaseMapper 的封装,是在 MyBatis 动态代理和 SQL 执行框架上的巧妙扩展。通过启动时的 SQL 注入和运行时的代理拦截,它实现了通用 CRUD 的零配置使用。链路追踪显示,这种设计既保留了 MyBatis 的灵活性,又通过自动化大幅提升了开发效率,堪称对 MyBatis 的“完美补完”。

        以上就是MyBatis-Plus基于MyBatis封装 BaseMapper的流程步骤的详细内容,更多关于MyBatis-Plus封装 BaseMapper的资料请关注编程客栈(www.devze.com)其它相关文章!

        0

        上一篇:

        下一篇:

        精彩评论

        暂无评论...
        验证码 换一张
        取 消

        最新开发

        开发排行榜