You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Nest.js + TypeORM:如何在不同Schema中执行相同迁移?

问题:多租户Schema迁移失败

我正在开发一个多租户应用,每个租户拥有独立Schema,示例Schema如下:

  • public(默认)
  • tenant_001
  • tenant_002

目前能在public Schema中正常执行迁移,但其他租户Schema无法完成完整迁移——仅生成了migrations表,没有创建其他业务表。

代码片段

迁移执行逻辑

...
...
  const conn = await dataSource('default').initialize();
  // await conn.synchronize();
  await conn.runMigrations();

  const schemas = await conn.query(
    'select schema_name as name from information_schema.schemata;',
  );

  for (let i = 0; i < schemas.length; i += 1) {
    const { name: schema } = schemas[i];

    if (schema.startsWith('tenant_')) {
      const connection = await getTenantConnection(schema);
      // await connection.synchronize();
      await connection.runMigrations();
      await connection.destroy();
    }
  }
...
...

获取租户连接

export async function getTenantConnection(
  tenantId: string,
): Promise<DataSource> {
  const connectionManager = dataSource(tenantId);

  if (connectionManager.isInitialized) {
    return await connectionManager;
  }

  return await connectionManager.initialize();
}

DataSource生成函数

export const dataSource = (schemaName: string) => {
  if (schemaName === 'default' || schemaName === '') {
    return new DataSource(connectionConfig);
  } else {
    const newConnectionConfig = {
      ...connectionConfig,
      schema: schemaName,
      autoLoadEntities: true,
      name: schemaName,
    };
    console.log({ newConnectionConfig });
    return new DataSource(newConnectionConfig);
  }
};

基础连接配置

const connectionConfig: DataSourceOptions = {
  type: process.env.DB_TYPE as any,
  host: process.env.DB_HOST,
  port: +process.env.DB_PORT,
  username: process.env.DB_USR,
  password: process.env.DB_PWD,
  database: process.env.DB_NAME,
  logging: true,
  synchronize: true,
  entities: ['dist/**/*.entity{.ts,.js}'],
  migrations: ['dist/**/*.migration{.ts,.js}'],
  name: 'default',
};

实际结果

租户Schema中仅存在migrations表,没有生成业务实体对应的表。

预期结果

所有租户Schema应与默认public Schema执行完全相同的迁移操作,生成所有业务表。

环境信息

  • macOS
  • 本地PostgreSQL 15
  • NestJS
  • TypeORM

问题分析与解决方案

核心问题

  1. 迁移脚本未绑定动态Schema,执行时默认操作public Schema而非当前租户Schema
  2. 实体未指定对应Schema,连接初始化后实体无法映射到租户Schema
  3. 基础配置中synchronize: true与迁移脚本存在冲突,可能干扰表结构生成

具体修复步骤

1. 给实体绑定动态Schema

修改实体类,通过装饰器动态获取当前连接的Schema:

import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
import { getConnection } from 'typeorm';

@Entity({ schema: () => getConnection().options.schema || 'public' })
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;
}

2. 修改迁移脚本适配多租户

手动编写的迁移脚本需在up/down方法中指定当前Schema:

import { MigrationInterface, QueryRunner } from 'typeorm';

export class CreateUserTable1620000000000 implements MigrationInterface {
  public async up(queryRunner: QueryRunner): Promise<void> {
    const schema = queryRunner.connection.options.schema || 'public';
    await queryRunner.query(`CREATE TABLE "${schema}"."user" ("id" SERIAL NOT NULL, "name" character varying NOT NULL, CONSTRAINT "PK_cace4a159ff9f2512dd42373760" PRIMARY KEY ("id"))`);
  }

  public async down(queryRunner: QueryRunner): Promise<void> {
    const schema = queryRunner.connection.options.schema || 'public';
    await queryRunner.query(`DROP TABLE "${schema}"."user"`);
  }
}

3. 调整DataSource配置

确保租户连接的迁移表名绑定当前Schema,并关闭自动同步避免冲突:

// 基础连接配置修改
const connectionConfig: DataSourceOptions = {
  // ...其他配置
  synchronize: false, // 关闭自动同步,统一用迁移脚本管理
  // ...
};

// DataSource生成函数修改
export const dataSource = (schemaName: string) => {
  if (schemaName === 'default' || schemaName === '') {
    return new DataSource(connectionConfig);
  } else {
    const newConnectionConfig = {
      ...connectionConfig,
      schema: schemaName,
      autoLoadEntities: true,
      name: schemaName,
      migrationsTableName: 'migrations', // 明确迁移表在当前Schema下
    };
    console.log({ newConnectionConfig });
    return new DataSource(newConnectionConfig);
  }
};

4. 强化租户迁移的Schema上下文

执行迁移前先切换到目标Schema,确保操作上下文正确:

// 迁移循环逻辑修改
if (schema.startsWith('tenant_')) {
  const connection = await getTenantConnection(schema);
  await connection.query(`SET search_path TO "${schema}"`);
  await connection.runMigrations();
  await connection.destroy();
}

内容的提问来源于stack exchange,提问作者Aravin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 09:33:11