|
@@ -70,6 +70,127 @@ Spring Boot 2.7.18 后端服务,MyBatis-Plus ORM,JWT 认证。
|
|
|
- 孩子快照:每次测评时保存孩子身高/体重/学校等动态信息到 DanAssessmentResult
|
|
- 孩子快照:每次测评时保存孩子身高/体重/学校等动态信息到 DanAssessmentResult
|
|
|
- 多数据源:cfc 主库 `spring.datasource.*` + sfms 源库 `sfms.datasource.*`(只读)
|
|
- 多数据源:cfc 主库 `spring.datasource.*` + sfms 源库 `sfms.datasource.*`(只读)
|
|
|
|
|
|
|
|
|
|
+## DATABASE MIGRATION WORKFLOW
|
|
|
|
|
+
|
|
|
|
|
+当出现 `Unknown column 'xxx' in 'field list'` 等字段不存在错误,或需要进行表结构变更时,按以下流程处理:
|
|
|
|
|
+
|
|
|
|
|
+### 工作流程
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+错误日志定位 → 查找实体类 → 检查 schema.sql → 添加迁移 → 同步 schema.sql → 编译验证
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### Step 1: 诊断错误
|
|
|
|
|
+
|
|
|
|
|
+后端日志中出现类似以下错误:
|
|
|
|
|
+```
|
|
|
|
|
+Unknown column 'xxx' in 'field list'
|
|
|
|
|
+Table 'xxx' doesn't exist
|
|
|
|
|
+```
|
|
|
|
|
+从错误信息中提取:**表名、列名/对象名**,并从异常堆栈找到触发点(哪个 Service/Controller)。
|
|
|
|
|
+
|
|
|
|
|
+### Step 2: 查找实体与建表 SQL
|
|
|
|
|
+
|
|
|
|
|
+- 在 `entity/` 目录找到对应实体类,确认字段已定义(`@TableField` 或成员变量)
|
|
|
|
|
+- 在 `src/main/resources/schema.sql` 中找到该表的 `CREATE TABLE` 语句,确认列是否已包含
|
|
|
|
|
+
|
|
|
|
|
+### Step 3: 判断修复位置
|
|
|
|
|
+
|
|
|
|
|
+| 情况 | 修复方式 |
|
|
|
|
|
+|------|---------|
|
|
|
|
|
+| schema.sql 建表语句有该列,但生产库没有(旧表) | 只需在 `DatabaseInitializer.runMigrations()` 添加 ALTER TABLE |
|
|
|
|
|
+| schema.sql 没有该列,实体也没有 | 在实体添加字段 + schema.sql 建表语句补列 + DatabaseInitializer 加迁移 |
|
|
|
|
|
+| 全新表 | schema.sql 添加 CREATE TABLE + DatabaseInitializer 的 runMigrations() 加迁移 |
|
|
|
|
|
+| 仅调整约束/类型/默认值 | DatabaseInitializer.runMigrations() 加 MODIFY COLUMN |
|
|
|
|
|
+
|
|
|
|
|
+### Step 4: 在 DatabaseInitializer 添加迁移
|
|
|
|
|
+
|
|
|
|
|
+`DatabaseInitializer.java` 的 `runMigrations()` 方法是所有 DDL 迁移的唯一入口。
|
|
|
|
|
+
|
|
|
|
|
+#### 添加列(首选 ensureColumn 辅助方法)
|
|
|
|
|
+
|
|
|
|
|
+`ensureColumn(table, column, definition)` 是已有的辅助方法(第 4099 行),自动处理重复列异常:
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+// 迁移N: xxx表添加yyy字段
|
|
|
|
|
+ensureColumn("table_name", "column_name", "VARCHAR(50) COMMENT '字段说明'");
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+`ensureColumn` 不可用时(如需要 MODIFY / 多个列在同一个 try 块),使用标准模式:
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+// 迁移N: xxx表添加yyy字段(原因描述/需求编号)
|
|
|
|
|
+try {
|
|
|
|
|
+ jdbcTemplate.execute("ALTER TABLE xxx ADD COLUMN yyy VARCHAR(50) COMMENT '字段说明'");
|
|
|
|
|
+ log.info("已添加yyy列到xxx表");
|
|
|
|
|
+} catch (Exception e) {
|
|
|
|
|
+ // 列已存在,忽略错误
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 创建新表
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+// 迁移N: 创建 xxx 表(功能描述)
|
|
|
|
|
+try {
|
|
|
|
|
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS xxx (" +
|
|
|
|
|
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
|
|
|
|
|
+ "...字段定义..." +
|
|
|
|
|
+ "INDEX idx_xxx (column)" +
|
|
|
|
|
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='表注释'");
|
|
|
|
|
+ log.info("已创建xxx表");
|
|
|
|
|
+} catch (Exception e) {
|
|
|
|
|
+ // 表已存在,忽略错误
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 添加索引
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+try {
|
|
|
|
|
+ jdbcTemplate.execute("ALTER TABLE xxx ADD INDEX idx_xxx (column)");
|
|
|
|
|
+ log.info("已添加idx_xxx索引到xxx表");
|
|
|
|
|
+} catch (Exception e) {
|
|
|
|
|
+ // 索引已存在,忽略错误
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 修改字段
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+try {
|
|
|
|
|
+ jdbcTemplate.execute("ALTER TABLE xxx MODIFY COLUMN yyy VARCHAR(100) COMMENT '新注释'");
|
|
|
|
|
+ log.info("已修改xxx.yyy字段");
|
|
|
|
|
+} catch (Exception e) {
|
|
|
|
|
+ // 修改失败,忽略错误
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### Step 5: 同步 schema.sql
|
|
|
|
|
+
|
|
|
|
|
+schema.sql 中保持该表最新的完整定义:
|
|
|
|
|
+
|
|
|
|
|
+- **新增列**:在 `schema.sql` 对应表的 `CREATE TABLE` 语句中也加上该列定义(保持建表即包含所有列)
|
|
|
|
|
+- **新建表**:在 `schema.sql` 追加该表的 `CREATE TABLE IF NOT EXISTS` 语句
|
|
|
|
|
+- **仅修改约束/默认值**:无需更新 schema.sql
|
|
|
|
|
+- **迁移中 `CREATE TABLE IF NOT EXISTS` 创建的表**:也追加到 schema.sql(保持 schema.sql 为完整定义快照)
|
|
|
|
|
+
|
|
|
|
|
+### Step 6: 验证
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+mvn clean compile # 编译验证通过即可
|
|
|
|
|
+```
|
|
|
|
|
+迁移代码是幂等的(try-catch 忽略已存在的列/表/索引),无需运行时验证。
|
|
|
|
|
+
|
|
|
|
|
+### 规范要点
|
|
|
|
|
+
|
|
|
|
|
+- **迁移编号**:按现有顺序递增(当前最大编号 `迁移N`,在文件末尾搜索 `// 迁移` 确认最新编号)
|
|
|
|
|
+- **注释格式**:`// 迁移N: 表名 操作说明(原因/需求编号)`
|
|
|
|
|
+- **日志**:每个成功的 ALTER/CREATE 后加 `log.info()`,失败统一用空 catch 或 `// 忽略`
|
|
|
|
|
+- **幂等性**:所有迁移必须可重复执行而不报错(try-catch 包裹 + 列存在检查)
|
|
|
|
|
+- **辅助方法**:优先使用 `ensureColumn()` 添加列,它自动处理重复列异常;原始 `try-catch` 模式用于索引/表/非标准操作
|
|
|
|
|
+- **同步原则**:**对 schema.sql 的每一条 DDL 变更,必须在 DatabaseInitializer 中有对应的迁移**,反之亦然——两者保持最终一致
|
|
|
|
|
+
|
|
|
## COMMANDS
|
|
## COMMANDS
|
|
|
|
|
|
|
|
```bash
|
|
```bash
|