18 KiB

实体类与数据库脚本同步参考手册

详细映射规则

数据类型映射表

基本数据类型

Java类型 MySQL类型 注解示例 说明
String VARCHAR(N) @Column(length=50) 默认长度255
String TEXT @Column(length=2000) 长文本
String LONGTEXT @Column(length=10000) 超长文本
Integer/int INT @Column 32位整数
Long/long BIGINT @Column 64位整数
Short/short SMALLINT @Column 16位整数
Byte/byte TINYINT @Column 8位整数
Float/float FLOAT @Column 单精度浮点
Double/double DOUBLE @Column 双精度浮点
Boolean/boolean TINYINT(1) @Column 布尔值
Character/char CHAR(1) @Column 单字符

数值类型

Java类型 MySQL类型 注解示例 说明
BigDecimal DECIMAL(M,D) @Column(precision=10, scale=2) 精确小数
BigInteger DECIMAL(M,0) @Column(precision=20) 大整数

日期时间类型

Java类型 MySQL类型 注解示例 说明
LocalDate DATE @Column 日期
LocalTime TIME @Column 时间
LocalDateTime DATETIME @Column 日期时间
Instant TIMESTAMP @Column 时间戳
Year YEAR @Column 年份

枚举类型

// 方式1: 字符串存储
@Enumerated(EnumType.STRING)
@Column(length = 20)
private UserType userType;

// 方式2: 整数存储
@Enumerated(EnumType.ORDINAL)
@Column
private Status status;

集合类型

// JSON格式存储
@JdbcTypeCode(SqlTypes.JSON)
@Column(columnDefinition = "JSON")
private List<String> tags;

// 或者使用单独的关联表
@ElementCollection
@CollectionTable(name = "user_roles", joinColumns = @JoinColumn(name = "user_id"))
private Set<String> roles;

注解详细说明

@Column注解参数

@Column(
    name = "column_name",           // 数据库列名
    length = 255,                   // VARCHAR长度
    nullable = true,                // 是否允许NULL
    unique = false,                 // 是否唯一
    precision = 10,                 // 数字总位数
    scale = 2,                      // 小数位数
    columnDefinition = "TEXT"       // 自定义列定义
)

@Id相关注解

// 自增主键
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

// UUID主键
@Id
@GeneratedValue(generator = "uuid2")
@GenericGenerator(name = "uuid2", strategy = "uuid2")
@Column(name = "id", length = 36)
private String id;

// 复合主键
@EmbeddedId
private UserRoleId id;

时间戳注解

// 创建时间自动设置
@CreationTimestamp
@Column(name = "created_time", updatable = false)
private LocalDateTime createdTime;

// 更新时间自动设置
@UpdateTimestamp
@Column(name = "updated_time")
private LocalDateTime updatedTime;

变更管理工具

变更脚本生成器

#!/usr/bin/env python3
# generate-change-scripts.py

import os
import re
import datetime
from typing import Dict, List

