10 KiB
Generator 基础设施补丁 — Kingbase Import Identity 修复记录
本文档详细记录 RuoYi Generator 代码生成器在 KingbaseES 数据库环境下的核心运行时兼容性问题排查、补丁设计、安全加固与验证全过程。
1. 问题背景
RailwaySystem 后台基于 RuoYi-Vue 架构改造,数据库由 MySQL 迁移至国产 KingbaseES (V8R6 / PostgreSQL 语法体系)。代码生成器(ruoyi-generator 模块)用于读取数据库系统元数据、导入数据表配置并自动生成前端 Vue 与后端 Java/MyBatis 代码。
在 Generator 元数据体系中,包含两张核心表:
gen_table:存储已导入代码生成的表元数据;gen_table_column:存储表关联的列元数据。
在最初从 MySQL 初始化表结构至 Kingbase 时,这两张表的自增主键定义未能正确映射为 Kingbase 标准的 Identity 列。
2. 第一次真实 Import 如何暴露问题
在推进首个业务模块试点开发(Teacher Pilot,教师主数据模块)期间,需要通过前端代码生成管理界面(/tool/gen)调用“导入表(Import Table)”功能将 train_teacher 表导入至 Generator 配置库中。
当点击导入并提交时,后端触发了 GenTableServiceImpl.importGenTable 调用。此时服务端直接抛出数据库异常,表导入操作失败,导致后续代码生成流程无法继续。
3. 错误现象
后端日志与数据库抛出类似如下的主键冲突与唯一约束冲突异常:
### Error updating database. Cause: com.kingbase8.util.KSQLException: ERROR: duplicate key value violates unique constraint "gen_table_pkey"
Detail: Key (table_id)=(0) already exists.
### The error may exist in URL [jar:file:.../ruoyi-generator.jar!/mapper/generator/GenTableMapper.xml]
### The error may involve com.ruoyi.generator.mapper.GenTableMapper.insertGenTable-Inline
### The error occurred while setting parameters
### SQL: insert into gen_table ( table_name, table_comment, class_name, ... ) values ( ?, ?, ?, ... )
对于 gen_table_column,同样在批量插入字段元数据时出现所有字段的 column_id 均被赋值为默认值 0,从而连续抛出主键冲突。
4. 根因分析
-
MyBatis 配置与运行时预期:
GenTableMapper.xml与GenTableColumnMapper.xml中的插入语句均配置了:<insert id="insertGenTable" parameterType="GenTable" useGeneratedKeys="true" keyProperty="tableId"> <insert id="insertGenTableColumn" parameterType="GenTableColumn" useGeneratedKeys="true" keyProperty="columnId">RuoYi 的应用层期望数据库能够在插入记录时自动生成唯一递增主键,并通过 JDBC 驱动的
getGeneratedKeys返回该主键值并回填至 Java 实体对象的tableId/columnId。 -
原始 MySQL 建表定义: 在
RuoYi-Vue/sql/ry_20250522.sql中,原始表结构为:CREATE TABLE gen_table ( table_id bigint(20) NOT NULL AUTO_INCREMENT COMMENT '编号', ... PRIMARY KEY (table_id) ); CREATE TABLE gen_table_column ( column_id bigint(20) NOT NULL AUTO_INCREMENT COMMENT '编号', ... PRIMARY KEY (column_id) ); -
Kingbase 结构缺失 Identity / Sequence: 在早期导入 Kingbase 实例时,由于 KingbaseES 不支持 MySQL 方言的
AUTO_INCREMENT关键字,该列在创建时仅退化为BIGINT NOT NULL,未配置GENERATED BY DEFAULT AS IDENTITY,也未创建对应的自增序列(Sequence)。 由于 INSERT 语句未指定主键值,在某些兼容模式下默认填入0,第二次插入必然导致唯一主键冲突。 -
定位结论: 本问题属于典型的 Generator 运行时基础设施兼容性缺陷(Generator Runtime Compatibility Bug),与业务层 Database V2 数据模型无关。
5. 补丁文件拆分与核心 DDL
为了避免局部补丁状态(Partial Patch)下执行发生冲突,补丁拆分为两个独立文件:
5.1 002a_gen_table_identity_patch.sql
ALTER TABLE gen_table
ALTER COLUMN table_id
ADD GENERATED BY DEFAULT AS IDENTITY;
5.2 002b_gen_table_column_identity_patch.sql
ALTER TABLE gen_table_column
ALTER COLUMN column_id
ADD GENERATED BY DEFAULT AS IDENTITY;
执行后,KingbaseES 底层会自动创建并维护两个关联序列:
gen_table_table_id_seqgen_table_column_column_id_seq
6. Precheck(预检)与 Partial Patch 安全机制
6.1 Partial Patch Safety(局部补丁状态安全)
在实际运维过程中,数据库可能处于以下四种状态之一:
- Case 1 (
CASE_1_BOTH_NEED_PATCH):两列均未配置 Identity; - Case 2 (
CASE_2_BOTH_ALREADY_PATCHED):两列均已配置 Identity; - Case 3 (
CASE_3_ONLY_COLUMN_NEEDS_PATCH):table_id已修复,但column_id未修复; - Case 4 (
CASE_4_ONLY_TABLE_NEEDS_PATCH):column_id已修复,但table_id未修复。
若在 Case 3 下全量执行整脚本,Kingbase 执行 gen_table ALTER 时将直接报错 column "table_id" of relation "gen_table" is already an identity column 并中断退出。
解决方案:
001_generator_identity_precheck.sql会直接输出全局 Case 判定;- 补丁拆分为
002a与002b,根据 Precheck 的判定选择性执行,实现精准幂等与局部安全性。
7. Existing Metadata Safety 与 Sequence Alignment
7.1 核心原则
Identity 属性存在 ≠ Sequence 一定适合下一次 Insert
在执行 ALTER TABLE ... ADD GENERATED BY DEFAULT AS IDENTITY 时:
- 空表环境(0 rows):默认 Sequence
start_value = 1,下一次插入分配 ID 为 1,天然安全; - 已有数据环境(已存在历史元数据):
- Kingbase
ADD IDENTITY默认创建从 1 开始的 Sequence,不宣称自动对齐已有数据的 MAX(id); - 若直接执行 Insert,Sequence 将尝试从 1 开始分配,如果已有数据包含
id = 1,将再次引发主键冲突!
- Kingbase
7.2 Sequence 对齐机制(Sequence Alignment)
对于非空表环境,执行 Patch 后必须通过 Verify 脚本校验序列游标:
- 计算表达式:
next_id = (CASE WHEN is_called THEN last_value + 1 ELSE last_value END) - 若
next_id > MAX(existing_id),判定为SAFE; - 若
next_id <= MAX(existing_id),判定为UNSAFE,必须由 DBA 执行专项对齐脚本:-- 同步 Sequence 游标至当前最大值之后 SELECT setval('gen_table_table_id_seq', (SELECT COALESCE(MAX(table_id), 0) + 1 FROM gen_table), false); SELECT setval('gen_table_column_column_id_seq', (SELECT COALESCE(MAX(column_id), 0) + 1 FROM gen_table_column), false);
8. Verify(复核验证)
执行只读复核脚本 003_generator_identity_verify.sql,执行四大维度的综合核验:
- 核验列属性:确认两列
attidentity = 'd'; - 核验 Sequence 存在性:确认关联序列存在且
relkind = 'S'; - 核验依赖关系:确认
sys_depend.deptype = 'i'(内部生命周期绑定); - 核验 Sequence 对齐安全性:确认两表的 Sequence 处于
SAFE或EMPTY_TABLE_SAFE状态; - 最终判定:四项维度全通过输出
VERIFY: PASS。
9. Rollback 风险说明
若因特殊场景需要撤销该修改,语法为:
ALTER TABLE gen_table ALTER COLUMN table_id DROP IDENTITY IF EXISTS;
ALTER TABLE gen_table_column ALTER COLUMN column_id DROP IDENTITY IF EXISTS;
回滚风险评估
- Generator 瘫痪:一旦移除 Identity,Generator 将立即恢复至无法导入表的故障状态;
- 元数据序列断裂:若已有元数据记录,回滚后再配置可能造成序列重新错位;
- 结论:该补丁作为 RuoYi 适配 Kingbase 必需的基础设施配置,严禁回滚;任何环境初始化后均应持久化保留此配置。
10. 新环境部署要求
在搭建全新 Kingbase 部署环境或自动化 CI/CD 环境时:
- 执行若依基础建表脚本;
- 执行预检:
ksql -h <HOST> -p <PORT> -U <USER> -d <DBNAME> -f RuoYi-Vue/sql/generator/kingbase/001_generator_identity_precheck.sql - 按照决策矩阵执行对应补丁(空表全新环境直接依次执行
002a和002b); - 执行复核验证并确认输出
VERIFY: PASS:ksql -h <HOST> -p <PORT> -U <USER> -d <DBNAME> -f RuoYi-Vue/sql/generator/kingbase/003_generator_identity_verify.sql
11. 与 Database V2 无关性声明
- 范围隔离:本修复纯属 Generator 工具层基础设施兼容补丁,仅修改
public.gen_table与public.gen_table_column两张系统级代码生成支撑表的主键属性; - 业务无关:零改动 21 张
train_*业务模型表;零改动任何业务枚举、外键约束与数据字典; - 版本独立:补丁存放于
RuoYi-Vue/sql/generator/kingbase/,独立于业务迁移目录RuoYi-Vue/sql/train/v2/。
12. Teacher Pilot 验证事实与当前共享库核验
- 在 Teacher Pilot(
feat/issue-01-01-teacher)开发期间,共享 Kingbase 数据库已应用该 DDL 修复; - 真实导入测试:调用 Generator 导入
train_teacher表,gen_table成功生成非零递增主键(table_id = 1),同时其 16 个字段成功写入gen_table_column(column_id = 1..16); - 代码生成测试:成功基于导入元数据生成教师主数据相关 Controller、Service、Mapper、Domain 及前端 Vue 页面,未产生任何主键冲突;
- 当前共享库只读核验数据(实测):
gen_table:row_count = 1,max_id = 1,sequence last_value = 2,is_called = true,下一个分配值为 3 > 1,状态为SAFE;gen_table_column:row_count = 16,max_id = 16,sequence last_value = 32,is_called = true,下一个分配值为 33 > 16,状态为SAFE;- 属性
attidentity = 'd',序列依赖均为deptype = 'i'; - 复核脚本输出:
VERIFY: PASS (所有 Identity、Sequence、依赖关系与序列对齐安全核验一致通过)。