手写MyBatis ORM框架(一)

手写MyBatis ORM框架(一)

引言

尝试手写一个MyBatis ORM框架。从中提升自己的编码能力,以及学习优秀的代码编写。

会什么会出现MyBatis这样的ORM框架?

大家都使用过JDBC吧

MyBatis 等 ORM 框架的出现是为了解决 JDBC 在开发效率和工程化层面的诸多痛点。

1. 样板代码冗余

JDBC 问题:每次操作数据库需手动处理连接、Statement、ResultSet 等资源,代码重复且易遗漏资源释放。

Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
    conn = DriverManager.getConnection(url, user, password);
    stmt = conn.prepareStatement("SELECT * FROM user WHERE id = ?");
    stmt.setInt(1, 1);
    rs = stmt.executeQuery();
    // 处理结果...
} finally {
    // 手动关闭资源
    if (rs != null) rs.close();
    if (stmt != null) stmt.close();
    if (conn != null) conn.close();
}

ORM 解决方案:自动管理连接池、资源释放,减少重复代码。


2. 手动对象-关系映射(ORM)繁琐

JDBC 问题:需手动从 ResultSet 提取数据并转换为 Java 对象。

List<User> users = new ArrayList<>();
while (rs.next()) {
    User user = new User();
    user.setId(rs.getInt("id"));
    user.setName(rs.getString("name"));
    // ...逐个字段映射
    users.add(user);
}
  • ORM 解决方案:自动将查询结果映射到对象(如 MyBatis 的 <resultMap>),减少手写代码。


3. SQL 硬编码与维护困难

JDBC 问题:SQL 嵌入 Java 代码,动态 SQL 拼接复杂。

String sql = "SELECT * FROM user WHERE 1=1";
if (name != null) {
    sql += " AND name = '" + name + "'"; // 存在 SQL 注入风险
}

ORM 解决方案:SQL 外置到 XML 或注解,支持动态 SQL(如 MyBatis 的 <if> 标签)。

<select id="findUser" resultType="User">
    SELECT * FROM user
    <where>
        <if test="name != null">AND name = #{name}</if>
    </where>
</select>
  • 运行 HTM


4. 事务管理复杂

JDBC 问题:需手动调用 commit()/rollback(),在分布式事务中尤为复杂。

try {
    conn.setAutoCommit(false);
    // 执行多个操作...
    conn.commit();
} catch (SQLException e) {
    conn.rollback();
}

ORM 解决方案:声明式事务管理(如通过Spring整合),简化事务控制。


5. 数据库兼容性差

JDBC 问题:不同数据库的 SQL 语法差异需手动处理(如分页查询)。

ORM 解决方案:提供方言(Dialect)机制,自动适配不同数据库。


6. 性能优化困难

  • JDBC 问题:缺乏内置缓存机制,重复查询效率低。

  • ORM 解决方案:集成一级/二级缓存(如 MyBatis 缓存),减少数据库压力。


7. 类型安全与 SQL 注入风险

JDBC 问题:参数拼接易引发 SQL 注入。

String sql = "SELECT * FROM user WHERE name = '" + userInput + "'"; // 不安全
  • ORM 解决方案:预编译参数绑定(如 #{param}),防止注入。


为什么选择 MyBatis 而非全自动 ORM(如 Hibernate)?

  • 灵活控制 SQL:适合复杂查询或需深度优化的场景。

  • 轻量级:学习成本低,与现有代码集成更灵活。

  • 透明化:开发者对最终执行的 SQL 有完全掌控权。


开始实现

大家看到了MyBatis的优秀,让我们一起来实现吧!

当们使用熟练MyBatis后,就会发现大概得核心链路是什么样的,简单来说呢就是给一个接口提供代理类,类中呢包括对Mapper也就是xml文件中的SQL信息,类型入参出参进行解析和处理,这个处理的过程就是对数据库的操作,以及返回对应的结果,给到我们的DAO 这一个过程。

当我们来设计一个 ORM 框架的过程中,首先要考虑怎么把用户定义的数据库操作接口、xml配置的SQL语句、数据库三者联系起来。其实最适合的操作就是使用代理的方式进行处理,因为代理可以封装一个复杂的流程为接口对象的实现类。

创建一个简单的映射器代理操作

在这里我们先要学会如何使用jdk动态代理。

可以看下我关于jdk动态代理的文章。

创建MapperProxy

我们创建一个MapperProxy 类 这个类 会实现 IncocationHandler 这个就是要实现动态代理的处理器 ,之后哟啊执行的方法就是 我们重写的invoke 方法

创建MapperProxyFactory

这个类就是负责创建我们的代理类的

总结

实现这么的一个效果

目前我们这里只是简单的封装了一个 sqlSession 的 Map 对象,你可以想象成所有的数据库语句操作,都是通过接口名称+方法名称作为key,操作作为逻辑的方式进行使用的。那么在反射调用中则获取对应的操作直接执行并返回结果即可。当然这还只是最核心的简化流程,后续不断补充内容后,会看到对数据库的操作。

最后

关于对MyBatis源码的学习,参考了https://bugstack.cn/ 这个大佬的文章,我就是在这里学会的,同时这位大佬还分享了很多其他的知识,让我受益匪浅,大家可以点击去学习,十分感谢。

LICENSED UNDER CC BY-NC-SA 4.0