跳到主要内容

6.5 兼容性与限制

本页记录 DLZ-DB 8.0 的构建边界、兼容策略和容易被误解的现实限制。它们应进入设计和测试,而不能只依赖框架默认行为。

1. Java 与构建矩阵

范围产物目标构建要求
dlz-db-coreJava 8CI 使用 JDK 8、17、21 验证
Spring Boot StarterJava 8CI 使用 JDK 8、17、21 验证
Solon PluginJava 8CI 使用 JDK 8、17、21 验证
Spring Boot 2 DemoJava 8JDK 8+
Solon 3 DemoJava 8JDK 8+
Spring Boot 3 DemoJava 17JDK 17+
根目录完整 reactor包含 Boot 3 DemoJDK 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.* 凭空创建数据源;容器必须已有 DataSource Bean。
  • 同时使用 MyBatis 与 DLZ-DB 时,能启动不等于事务语义相同。必须验证两者取得的是同一预期数据源和事务连接。

3. 数据库与方言

内置注册表能识别 MySQL/MariaDB、PostgreSQL、Oracle、DM8、SQLite、H2/HSQL 和 MSSQL/SQL Server 的产品名或 URL。

“注册表能识别”不等于所有数据库版本、驱动、DDL、分页、自动主键和结果映射路径都已经完成真实集成验证。选用数据库时至少验证:

  1. 基础 CRUD、批量和事务。
  2. 分页与 count SQL。
  3. 自动主键回填和目标 IdType
  4. 日期、布尔、LOB 和厂商特殊类型映射。
  5. 开启自动更新表结构时的 DDL;生产环境不建议开启该功能。

4. Wrapper 与复杂 SQL

Pojo/Table Wrapper 主要面向单表。JOIN、CTE、UNION、窗口函数、复杂聚合和数据库专有语法应使用 DB.jdbcDB.sql

DB.jdbc.count/page 的自动 count 改写是轻量字符串改写,当前依赖 SQL 中的大写 FROM。含 GROUP BYDISTINCTUNION、子查询或复杂投影时,不应假定自动 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.updateDB.batch.execute 不属于这条自动处理路径。
  • 单条插入只在调用方没有提供逻辑删除字段时补入未删除值 0DB.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 时会写入 SQL NULL,不沿用单条更新的忽略空值行为。
  • 批量更新 SQL 的 WHERE 只有主键条件,不会自动加入逻辑删除过滤;Table 批量更新还会把缺失或为 null 的逻辑删除值设为 0。需要保护已删除数据时,应改用带条件的 Wrapper 或显式写出 WHERE id = ? AND deleted = 0 的 JDBC 批量 SQL。
  • 批量插入使用固定列集合;启用逻辑删除时会把可识别的逻辑删除字段强制覆盖为 0。这与单条插入“调用方未提供时才补 0”的语义不同。
  • insertOrUpdateByIdDB.table.insertOrUpdate 根据主键是否为空选择 INSERT 或 UPDATE,不是数据库原子 Upsert。
  • BatchResult 可能表示部分批次失败。必须通过 isSuccess()status()failedPositions()cause() 检查,而不是把返回值当作 boolean。

9. 参数与 SQL 安全

  • DB.jdbc 使用 ?DB.sql 和条件 sql(...) 使用 #{key}
  • ${key} 是直接 SQL 替换,只能接收经过白名单验证的列名、排序或 SQL 片段,不能接收普通用户输入。
  • in/notIn 优先接收 CollectionObject[]、CSV 字符串或 sql:子查询;源码也接受 Number 标量,但单值使用 eq/ne 更清楚,原生类型数组不受支持。
  • sql:子查询 和自定义 SQL 片段由调用方承担 SQL 注入与方言正确性责任。

10. 初始化和配置

只依赖 core 手动启动时,方言、插件、全局预设 SQL、数据源和执行器必须在调用 DB.config.init() 前注册;该调用完成后 DB.config 不可变。Spring Boot 和 Solon 会绕过这套手动配置状态,框架应用应使用容器 DataSourcedlz.db.*

helper.auto-update 不是数据库迁移系统;生产环境应使用可评审、可回滚的 schema migration。配置字段的实际消费情况见 配置参考