全面解构传统持久层框架,拥抱真正的 SQL-First
全面解构传统持久层框架拥抱真正的 SQL-First写在前面我从一开始就反复强调一句话“数据库只认 SQL。”50 年了没有任何东西能替代它。所有试图封装 SQL、隐藏 SQL、生成 SQL、翻译 SQL 的框架最终都逃不开一个结局——你还是要写 SQL而且你还要多学一层框架的垃圾话。今天我把这些年对持久层框架的观察、踩坑、思考一次性摊开来讲。第一部分解构 MyBatis——XML 不是优雅是噪音MyBatis 号称“SQL-First”我承认它确实让你写 SQL。但问题在于你在哪里写用什么方式写答案是在 XML 里用一堆标签包着 SQL 写。1. SQL 和 Java 分离分明是割裂他们说“SQL 和 Java 分离更清晰”。但分离不等于解耦更不等于更好的维护性。你把 SQL 从 Java 里挪到了 XML但代价是什么两个文件之间来回切——改个字段名Java 改一遍XML 改一遍四种语言混在一起——Java、XML、OGNL、SQL你要在同一个文件里处理四种语法无法跳转、无法重构、无法追踪引用——你的 IDE 帮不了你因为 XML 不是 Java这叫解耦这叫耦合Plus耦合Max。Java 和 SQL 之间没有“分离”只是从一种耦合变成了更隐蔽、更难维护的耦合。2. 动态 SQL本质是低配版字符串拼接MyBatis 吹得最响的就是“动态 SQL”iftestname ! null and name ! AND name #{name}/if我来问一个问题这跟你在 Java 里写if (name ! null) { sql.append(AND name ?); params.add(name); }有什么本质区别答案是没有。都是字符串拼接。唯一的区别是Java 里写是一行ifXML 里写是三行标签。Java 里你能用replace、format、正则、流式处理XML 里你只能靠那几套标签死撑。动态 SQL 的灵魂在于“动态”二字在于逻辑。逻辑用 Java 写是天经地义的用 XML 写是在自缚手脚。3. resultMap 的“灵活”是个定时炸弹resultMapiduserMaptypeUserassociationpropertydeptcolumndept_idselectselectDept//resultMap这个东西看起来很美好你查一个用户它自动帮你把部门也带出来。但实际生产环境没人敢用。因为你不知道它什么时候炸怎么炸炸多响。你查 100 条用户它就发 101 条 SQL查 1000 条就发 1001 条。有经验的 MyBatis 开发者会怎么做退回到“11 查询”——主表查一次子表查一次然后在内存里用 Java 组装。一旦退回到这一步MyBatis 的association就成了摆设。你付出的是每一次查询都要解析 XML 的代价换来的只是自动把ResultSet转成对象。这个功能 Spring 的BeanPropertyRowMapper也能做到而且不用解析 XML。那 MyBatis 还剩什么优势4. 31 类异常 vs 2 类异常MyBatis 自己制造了 31 类异常。你花时间学的不是“数据库出了什么问题”而是“框架的哪一层又炸了”。你调试的不是 SQL 逻辑是 XML 标签、是 OGNL 语法、是插件冲突、是缓存配置。你伺候的是框架不是业务。Spring JDBC 只有两到三类异常。SimpleDAO 也继承 Spring JDBC同样是两到三类。你看到的异常就是数据库报的异常没有框架中间层给你包装一层“框架相关”的废话。5. 生态繁荣那是补坑生态为什么 MyBatis 有那么多插件PageHelper、MyBatis-Plus、通用 Mapper、MyBatis-Flex……因为这些功能 MyBatis 自己都不提供。分页不提供你要装插件数据权限不提供你要自己写拦截器脱敏不提供你要自己实现 TypeHandler单表 CRUD 简化不提供你要用 Plus 或 Flex。这叫生态繁荣这叫框架缺了太多东西全靠别人补。第二部分解构 ORM——单表一时爽联表火葬场ORM 最大的谎言是“单表解决 80% 的问题。”真实的企业开发中单表查询占比连 10% 都不到。用户权限、数据统计、业务流程落地、报表聚合……全部都需要多表联查。单表只存在于基础增删改查的测试阶段或极简模块。1. 单表对象化的价值只在增删改插入对象 → 一行记录天然映射更新对象 → 一行记录天然映射删除按主键删一行天然映射这三件事ORM 做得好做得省力。但增删改在业务代码中的占比有多少不到 10%。2. 查询场景下ORM 只能躺平联表查询、聚合查询、子查询、半连接、窗口函数……你一进入查询场景ORM 就废了。你写 JPQL、HQL、Criteria、QueryDSL最后发现它们的能力连 SQL 的 1/3 都发挥不出来。标量子查询写不出来派生子查询写不出来窗口函数没有CTE 没有。于是 ORM 厂商在文档里写复杂查询请使用原生 SQL。这句话翻译一下就是“我们帮你解决了 10% 的单表增删改剩下的 90% 请你自己写 SQL。”3. 关系对象化根本是错的ORM 试图把“关系数据库”变成“对象数据库”把“关系”变成“对象树”。但数据库的本质是关系不是对象。两张表 JOIN 之后的结果既不是“父对象”也不是“子对象”它就是“两张表的部分字段组合”。你硬要把它变成一个对象树的嵌套结构就是在扭曲数据的本质。关系是网不是树。第三部分解构 DSL——翻译带来的只有成本没有收益jOOQ 把 DSL 推向极致全面翻译。每个关键字、每个函数、每个操作符都翻译成 Java 方法。1. 类型安全收益极低jOOQ 的逻辑是我帮你提前检查字段名拼写错误这叫类型安全。但你想想你写完程序不点一下吗点一下不就发现报错了吗你不自测吗自测一下不就发现字段名写错了吗团队没有测试吗测试不就能覆盖吗如果有人偷偷改数据库字段你手写 SQL 不也一样报错吗类型安全唯一的收益是“编译时报错比运行时快一步”但这一步的收益根本不足以覆盖它带来的成本。2. 逻辑错误你永远检查不出来SELECT * FROM user WHERE age 18写成SELECT * FROM user WHERE age 80语法完全正确类型完全正确jOOQ 不会报任何错。但业务数据全漏了。类型安全只覆盖“拼写错误”这 1% 的问题另外 99% 的逻辑错误和性能问题它一个都帮不上。3. 成本收益严重倒挂你写一行 SQLSELECT*FROMuserWHEREage18jOOQ 要写context.selectFrom(USER).where(USER.AGE.gt(18)).fetch()代码长了两倍还要学一整套 API还要生成一堆类还要维护一个代码生成流程。你付出的一切只是为了把字段名拼错的风险降到最低——而这个风险本来就不高。4. DSL 能力上限的排序jOOQ上限最高 MyBatis Flex MyBatis Plus。jOOQ 能做“全面翻译”理论上能覆盖 SQL 全部能力。Flex 覆盖联表场景留了原生 SQL 后门。Plus 只能覆盖单表查询。但无论哪一种 DSL只要它还在“翻译”就永远不可能比“直写 SQL”更简洁、更透明、更容易维护。因为你学 DSL 的逻辑本质上是在学一套 SQL 的方言。你付出的每一分精力都是在为“翻译”买单。第四部分SimpleDAO——真正的 SQL-First前面说了这么多“什么是错的”那什么是对的SimpleDAO 的定位很简单极致的字符串拼接和参数收集。1. 三条底线不封装关键字SELECT、FROM、JOIN、GROUP BY 你手写框架不碰不封装运算符、、LIKE、IN、EXISTS 你手写框架不碰不做语法翻不解析 SQL、不生成 SQL、不改写 SQL2. 一套心智模型打到底单表UserCondcondUserCond.builder().name(张).ageMin(18).build();userDao.page(cond);联表同样的 APIStringsqlSELECT u.*, d.name dept_name FROM sys_user u LEFT JOIN sys_dept d ON u.dept_id d.id;userDao.page(sql,cond,UserVO.class);复杂条件还是同样的 APIcond.add(AND EXISTS (SELECT 1 FROM bus_order WHERE user_id u.id),flag);cond.add(AND u.age BETWEEN ? AND ?,ageMin,ageMax);cond.add(AND (u.status ? OR u.status ?),status1,status2);从单表到联表到子查询到 CTE 到窗口函数全是这一套。没有 XML、没有 DSL、没有三套语法切换。SQL 还是 SQLJava 还是 Java。3. 你写的是 SQL框架只帮你拼条件和收集参数这是 SimpleDAO 和所有传统框架最本质的区别框架做的事情MyBatis在 XML 里写 SQL用标签控制动态ORM单表对象化联表躺平jOOQ把 SQL 翻译成 Java 方法链SimpleDAOSQL 就是 SQL放在 Java 里框架只帮你拼条件和收集参数4. 没有上限因为数据库只认 SQL而你写的就是 SQL。你的能力上限 SQL 的能力上限 无限。只要是 SQL 合法的语法——BETWEEN、OR、EXISTS、CASE WHEN、窗口函数、递归 CTE——全都能拼进去。框架不拦着你框架不限制你。结语这些年我见过太多人在框架里绕弯子学 XML 标签、学 OGNL 表达式、学 resultMap 嵌套学 QueryWrapper 链式调用、学 Lambda 表达式构建条件学 JPQL、HQL、Criteria、QueryDSL……学 jOOQ 那套从数据库生成代码的流程最终你发现所有这些学习曲线都是为了绕开一个你本来就该会的东西SQL。SimpleDAO 不让你绕弯子。它的价值从来不是“功能最多、最智能、最花哨”。它的价值是“让你在写 SQL 的时候少写那些重复的if和params.add()。”仅此而已。但正是这个“仅此而已”让 SimpleDAO 成为我从 2017 年一直迭代到今天的项目——因为它没有谎言没有泡沫没有承诺它做不到的事情。它是一个勤勤恳恳的 SQL 脚手架。如果你也受够了 XML 的折磨、OR M 的谎言、DSL 的翻译成本欢迎试试 SimpleDAO。开源地址核心框架源码https://gitee.com/gao_zhenzhong/simple-dao系统底座https://gitee.com/gao_zhenzhong/simple-dao-starter代码生成器https://gitee.com/gao_zhenzhong/simple-dao-coder实战案例全部源码https://gitee.com/gao_zhenzhong/simple-dao-demo把时间留给 SQL而不是框架。