class ChangeScriptGenerator:
    def __init__(self, project_root: str):
        self.project_root = project_root
        # 部署目录结构:与开发代码分离
        self.deploy_dir = os.path.join(project_root, "deploy", "sql")
        self.upgrade_dir = os.path.join(self.deploy_dir, "upgrade")
        self.init_dir = os.path.join(self.deploy_dir, "init")
        
    def create_version_directory(self, version: str) -> str:
        """创建版本目录"""
        version_dir = os.path.join(self.upgrade_dir, version)
        os.makedirs(version_dir, exist_ok=True)
        os.makedirs(os.path.join(version_dir, "rollback"), exist_ok=True)
        return version_dir
    
    def generate_alter_script(self, table_name: str, changes: Dict, version: str) -> str:
        """生成ALTER TABLE脚本"""
        today = datetime.date.today().strftime('%Y%m%d')
        timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        
        # 生成正向变更脚本
        forward_content = f"""-- ============================================
-- 变更描述: {changes.get('description', '数据库结构变更')}
-- 变更时间: {timestamp}
-- 影响版本: {version}
-- 影响表: {table_name}
-- ============================================

"""
        
        # 添加字段变更
        if 'add_columns' in changes:
            forward_content += "-- 1. 添加字段\n"
            for column_def in changes['add_columns']:
                forward_content += f"ALTER TABLE `{table_name}` ADD COLUMN {column_def};\n"
            forward_content += "\n"
        
        if 'modify_columns' in changes:
            forward_content += "-- 2. 修改字段\n"
            for column_def in changes['modify_columns']:
                forward_content += f"ALTER TABLE `{table_name}` MODIFY COLUMN {column_def};\n"
            forward_content += "\n"
        
        if 'drop_columns' in changes:
            forward_content += "-- 3. 删除字段\n"
            for column_name in changes['drop_columns']:
                forward_content += f"ALTER TABLE `{table_name}` DROP COLUMN `{column_name}`;\n"
            forward_content += "\n"
        
        # 添加索引变更
        if 'add_indexes' in changes:
            forward_content += "-- 4. 添加索引\n"
            for index_def in changes['add_indexes']:
                forward_content += f"ALTER TABLE `{table_name}` ADD {index_def};\n"
            forward_content += "\n"
        
        # 生成回滚脚本
        rollback_content = self.generate_rollback_script(table_name, changes, timestamp, version)
        
        # 保存脚本文件
        version_dir = self.create_version_directory(version)
        forward_filename = f"{today}_{self.sanitize_filename(changes.get('description', 'change'))}.sql"
        rollback_filename = f"{today}_{self.sanitize_filename(changes.get('description', 'change'))}_rollback.sql"
        
        forward_path = os.path.join(version_dir, forward_filename)
        rollback_path = os.path.join(version_dir, "rollback", rollback_filename)
        
        with open(forward_path, 'w', encoding='utf-8') as f:
            f.write(forward_content)
        
        with open(rollback_path, 'w', encoding='utf-8') as f:
            f.write(rollback_content)
        
        return forward_path
    
    def generate_rollback_script(self, table_name: str, changes: Dict, timestamp: str, version: str) -> str:
        """生成回滚脚本"""
        rollback_content = f"""-- ============================================
-- 回滚描述: 撤销 {changes.get('description', '数据库变更')}
-- 回滚时间: {timestamp}
-- 原版本: {version}
-- 影响表: {table_name}
-- ============================================

"""
        
        # 删除索引(逆序)
        if 'add_indexes' in changes:
            rollback_content += "-- 1. 删除新增的索引\n"
            for index_def in reversed(changes['add_indexes']):
                index_name = self.extract_index_name(index_def)
                if index_name:
                    rollback_content += f"ALTER TABLE `{table_name}` DROP INDEX `{index_name}`;\n"
            rollback_content += "\n"
        
        # 删除字段(逆序)
        if 'add_columns' in changes:
            rollback_content += "-- 2. 删除新增的字段\n"
            for column_def in reversed(changes['add_columns']):
                column_name = self.extract_column_name(column_def)
                if column_name:
                    rollback_content += f"ALTER TABLE `{table_name}` DROP COLUMN `{column_name}`;\n"
            rollback_content += "\n"
        
        # 恢复修改的字段
        if 'modify_columns' in changes:
            rollback_content += "-- 3. 恢复修改的字段\n"
            # 这里需要保存原始定义才能准确回滚
            rollback_content += "-- TODO: 需要手动添加原始字段定义\n"
            rollback_content += "\n"
        
        return rollback_content
    
    def sanitize_filename(self, text: str) -> str:
        """清理文件名"""
        return re.sub(r'[^\w\-_]', '_', text.lower())
    
    def extract_column_name(self, column_def: str) -> str:
        """从列定义中提取列名"""
        match = re.search(r'`([^`]+)`', column_def)
        return match.group(1) if match else ""
    
    def extract_index_name(self, index_def: str) -> str:
        """从索引定义中提取索引名"""
        match = re.search(r'`([^`]+)`\s*\(', index_def)
        return match.group(1) if match else ""

# 使用示例
if __name__ == "__main__":
    generator = ChangeScriptGenerator("/path/to/project")
    
    changes = {
        'description': '为用户表添加手机号字段',
        'add_columns': [
            '`phone` VARCHAR(20) COMMENT \'手机号码\'',
            '`wechat_id` VARCHAR(50) COMMENT \'微信ID\''
        ],
        'add_indexes': [
            'INDEX `idx_phone` (`phone`)',
            'INDEX `idx_wechat_id` (`wechat_id`)'
        ]
    }
    
    script_path = generator.generate_alter_script('user', changes, 'v1.1.0')
    print(f"生成变更脚本: {script_path}")

文档同步工具

#!/usr/bin/env python3
# sync-documentation.py

import os
import json
from typing import Dict, List

