Trae复刻Mybatis之旅(一):创建SqlSession会话,构建代理

记录通过AI,从零构建复刻简化版Mybatis之旅, 项目地址:mini-mybatis

前言

在之前的Mybatis 源码分析专栏里,笔者带着大家深入研究Mybatis的源码。当时,我用解读核心代码、绘制类调用图的方式,一点一点剖析Mybatis的内部结构,就是想把它的工作原理清清楚楚地展示给大家。不过话说回来,光看代码、调试流程,效果还是有限。就像学骑车,光看教程、看别人骑,不如自己跨上车试试,代码只有亲手写一遍代码,印象才更深刻。

我们的目标也很简单,通过 AI 辅助开发一个简化版 MyBatis实现从 XML 解析到代理构建 的核心流程,并完成 SQL 执行。具体以下代码为参照,逐步复刻其背后逻辑:

java 复制代码
  //<1> 加载配置文件
  InputStream is = Resources.getResourceAsStream("mybatis.xml");
  //<2> 创建sessionFactory对象
  sessionFactory = new SqlSessionFactoryBuilder().build(is);
  //<3> 获取sqlSession对象信息
  SqlSession session = factoy.openSqlSession()
  //<4> 构建映射器的代理对象
  UserMapper mapper = session.getMapper(UserMapper.class);
  // .....调用相关方法信息

上述代码的逻辑即:

  1. 通过工厂构造器构建一个SqlSessionFactory对象

  2. 通过SqlSessionFactory获取到一个sqlSession对象

  3. sqlSession中传入一个Mapper对象信息,同时获取到一个Mapper对象

  4. 获取Mapper对象的代理类,调用接口中的方法,从而执行相关sql语句

核心目标与架构规划

作为系列开篇,本次聚焦于 构建 SqlSession 会话实现 Mapper 代理对象的动态生成。MyBatis 核心逻辑可简化为以下流程:

SqlSession 接口设计与精简

初始设计:基于 AI 生成的完整接口

Q: 你好我现在期待从零构模拟建一个Mybatis,我们今天首先搭建一个最基础的SqlSession接口

A: 下面为你从零开始搭建一个最基础的 SqlSession 接口。SqlSession 是 MyBatis 里核心接口之一,它提供了执行 SQL 语句、管理事务等功能。 在你当前编辑的文件 sqlSession/SqLSession.java

java 复制代码
package cn.mini.mybatis.sqlSession;

import java.util.List;

/**
 * SqlSession 是 MyBatis 的核心接口,提供了执行 SQL 语句和管理事务的方法。
 */
public interface SqLSession {

    /**
     * 根据指定的 SQL 语句和参数查询单条记录,并将结果映射到指定类型的对象中。
     * 
     * @param <T>       结果对象的类型
     * @param statement SQL 语句的唯一标识,通常为 "命名空间.id" 的形式
     * @param parameter 查询参数
     * @return 查询结果对象,如果没有匹配的记录则返回 null
     */
    <T> T selectOne(String statement, Object parameter);


    /**
     * 根据指定的 SQL 语句和参数查询多条记录,并将结果映射到指定类型的对象列表中。
     * 
     * @param <E>       结果对象的类型
     * @param statement SQL 语句的唯一标识,通常为 "命名空间.id" 的形式
     * @param parameter 查询参数
     * @return 查询结果对象列表,如果没有匹配的记录则返回空列表
     */
    <E> List<E> selectList(String statement, Object parameter);


    /**
     * 根据指定的 SQL 语句和参数插入一条记录。
     * 
     * @param statement SQL 语句的唯一标识,通常为 "命名空间.id" 的形式
     * @param parameter 插入参数
     * @return 插入操作影响的行数
     */
    int insert(String statement, Object parameter);

 
   // .... 省略部分方法
}

AI生成的SqlSession代码来看,其基本就是MybatisSqlSession的完全版.其包含了Sql的执行,事务管理等操作。

精简设计:聚焦核心功能

而作为第一次的项目的初始化,完全没必要如此多的方法。同时,为了避免一开始就使得项目变的庞大,我们继续对其提出进一步的优化建议:

Q : 我们最开始的SqlSession无需如此多的内容,我们仅需构建个查询逻辑,一个获取动态代理的Mapper的方法即可。

A: 以下是简化后的 SqlSession 接口代码,仅包含一个查询逻辑方法和一个获取动态代理的 Mapper 方法:

java 复制代码
/**
 * SqlSession 是 MyBatis 简化版的核心接口,提供基本的查询功能和获取 Mapper 动态代理的方法。
 */
public interface SqLSession {

    /**
     * 根据指定的 SQL 语句和参数查询多条记录,并将结果映射到指定类型的对象列表中。
     * 
     * @param <E>       结果对象的类型
     * @param statement SQL 语句的唯一标识,通常为 "命名空间.id" 的形式
     * @param parameter 查询参数
     * @return 查询结果对象列表,如果没有匹配的记录则返回空列表
     */
    <E> List<E> selectList(String statement, Object parameter);

    /**
     * 获取指定类型的 Mapper 接口的动态代理对象。
     * 
     * @param <T>  Mapper 接口的类型
     * @param type Mapper 接口的 Class 对象
     * @return 指定类型的 Mapper 接口的动态代理对象
     */
    <T> T getMapper(Class<T> type);
}

经过精简后SqlSession中仅包含两个方法。selectList 方法用于执行查询操作,返回一个对象列表;getMapper 方法用于获取 Mapper 接口的动态代理对象。

