logo
火山博客
导航

TypeORM 迁移配置及外键约束修改指南

2024-11-21
14阅读时间3分钟

1问题背景

删除文章时遇到外键约束错误:

Sql
删除文章失败: Cannot delete or update a parent row: a foreign key constraint fails (`blog`.`article_tags`, CONSTRAINT `FK_f8c9234a4c4cb37806387f0c9e9` FOREIGN KEY (`article_id`) REFERENCES `article` (`id`))

2配置步骤

安装必要依赖

Bash
pnpm add -D typeorm @types/node ts-node typescript dotenv @types/dotenv @nestjs/config @nestjs/typeorm mysql2

添加 TypeORM 配置文件

Typescript
// src/config/typeorm.config.ts:
import { DataSource } from 'typeorm';
import { ConfigService } from '@nestjs/config';
import { config } from 'dotenv';

config();

const configService = new ConfigService();

export default new DataSource({
  type: 'mysql',
  host: process.env.DB_HOST || 'localhost',
  port: parseInt(process.env.DB_PORT, 10) || 3306,
  username: process.env.DB_USERNAME || 'root',
  password: process.env.DB_PASSWORD || '123456',
  database: process.env.DB_NAME || 'blog',
  entities: ['src/modules/**/*.entity{.ts,.js}'],
  migrations: ['src/migrations/*{.ts,.js}'],
  synchronize: false,
  logging: process.env.DB_LOGGING || ['error'],
  poolSize: parseInt(process.env.DB_POOL_SIZE, 10) || 10,
  connectorPackage: 'mysql2',
  extra: {
    authPlugin: 'sha256_password',
  }
});

添加迁移脚本

在 package.json 中添加:

Json
{
  "scripts": {
    "typeorm": "typeorm-ts-node-commonjs",
    "migration:generate": "npm run typeorm -- migration:generate -d src/config/typeorm.config.ts",
    "migration:run": "npm run typeorm -- migration:run -d src/config/typeorm.config.ts",
    "migration:revert": "npm run typeorm -- migration:revert -d src/config/typeorm.config.ts"
  }
}

创建迁移文件

Bash
pnpm run migration:generate src/migrations/UpdateArticleTagsCascade

就会创建这样的文件: src/migrations/1698345600000-UpdateArticleTagsCascade.ts:

Typescript
// src/migrations/1698345600000-UpdateArticleTagsCascade.ts:
import { MigrationInterface, QueryRunner } from "typeorm";

export class UpdateArticleTagsCascade1698345600000 implements MigrationInterface {
    public async up(queryRunner: QueryRunner): Promise<void> {
        // 先删除旧的外键约束
        await queryRunner.query(`
            ALTER TABLE article_tags 
            DROP FOREIGN KEY FK_f8c9234a4c4cb37806387f0c9e9;
        `);

        // 添加新的级联删除外键约束
        await queryRunner.query(`
            ALTER TABLE article_tags 
            ADD CONSTRAINT FK_f8c9234a4c4cb37806387f0c9e9
            FOREIGN KEY (article_id) 
            REFERENCES article(id) 
            ON DELETE CASCADE;
        `);
    }

    public async down(queryRunner: QueryRunner): Promise<void> {
        // 回滚:删除带CASCADE的外键
        await queryRunner.query(`
            ALTER TABLE article_tags 
            DROP FOREIGN KEY FK_f8c9234a4c4cb37806387f0c9e9;
        `);

        // 添加回原来的外键约束(不带CASCADE)
        await queryRunner.query(`
            ALTER TABLE article_tags 
            ADD CONSTRAINT FK_f8c9234a4c4cb37806387f0c9e9
            FOREIGN KEY (article_id) 
            REFERENCES article(id);
        `);
    }
}

实体类配置

  1. 修改 ArticleTag 实体:
Typescript
@Entity('article_tags')
export class ArticleTag {
  @ManyToOne(() => Article, article => article.articleTags, {
    onDelete: 'CASCADE'
  })
  @JoinColumn({ name: 'article_id' })
  article: Article;
  // ...其他代码
}
  1. 修改 Article 实体:
Typescript
@Entity()
export class Article {
  @OneToMany(() => ArticleTag, articleTag => articleTag.article, {
    cascade: true,
    onDelete: 'CASCADE'
  })
  articleTags: ArticleTag[];
  // ...其他代码
}

3执行迁移

Bash
# 执行迁移
pnpm run migration:run

# 如需回滚
pnpm run migration:revert

4验证

1.检查数据库外键约束:

Sql
SHOW CREATE TABLE article_tags;

注意事项

  1. 迁移执行前建议备份数据库
  2. 确保迁移文件中的外键名称与数据库中的一致
  3. 生产环境执行迁移时要谨慎,最好在维护时间进行
  4. 如果出现问题可以使用 migration:revert 回滚
  5. 如果没差异的话,用命令创建文件是创建不了的。这是因为 TypeORM 比较当前实体配置和数据库状态时,没有发现需要更改的地方。
2024 © Powered by
hsBlog
|
后台管理