你以为 MyBatis-Plus 单表是 yyds?这场心智负担的比拼,它从一开始就输了 你以为 MyBatis-Plus 单表是 yyds这场心智负担的比拼它从一开始就输了聊持久层选型总有一个根深蒂固的刻板印象单表 CRUD 当然用 MyBatis-Plus继承 BaseMapper 零代码搞定Spring JDBC 这类原生框架单表太啰嗦要手写一堆 SQL效率差远了。很多人默认单表场景是 MP 的绝对主场比拼的就是「功能有没有」。但我做了十几年后端写烂了十几套持久层封装得出一个完全相反的结论单表场景从来不是「能不能做」的比拼是心智负担、设计质感、踩坑概率的全面较量。所有框架都能做单表增删改查差别只在于你是靠堆插件、堆注解、堆零散 API 凑出一套功能用着处处别扭、时不时踩坑还是靠统一范式、正交设计、约定式自动化做出一套体系拿来就用、几乎没有心智负担。今天我们就把单表能力拆碎了对标看看 Spring JDBC Ultra 是不是连「MP的主场」也能全面胜出。单表核心 API 全景对标能力分类细分场景Spring JDBC UltraMyBatis-PlusSpring Data JPA新增单条新增save(T entity)自动填充主键、审计字段insert(T entity)需配置主键策略save(T entity)新增/更新二义性新手易踩坑批量新增saveBatch(ListT)原生批处理自动填充审计saveBatch需引入扩展插件无原生批量需自行实现批量替换/UpsertreplaceBatch(ListT)原生支持无原生能力需手写SQL无原生能力更新非空字段更新90%场景update(T entity)null 字段不参与语义明确需配合UpdateWrapper或全局配置不直观无原生区分save 全量覆盖全字段更新含nullupdateNull(T entity)显式调用边界清晰需手动设置字段策略save()默认全量更新易误覆盖按条件更新update(T entity, Condition cond)复用统一条件类需单独构建UpdateWrapper两套API割裂需写Modifying JPQL退回SQL删除主键删除delete(id)自动识别逻辑/物理删除需配置TableLogic插件需写SQLDelete注解按条件删除delete(Condition cond)复用条件类需构建QueryWrapper需手写删除语句查询四象限多行多列列表list(cond)实体列表原生支持list(queryWrapper)需构造条件findAll(spec)条件构造繁琐单行多列单条row(cond)/findById(id)语义明确getOne(queryWrapper)多结果易异常findById(id)单条可用多条麻烦多行单列字段集合columns(cond, fieldType)原生返回单列集合需 select 指定字段 手动转换无原生API无原生能力需投影映射单行单列单个值field(cond, fieldType)直接返回聚合/字段值无原生API需手动取第一个值无原生能力需写JPQL统计判断计数count(cond)一行完成count(queryWrapper)需构造器count(spec)条件繁琐存在性判断exists(cond)直接返回 boolean需 count 后手动判断 0无原生方法需自行判断分页查询page(cond)自动 count 分页方言适配需引入 PageHelper 或 MP 分页插件Pageable黑盒生成SQL不可控增强能力审计字段约定字段名自动填充零配置需配置 MetaObjectHandler 插件需配置 EntityListener 注解逻辑删除有dr字段自动生效无则物理删除零注解需全局插件 TableLogic注解需SQLDeleteWhere注解行锁查询findById(id, true)一行开启 FOR UPDATE需手写 selectForUpdate 或 Wrapper需加Lock注解兼容性差一、查询 API从零散堆砌到四象限正交完备单表查询看似就是「查列表、查单条」那点事可不同框架的设计水准天差地别。MyBatis-Plus 的查询 API 是典型的「凑功能式设计」查列表用list()查单条用getOne()遇到多条结果还可能抛异常想查某一列的所有值没有原生方法得先 select 指定字段再手动 stream 映射提取想查单个聚合值COUNT、SUM得先查 count 再手动判断没有直接返回基础类型的原生接口。Spring Data JPA 更甚单条查用findById()列表查用findAll()但凡涉及单列、聚合值全要手写 JPQL 或投影映射繁琐至极。它们的共性是API 是一个个加上去的没有统一的底层抽象覆盖不全、语义模糊开发者要记很多「特殊写法」。而 Spring JDBC Ultra 的查询体系是基于「结果集形状」做的数学级正交抽象——任何查询返回结果永远落在四个象限里结果形态多行单行多列list(cond)实体列表row(cond)/findById(id)单条实体单列columns(cond, field)字段集合field(cond, field)单个聚合/字段值四个方法不重叠、不遗漏见名知意覆盖所有查询场景要用户列表 →list()要单条用户详情 →row()/findById()要所有用户ID集合 →columns()要用户总数 / 最大金额 →field()没有多余方法没有特殊写法没有「查出来再手动转换」的样板代码。分页更是天然复用这套体系本质就是list()查当前页数据 field()算总条数一套条件类、一套执行逻辑没有额外的黑盒分页生成器。这不是功能多少的差距是设计维度的代差。一个是面向 SQL 操作凑功能一个是面向业务结果做抽象一个让你迁就框架的零散 API一个让你用最符合业务直觉的方式拿数据。二、更新语义从歧义黑盒到明确双策略单表更新是所有框架里坑最多的地方没有之一。先说说大家都踩过的经典坑MyBatis-PlusupdateById()默认全字段更新null 值会直接写入数据库。想实现「非空字段更新」要么全局配置字段策略要么手写UpdateWrapper配置和代码不同步就是线上事故。Spring Data JPAsave()方法天生「新增/更新二义性」新手经常把「部分字段更新」写成「全量覆盖」把非空字段刷成 null排查起来极其隐蔽。它们的问题本质是一样的把最常用的场景语义做模糊了把选择权甩给了配置和注解。开发者要记规则、看配置、踩过坑才知道「原来默认行为是这样」。而 Spring JDBC Ultra 直接用两个方法把语义焊死零配置、零歧义userDao.update(user);// 90% 场景null 字段不参与更新userDao.updateNull(user);// 显式调用null 也参与全量更新日常业务 90% 的场景直接用update()不用想、不用配永远不会把 null 写进数据库真的需要全量覆盖就显式调用updateNull()代码本身就是注释所有人一看就懂。更关键的是按条件更新MP 要单独构建UpdateWrapper和查询用的QueryWrapper是两套东西JPA 要写Modifying注解 JPQL基本等于退回手写 SQLUltra 直接复用同一套 Condition 条件类update(entity, cond)查询怎么写条件更新就怎么写条件心智完全统一。好的设计就是把最常用的场景做对把边界划清楚。不让开发者猜不让开发者记配置不让开发者踩「默认行为和直觉相反」的坑。三、自动化能力从配置堆砌到约定大于配置审计字段自动填充、逻辑删除自动处理这俩是单表的标配能力。但「自动」和「自动」之间含金量天差地别。MyBatis-Plus 的「自动」插件 注解 实现类一套下来几十行代码审计字段要写MetaObjectHandler实现类要给字段加TableField注解要配置填充策略逻辑删除要开启全局插件要给字段加TableLogic注解还要配置未删除、已删除的值。Spring Data JPA 的「自动」监听器 注解层层套娃审计字段要加EntityListeners(AuditingEntityListener.class)字段标CreatedDate/LastModifiedDate还要开启全局审计配置逻辑删除要写SQLDelete自定义删除语句再加Where注解过滤已删除数据。它们的「自动」本质是**「配置出来的自动」**你要先学一堆注解、配一堆插件、写一堆扩展类才能换来「不用手动 set」的便利。学配置、排配置问题的时间早就超过了省下来的代码时间。而 Spring JDBC Ultra 的「自动」是**「约定出来的自动」**零注解、零插件、零配置实体里有createTime/createBy字段新增时自动填充有updateTime/updateBy更新时自动赋值实体里有dr字段删除时自动走UPDATE SET dr1逻辑删除没有这个字段自动走DELETE FROM物理删除。符合约定就生效不符合就走默认行为。不需要记注解不需要配插件不需要写扩展类。真正的自动化从来不是堆配置堆出来的是靠合理的约定自然实现的。四、条件体系从多套割裂到一套通用很多人没意识到单表场景最大的心智负担来自「条件构建的割裂」。MyBatis-Plus 是典型的反面教材查询用QueryWrapper更新用UpdateWrapper删除又有自己的条件写法看似都是 Wrapper实则 API 风格、使用细节各不相同同样的筛选逻辑不同场景要重写一遍。JPA 更夸张简单查询用方法命名规则findByNameAndAge复杂点用 Specification再复杂点写 JPQL三套条件体系语法、能力、调试难度天差地别换个场景就要换一套思维。而 Spring JDBC Ultra 从根上就解决了这个问题整个框架只有一套 Condition 条件类查询、更新、删除、计数、存在性判断全场景复用。UserCondcondUserCond.builder().name(张).ageMin(18).build();// 查询列表userDao.list(cond);// 分页查询userDao.page(cond);// 统计数量userDao.count(cond);// 判断存在userDao.exists(cond);// 按条件更新userDao.update(user,cond);// 按条件删除userDao.delete(cond);定义一次条件所有操作通用。不用切换 API不用转换对象不用记多套规则。心智负担直接砍掉一半。顺带一提count()和exists()看似简单恰恰最能体现设计细腻度MP / JPA 判断存在得先查 count再手动写count 0多一行样板代码Ultra 原生exists(cond)直接返回 boolean语义一步到位不用二次加工。五、架构分层从割裂投降到递进增强这是最容易被忽略、但架构价值最大的差距单表和复杂场景的衔接方式。所有框架都留了「逃生通道」——单表封装搞不定了就退回手写原生 SQL。但「逃生通道」的设计天差地别MyBatis-Plus / JPA逃生 推倒重来MP单表用 Wrapper 对象思维遇到联表、复杂查询直接退回 XML 写 SQL。两套体系、两套工具、两套思维之前的封装收益全部清零条件不能复用API 完全不同。JPA单表用方法命名 / Specification遇到复杂 SQL 退回nativeQuery true。对象映射、自动化、类型安全所有承诺全部作废等于换了个框架写代码。它们的单表封装和原生 SQL 之间是断裂的、割裂的。简单场景有多爽复杂场景切换时就有多痛苦。Spring JDBC Ultra逃生 自然递进它的设计是分层的越上层越简洁越底层越自由BaseDao 层面向单表场景继承空类即用API 极简覆盖 90% 日常需求BaseSql 层面向全场景是底层能力底座自定义 SQL、联表、存储过程、函数调用全能接。遇到单表简单场景你用 BaseDao 舒舒服服写遇到复杂联表、报表、子查询你直接调用父类 BaseSql 的能力手写 SQL 复用 Condition 条件类API 风格、参数规则、执行逻辑完全一致。没有切换成本没有思维断层没有「退回到原生模式」的割裂感。它不是给你留了个逃生门而是给你铺了一条向上的路。简单有简单的写法复杂有复杂的写法从头到尾一套心智模型。六、执行透明从黑盒猜想到所见即所得最后说一个很影响开发体验的点SQL 到底是不是你写的那个。同样是单表自动生成 SQL本质天差地别JPA生成的 SQL 你控制不了加个关联就可能带出 N1线上慢 SQL 排查全靠猜MP生成的 SQL 受插件链、全局配置、字段策略多重影响排查问题要翻好几层源码分页插件、租户插件、逻辑删除插件哪个环节出问题都要扒源码调试。它们都是「黑盒生成 SQL」——你只知道调用了方法不知道底层最终执行的语句长什么样出了问题只能靠日志猜、靠注释试。而 Spring JDBC Ultra 的单表 SQL规则简单透明日志直接打印带参数的完整可执行 SQL复制出来就能在数据库客户端直接跑审计、逻辑删除、条件拼接本质都是简单的 SQL 片段拼接没有拦截器黑盒没有动态代理魔法出了问题断点直接打进业务代码秒级定位不用翻框架源码不用猜插件执行顺序。这份「透明感」平时感觉不明显真到线上排错、调试复杂逻辑的时候体验差出一个数量级。最后单表都赢联表碾压 延伸阅读持久层的主战场从来都是联表很多人觉得不就是单表增删改查吗能有多大差距恰恰是这种「大家都能做」的基础场景最能看出框架的设计功力。差的框架把功能堆给你就完事了你要记配置、踩坑、切换多套体系心智负担全甩给开发者好的框架让你用的时候几乎感觉不到它的存在——不用记一堆规则不用怕踩歧义的坑不用在多套 API 之间反复横跳。Spring JDBC Ultra 就是后者。很多人以为它只在复杂联表、报表场景有优势实际上哪怕是 MP 最引以为傲的单表主场它在设计质感、心智负担、长期维护性上也依然是全面胜出。毕竟框架的终极价值从来不是「提供了多少功能」而是「帮开发者省了多少心思」。你以为 MyBatis-Plus 是单表 yyds其实它赢的只是「先入为主的使用惯性」。论设计、论体验、论长期维护的综合成本它从一开始就输了。我们不是只做了一个核心框架而是一整套完整的生产级解决方案项目地址说明核心框架simple-daoSpring JDBC Ultra 核心内核系统底座simple-dao-starter内置 RBAC 权限、用户/部门/字典/菜单管理开箱即用代码生成器simple-dao-coder全栈代码生成四套模板覆盖常规业务实战案例simple-dao-demo8 个可运行 Demo从入门到高阶全覆盖Spring JDBC Ultra 已在生产环境稳定运行 3 年支撑日均百万级请求服务十余家企业客户。如果你厌倦了复杂框架的折磨相信简单的力量欢迎来试试。技术可以更简单开发可以更愉快程序员可以早下班。