代码生成治理、数据库迁移与团队协作规范实战
在前几篇中,我们已经逐步完成了:
- tio-boot + jOOQ 基础整合
- 事务管理
- Codegen 强类型升级
- Record / POJO CRUD
- 批量、分页、动态 SQL
- PostgreSQL UPSERT、returning
- 多表查询、DTO 投影、聚合统计
- JSONB、窗口函数、CTE 与 PostgreSQL 高级 SQL
- 审计字段、乐观锁、数据权限与企业级 Repository 设计
- 测试策略、SQL 日志、性能诊断与生产排障
- 多租户、读写分离与多数据源设计
到这里,这套 tio-boot + jOOQ 体系在“运行时能力”上已经相当完整。
但只要团队进入多人协作阶段,很快就会遇到另一类问题:
- 数据库表结构变了,谁来改生成代码
- 迁移脚本和代码生成先后顺序怎么约定
- 生成代码到底要不要提交 Git
- 多人同时改表时,如何避免冲突
- 测试环境、开发环境、生产环境如何保证 schema 一致
- Codegen 配置如何治理,避免越长越乱
- 团队里怎样建立统一的数据库变更流程
这一篇的主题就是:
如何把 tio-boot + jOOQ 从“个人开发可用”提升到“团队协作可治理”。
本文会系统讲清:
- jOOQ Codegen 在团队协作中的真实定位
- 数据库迁移工具的职责与边界
- 代码生成、迁移脚本、业务代码三者的协同关系
- 生成代码是否提交 Git 的取舍
- 目录结构与工程分层建议
- Codegen 配置治理
- 团队数据库变更流程
- 多环境一致性保障
- 最容易踩的协作坑
一、为什么这一篇是“团队工程化篇”
前面的内容主要解决的是:
- 一个人怎么把 jOOQ 用起来
- SQL 怎么写得更强类型
- 高级查询怎么表达
- 企业级通用能力怎么下沉
但多人协作时,真正决定项目是否稳定的,往往不是单个 SQL 写法,而是这些问题:
- 新加了一列,为什么别人本地编译不过
- 开发库结构和测试库结构不一致,为什么测试用例时好时坏
- 迁移脚本已经改了,但生成代码没更新,为什么 CI 挂了
- 两个人同时改一张表,为什么 codegen 冲突这么多
- 某个字段在数据库已经删除了,业务代码为什么还在引用
这些问题本质上都不是“jOOQ API 怎么用”的问题,而是:
数据库结构变更如何被团队稳定地协作、同步、验证和落地。
所以这一篇真正讨论的是:
- schema 如何演进
- 代码如何跟着 schema 演进
- 团队怎样围绕数据库变更建立规则
二、先看清三个角色:数据库迁移、代码生成、业务代码
这一篇最重要的认知锚点是:
数据库迁移、jOOQ 代码生成、业务代码,是三件彼此相关但职责不同的事。
2.1 数据库迁移负责“改变 schema”
数据库迁移工具,例如常见的:
- Flyway
- Liquibase
它们的职责是:
用脚本或变更描述,把数据库 schema 从旧版本演进到新版本。
例如:
- 新增表
- 新增字段
- 修改索引
- 增加唯一约束
- 创建视图
- 初始化字典数据
它解决的是“数据库结构怎么变”。
2.2 jOOQ Codegen 负责“把 schema 映射成 Java 类型”
jOOQ 代码生成的职责是:
读取当前数据库结构,把表、字段、Record、POJO、Keys、Indexes 等元数据转换成 Java 类。
它不负责变更数据库,只负责基于数据库当前状态生成 Java 代码。
它解决的是“数据库结构如何进入 Java 编译期类型系统”。
2.3 业务代码负责“使用生成结果完成业务逻辑”
业务代码,例如:
- Repository
- Service
- Controller
它依赖:
- 数据库已经迁移到正确版本
- jOOQ 生成代码已经同步到最新 schema
它解决的是“基于当前 schema 做业务开发”。
2.4 三者的先后关系
这三个角色的执行顺序,在工程上应该非常明确:
第一步:迁移数据库 schema
先让数据库结构变化落地。
第二步:执行 jOOQ Codegen
基于新的 schema 重新生成 Java 元数据。
第三步:编写或修改业务代码
使用新的生成类进行开发。
也就是说:
迁移在前,生成在后,业务代码最后跟进。
这条顺序一旦混乱,团队协作就会频繁出问题。
三、jOOQ Codegen 在团队里的真实定位
很多团队一开始对 codegen 会有两个极端误区。
3.1 误区一:把 codegen 当成“可有可无的辅助功能”
例如觉得:
- 不生成也能写字符串 DSL
- 生成代码只是锦上添花
- 有空再更新
这种做法在团队里风险很大,因为一旦项目已经进入 codegen 模式,生成代码其实已经成为:
数据库 schema 在 Java 层的正式接口。
表名、字段名、索引、主键变化,都会直接体现在生成类里。
所以 codegen 一旦启用,就不再只是“可选增强”,而是协作基础设施。
3.2 误区二:把 codegen 当成“完全自动、无需治理的结果物”
另一种常见误区是:
- 反正是生成的,随便配一下就行
- 插件能跑就好
- 谁改坏了再修
但真实项目里,codegen 如果不治理,很快会出现:
- 生成范围越来越大
- 生成结果越来越臃肿
- 包名混乱
- 多 schema 混在一起
- 不同环境生成结果不一致
- CI 上生成结果和本地不一致
所以 codegen 在团队中真正的定位应该是:
数据库契约的 Java 映射层,需要和迁移策略一起治理。
四、数据库迁移工具:为什么必须引入
只要项目进入多人协作阶段,就非常推荐引入数据库迁移工具。
哪怕项目再轻量,也不建议长期依赖:
- 手工执行 SQL
- 群里发一段 DDL
- 谁本地没改就自己补
这种方式在单人项目里还能勉强撑住,在团队里基本必乱。
4.1 数据库迁移工具解决什么问题
它主要解决:
1. schema 变更可追踪
每次变更都有脚本、有版本。
2. 环境一致性更高
开发、测试、预发、生产都能按同一顺序升级。
3. 团队协作有明确入口
任何数据库变更都要通过迁移脚本,而不是口头同步。
4. 便于 CI / 自动化集成
测试前可以自动把库迁移到最新版本。
4.2 推荐思路:迁移工具负责 schema,jOOQ 负责生成
这两者不要混淆。
迁移工具不是 codegen 的替代品,codegen 也不是迁移工具的替代品。
推荐的职责边界是:
- Flyway / Liquibase:负责把库结构升级到最新
- jOOQ codegen:负责基于最新结构生成 Java 类
这两个工具应当配合,而不是二选一。
五、推荐的目录结构
多人协作时,目录结构清晰非常重要。
下面给一个比较实用的推荐结构。
src/main/java/
demo/jooq/
config/
controller/
service/
repository/
model/
dto/
tx/
datasource/
audit/
security/
src/main/resources/
db/
migration/
V1__init.sql
V2__add_system_admin_audit_fields.sql
V3__add_user_profile.sql
V4__add_indexes.sql
target/generated-sources/jooq/
demo/jooq/gen/
Tables.java
Keys.java
Indexes.java
tables/
tables/records/
tables/pojos/
这里的关键点是:
- 迁移脚本单独放在
db/migration - jOOQ 生成代码单独放在
generated-sources - 业务代码不要和生成代码混在一起
5.1 为什么生成代码一定要单独目录
因为生成代码和业务代码本质上不是一类内容。
业务代码
是团队手写、可维护、可设计的逻辑。
生成代码
是数据库 schema 的编译期映射,应该尽量视为“产物”。
把两者分开有几个好处:
- 避免误改生成代码
- IDE 更容易标记为 Generated Sources
- 包边界更清晰
- 代码审查时更容易聚焦真正手写的变更
六、Codegen 配置治理:不要越配越乱
到了团队阶段,pom.xml 里的 jOOQ 插件配置如果不治理,通常会逐渐失控。
6.1 推荐统一版本变量
例如:
<properties>
<jooq.version>3.18.6</jooq.version>
<postgresql.version>42.2.24</postgresql.version>
</properties>
这样:
- 运行时 jOOQ 版本
- codegen 插件版本
- JDBC 驱动版本
更容易统一管理。
6.2 推荐固定生成目录与包名
例如:
<target>
<packageName>demo.jooq.gen</packageName>
<directory>${project.build.directory}/generated-sources/jooq</directory>
</target>
不要一会儿放:
src/main/java- 一会儿放
gen - 一会儿又换包名
否则团队协作很难稳定。
6.3 推荐显式控制生成范围
大库里不要默认全生成。
例如:
<includes>system_admin|system_role|system_admin_role|user_profile|user_score_log</includes>
或者排除掉无关表:
<excludes>flyway_schema_history|tmp_.*|bak_.*</excludes>
这样做有两个好处:
- 生成结果更干净
- 团队不会因为整个数据库里无关表变化而频繁生成大量 diff
6.4 为什么不建议“能扫到的全扫”
如果项目所在数据库里还有:
- 旧系统表
- 运维表
- 临时表
- 历史归档表
- 测试表
而 codegen 全扫,会带来很多问题:
- 生成量大
- 编译慢
- diff 噪音多
- 真正业务表变更淹没在大量无关改动里
所以团队协作里,codegen 生成范围应当尽量收敛。
七、生成代码要不要提交 Git
这是团队里最常见、也最容易争论的问题之一。
其实没有放之四海而皆准的唯一答案,但可以讲清楚取舍。
7.1 方案一:提交生成代码到 Git
也就是把:
target/generated-sources/jooq
或者拷贝后的生成目录纳入版本控制。
优点
- 拉代码后不一定非要立刻本地生成
- CI 编译更直观
- 数据库未连接时也能编译
- code review 时能直接看到 schema 映射变化
缺点
- diff 可能很多
- 合并冲突概率更高
- 仓库会变大
- 需要大家遵守“不手改生成代码”
7.2 方案二:不提交生成代码
只提交:
- 迁移脚本
- codegen 配置
- 手写业务代码
然后要求:
- 本地或 CI 必须先生成再编译
优点
- 仓库更干净
- 不会有大量生成类 diff
- 合并冲突更少
缺点
- 构建依赖数据库 schema 状态
- 本地环境要求更高
- 生成失败会导致编译失败
- CI 需要更完整的数据库准备流程
7.3 什么时候更适合提交
如果团队处于这些阶段,通常更适合先提交生成代码:
- 团队还不大
- CI 还不够完善
- 数据库环境准备流程还不稳定
- 想降低新同学接手门槛
- 希望 code review 直接看到生成类变化
7.4 什么时候更适合不提交
如果团队已经具备这些条件,则可以考虑不提交:
- CI 流程成熟
- 测试数据库自动准备完善
- 迁移 + 生成 + 编译流程高度自动化
- 团队对 codegen 与迁移的协作约束很强
7.5 我的建议
对大多数正在建设中的团队,我更推荐:
前期可以提交生成代码,等 CI 与迁移流程成熟后,再评估是否切换为不提交。
原因很现实:
- 先稳定协作,再追求仓库洁癖
- 先让流程可靠,再减少产物提交
八、数据库迁移、Codegen、业务代码的协作流程
这一部分最重要,因为它直接决定团队协作是否顺畅。
推荐一个非常清晰的流程。
8.1 标准变更流程
假设你要给 system_admin 表加一个新字段 email。
推荐流程是:
第一步:写迁移脚本
例如:
-- V5__add_system_admin_email.sql
ALTER TABLE system_admin
ADD COLUMN email VARCHAR(128);
第二步:执行迁移
让本地开发库更新到最新 schema。
第三步:执行 jOOQ Codegen
重新生成:
SYSTEM_ADMIN.EMAIL- Record / POJO 中的新字段
第四步:修改业务代码
例如 Repository、DTO、接口逻辑。
第五步:补测试
验证:
- 迁移后 schema 正确
- 生成代码正确
- 新业务逻辑正确
第六步:提交代码
一起提交:
- 迁移脚本
- 生成代码(如果团队选择提交)
- 业务代码
- 测试代码
这套流程应该成为团队共识。
8.2 为什么不推荐“先写业务代码,再补迁移”
因为这样通常会导致:
- 本地编译依赖你自己手改数据库
- 其他人拉代码无法运行
- CI 环境缺 schema
- 生成类与业务代码不匹配
所以正确顺序永远应该是:
schema 先变,生成再跟,业务最后。
九、Code Review 应该审什么
多人协作时,数据库变更类 PR 不能只看业务代码。
对于这类变更,建议 review 至少看下面 4 类内容。
9.1 迁移脚本是否正确
关注:
- DDL 是否可执行
- 是否考虑了默认值、非空、回填
- 索引是否合理
- 是否影响线上老数据
9.2 生成代码变化是否符合预期
如果提交生成代码,则要看:
- 是否只改到了预期表
- 是否出现了无关大范围变更
- 字段类型映射是否正确
- 是否误把无关 schema 一起生成了
9.3 业务代码是否跟上了 schema 变化
例如:
- 新增字段是否真正使用了
- 删除字段后是否还有旧引用
- DTO / Repository / Service 是否同步调整
9.4 测试是否覆盖到关键路径
尤其关注:
- 迁移后的查询是否还正常
- 新索引、新约束相关逻辑是否测试
- 逻辑删除、乐观锁、数据权限是否被意外影响
十、多人同时改表时怎么减少冲突
这是团队协作里非常现实的问题。
10.1 冲突主要有哪几类
1. 迁移脚本冲突
两个人都加了下一版脚本,编号或命名冲突。
2. 生成代码冲突
两个人都生成了同一批类,合并时冲突。
3. 业务代码冲突
表字段变化导致同一 Repository、DTO 被多人同时修改。
10.2 如何降低迁移脚本冲突
推荐:
- 使用严格版本命名规则
- 每个变更一个独立脚本
- 脚本名称表达清楚意图
例如:
V10__add_system_admin_email.sql
V11__add_user_profile_gin_index.sql
V12__create_v_admin_role_view.sql
不要用:
V10__fix.sql
V11__update.sql
这种名字,因为后期几乎无法理解。
10.3 如何降低生成代码冲突
如果团队选择提交生成代码,建议:
1. 收敛生成范围
避免每次生成整个大库。
2. 保持 codegen 配置稳定
不要今天改包名,明天改输出目录。
3. 迁移脚本和生成代码一起提交
避免别人先拿到业务代码,后拿到生成类。
4. Code Review 时重点看“是否出现无关生成 diff”
如果改一个字段,结果生成了一大堆不相关变化,说明流程或配置可能有问题。
十一、多环境一致性:开发、测试、生产怎么保持同步
多人协作里最怕的就是:
- 开发库是新的
- 测试库是旧的
- 生产库又是另一版
所以迁移体系真正的目标不是“会跑脚本”,而是:
让所有环境尽量通过同一套迁移顺序演进。
11.1 开发环境
推荐:
- 本地启动前先执行迁移
- codegen 基于迁移后的最新开发库生成
这样能保证本地看到的 schema 和脚本是一致的。
11.2 测试环境
推荐:
- 测试运行前自动迁移到最新版本
- 集成测试基于迁移后的测试库执行
这样测试才能真正验证“当前代码 + 当前 schema”。
11.3 生产环境
推荐:
- 发布前先评审迁移脚本
- 发布过程先迁移数据库,再发布应用
- 若迁移包含高风险变更,提前评估兼容窗口
尤其要注意:
应用发布顺序必须和 schema 兼容性设计一致。
例如:
- 先加字段,再发新代码,通常更安全
- 先删字段,再发旧代码,通常很危险
十二、向后兼容与灰度发布:数据库变更不能太激进
这是数据库协作里最容易忽略的点。
12.1 推荐的兼容性思路
如果要改一张线上表,优先采用“两步或三步变更”:
场景:字段替换
假设要把 login_name 替换为 username。
不推荐直接:
- 删旧字段
- 加新字段
- 业务代码一次性切换
更推荐:
第一步
加新字段 username,旧字段先保留。
第二步
代码同时兼容旧字段和新字段,逐步迁移数据。
第三步
确认全部切换完成后,再删旧字段。
这种渐进式变更,才适合线上系统。
12.2 为什么数据库变更要比 Java 代码更保守
因为 Java 代码可以快速回滚,但数据库结构和数据本身往往更难回退。
所以经验上:
数据库迁移要偏保守、偏兼容,应用代码可以偏敏捷。
十三、CI/CD 中应该如何接入
只要团队已经有 CI,就非常建议把迁移和 codegen 纳入流水线约束。
13.1 最基本的 CI 检查项
至少建议包含:
1. 迁移脚本可执行
在测试数据库上执行迁移不报错。
2. Codegen 可正常执行
基于迁移后的 schema 成功生成代码。
3. 业务代码可编译
Repository / Service / 测试通过编译。
4. 测试通过
尤其是数据库相关集成测试。
13.2 如果团队提交生成代码
那 CI 还可以进一步校验:
当前迁移 + 当前 codegen 配置重新生成后的结果,是否与仓库提交一致。
如果不一致,说明有人:
- 改了 schema 没重新生成
- 改了生成代码却没同步提交
- 环境差异导致生成结果不稳定
这类检查非常实用。
十四、团队协作规范:建议明确写进 README 或开发手册
到了团队阶段,不要把这些规则停留在“大家口头知道”。
更推荐显式写进:
- README
- 开发手册
- 架构规范文档
- PR 模板
14.1 至少应明确这些规则
1. 任何表结构变化必须通过迁移脚本
不能只改本地库,不提脚本。
2. schema 变更后必须重新执行 codegen
否则业务代码和生成类不一致。
3. 迁移脚本、生成代码、业务代码应尽量同一个 PR 提交
避免分散提交导致别人中间态无法使用。
4. 不允许手改生成代码
生成代码应视为产物。
5. 生成范围、包名、输出目录不得随意变更
这类修改应当视为架构级调整,而不是普通开发行为。
6. 数据库高风险变更必须评审
例如:
- 删除字段
- 修改类型
- 加非空约束
- 大表建索引
- 重命名字段/表
这些都不能“想改就改”。
十五、一个推荐的 Maven 工作流
为了让团队有固定动作,可以约定一个简单工作流。
15.1 开发时
修改 schema
先新增迁移脚本。
更新本地数据库
执行迁移。
生成代码
执行:
mvn -DskipTests generate-sources
开发业务代码
使用新的 Tables / Record / POJO。
15.2 只编译不重新生成时
如果团队支持跳过 jOOQ 生成,可约定:
mvn clean install -DskipTests -Djooq.codegen.skip=true
这适合:
- 当前没改 schema
- 只想快速编译业务代码
但必须注意:
一旦 schema 改了,就不能偷懒跳过生成。
十六、几个最容易踩的坑
16.1 开发库 schema 已变,但迁移脚本没提交
这是最常见的坑之一。
表现是:
- 你本地一切正常
- 别人拉代码就报错
- CI 无法重现你的环境
这说明你修改了数据库,但没有把变更纳入正式迁移流程。
16.2 迁移脚本提交了,但没重新生成代码
表现是:
- 数据库能升级
- 但业务代码仍引用旧字段
- 或者新字段在
Tables中根本没有
这说明 schema 和 codegen 没同步。
16.3 生成范围过大导致无关 diff 爆炸
表现是:
- 改一张表
- PR 里几十上百个生成类都变了
这通常说明:
- codegen 配置没收敛
- 生成范围太宽
- 环境里存在无关 schema 干扰
16.4 在生产上做破坏性变更但代码没做好兼容
例如:
- 先删字段
- 应用还没发布新版本
这种顺序很容易直接导致线上异常。
16.5 团队没人真正对 schema 负责
如果数据库变更人人都能随便改,又没人把控:
- 表设计质量
- 索引策略
- 字段命名
- 迁移兼容性
那再好的 jOOQ 体系也会越来越乱。
所以一定要有明确的数据库变更评审意识。
十七、推荐的一套团队协作闭环
如果把这一篇压缩成一套最实用的闭环,我建议是:
1. 先写迁移脚本
2. 本地执行迁移
3. 重新执行 codegen
4. 修改业务代码
5. 补测试
6. 一起提交 PR
7. CI 执行迁移 + 生成 + 编译 + 测试
8. 发布前评审高风险数据库变更
这 8 步如果团队长期执行,数据库协作会稳定很多。
十八、本篇总结
这一篇把 tio-boot + jOOQ 从“运行时能力”继续推进到了“团队协作与工程治理能力”。
通过本文,我们完成了:
- 明确数据库迁移、jOOQ 代码生成、业务代码三者的职责边界
- 理解为什么团队阶段必须引入迁移工具
- 设计清晰的目录结构与 codegen 输出边界
- 分析生成代码是否提交 Git 的取舍
- 建立 schema 变更 → codegen → 业务代码 的标准协作流程
- 明确 Code Review、CI/CD、多环境一致性的关键检查点
- 理解数据库向后兼容与灰度发布的重要性
- 形成一套可写入开发手册的团队协作规范
一句话总结:
jOOQ Codegen 真正的价值,不只是“少写字符串 SQL”,而是让数据库 schema 变化能够被团队以编译期可见、流程可控的方式协作起来。
到这里,整个 tio-boot + jOOQ 系列已经从:
- 基础整合
- SQL 表达能力
- PostgreSQL 高级能力
- 企业治理能力
- 测试与排障能力
- 多数据源架构能力
继续扩展到了:
- 数据库迁移治理
- codegen 治理
- 团队协作规范
这已经形成了一套比较完整、可长期演进的现代 Java 数据访问体系。
