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

如何替换uuid_generate_v4()?解决NestJS迁移跨PG/SQLite兼容问题

解决NestJS+TypeORM迁移脚本在PostgreSQL与SQLite间的UUID兼容问题

问题场景

在NestJS项目中使用typeorm:generate-migration生成迁移脚本时,脚本依赖PostgreSQL的uuid_generate_v4()函数,导致迁移在SQLite测试环境执行时报错,无法实现跨数据库兼容。

用户实体代码

@Entity()
export class User {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column()
  name: string;
}

生成的迁移脚本

export class initUser1667668869578 implements MigrationInterface {
    name = 'initUser1667668869578'

    public async up(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.query(`CREATE TABLE "user" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying NOT NULL, CONSTRAINT "PK_cace4a159ff9f2512dd42373760" PRIMARY KEY ("id"))`);
    }

    public async down(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.query(`DROP TABLE "user"`);
    }
}

报错信息

Migration "initUser1667668869578" failed, error: SQLITE_ERROR: near "(": syntax error

解决方案

方案1:动态适配数据库的迁移脚本

在迁移脚本中通过queryRunner判断当前数据库类型,生成对应适配的SQL语句:

export class initUser1667668869578 implements MigrationInterface {
    name = 'initUser1667668869578'

    public async up(queryRunner: QueryRunner): Promise<void> {
        let idDefaultClause = 'DEFAULT uuid_generate_v4()';
        // 判断是否为SQLite数据库
        if (queryRunner.connection.options.type === 'sqlite') {
            // SQLite模拟UUIDv4生成逻辑
            idDefaultClause = `DEFAULT (lower(hex(randomblob(4))) || '-' || lower(hex(randomblob(2))) || '-' || '4' || substr(lower(hex(randomblob(2))),2) || '-' || substr('89ab',abs(random()) % 4 + 1,1) || substr(lower(hex(randomblob(2))),2) || '-' || lower(hex(randomblob(6))))`;
        }

        await queryRunner.query(`CREATE TABLE "user" ("id" uuid NOT NULL ${idDefaultClause}, "name" character varying NOT NULL, CONSTRAINT "PK_cace4a159ff9f2512dd42373760" PRIMARY KEY ("id"))`);
    }

    public async down(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.query(`DROP TABLE "user"`);
    }
}

说明:SQLite无内置UUID生成函数,上述代码用SQLite原生函数拼接出符合UUIDv4规范的字符串。

方案2:为不同数据库单独生成迁移脚本

使用TypeORM的--database参数分别为PostgreSQL和SQLite生成适配的迁移脚本:

# 生成PostgreSQL专属迁移
npx typeorm-ts-node-commonjs migration:generate ./src/migrations/initUserPostgres -d ./src/data-source.ts --database postgres
# 生成SQLite专属迁移
npx typeorm-ts-node-commonjs migration:generate ./src/migrations/initUserSqlite -d ./src/data-source.ts --database sqlite

运行迁移时,根据当前环境变量选择对应的迁移脚本执行,比如在package.json中配置脚本:

{
  "scripts": {
    "migrate:dev": "npx typeorm-ts-node-commonjs migration:run -d ./src/data-source.ts -t postgres",
    "migrate:test": "npx typeorm-ts-node-commonjs migration:run -d ./src/data-source.ts -t sqlite"
  }
}

方案3:应用层生成UUID,脱离数据库依赖

修改实体定义,使用uuid库在应用层生成UUID,避免依赖数据库函数:

import { v4 as uuidv4 } from 'uuid';
import { Entity, PrimaryColumn, Column } from 'typeorm';

@Entity()
export class User {
  @PrimaryColumn()
  id: string = uuidv4();

  @Column()
  name: string;
}

对应的迁移脚本会生成普通字符串类型的ID字段,无需数据库默认值:

export class initUser1667668869578 implements MigrationInterface {
    name = 'initUser1667668869578'

    public async up(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.query(`CREATE TABLE "user" ("id" character varying NOT NULL, "name" character varying NOT NULL, CONSTRAINT "PK_cace4a159ff9f2512dd42373760" PRIMARY KEY ("id"))`);
    }

    public async down(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.query(`DROP TABLE "user"`);
    }
}

优点:完全兼容所有数据库;注意:需先安装uuid库(npm install uuid),并在TypeScript配置中开启esModuleInterop以支持导入。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 09:20:42