喜欢 MyBatis-Plus 的顺滑链式,又舍不得 sqltoy 的动态 SQL、缓存翻译和四层分页优化?现在不用二选一了。
SqlToy 是"JPA 式对象操作"与"SQL 优先查询"的融合体:简单的单表 CRUD 走对象,复杂的查询直接写 SQL(#[...] 动态段比 MyBatis 的 <if>/<foreach> 直观一个量级),再加缓存翻译、行转列、跨 25 种数据库方言这些硬能力。过去它相对生态的唯一"短板",是没有一套类型安全的 Lambda 条件 API——单表条件要么手写 where 字符串,要么引入第三方增强包 sqltoy-plus。
6.0.8 把 sqltoy-plus 的能力并入核心并深度重构,6.0.9 完成跨库实证修补与体验细化。 两个版本落地后,从单表 CRUD、多表 join、分组聚合,到 CTE、UNION、@fast 快速分页,lambda 层对 sqltoy 的功能面实现了完整覆盖,同时在线文档全面焕新(128 页、中英双语)。
<dependency>
<groupId>com.sagframe</groupId>
<artifactId>sagacity-sqltoy-spring-starter</artifactId>
<version>6.0.9</version>
</dependency>
一、Lambda 条件体系:三个入口,一套构建器
入口只有三个,分别对应查、改、删:
lightDao.lambdaQuery(StaffInfo.class) // 查询(单表或 join)
lightDao.lambdaUpdate(StaffInfo.class) // 更新(单表或 join)
lightDao.lambdaDelete(StaffInfo.class) // 删除(单表或 join)
设计上最值得称道的一点:单表只是 join 的退化形态。不调用 join 步骤就是单表(条件内部转 EntityQuery/EntityUpdate,复用 sqltoy 实体链路的全部内核能力);调用 leftJoin(...).on(...) 即切换 join 通道拼装完整 SQL。不需要预先选择构建器类型,一套 API 通吃。
典型例子 1:单表动态查询——IfPresent 语法糖让"参数有没有就拼不拼"退化成一行一个条件,再也不用手写 if 判空:
List<StaffInfo> list = lightDao.lambdaQuery(StaffInfo.class)
.eqIfPresent(StaffInfo::getStatus, status) // null 自动跳过
.likeIfPresent(StaffInfo::getStaffName, name) // 空白串自动跳过
.inIfPresent(StaffInfo::getOrganId, organIds) // 空集合自动跳过
.betweenIfPresent(StaffInfo::getEntryTime, start, end) // 半开区间自动退化为 >= / <=
.orderByDesc(StaffInfo::getEntryTime)
.find();
典型例子 2:多表 join 查 VO——字段按实体自动限定别名,AS 别名直接映射 VO 属性:
List<StaffInfoVO> rows = lightDao.lambdaQuery(StaffInfoVO.class)
.selectAll()
.from(StaffInfo.class)
.leftJoin(OrganInfo.class).on(StaffInfo::getOrganId, OrganInfo::getOrganId)
.select(JoinCol.of(OrganInfo::getOrganName).as("organName"))
.eqIfPresent(StaffInfo::getStatus, status)
.find();
典型例子 3:条件更新与表达式自增:
lightDao.lambdaUpdate(StaffInfo.class)
.set(StaffInfo::getStatus, 2)
.setExpr(StaffInfo::getLoginCount, "loginCount + 1") // 基于当前值计算
.eq(StaffInfo::getStaffId, staffId)
.update();
典型例子 4:分组聚合——JoinCol.sum/avg/count/min/max 配合 groupBy/having,单表聚合无需 join:
List<OrganSalaryStatVO> stats = lightDao.lambdaQuery(OrganSalaryStatVO.class)
.select(JoinCol.of(StaffInfo::getOrganId).as("organId"),
JoinCol.sum(StaffInfo::getSalary).as("totalSalary"),
JoinCol.count(StaffInfo::getStaffId).as("staffCount"))
.from(StaffInfo.class)
.groupBy(StaffInfo::getOrganId)
.having("sum(SALARY) > :min", MapKit.map("min", 10000))
.find();
二、关键点清单:不只是"能写",更是"写得安全、写得跨库"
- 类型安全:以
User::getStatus方法引用组织条件,字段改名、拼写错误编译期即暴露; - 终端全家桶:
find / getOne / findPage / findTop / findRandom / count / exists,join 通道同样可用; - 严格 getOne:匹配多条直接抛异常(含匹配条数),不静默取第一条掩盖条件缺陷;
- 全表更新守卫:无 where 条件的 update/delete 默认抛异常,显式
.matchAll()才放行——从 API 层杜绝"忘写 where 的全表 UPDATE"事故; - 关联子查询:
exists/notExists用OuterRef.of(外层字段)类型安全地表达外层列引用,支持多层嵌套; - join 更新/删除:采用可移植的 EXISTS-over-derived 改写,不依赖 MySQL 专有语法,已在 mysql/postgresql/sqlserver/oracle/db2 五类真库验证;
- 组合查询:CTE(含
WITH RECURSIVE递归、PostgreSQLMATERIALIZED物化 hint 自动按库保留/剔除)、@fast分页下推派生表、UNION / UNION ALL,多个 lambdaQuery 组装成一条大 SQL,参数全局唯一不撞号; - 多数据源与分页优化:
.dataSource(ds)指定本次操作的数据源;findPage支持.pageOptimize(...)复用 sqltoy 四层分页优化(并行 count + count 结果缓存); - 单表通道白嫖企业级能力:分库分表、缓存翻译、加解密脱敏、租户过滤在单表 lambda 查询上自动生效——这是"并入核心"相对外挂增强包的本质优势;
- 6.0.9 细化:新增
likeExact族(用户输入含%时按普通字符匹配,不被当通配符放大命中范围)、空 groupBy/having 跳过组装、setExpr 危险形态快速失败。
三、6.0.9:跨库不是"理论上支持",是真库跑出来的
6.0.9 的主题是"跨库实证修补"——每一个修正背后都是真库实测反证:
WITH xx AS MATERIALIZED仅 PostgreSQL 系原生支持,其余库(mysql/sqlserver/oracle/db2 等)解析期自动剔除 hint,同一份 SQL 无需再按方言各写一个 sqlId(PG18/openGauss/金仓/瀚高/Vastbase 保留执行,mysql 9.3/tidb 8.5/mssql 2025 实测反证,8 库真库零跳过);to_char/to_date的裸HHtoken 全库统一为 24 小时制(DB2/H2 真库实测原先按 12 小时输出,属静默错值);to_date目标为 PG 系时自动改写to_timestamp,规避时间部分被静默截断;trim(from x)省略剔除字符形态在 mysql 报 1064、oracle 报 ORA-30000,自动改写为等价trim/ltrim/rtrim;- 分页 count 缓存键由
sqlId升级为sqlId@dialect,多数据源跨方言执行时 count 结果不再互相串用。
同版本的 GraalVM native 支持也值得一提:6.0.8 重写了 AOT 体系(修复 5.6 以来实体反射 hints 恒为空的缺陷,按配置动态注册资源与扩展类),showcase 项目 native 镜像实测启动 0.05 秒,CRUD、缓存翻译、lambda 全链路可用——云原生与信创场景的部署形态补齐了。
四、在线文档全面焕新:128 页、中英双语
文档站点(https://sagframe.github.io/sqltoy-docs/ )完成重整,从环境准备、快速上手到企业级特性共 128 页,中英双语全覆盖:
- 新增 《Lambda 条件查询与更新》 专章:条件方法速查表、动态化惯用法、join/聚合/CTE/UNION 全示例,以及"安全边界与错误语义速查表"——每一处"会抛异常的场景"都写在明面上;
- 新增 GraalVM Native Image 指引、6.0 升级指南(含 sqltoy-plus 逐项迁移对照表)与 版本更新日志索引;
- 快速上手推荐 sqltoy-quickstart:clone 后
mvn test即可运行,内置 H2 内存库,9 节课程从 CRUD 覆盖到真库跨库验证。
五、"完整覆盖"不是口号,是测试矩阵跑出来的
lambda 体系在核心仓库内配有 13 个专项测试类:从 lambda 元信息提取、SQL 组装文本锁定、H2 端到端执行、join 通道、组合查询(CTE/@fast/UNION)到多数据源与回归缺陷测试,其中 LambdaFullCoverageH2Test 以独立数据集对全部条件族、逻辑组合、动态化语法糖、查询终端、更新/删除安全边界做全功能覆盖验证;join 更新/删除与函数适配属发版多库门禁范围,经 mysql/postgresql/sqlserver/oracle/db2 等真实数据库回归。
至此,sqltoy 的能力面形成完整闭环:JPA 式对象操作(CRUD/级联/updateFetch 高并发台账)+ Lambda 条件体系(单表/join/聚合/CTE)+ SQL 模式(#[...] 动态段 + 缓存翻译 + 四层分页优化 + 行列转换/同比环比)+ 企业级特性(分库分表/多租户/脱敏加解密/数据权限)+ 25 种数据库方言 + NoSQL(ES/MongoDB)+ GraalVM native。简单业务一套链式写到底,复杂查询随时切 SQL 模式,两种风格共用同一个 LightDao。
六、升级成本:几乎为零
- 普通用户:只改版本号为 6.0.9;
- 原 sqltoy-plus 用户:移除增强包依赖,import 从
com.sagframe.sagacity.sqltoy.plus.*换成org.sagacity.sqltoy.lambda.*,对照文档迁移表逐项替换即可,无需任何@Enable配置; - JDK 8 存量项目:留在
5.6.96.jre8(jre8 最终版,持续可用)。
资源直达
| 资源 | 地址 |
|---|---|
| 在线文档 | https://sagframe.github.io/sqltoy-docs/ |
| Lambda 专章 | https://sagframe.github.io/sqltoy-docs/#/query/lambda_query |
| GitHub | https://github.com/sagframe/sagacity-sqltoy |
| Gitee | https://gitee.com/sagacity/sagacity-sqltoy |
| 入门演示 quickstart | https://github.com/sagframe/sqltoy-quickstart |
| 全功能演示 showcase | https://github.com/sagframe/sqltoy-showcase |
一套 SQL 跑遍 25 种数据库,一个 LightDao 用遍对象操作与 Lambda 条件。SqlToy 6.0.9,值得你花十分钟试试。