记录通过
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);
// .....调用相关方法信息
上述代码的逻辑即:
-
通过工厂构造器构建一个
SqlSessionFactory
对象 -
通过
SqlSessionFactory
获取到一个sqlSession
对象 -
向
sqlSession
中传入一个Mapper
对象信息,同时获取到一个Mapper
对象 -
获取
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
代码来看,其基本就是Mybatis
中SqlSession
的完全版.其包含了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();
}
验证结果
执行测试后,控制台输出:

待优化点与后续规划
当前不足
- XML 解析缺失 :未实现从
mybatis.xml
加载配置(如Mapper
路径、SQL
语句)。 - 动态代理优化 :当前
SQL
标识解析逻辑简单,需支持注解(如@Select
)或 XML 配置映射。
后续计划
- 实现 XML 配置解析 :使用
DOM
或XPath
解析mybatis.xml
,加载Mapper
信息到Configuration
对象。 - 完善代理逻辑 :引入
MapperRegistry
管理代理工厂,通过MethodResolver
匹配SQL
语句。 - 添加执行器组件 :实现
Executor
接口,分离SQL
执行逻辑(如预处理、参数映射)。
总结
本次通过 AI
辅助,完成了简化版 MyBatis
的核心模块搭建:定义 SqlSession
接口、实现动态代理生成、验证基本查询流程。尽管当前实现较为初级,后续将结合 MyBatis
源码设计,逐步添加配置解析、事务管理等功能,最终实现一个可运行的微型 ORM
框架。