Spring JDBC Ultra:一个被遗忘的生态位,正在重新定义持久层框架
引言:持久层框架的“地图”,少了一块
在 Java 持久层框架的版图上,我们熟悉的格局是这样的:
- ORM(Hibernate/JPA) :以对象为中心,试图把数据库“藏”起来
- SQL 映射器(MyBatis) :以 XML 为中心,把 SQL 从 Java 里“搬”出去
- SQL 构建器(JOOQ/QueryDSL) :以类型安全为中心,把 SQL“翻译”成 Java DSL
- Spring JDBC:以数据库为中心,最薄、最诚实的一层
这张地图看起来挺完整——每种需求似乎都有对应的框架。但细看之下,你会发现中间缺了一大块:一个既保留 SQL 完整表达力、又帮业务开发者省掉重复劳动、还保持 100% 透明可控的生态位。
Spring JDBC Ultra(开源项目名:SimpleDAO)的出现,恰恰填补了这个空白。
它不是“又一个持久层框架”,而是一个全新的生态位。
一、先看清现实:三大主流框架,各有各的死穴
1. ORM(Hibernate/JPA):唯一的收益是单表对象化
ORM 最大的价值在于单表 CRUD 的自动化——session.save(user)、session.get(User.class, id),确实省掉了写 INSERT 和 SELECT 的体力活。
但代价极其沉重:
代价一:SQL 能力被锁死在约 1/3。 标量子查询、派生子查询、半连接、窗口函数——这些在 ORM 的世界里就是“死刑”,你只能退回原生 SQL。
代价二:黑盒程度极高。 缓存机制、代理生成、懒加载、脏检查——每一步都在增加不可控性。
更致命的是,ORM 有一个逃生舱悖论:复杂场景你必须手写 SQL,那“只在简单场景有效的 ORM”还能叫 ORM 吗?它只是一个“单表对象化工具”而已。
2. MyBatis:唯一的收益是 SQL 能力 100%
MyBatis 确实保留了 SQL 的全部表达力——数据库能写的它都能写。
但代价是枷锁极重,且大多无价值:
- Mapper 接口 + XML 双向绑定,跨文件维护成本极高
- 上百个属性、几十个标签、十几个注解,你学这些的唯一目的就是为了“能写 SQL”——而“能写 SQL”是 Spring JDBC 本来就有、默认就有的能力
- 运行时黑盒,你想介入只能靠拦截器——操作 MappedStatement、BoundSql、ParameterHandler 等内部对象,门槛极高
- 所以才有大量分页插件、数据权限插件——这不是生态繁荣,是补坑生态
你撕开 XML 那层皮,里面到底是什么?不还是一坨低级的字符串拼接吗?Java 里有 replace、format、正则、流式处理,能把字符串玩出花来。XML 有什么?XML 连个像样的循环和字符串截取都写得费劲。
3. Spring JDBC:最诚实,但“裸”到需要自己拼条件
Spring JDBC 面向数据库设计,它把所有精力都放在如何干净、直接、无损耗地执行 SQL 上。它不关心业务长什么样,只关心“你能不能把 SQL 发过去,把结果拿回来”。
但它的问题是:太“裸”了。每次写查询,都要手动写 if (name != null) { sql.append("AND name = ?"); params.add(name); }——大量重复的样板代码。
二、Spring JDBC:最诚实的底座
在深入 Ultra 之前,必须先说清楚一件事:Spring JDBC Ultra 不是要替代 Spring JDBC,它是让 Spring JDBC 更好用。
Spring JDBC 的“诚实”在于它不做任何语义层面的假定——它不知道什么是“单表对象化”,不知道什么是“关系对象化”,它只认 SQL 字符串和参数数组。你给它什么,它就执行什么。它不替你做决定,也不限制你的选择。
Spring JDBC 是“数据库在 Java 世界里的镜子”——它反射的是数据库的原貌,不增不减。
而 Ultra 是在这个“诚实的底座”上,做了一层“面向业务的脚手架”。它不改变 Spring JDBC 的任何行为,它只是在上面搭了一层梯子,让业务开发者能更快、更干净地写出数据访问代码。
三、Spring JDBC Ultra:一个全新的生态位
1. 面向业务设计,而不是面向数据库
这是 Ultra 和其他所有框架最根本的区别。
| Spring JDBC | 面向数据库 | 极简、直接地执行 SQL | 关系型数据库的操作接口 |
| Spring JDBC Ultra | 面向业务 | 让业务代码更少、更清晰、更易维护 | SQL 语句中动态变化的部分 |
Spring JDBC 尊敬的是数据库——它把所有精力都放在如何干净地执行 SQL 上。Spring JDBC Ultra 尊敬的是业务——它承认 SQL 结构本身是有规律的,然后针对这个规律做抽象。
Ultra 是“业务需求在 Spring JDBC 上的脚手架”——它不改变 Spring JDBC 的任何行为,只是帮开发者省掉重复劳动。
2. 五大独创特性,构成一个精密咬合的闭环
Ultra 有五个独创特性,它们不是各自独立的“功能堆砌”,而是一个环环相扣的闭环——抽掉任何一个,其他四个的结构性优势都会崩塌。
独创一:四种结果形态(list/row/columns/field)
关系代数无论怎么运算,结果永远逃不出四种形态:多行多列(list)、单行多列(row)、多行单列(columns)、单行单列(field)。Ultra 用四个方法覆盖了数据库查询的所有输出形态。
独创二:BaseCondition 不区分单表联表
WHERE 子句在单表和联表中语义完全一致。Ultra 把这个结构抽出来独立成类,不绑定任何表——同一个条件对象,既可以用在单表查询,也可以用在联表查询。
独创三:不封装关键字、运算符、异常
AND 就是 AND,> 就是 >,数据库抛什么异常就抛什么异常。Ultra 只封装 SQL 的“结构”,绝不封装 SQL 的“语义” 。这是“极致可见性”的底线。
独创四:mergeParams 自动合并多条件参数
在复杂联表场景下,多个条件对象对应多个参数列表。mergeParams 把它们按顺序合并成一个平铺数组,顺序即 SQL 中 ? 出现的顺序——一行代码解决联表参数管理问题。
独创五:永不退居二线的 API
单表是特例,联表是常态。Ultra 不需要为“特例”设计一套 API,再为“常态”设计另一套——因为它在结构层面已经统一了。任何查询场景,都能用同一套 API 覆盖。
3. 业务代码浓度 ≈ 100%
这是一个极其重要的视角:你写的每一行代码,到底是在解决业务问题,还是在伺候框架?
在 MyBatis 里写一个带 5 个条件的查询,你需要:
- 接口里写方法声明 + 5 个 @Param 注解
- XML 里写 <mapper namespace>、<select id>、5 个 <if test> 标签
- 每个条件:3 行 XML(<if> 开标签 + 业务 SQL + </if> 闭标签)
在这 20+ 行代码里,真正表达业务逻辑的只有 5 行 SQL 条件。业务代码浓度 < 30%。
而在 Ultra 里,同样的 5 个条件:
@Override
protected void addCondition() {
and("name LIKE", name, 3); // 业务:按姓名模糊查询
and("age >=", ageMin); // 业务:年龄下限
and("age <=", ageMax); // 业务:年龄上限
add("AND u.name LIKE ?", userName, 3); // 业务:联表用户姓名模糊
add("AND u.dr = 0"); // 业务:过滤已删除
}
5 行代码 = 5 个业务条件,业务代码浓度 100%。
没有接口、没有 XML 头、没有命名空间、没有 OGNL 表达式、没有 @Param 注解、没有 resultMap——没有任何一行代码是“为了伺候框架”而写的。
四、实战印证:从 demo01 到 demo08
Ultra 的 8 个实战案例(simple-dao-demo 项目)覆盖了从单表到复杂联表的全场景:
demo01:单表 CRUD + 审计 + 逻辑删除
@Repository
public class UserDao extends BaseDao<User> {
// 空类,全部 CRUD 能力自动获得
}
继承空类,立即拥有 save、update、delete、findById、list、page 全套方法。
demo02:联表查询 + 分页
private static final String 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
""";
public Page<OrderVO> pageJoin(OrderCond cond) {
return page(SQL, cond, OrderVO.class); // 联表分页,一行搞定
}
单表和联表用的是同一套 API——page(SQL, cond, VO.class)。
demo03:条件进阶(IN + 子查询)
add("AND t.age IN ", inAges); // IN 自动展开
add("AND t.id IN (SELECT user_id FROM bus_order WHERE dr=0)", subQuery); // 动态子查询
IN 条件自动展开成 (?,?,?),子查询直接写在字符串里。
demo04:多表联查 + 复杂条件 三表 JOIN,时间范围、模糊查询、IN 条件任意组合,条件传值即生效、不传即忽略。
demo05:报表聚合(GROUP BY + 聚合函数) 同一个 ReportCond 控制多张报表的不同条件组合,SQL 直接写 GROUP BY 和聚合函数。
demo06:mergeParams 多组条件合并
return list(SQL, ReportVo.class, mergeParams(timeCond, bizCond, validCond));
3 个独立条件类分别控制 SQL 中 3 个不同位置,参数一行合并。
demo07:多租户 + 数据权限(AOP 破局)
@Around("@annotation(auth)")
public Object around(ProceedingJoinPoint pjp, DataAuth auth) {
BaseCondition cond = (BaseCondition) pjp.getArgs()[0];
cond.setExtendCondition(" AND " + auth.userField() + " IN (" + userId + ",0)");
return pjp.proceed();
}
Spring AOP + 注解,在 BaseCondition 里动态追加条件——不到 30 行代码解决数据权限。
demo08:字段脱敏 + 审计扩展 Service 层通过 AOP 做字段脱敏,DAO 层完全不知情——横切关注点与持久层彻底解耦。
五、总结:一个被遗忘的生态位
Spring JDBC Ultra 占据的生态位,可以用一张表说清楚:
| ORM (Hibernate) | ✅ 做得好 | ❌ 稀烂 (<1/10) | ~40% | ❌ 黑盒 |
| MyBatis | ⚠️ 靠 Plus | ❌ 联表退回 XML | ~30% | ⚠️ 标签切割 |
| JOOQ | ⚠️ 不自动 | ✅ 能做 | ~50% | ⚠️ DSL 翻译 |
| Spring JDBC | ❌ 完全手动 | ✅ 能做 | 100% | ✅ 100% 透明 |
| Spring JDBC Ultra | ✅ 做得最好 | ✅ 做得最好 | ≈100% | ✅ 100% 透明 |
没有任何一个框架在“单表对象化”和“关系对象化”两个维度上同时做得好。 Ultra 是唯一一个在单表上达到 ORM 级别、在关系查询上达到原生 SQL 级别的框架。
它的核心哲学极其简单:
- 在“单表 CRUD”这个机械劳动场景:自动化
- 在“关系查询”这个语义丰富场景:放弃自动化,回到 SQL 本源
- 在“条件拼接”这个重复劳动场景:用 BaseCondition 抽象
- 在“扩展”这个横切场景:用 Spring AOP,不挖框架地道
Spring JDBC 面向数据库设计,Ultra 面向业务设计。 前者是“数据库在 Java 世界里的镜子”,后者是“业务需求在 Spring JDBC 上的脚手架”。
这套框架已经在生产环境稳定运行 3 年+,支撑日均百万级请求,服务十余家企业客户。它不是“又一个持久层框架”,它是在 Spring JDBC 这个最诚实的底座上,开辟的一个全新的、被长期忽视的生态位。
相关开源地址
删繁就简三秋树,标新立异二月花。 Spring JDBC Ultra 做的不是“又一个框架”,它做的是——去魅。去 MyBatis 之魅、去 ORM 之魅、去复杂性之魅。把 SQL 从 XML 里解救出来,把程序员从框架的盲从里解救出来,把简单从复杂的包装里解救出来。







