MyBatis 的技术“优越感”到底从何而来?一场关于工程化的反思

(附:Spring JDBC Ultra 的现实回答)

一个框架的真正价值,不在于它制造了多少概念,而在于它帮你省去了多少麻烦。
在 Java 持久层框架的江湖里,MyBatis 几乎占据了统治地位。在面试中,我们会被问及 MyBatis 的缓存机制、插件原理、Mapper 代理;在项目中,我们习惯性地创建 Mapper 接口、编写 XML 文件。久而久之,似乎形成了一种默认的共识:MyBatis 是专业的、高级的,而不使用 MyBatis 似乎就显得不够“规范”。
但最近在一次深度技术讨论中,我产生了一个极大的困惑:MyBatis 的这种“优越感”,究竟源于何处? 当我们剥离掉那些营销话术和行业惯性,纯粹从软件工程的角度去审视它,得到的结论可能出乎所有人意料——这或许是一场工程学的倒退。

一、“优越感”的假象:把繁琐当成了高深

为什么很多人会觉得 MyBatis 比直接写 SQL 更“高级”?
这种优越感,很大程度上源于一种认知偏差。相比于 Spring JDBC 把 SQL 直接写在代码里,MyBatis 引入了 XML 配置、Mapper 接口、动态标签。这种“复杂度”给开发者一种错觉:我正在做架构层面的工作,而不是简单的拼接字符串。
但这是一种把“繁琐”当“高深”的误解。Spring JDBC 的哲学是“把简单的事情做简单”,而 MyBatis 的哲学却是“把简单的事情搞复杂”。这种复杂度,并没有带来实质性的技术收益,反而制造了门槛。

二、所谓的“解耦”,其实是“耦合 Max”

MyBatis 最大的卖点之一是“SQL 与代码分离,实现解耦”。然而,这经不起推敲。
原本,查询逻辑、参数判断、SQL 拼接都在一个 Java 方法里,这叫逻辑内聚。MyBatis 强行将这三部分撕裂:

  • 1/3 Java 代码: 只负责传参。
  • 1/3 XML 标签: 负责“弱鸡”的逻辑判断。
  • 1/3 SQL 语句: 被标签包裹,面目全非。
    这哪里是解耦?这分明是**“碎尸式耦合”**。
    一旦需要修改一个查询条件,你需要同时修改 Java 接口、XML 文件、Entity 实体类。这种跨文件的强依赖,是最高级别的耦合,极大地增加了维护成本。

三、技术的倒退:从高级语言退回到“弱鸡脚本”

MyBatis 最令人诟病的一点,是它将拼接 SQL 的动作,从 Java 这种高级语言,转移到了 XML 这种“弱鸡语言”中。

  • Java 的能力: 强类型检查、编译期报错、IDE 重构支持、Lambda 表达式、Stream API。这是现代工业级语言的标杆。
  • MyBatis XML 的能力: <if>, <where>, <foreach> 标签,加上 OGNL 表达式。这是一套简陋的、无类型、无编译检查的脚本语言
    放着好好的 Java if/else 不用,非要在 XML 里写 <if test="name != null">。写错了,IDE 不报错,只有运行时才炸雷。这不仅仅是技术的倒退,更是自废武功

四、调试权的丧失:工程学的灾难

对于一个框架来说,能不能方便地调试是核心指标。

  • Spring JDBC: SQL 就在方法里,想在哪打断点就在哪打断点,变量值一目了然,拼接过程完全透明。
  • MyBatis: XML 里能打断点吗?不能。出了问题,只能开启 DEBUG 日志,在几万行日志里去“猜”它生成的 SQL 到底长什么样。
    这剥夺了开发者的调试权。Spring JDBC 让我们是“驾驶员”,MyBatis 却让我们变成了“盲人”。这种“两眼一抹黑”的开发体验,是工程化的巨大灾难。

五、扩展性的谎言:拦截器 VS 普通 Java 代码

MyBatis 常被吹嘘扩展性强,支持插件(拦截器)。但事实是:80% 的程序员都搞不定 MyBatis 的拦截器。
要写一个分页插件,你需要懂 JDK 动动代理、懂 MyBatis 内部的四大对象、还得通过反射去修改私有属性。这是在源码级别做黑客操作。
而在 Spring JDBC 下呢?

  • 分页? LIMIT :offset, :size,这就是小学生数学题。
  • 多租户?数据权限? 结合 AOP,在参数里加个条件,逻辑清晰明了。
    MyBatis 把原本的**“业务问题”(拼 SQL),硬生生拔高成了“框架源码级问题”**。Spring JDBC 则让一切回归常识:这就是普通的 Java 业务逻辑。

六、“跨库迁移”的伪命题

最后,MyBatis 官方定义自己是“SQL 映射工具”,这直接戳破了它“支持跨库迁移”的泡沫。
既然是 SQL 映射,那你不可避免地会写数据库专有语法(如 MySQL 的 LIMIT,Oracle 的 ROWNUM)。一旦写了这些,XML 就和特定数据库锁死了。
为了所谓的迁移,MyBatis 甚至要在 XML 里写 <if test="_databaseId == 'mysql'"> 这种判断。这不叫移植性,这叫自找麻烦。真正的移植性应该由 Hibernate/JPA 这种抽象层提供,而不是靠在 XML 里写方言分支。

七、结语:到底什么是收益?

