一个基于 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 里拼完,框架内部渲染、参数绑定、结果映射
  • 你想介入,只能靠拦截器——门槛极高,你要操作 MappedStatementBoundSqlParameterHandler 等内部对象
  • 所以才有大量分页插件、数据权限插件——这不是生态繁荣,是补坑生态

Spring JDBC

收益 1:最纯白盒,扩展成本极低

  • SQL 是你写的,参数是你绑的,结果是你映射的
  • 想加数据权限?加一个 if 拼个字符串就行

收益 2:SQL 能力 100%

代价:没有单表对象化,没有条件自动化

  • saveupdatedeletefindById 全都要自己写 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 INTOKeyHolderPreparedStatement 回调)。

Spring JDBC Ultra:

@Repository
public class UserDao extends BaseDao<User> {
    // 空的
}

收益: 获得 saveupdatedeletefindByIdlistpage。审计字段自动填充。

收益 5:审计字段——全自动

Spring JDBC:

user.setCreateTime(LocalDateTime.now());
user.setCreateBy(currentUserId());

Spring JDBC Ultra: save() 自动填充 createTimecreateBydrupdate() 自动填充 updateTimeupdateBy

收益 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());

收益: addDynamicPreparedStatement 占位符,与框架原有条件同样安全。数据权限、多租户、行级过滤,全部通过 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 年+,支撑日均百万级请求。

Logo

一站式 AI 云服务平台

更多推荐