class DocumentationSync:
    def __init__(self, project_root: str):
        self.project_root = project_root
        self.docs_dir = os.path.join(project_root, "docs")
        self.db_docs_dir = os.path.join(self.docs_dir, "database")
        
    def update_table_documentation(self, table_name: str, entity_info: Dict, changes: Dict):
        """更新表结构文档"""
        doc_file = os.path.join(self.db_docs_dir, f"{table_name}.md")
        
        # 读取现有文档或创建新文档
        if os.path.exists(doc_file):
            with open(doc_file, 'r', encoding='utf-8') as f:
                content = f.read()
        else:
            content = self.create_table_template(table_name)
        
        # 更新字段信息
        content = self.update_fields_section(content, entity_info['fields'])
        
        # 添加变更历史
        content = self.add_change_history(content, changes)
        
        # 保存更新后的文档
        os.makedirs(os.path.dirname(doc_file), exist_ok=True)
        with open(doc_file, 'w', encoding='utf-8') as f:
            f.write(content)
    
    def create_table_template(self, table_name: str) -> str:
        """创建表文档模板"""
        return f"""# {table_name} 表结构文档

## 表基本信息
- **表名**: `{table_name}`
- **描述**: 
- **引擎**: InnoDB
- **字符集**: utf8mb4

## 字段定义
| 字段名 | 类型 | 允许NULL | 默认值 | 描述 |
|--------|------|----------|--------|------|
| id | BIGINT | NO | AUTO_INCREMENT | 主键ID |

## 索引信息
| 索引名 | 类型 | 字段 | 描述 |
|--------|------|------|------|
| PRIMARY | PRIMARY | id | 主键索引 |

## 变更历史
| 日期 | 版本 | 变更描述 | 变更人 |
|------|------|----------|--------|
"""
    
    def update_fields_section(self, content: str, fields: List[Dict]) -> str:
        """更新字段定义部分"""
        # 找到字段定义表格的位置并替换
        lines = content.split('\n')
        in_fields_section = False
        field_lines = []
        
        for line in lines:
            if line.strip() == '## 字段定义':
                in_fields_section = True
                field_lines.append(line)
                continue
            
            if in_fields_section and line.startswith('|') and '字段名' in line:
                # 保留表头
                field_lines.append(line)
                continue
            
            if in_fields_section and line.startswith('|----'):
                # 保留分隔线
                field_lines.append(line)
                continue
            
            if in_fields_section and line.startswith('|') and line.count('|') >= 5:
                # 跳过旧的字段行
                continue
            
            if in_fields_section and not line.strip():
                # 结束字段部分
                in_fields_section = False
                # 添加新的字段定义
                for field in fields:
                    field_lines.append(self.format_field_row(field))
                field_lines.append('')
            
            field_lines.append(line)
        
        return '\n'.join(field_lines)
    
    def format_field_row(self, field: Dict) -> str:
        """格式化字段行"""
        nullable = 'YES' if field.get('nullable', True) else 'NO'
        default_val = field.get('default', '')
        description = field.get('comment', '')
        
        return f"| {field['name']} | {field['type']} | {nullable} | {default_val} | {description} |"
    
    def add_change_history(self, content: str, changes: Dict) -> str:
        """添加变更历史"""
        import datetime
        today = datetime.date.today().strftime('%Y-%m-%d')
        
        change_line = f"| {today} | {changes.get('version', 'unknown')} | {changes.get('description', '')} | {changes.get('author', 'system')} |"
        
        # 在变更历史部分添加新行
        lines = content.split('\n')
        for i, line in enumerate(lines):
            if '| 日期 | 版本 | 变更描述 | 变更人 |' in line:
                # 在表头后插入新行
                lines.insert(i + 2, change_line)
                break
        
        return '\n'.join(lines)

# 配置文件示例
CONFIG = {
    "documentation": {
        "enabled": True,
        "output_dir": "docs/database",
        "auto_update": True
    },
    "change_scripts": {
        "upgrade_dir": "src/main/resources/sql/upgrade",
        "version_pattern": "v{major}.{minor}.{patch}",
        "naming_convention": "{date}_{description}.sql"
    }
}

生产环境部署规范

变更脚本目录结构

