6.5 兼容性与限制
本页记录 DLZ-DB 8.0 的构建边界、兼容策略和容易被误解的现实限制。它们应进入设计和测试,而不能只依赖框架默认行为。
1. Java 与构建矩阵
| 范围 | 产物目标 | 构建要求 |
|---|---|---|
dlz-db-core | Java 8 | CI 使用 JDK 8、17、21 验证 |
| Spring Boot Starter | Java 8 | CI 使用 JDK 8、17、21 验证 |
| Solon Plugin | Java 8 | CI 使用 JDK 8、17、21 验证 |
| Spring Boot 2 Demo | Java 8 | JDK 8+ |
| Solon 3 Demo | Java 8 | JDK 8+ |
| Spring Boot 3 Demo | Java 17 | JDK 17+ |
| 根目录完整 reactor | 包含 Boot 3 Demo | JDK 17+ |
“发行库兼容 Java 8”和“根项目完整构建要求 JDK 17”可以同时成立。JDK 8 任务只验证三个发行库模块,不聚合 Spring Boot 3 Demo。实际命令和 CI 矩阵以根目录 TESTING.md、POM 与 workflow 为准。
2. 框架集成
- 同一个 Spring Boot Starter 支持当前项目中的 Spring Boot 2 和 3 集成方式:Boot 2 通过
spring.factories,Boot 3 通过AutoConfiguration.imports。 - Spring Boot 应用只需提供容器
DataSource,不继承 DLZ-DB 配置类。 - Solon Plugin 通过 SPI 加载,但不会根据
dlz.db.*凭空创建数据源;容器必须已有DataSourceBean。 - 同时使用 MyBatis 与 DLZ-DB 时,能启动不等于事务语义相同。必须验证两者取得的是同一预期数据源和事务连接。
3. 数据库与方言
内置注册表能识别 MySQL/MariaDB、PostgreSQL、Oracle、DM8、SQLite、H2/HSQL 和 MSSQL/SQL Server 的产品名或 URL。
“注册表能识别”不等于所有数据库版本、驱动、DDL、分页、自动主键和结果映射路径都已经完成真实集成验证。选用数据库时至少验证:
- 基础 CRUD、批量和事务。
- 分页与 count SQL。
- 自动主键回填和目标
IdType。 - 日期、布尔、LOB 和厂商特殊类型映射。
- 开启自动更新表结构时的 DDL;生产环境不建议开启该功能。
4. Wrapper 与复杂 SQL
Pojo/Table Wrapper 主要面向单表。JOIN、CTE、UNION、窗口函数、复杂聚合和数据库专有语法应使用 DB.jdbc 或 DB.sql。
DB.jdbc.count/page 的自动 count 改写是轻量字符串改写,当前依赖 SQL 中的大写 FROM。含 GROUP BY、DISTINCT、UNION、子查询或复杂投影时,不应假定自动 count 正确;应使用经过测试的显式 count SQL 或预设 SQL Wrapper。
DB.sql 没有和 DB.jdbc.page(...) 对称的直接分页方法,需使用:
Page<User> page = DB.sql.selectWrapper("key.user.search")
.addPara("status", 1)
.page(1, 20)
.queryPage(User.class);
Page.setSize 会把页大小限制到 5000。更大的数据导出应采用分批或游标方案。
5. 写操作安全
UPDATE/DELETE Wrapper 必须显式增加业务条件。构建器在最终 WHERE 为空时会使用 WHERE false,但逻辑删除插件自动加入的 deleted = 0 已经构成 WHERE,因此“没有业务条件”仍可能更新或删除全部未删除数据。
// 正确:明确限定业务主键
int affected = DB.pojo.updateWrapper(User.class)
.set(User::getStatus, 2)
.eq(User::getId, id)
.execute();
不要把框架兜底当成防误更新、误删的唯一保护。代码评审和测试应检查每个写 Wrapper 的业务条件。
6. 逻辑删除
- 表中存在配置的逻辑删除列时,Pojo/Table Wrapper 及其单条 CRUD 会在查询、更新和逻辑删除时自动增加未删除条件;
DB.batch.update和DB.batch.execute不属于这条自动处理路径。 - 单条插入只在调用方没有提供逻辑删除字段时补入未删除值
0;DB.batch.insert则会在绑定参数前把可识别的逻辑删除字段直接覆盖为0,即使调用方已经提供其他值,并会修改传入的 Entity 或JSONMap。 DB.batch.update只按主键生成 WHERE,不追加未删除条件。Pojo 路径会全字段写回,Entity 映射了逻辑删除字段时其null/0/1也会原样参与更新;Table 路径在逻辑删除值缺失或为null时会补成0。两者都可能更新已删除数据,旧快照中的0还可能“复活”记录。- 删除默认改写为已删除值
1;物理删除必须显式选择。 SelectOption.INCLUDE_DELETED只适用于接收DbOption...的直接查询。Pojo 查询 Wrapper 当前没有对称的稳定便捷入口。
逻辑删除字段的历史数据必须统一;NULL、其他枚举值或数据库默认值不一致会造成查询差异。
7. 事务与多数据源
DB.ds.use(...)只切换数据源,不开启事务。DB.tx.run(...)只承诺单数据源本地事务。- 多个数据源之间不提供分布式原子提交或回滚。
DB.tx当前公开入口提供默认传播行为;源码中的TxOptions尚未被DB.tx消费,不应生成基于它的事务代码。
需要跨库一致性时,应在业务层选择消息、补偿、TCC/XA 等独立方案。
8. 批量、Upsert 与结果
DB.batch当前提供插入、更新和 JDBC 批量执行,没有DB.batch.delete(...)。- Pojo 批量更新会更新主键之外的全部映射字段;Table 批量更新会更新主键之外的全部表列。普通字段缺失或为
null时会写入 SQLNULL,不沿用单条更新的忽略空值行为。 - 批量更新 SQL 的 WHERE 只有主键条件,不会自动加入逻辑删除过滤;Table 批量更新还会把缺失或为
null的逻辑删除值设为0。需要保护已删除数据时,应改用带条件的 Wrapper 或显式写出WHERE id = ? AND deleted = 0的 JDBC 批量 SQL。 - 批量插入使用固定列集合;启用逻辑删除时会把可识别的逻辑删除字段强制覆盖为
0。这与单条插入“调用方未提供时才补0”的语义不同。 insertOrUpdateById和DB.table.insertOrUpdate根据主键是否为空选择 INSERT 或 UPDATE,不是数据库原子 Upsert。BatchResult可能表示部分批次失败。必须通过isSuccess()、status()、failedPositions()和cause()检查,而不是把返回值当作 boolean。
9. 参数与 SQL 安全
DB.jdbc使用?,DB.sql和条件sql(...)使用#{key}。${key}是直接 SQL 替换,只能接收经过白名单验证的列名、排序或 SQL 片段,不能接收普通用户输入。in/notIn优先接收Collection、Object[]、CSV 字符串或sql:子查询;源码也接受Number标量,但单值使用eq/ne更清楚,原生类型数组不受支持。sql:子查询和自定义 SQL 片段由调用方承担 SQL 注入与方言正确性责任。
10. 初始化和配置
只依赖 core 手动启动时,方言、插件、全局预设 SQL、数据源和执行器必须在调用 DB.config.init() 前注册;该调用完成后 DB.config 不可变。Spring Boot 和 Solon 会绕过这套手动配置状态,框架应用应使用容器 DataSource 和 dlz.db.*。
helper.auto-update 不是数据库迁移系统;生产环境应使用可评审、可回滚的 schema migration。配置字段的实际消费情况见 配置参考。