Spring JDBC Ultra 收益盘点
一个基于 Spring JDBC 的极简持久层增强工具,代码量压缩至传统框架的 1/3 ~ 1/4
一、定位:它不是框架,它是 Spring JDBC 的“业务手套”
在开始盘点收益之前,必须先说清楚一件事:Spring JDBC Ultra 不是要替代 Spring JDBC,它是让 Spring JDBC 更好用。
两个视角,两个设计
| 框架 | 设计视角 | 设计目标 | 抽象对象 |
|---|---|---|---|
| Spring JDBC | 面向数据库 | 极简、直接、无阻碍地执行 SQL | 关系型数据库的操作接口 |
| Spring JDBC Ultra | 面向业务 | 让业务代码更少、更清晰、更易维护 | SQL 语句中动态变化的部分 |
Spring JDBC 尊敬的是数据库——它把所有精力都放在如何干净、直接、无损耗地执行 SQL 上。它不关心业务长什么样,它只关心“你能不能把 SQL 发过去,把结果拿回来”。这是它的纯粹性所在。
Spring JDBC Ultra 尊敬的是业务——它承认 SQL 结构本身是有规律的,承认“WHERE 部分永远是动态的,FROM/JOIN 部分几乎是静态的”,然后针对这个规律做抽象。它不破坏 Spring JDBC 的纯粹性,只是在它上面加了一层“业务友好的手套”。
抽象的落脚点:SQL 的动态梯度
在任何一条 SQL 里,不同部分的“变化频率”完全不同:
SELECT 字段列表 ← 几乎静态(实体类固定,VO 固定)
FROM 主表 ← 几乎静态(表固定)
JOIN 关联表 ← 几乎静态(关联结构固定)
WHERE 条件1 = ? ← 高度动态(业务决定)
AND 条件2 LIKE ? ← 高度动态(业务决定)
AND 条件3 IN (?,?,?) ← 高度动态(业务决定)
ORDER BY 字段 ← 中度动态(有时需要排序)
LIMIT ?, ? ← 高度动态(分页参数)
Spring JDBC Ultra 的抽象正是针对 SQL 的动态梯度做的:
- 静态部分:你手写 SQL,写在 Java 常量里,框架不碰它
- 中度动态部分(ORDER BY):你通过
cond.setOrders()控制 - 高度动态部分(WHERE、LIMIT):你通过
BaseCondition声明,框架自动拼接参数、计算分页
这个抽象是跨语言的——任何支持连接数据库的语言,都会面对同样的 SQL 动态性问题。这正是 Spring JDBC Ultra 的设计可以被复刻到 Python、Go、Node.js 等其他语言的根本原因。
二、前置认知:三大主流持久层框架的真相
要理解 Spring JDBC Ultra 的价值,必须先看清现状。
Hibernate / JPA
唯一的收益:单表对象化
session.save(user)、session.get(User.class, id)- 对单表 CRUD 场景,代码量确实少
代价 1:SQL 能力被锁死在约 1/3
- JPQL 不支持窗口函数、递归 CTE、某些复杂的 JOIN 类型
- 遇到复杂查询只能退回原生 SQL
代价 2:黑盒程度极高
- 缓存机制、代理生成、懒加载、脏检查——每一步都在增加不可控性
MyBatis
唯一的收益:SQL 能力 100%
- 手写 SQL,数据库能写的它都能写
代价 1:枷锁极重,且无价值
- Mapper 接口 + XML 双向绑定,跨文件维护成本极高
- 上百个属性、几十个标签、十几个注解,学了大量与业务无关的框架私有知识
- 你学这些的唯一目的,就是为了“能写 SQL”——而“能写 SQL”是 Spring JDBC 本来就有、默认就有的能力
代价 2:运行时黑盒,扩展难如登天
- SQL 在 XML 里拼完,框架内部渲染、参数绑定、结果映射
- 你想介入,只能靠拦截器——门槛极高,你要操作
MappedStatement、BoundSql、ParameterHandler等内部对象 - 所以才有大量分页插件、数据权限插件——这不是生态繁荣,是补坑生态
Spring JDBC
收益 1:最纯白盒,扩展成本极低
- SQL 是你写的,参数是你绑的,结果是你映射的
- 想加数据权限?加一个
if拼个字符串就行
收益 2:SQL 能力 100%
代价:没有单表对象化,没有条件自动化
save、update、delete、findById全都要自己写 SQL- 条件拼接要自己维护
StringBuilder+List<Object> - 分页要自己算 COUNT 和 LIMIT
- 审计字段要自己填
三、Spring JDBC Ultra 的定位:一个全新的生态位
三大主流优点全有,缺点全没有
| 能力维度 | Hibernate | MyBatis | Spring JDBC | Spring JDBC Ultra |
|---|---|---|---|---|
| 单表对象化 | ✅ | ❌(需 MP 插件) | ❌ | ✅ |
| SQL 能力 100% | ❌(约 1/3) | ✅ | ✅ | ✅ |
| 运行时白盒 | ❌ | ❌ | ✅ | ✅ |
| 扩展成本低 | ❌ | ❌ | ✅ | ✅ |
| 无框架枷锁 | ❌ | ❌ | ✅ | ✅ |
| 条件管理零样板 | ❌ | ❌ | ❌ | ✅ |
| 分页自动处理 | ❌(需 Pageable) | ❌(需插件) | ❌(手写) | ✅ |
| 审计字段自动填充 | ❌(需 Listener) | ❌(需 MP 插件) | ❌(手写) | ✅ |
一句话概括
三大主流持久层框架的收益,Spring JDBC Ultra 全都有;三大主流持久层框架的代价,Spring JDBC Ultra 全都没有。
- Hibernate 的单表对象化 → 继承
BaseDao,零代码获得 CRUD - MyBatis 的 SQL 能力 100% → 联表查询手写完整 SQL,不受任何限制
- Spring JDBC 的纯白盒 → 你写的 SQL 就是执行的 SQL,没有黑盒改写
它不是“另一个框架”——它是 Spring JDBC 的语法糖。没有桥接层、没有适配层、没有新的执行链路,性能等价于 Spring JDBC。
四、核心设计:三大主类,各司其职
框架对外暴露三个核心类:
| 类 | 职责 | 对应 Spring JDBC 概念 |
|---|---|---|
BaseCondition |
条件管理 | 手写的 StringBuilder + List<Object> |
BaseDao |
单表对象化 | 手写 CRUD SQL + KeyHolder + 审计字段 |
BaseSql |
SQL 执行层 | JdbcTemplate + BeanPropertyRowMapper |
BaseCondition:条件管理
public class UserCond extends BaseCondition {
private String name;
private Integer ageMin;
private Integer ageMax;
private Object[] ids;
@Override
protected void addCondition() {
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);
}
}
BaseDao:单表 CRUD,零代码
@Repository
public class UserDao extends BaseDao<User> {
// 空的。所有 CRUD 能力都在父类里。
}
BaseSql:自定义 SQL 执行层
// 单表分页
Page<User> page = userDao.page(cond);
// 联表分页(同一套 API,多传一个 SQL)
Page<UserVO> page = userDao.page(JOIN_SQL, cond, UserVO.class);
五、完整收益盘点
以下逐条对比 Spring JDBC 原生写法 与 Spring JDBC Ultra 写法。
收益 1:条件管理——从手工账本到声明式注册
Spring JDBC:
StringBuilder sql = new StringBuilder("SELECT * FROM user WHERE 1=1");
List<Object> params = new ArrayList<>();
if (name != null && !name.isEmpty()) {
sql.append(" AND name LIKE ?");
params.add("%" + name + "%");
}
if (ageMin != null) {
sql.append(" AND age >= ?");
params.add(ageMin);
}
// 8 个条件后,sql 和 params 已经不同步了
Object[] finalParams = params.toArray();
Spring JDBC Ultra:
@Override
protected void addCondition() {
and("name LIKE", name, 3);
and("age >=", ageMin);
and("age <=", ageMax);
in("id", ids);
}
收益: 条件和参数绑定在一起,不会错位;新增条件一行代码;array() 自动导出参数。
收益 2:模糊查询——自动处理 %
Spring JDBC:
params.add("%" + name + "%");
Spring JDBC Ultra:
and("name LIKE", name, 3); // 3=前后都加 %
收益: 1=左 %name,2=右 name%,3=前后 %name%,省掉手动包装。
收益 3:IN 查询——数组自动展开
Spring JDBC:
if (ids != null && ids.length > 0) {
sql.append(" AND id IN (");
for (int i = 0; i < ids.length; i++) {
sql.append(i == 0 ? "?" : ",?");
}
sql.append(")");
params.addAll(Arrays.asList(ids));
}
Spring JDBC Ultra:
in("id", ids);
收益: 不再手动算 ? 个数,不再手动循环拼接,不再手动 addAll。
收益 4:单表 CRUD——继承空类即用
Spring JDBC:
25 行样板代码(INSERT INTO、KeyHolder、PreparedStatement 回调)。
Spring JDBC Ultra:
@Repository
public class UserDao extends BaseDao<User> {
// 空的
}
收益: 获得 save、update、delete、findById、list、page。审计字段自动填充。
收益 5:审计字段——全自动
Spring JDBC:
user.setCreateTime(LocalDateTime.now());
user.setCreateBy(currentUserId());
Spring JDBC Ultra: save() 自动填充 createTime、createBy、dr;update() 自动填充 updateTime、updateBy。
收益 6:逻辑删除——自动判断
Spring JDBC:
有 dr 字段要写 UPDATE SET dr=1,没有要写 DELETE FROM。
Spring JDBC Ultra:
userDao.delete(id);
收益: 有 dr 字段则逻辑删除,无则物理删除,框架自动判断。
收益 7:行锁——一行开启
Spring JDBC:
SELECT * FROM user WHERE id = ? FOR UPDATE
Spring JDBC Ultra:
User user = userDao.findById(id, true);
收益 8:更新空值控制——按场景选择
Spring JDBC: 要自己判断哪些字段是 null,决定要不要放 UPDATE 语句里。
Spring JDBC Ultra:
userDao.update(user); // null 不参与更新(90% 场景)
userDao.updateNull(user); // null 也参与更新
收益 9:单表联表同一套 API(独创)
Spring JDBC: 单表用 BeanPropertyRowMapper,联表要么写 RowMapper,要么手动映射。
Spring JDBC Ultra:
// 单表查
Page<User> page = userDao.page(cond);
// 联表查(同样是 page 方法,多传一个 SQL)
Page<UserVO> page = userDao.page(JOIN_SQL, cond, UserVO.class);
收益: 你学一个 page(),单表用,联表也用。
收益 10:单表联表同一套条件(独创)
Spring JDBC: 单表条件和联表条件是两套东西,参数要重新管理。
Spring JDBC Ultra:
UserCond cond = UserCond.builder().name("张").build();
// 单表用这套条件
userDao.page(cond);
// 联表也用这套条件
userDao.page(JOIN_SQL, cond, UserVO.class);
收益: 条件定义一次,单表联表共用。条件层不关心查几张表。
收益 11:分页——一行搞定
Spring JDBC:
String countSql = "SELECT COUNT(*) FROM user" + where;
Integer total = jdbc.queryForObject(countSql, Integer.class, params);
String pageSql = sql + where + " LIMIT ? OFFSET ?";
// 8 行代码
Spring JDBC Ultra:
Page<User> page = userDao.page(cond);
收益: COUNT SQL 自动生成,LIMIT 自动计算,方言自动适配(MySQL、PostgreSQL、SQL Server、Oracle)。
收益 12:批量操作——自动填充审计字段
Spring JDBC: 自己拼 batchArgs 二维数组,自己填审计字段。
Spring JDBC Ultra:
userDao.saveBatch(list);
收益: 框架自动生成批量 INSERT SQL,每条记录独立填充审计字段。
收益 13:存在性校验——一行完成
Spring JDBC:
Integer count = jdbc.queryForObject("SELECT COUNT(*) FROM user WHERE id = ?", Integer.class, id);
boolean exists = count > 0;
Spring JDBC Ultra:
boolean exists = userDao.exists(cond);
收益 14:运行时注入的参数化动态条件(独创)
MyBatis 拦截器能做到吗? 不能。
MyBatis 的“动态”是在 XML 里用 <if> 预判所有可能的分支——编译期决定的,不是运行期决定的。你想在运行时追加一个条件,只能去拦截器里操作 BoundSql,改的是字符串,参数要自己处理映射。
Spring JDBC Ultra:
// 在 AOP 切面中运行时注入
cond.addDynamic(" AND school_id = ?", currentUser.getSchoolId());
收益: addDynamic 走 PreparedStatement 占位符,与框架原有条件同样安全。数据权限、多租户、行级过滤,全部通过 AOP 切面零侵入完成。
收益 15:执行层正交完备——16 个方法全覆盖
4 种查询形态 × 2 种参数类型 × 有无日志重载:
| 方法 | 返回类型 | 用途 |
|---|---|---|
list() |
List<T> |
多行多列 |
row() |
T |
单行多列(行数不唯一抛异常) |
columns() |
List<T> |
多行单列 |
field() |
T |
单行单列 |
参数形式:cond 自动传参,或手动传 Object... params。每个方法都有 boolean showSql 重载。
收益 16:完整 SQL 日志,复制即执行
Spring JDBC + SimpleDAO: 日志打印的是完整 SQL(参数已替换),复制出来就能在数据库客户端执行验证。不需要手动把 ? 替换成参数值。
收益 17:异常就是 JDBC 异常,没有中间商
Spring JDBC Ultra: 完全继承 Spring JDBC 的异常体系,不额外包装。你看到的异常就是数据库报的错,没有 MyBatis 那 31 类框架专属异常。
收益 18:配置项极少——四五个,开箱即用
simple-dao:
logic-delete.field: dr # 逻辑删除字段
show-sql: true # 是否打印带参 SQL
worker-id: 0 # 雪花 ID 工作节点
data-center-id: 0 # 雪花 ID 数据中心
dialect: mysql # 数据库方言(自动检测,可显式指定)
收益 19:自研框架标准化
Spring JDBC 的 20% 直接用户中,相当一部分是自己封装了一套工具类。Spring JDBC Ultra 提供了一个标准化的封装方案,覆盖条件拼接、参数管理、分页、审计、逻辑删除——不用重复造轮子了。
六、代码量压缩对比
以一个“带 5 个条件 + 分页的联表查询”为例:
| 环节 | Spring JDBC 手写行数 | Spring JDBC Ultra 行数 |
|---|---|---|
| 条件拼接 + 参数管理 | 15-20 行 | 5 行(addCondition) |
| 分页 COUNT SQL | 3-5 行 | 0 行 |
| 分页 LIMIT / OFFSET | 2-3 行 | 0 行 |
| 参数数组导出 | 1 行 | 0 行 |
| 联表 RowMapper | 10-20 行 | 0 行 |
| 单表 CRUD | 20-30 行/方法 | 0 行 |
| 审计字段填充 | 3-5 行/方法 | 0 行 |
| 逻辑删除 | 3-5 行/方法 | 0 行 |
结论:代码量压缩至 MyBatis 的 1/3 ~ 1/4。
七、总结:一个全新的生态位
Spring JDBC Ultra 不是另一个框架——它是 Spring JDBC 的语法糖。
它没有替换 Spring JDBC 的任何能力,它只是把你要手写的样板代码自动化了。你不需要离开 Spring JDBC 的生态,因为 Spring JDBC Ultra 就长在它上面。
它的设计是:
- Hibernate 的单表对象化 → 继承
BaseDao,零代码 CRUD - MyBatis 的 100% SQL 能力 → 联表查询手写完整 SQL,不限制任何数据库语法
- Spring JDBC 的纯白盒 → 你写的 SQL 就是执行的 SQL,没有黑盒改写
- 三大主流的全部代价 → 全都没有(XML 枷锁、黑盒执行、SQL 限制、拦截器地狱)
它没有引入任何新问题,也没有牺牲任何 Spring JDBC 原有的优势。
八、配套工具链
| 项目 | 地址 | 说明 |
|---|---|---|
| 核心框架 | https://gitee.com/gao_zhenzhong/simple-dao | Spring JDBC Ultra 核心 |
| 系统底座 | https://gitee.com/gao_zhenzhong/simple-dao-starter | 用户/部门/字典/参数/菜单/RBAC |
| 代码生成器 | https://gitee.com/gao_zhenzhong/simple-dao-coder | 全栈代码生成,三套模板 |
| 实战案例 | https://gitee.com/gao_zhenzhong/simple-dao-demo | 8 个案例全覆盖 |
Spring JDBC Ultra 已在生产环境稳定运行 3 年+,支撑日均百万级请求。
更多推荐


所有评论(0)