一个被说错了二十年的词
“SQL 跟 Java 分离”——这个说法在 MyBatis 的推广中被反复提及。听起来很合理:SQL 放在 XML 里,Java 代码里没有 SQL 字符串,这就是解耦。
但事实是:分离不等于解耦。把两样东西分开存放,不等于它们之间的依赖关系消失了。
耦合的标准定义
耦合,是一个模块的变化导致另一个模块必须跟着变化。
按这个标准,MyBatis 的 SQL 跟 Java 之间存在清晰的耦合链:
- 你改了实体类的字段名(Java)→ XML 里的
resultMap要改,#{}里的属性名要改,<if>里的test表达式要改 - 你改了方法名(Java)→ XML 里的
id必须同步改 - 你加了查询参数(Java)→ XML 里的
#{xxx}要加,参数顺序要对
你一动 Java,XML 必动。这叫“跨文件的强耦合”。
为什么叫“耦合 Plus”?
打开任何一个 MyBatis 的 XML 文件,你会发现一个事实:它里面只有三分之一是 SQL,另外三分之二是标签和表达式。
- 1/3 是 SQL 语句本身
- 1/3 是标签(
<if>、<foreach>、<where>、<set>、<choose>) - 1/3 是 OGNL 表达式(
test、item、index、collection)
你为了维护这个 XML 文件,需要同时处理三种不同的语法:SQL 语法、MyBatis 标签语法、OGNL 表达式语法。这三者之间的关系决定了最终生成的 SQL 是否正确。任何一个环节出错,整个查询失败。
你把一个 Java 方法里本来可以写清楚的逻辑,拆成了一个 Java 接口 + 一个 XML 文件,XML 里还要塞进三种不同语法。这不是解耦,这是“耦合 Plus”。
既然要破,就要立
如果你把条件写在 Java 里,用语义化的方式组织,会是什么样?
publicclassUserCondextendsBaseCondition{privateStringname;privateIntegerageMin;privateIntegerageMax;privateObject[]ids;privateBooleanhasOrder;@OverrideprotectedvoidaddCondition(){and("name LIKE",name,3);// 模糊查询and("age >=",ageMin);// 大于and("age <=",ageMax);// 小于in("id",ids);// IN 集合add("AND EXISTS (SELECT 1 FROM order WHERE user_id = t.id)",hasOrder);// 子查询 + 动态开关}}六个条件,六行,全是add()和and(),没有 XML,没有 OGNL,没有标签,没有三个文件来回跳。你写的每一行都是业务条件,没有一行是在伺候框架。
条件层的完整实现:BaseCondition自动收集参数、自动拼接WHERE、自动管理占位符顺序。你只需要关心“这个条件要不要加”,不需要关心“第几个问号对应第几个参数”。
@RepositorypublicclassOrderDaoextendsBaseDao<Order>{// 完整的联表 SQL,直接写在常量里privatestaticfinalStringJOIN_SQL=""" SELECT t.*, u.name user_name, u.phone user_phone FROM bus_order t LEFT JOIN sys_user u ON t.user_id = u.id """;// 联表分页——跟单表分页是同一个 APIpublicPage<OrderVO>pageJoin(OrderCondcond){returnpage(JOIN_SQL,cond,OrderVO.class);}}这才是解耦:SQL 稳定部分(SELECT/FROM/JOIN)写在常量里,动态部分(WHERE)用 Java 语义单元管理,二者通过BaseCondition桥接,互不干扰。
开源地址
- 核心框架: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