project/
├── deploy/                    # 部署相关文件(与开发代码分离)
│   └── sql/
│       ├── init/              # 初始部署脚本
│       │   ├── v1.0.0/
│       │   │   ├── 01_create_tables.sql
│       │   │   ├── 02_insert_init_data.sql
│       │   │   └── README.md
│       │   └── v1.1.0/
│       │       └── 01_upgrade_v1.1.0.sql
│       └── upgrade/           # 增量变更脚本
│           ├── v1.1.0/
│           │   ├── 20240115_add_user_phone.sql
│           │   ├── 20240115_add_user_phone_rollback.sql
│           │   └── changelog.md
│           ├── v1.2.0/
│           │   ├── 20240201_modify_order_amount.sql
│           │   ├── 20240201_modify_order_amount_rollback.sql
│           │   └── changelog.md
│           └── current/       # 当前开发中的变更
│               └── draft_changes/
└── src/main/resources/        # 开发代码目录(保持独立)

生产环境部署规范

变更脚本执行流程

#!/bin/bash
# deploy-database-changes.sh

ENVIRONMENT=${1:-"test"}
VERSION=${2:-"latest"}
BACKUP_FIRST=${3:-"true"}

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="${SCRIPT_DIR}/../.."
# 使用部署目录
DEPLOY_SQL_DIR="${PROJECT_ROOT}/deploy/sql"

echo "=== 数据库变更部署 ==="
echo "环境: ${ENVIRONMENT}"
echo "版本: ${VERSION}"
echo "备份: ${BACKUP_FIRST}"
echo ""

# 1. 数据库备份
if [ "${BACKUP_FIRST}" = "true" ]; then
    echo "1. 执行数据库备份..."
    BACKUP_FILE="/tmp/db_backup_$(date +%Y%m%d_%H%M%S).sql"
    mysqldump -h${DB_HOST} -u${DB_USER} -p${DB_PASSWORD} ${DB_NAME} > "${BACKUP_FILE}"
    echo "备份完成: ${BACKUP_FILE}"
    echo ""
fi

# 2. 确定要执行的脚本
if [ "${VERSION}" = "latest" ]; then
    # 执行所有未应用的变更
    CHANGE_SCRIPTS=$(find "${DEPLOY_SQL_DIR}/upgrade" -name "*.sql" ! -name "*_rollback.sql" | sort)
else
    # 执行指定版本的变更
    CHANGE_SCRIPTS=$(find "${DEPLOY_SQL_DIR}/upgrade/${VERSION}" -name "*.sql" ! -name "*_rollback.sql" | sort)
fi

3. 执行变更脚本

echo "2. 执行变更脚本..." for script in ${CHANGE_SCRIPTS}; do echo "执行: $(basename ${script})"

# 记录执行日志
mysql -h${DB_HOST} -u${DB_USER} -p${DB_PASSWORD} ${DB_NAME} < "${script}"

if [ $? -eq 0 ]; then
    echo "✓ 执行成功"
    # 记录到变更历史表
    echo "INSERT INTO db_changelog (script_name, execute_time, environment) VALUES ('$(basename ${script})', NOW(), '${ENVIRONMENT}');" | \
    mysql -h${DB_HOST} -u${DB_USER} -p${DB_PASSWORD} ${DB_NAME}
else
    echo "✗ 执行失败"
    exit 1
fi

done

4. 验证变更

echo "" echo "3. 验证变更结果..."

执行验证脚本

VERIFY_SCRIPT="${DEPLOY_SQL_DIR}/upgrade/${VERSION}/verify_changes.sql" if [ -f "${VERIFY_SCRIPT}" ]; then mysql -h${DB_HOST} -u${DB_USER} -p${DB_PASSWORD} ${DB_NAME} < "${VERIFY_SCRIPT}" fi

echo "数据库变更部署完成!"


### 变更风险评估模板
```markdown
# 数据库变更风险评估表

## 基本信息
- **变更编号**: DB-20240115-001
- **变更类型**: 字段添加/修改/删除
- **影响表**: user
- **预计执行时间**: 5分钟
- **回滚时间**: 2分钟

## 影响评估
### 业务影响
- [ ] 用户注册/登录功能
- [ ] 数据查询性能
- [ ] 报表统计准确性
- [ ] 第三方系统对接

### 技术风险
- [ ] 数据一致性风险
- [ ] 性能下降风险
- [ ] 锁表时间过长
- [ ] 空间不足风险

## 预防措施
- [ ] 执行前完整备份
- [ ] 低峰期执行变更
- [ ] 准备回滚方案
- [ ] 安排专人监控

## 应急预案
- **回滚步骤**: [详细回滚操作步骤]
- **联系人员**: [紧急联系人列表]
- **监控指标**: [关键监控项]

这份参考手册提供了完整的实体类与数据库脚本同步的技术细节、工具脚本和生产环境部署规范。