Skip to content

代码生成治理、数据库迁移与团队协作规范实战 ​

在前几篇中,我们已经逐步完成了:

  • 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 类

这两个工具应当配合,而不是二选一。


五、推荐的目录结构 ​

多人协作时,目录结构清晰非常重要。

下面给一个比较实用的推荐结构。

text
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 推荐统一版本变量 ​

例如:

xml
<properties>
  <jooq.version>3.18.6</jooq.version>
  <postgresql.version>42.2.24</postgresql.version>
</properties>

这样:

  • 运行时 jOOQ 版本
  • codegen 插件版本
  • JDBC 驱动版本

更容易统一管理。


6.2 推荐固定生成目录与包名 ​

例如:

xml
<target>
  <packageName>demo.jooq.gen</packageName>
  <directory>${project.build.directory}/generated-sources/jooq</directory>
</target>

不要一会儿放:

  • src/main/java
  • 一会儿放 gen
  • 一会儿又换包名

否则团队协作很难稳定。


6.3 推荐显式控制生成范围 ​

大库里不要默认全生成。

例如:

xml
<includes>system_admin|system_role|system_admin_role|user_profile|user_score_log</includes>

或者排除掉无关表:

xml
<excludes>flyway_schema_history|tmp_.*|bak_.*</excludes>

这样做有两个好处:

  • 生成结果更干净
  • 团队不会因为整个数据库里无关表变化而频繁生成大量 diff

6.4 为什么不建议“能扫到的全扫” ​

如果项目所在数据库里还有:

  • 旧系统表
  • 运维表
  • 临时表
  • 历史归档表
  • 测试表

而 codegen 全扫,会带来很多问题:

  • 生成量大
  • 编译慢
  • diff 噪音多
  • 真正业务表变更淹没在大量无关改动里

所以团队协作里,codegen 生成范围应当尽量收敛。


七、生成代码要不要提交 Git ​

这是团队里最常见、也最容易争论的问题之一。

其实没有放之四海而皆准的唯一答案,但可以讲清楚取舍。


7.1 方案一:提交生成代码到 Git ​

也就是把:

text
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。

推荐流程是:

第一步:写迁移脚本 ​

例如:

sql
-- 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 如何降低迁移脚本冲突 ​

推荐:

  • 使用严格版本命名规则
  • 每个变更一个独立脚本
  • 脚本名称表达清楚意图

例如:

text
V10__add_system_admin_email.sql
V11__add_user_profile_gin_index.sql
V12__create_v_admin_role_view.sql

不要用:

text
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 ​

先新增迁移脚本。

更新本地数据库 ​

执行迁移。

生成代码 ​

执行:

bash
mvn -DskipTests generate-sources

开发业务代码 ​

使用新的 Tables / Record / POJO。


15.2 只编译不重新生成时 ​

如果团队支持跳过 jOOQ 生成,可约定:

bash
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 数据访问体系。

MIT Licensed