构建 DefaultSqlSession实现类

动态代理基础实现

完成SqlSession会话的构建,下一步就是完善SqlSession接口的逻辑。具体来看,在Myabtis内部其会体用一个SqlSession接口的默认实现DefaltSqlSession用以完成具体逻辑,而我们本次逻辑其实不会太过复杂,在我们的DefaultSqlSession中我们主要实现一个代理对象的构建,以及通过代理对象,完成Sql的模拟操作。

Q: 此时我已经我构建了一个DefaultSqLSession的默认会话实现类,完善其中getMapper逻辑,无需实现太复杂,实现基础的动态代理即可

A: 下面为你完善DefaultSqlSession类里的 getMapper 方法,实现基础的动态代理逻辑。getMapper 方法的主要功能是依据传入的 Mapper 接口类型,生成对应的动态代理对象。

java 复制代码
package cn.mini.mybatis.sqlSession.defaults;

import cn.mini.mybatis.sqlSession.SqLSession;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class DefaultSqlSession implements SqLSession {
    @Override
    public <E> E selectOne(String statement, Object parameter) {
        return null;
    }

    @Override
    public <T> T getMapper(Class<T> type) {
        // 使用 Java 的动态代理生成 Mapper 接口的代理对象
        return (T) Proxy.newProxyInstance(
                type.getClassLoader(),
                new Class<?>[]{type},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                     
                        return null;
                    }
                });
    }
}

优化代理逻辑:关联查询方法

Trae构建代理其实就是动态代理的逻辑。Mybatis内部其方法的代理其本质是会回调至SqlSession中的方法来完成sql的执行,为此我们进行如下改造。

java 复制代码
@Slf4j
public class DefaultSqlSession implements SqLSession {



    @Override
    public <E> E selectOne(String statement, Object parameter) {
        log.info("模拟数据库查询操作........");
        return null;
    }

    @Override
    public <T> T getMapper(Class<T> type) {
        // 使用 Java 的动态代理生成 Mapper 接口的代理对象
        return (T) Proxy.newProxyInstance(
                type.getClassLoader(),
                new Class<?>[]{type},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        new DefaultSqlSession().selectOne(null,null);
                        // 这里简单打印方法调用信息,实际开发中可替换为执行 SQL 逻辑
                        System.out.println("调用了 Mapper 方法: " + method.getName());
                        System.out.println("参数: " + (args != null ? String.join(", ", args.toString()) : "无"));
                        return null;
                    }
                });
    }
}

至此,我们的简化版的SqlSession就算构建完成了。接下来我们通过AI生成单元测试用例进行验证,其代码是否可行。

单元测试与验证

测试用例设计

java 复制代码
@Test
public void testSelectOne() {
    SqLSession sqLSession = new DefaultSqlSession();
    UserMapper mapper = sqLSession.getMapper(UserMapper.class);
    mapper.selectUser();
}

// Mapper 接口示例  
public interface UserMapper {  
    List<User> selectUserList();  
}  

验证结果

执行测试后,控制台输出:

待优化点与后续规划

当前不足

  1. XML 解析缺失 :未实现从 mybatis.xml 加载配置(如 Mapper 路径、SQL 语句)。
  2. 动态代理优化 :当前 SQL 标识解析逻辑简单,需支持注解(如 @Select)或 XML 配置映射。

后续计划

  1. 实现 XML 配置解析 :使用 DOMXPath 解析 mybatis.xml,加载 Mapper 信息到 Configuration 对象。
  2. 完善代理逻辑 :引入 MapperRegistry 管理代理工厂,通过 MethodResolver 匹配 SQL 语句。
  3. 添加执行器组件 :实现 Executor 接口,分离 SQL 执行逻辑(如预处理、参数映射)。

总结

本次通过 AI 辅助,完成了简化版 MyBatis 的核心模块搭建:定义 SqlSession 接口、实现动态代理生成、验证基本查询流程。尽管当前实现较为初级,后续将结合 MyBatis 源码设计,逐步添加配置解析、事务管理等功能,最终实现一个可运行的微型 ORM 框架。

相关推荐
兵临天下api1 分钟前
1688 item_get_app 接口深度分析及 Python 实现
trae
AAA修煤气灶刘哥21 分钟前
ES 聚合爽到飞起!从分桶到 Java 实操,再也不用翻烂文档
后端·elasticsearch·面试
爱读源码的大都督33 分钟前
Java已死?别慌,看我如何用Java手写一个Qwen Code Agent,拯救Java
java·人工智能·后端
星辰大海的精灵1 小时前
SpringBoot与Quartz整合,实现订单自动取消功能
java·后端·算法
天天摸鱼的java工程师1 小时前
RestTemplate 如何优化连接池?—— 八年 Java 开发的踩坑与优化指南
java·后端
一乐小哥1 小时前
一口气同步10年豆瓣记录———豆瓣书影音同步 Notion分享 🚀
后端·python
LSTM971 小时前
如何使用C#实现Excel和CSV互转:基于Spire.XLS for .NET的专业指南
后端
兵临天下api1 小时前
1688 item_review 接口深度分析及 Python 实现
trae
三十_1 小时前
【NestJS】构建可复用的数据存储模块 - 动态模块
前端·后端·nestjs
武子康1 小时前
大数据-91 Spark广播变量:高效共享只读数据的最佳实践 RDD+Scala编程
大数据·后端·spark