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 框架。

相关推荐
paopaokaka_luck1 小时前
基于SpringBoot+Vue的电影售票系统(协同过滤算法)
vue.js·spring boot·后端
IT_10247 小时前
Spring Boot项目开发实战销售管理系统——系统设计!
大数据·spring boot·后端
ai小鬼头8 小时前
AIStarter最新版怎么卸载AI项目?一键删除操作指南(附路径设置技巧)
前端·后端·github
Touper.8 小时前
SpringBoot -- 自动配置原理
java·spring boot·后端
一只叫煤球的猫9 小时前
普通程序员,从开发到管理岗,为什么我越升职越痛苦?
前端·后端·全栈
一只鹿鹿鹿9 小时前
信息化项目验收,软件工程评审和检查表单
大数据·人工智能·后端·智慧城市·软件工程
专注VB编程开发20年9 小时前
开机自动后台运行,在Windows服务中托管ASP.NET Core
windows·后端·asp.net
程序员岳焱9 小时前
Java 与 MySQL 性能优化:MySQL全文检索查询优化实践
后端·mysql·性能优化
一只叫煤球的猫10 小时前
手撕@Transactional!别再问事务为什么失效了!Spring-tx源码全面解析!
后端·spring·面试
喜欢敲代码的程序员10 小时前
SpringBoot+Mybatis+MySQL+Vue+ElementUI前后端分离版:项目搭建(一)
spring boot·mysql·elementui·vue·mybatis