软件工程的核心价值观是:抽象、封装、简化。
我们引入框架,是为了少写代码,为了逻辑更清晰。

  • MyBatis: 引入了 XML 解析、动态代理、OGNL 表达式、大量的标签和注解。这是“负收益”。
  • Spring JDBC: 没有任何多余的概念,SQL 在手,天下我有。逻辑内聚,调试透明,扩展简单。
    MyBatis 的流行,是一场**“营销学的胜利,工程学的悲剧”**。它成功地让一代开发者相信:写一堆繁琐的 XML 配置,比写几行清晰的 Java 代码,更显得“专业”。
    但实际上,Spring JDBC 才是那个拥有高收益、低负债、符合现代工程理念的真正利器。 它是“大道至简”的最好诠释。

八、现实中的答案:Spring JDBC Ultra (SimpleDAO)

当我们在争论 MyBatis 与 Spring JDBC 的优劣时,一个名为 Spring JDBC Ultra(内核为 SimpleDAO)的框架已经悄然给出了现实中的答案。它并非重新发明轮子,而是在 Spring JDBC 这块坚实基石上,进行了面向实战的深度封装,精准地解决了原生的样板代码问题,又完全规避了 MyBatis 的所有痛点

1. 终结 XML,逻辑回归 Java

SimpleDAO 的核心理念是“大道至简”。它彻底摒弃 XML 配置,将 SQL 与条件逻辑完整地交还给 Java 语言。

@Repository
public class UserDao extends BaseDao<User> {
    // 空类即可获得所有 CRUD 能力
}
@Repository
public class OrderDao extends BaseDao<Order> {
    private final static String SQL = """
        SELECT t.*, u.name user_name
        FROM bus_order t 
        LEFT JOIN sys_user u ON t.user_id = u.id""";
    
    public Page<OrderVO> pageJoin(OrderCond cond) {
        return page(SQL, cond, OrderVO.class);
    }
}

对比 MyBatis: 不需要 Mapper 接口、XML 文件、ResultMap。SQL 和逻辑高度内聚,修改查询条件无需跨文件跳转。

2. 动态条件:强类型与全透明

条件构造通过继承 BaseCondition 实现,利用 Java 的强类型与逻辑能力,完全透明。

@Override
protected void addCondition() {
    add("AND u.name LIKE ?", userName, 3); // 3 表示前后加 %
    add("AND o.create_time >= ?", createTimeStart);
    if (ArrayUtils.isNotEmpty(goodsNames)) {
        add("AND o.id IN (SELECT order_id FROM bus_goods WHERE goods_name IN", goodsNames);
        add("AND dr=0)");
    }
}

对比 MyBatis: 不再使用 <if><where> 等弱类型标签。逻辑正确性由编译器保证,支持完整的断点调试,条件构造过程一目了然。

3. 扩展性:小学生都能懂的解决方案

对于 MyBatis 中复杂的拦截器,SimpleDAO 用最朴素的 Java 方案(如 AOP)给出了解答。

@Aspect
@Component
public class DataAuthAspect {
    @Before("@annotation(auth)")
    public void beforeQuery(JoinPoint point, DataAuth auth) {
        BaseCondition cond = (BaseCondition) point.getArgs()[0];
        // 直接往条件里追加一段 SQL,实现数据权限过滤
        String and = " AND " + auth.userField() + " IN (" + userId + ",0)";
        cond.setExtendCondition(and);
    }
}

对比 MyBatis: 无需理解复杂的代理与反射机制,一个切面、一个字符串拼接,逻辑清晰易懂,真正实现了“零门槛”的扩展。

4. 企业级特性:一站式解决方案

SimpleDAO 内置了开箱即用的企业级特性:

  • 分页: 自动处理 COUNT 查询与分页逻辑,无需额外插件。
  • 逻辑删除: 通过简单配置字段名即可启用。
  • 审计字段: 可配置 UserIdProvider 自动填充创建人、修改人等。
  • 多表联查: 支持复杂 SQL(子查询、多表 JOIN)无缝衔接,映射到自定义 VO。

5. 终极收益:代码量减少,效率倍增

一个典型的单表 DAO,仅继承 BaseDao 即可拥有 save, update, delete, findById, list, page 等 20+ 方法。复杂的联表查询也只需定义 SQL 和条件类。这是真正的“正收益”,将开发者从配置泥潭中彻底解放出来,专注于业务逻辑。

九、工具链与资源

Spring JDBC Ultra (SimpleDAO) 提供了完整的工具链,覆盖从开发到生成的全流程:

组件链接说明
核心框架源码https://gitee.com/gao_zhenzhong/simple-dao框架核心,包含所有基础功能与抽象。
系统底座https://gitee.com/gao_zhenzhong/simple-dao-starter含前后端完整工程。内置 RBAC 权限体系、JWT 鉴权、操作日志、字典管理、部门管理、菜单管理,开箱即用
代码生成器https://gitee.com/gao_zhenzhong/simple-dao-coder根据数据库表结构生成 Entity、Condition、DAO 等代码,进一步提效。
实战案例https://gitee.com/gao_zhenzhong/simple-dao-demo包含单表 CRUD、联表查询、数据权限、脱敏等完整案例,是学习的最佳起点。

写在最后:
技术选型不应盲从。当我们在 MyBatis 的 XML 泥潭中挣扎时,不妨回头看一眼那个一直被我们忽视的 Spring JDBC,并了解一下基于它构建的 Spring JDBC Ultra。或许,那里才有我们真正需要的简洁、自由与工程上的安宁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值