一个基于 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条件2LIKE? ← 高度动态(业务决定)AND条件3IN(?,?,?)← 高度动态(业务决定)ORDERBY字段 ← 中度动态(有时需要排序)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:条件管理
publicclassUserCondextendsBaseCondition{privateStringname;privateIntegerageMin;privateIntegerageMax;privateObject[]ids;@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);}}BaseDao:单表 CRUD,零代码
@RepositorypublicclassUserDaoextendsBaseDao<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:
StringBuildersql=newStringBuilder("SELECT * FROM user WHERE 1=1");List<Object>params=newArrayList<>();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:
@OverrideprotectedvoidaddCondition(){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(inti=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:
@RepositorypublicclassUserDaoextendsBaseDao<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*FROMuserWHEREid=?FORUPDATESpring JDBC Ultra:
Useruser=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:
UserCondcond=UserCond.builder().name("张").build();// 单表用这套条件userDao.page(cond);// 联表也用这套条件userDao.page(JOIN_SQL,cond,UserVO.class);收益:条件定义一次,单表联表共用。条件层不关心查几张表。
收益 11:分页——一行搞定
Spring JDBC:
StringcountSql="SELECT COUNT(*) FROM user"+where;Integertotal=jdbc.queryForObject(countSql,Integer.class,params);StringpageSql=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:
Integercount=jdbc.queryForObject("SELECT COUNT(*) FROM user WHERE id = ?",Integer.class,id);booleanexists=count>0;Spring JDBC Ultra:
booleanexists=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# 是否打印带参 SQLworker-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 年+,支撑日均百万级请求。