上一篇 记录了
nestjs nacos+nest-typed-config 实现异步配置加载
呢么接下来就是数据迁移方案了,我们依然采用typeorm migrations 方案.
不过原生的typeorm不支持异步创建datasource所以我们需要改造一下.采用自定义的方式进行迁移.
首先目录结构如下:
data-source.ts
datasource对象异步创建
需要注意一个报错:[### TypeError: someClass is not a constructor · Issue #5458](https://github.com/typeorm/typeorm/issues/5458)
大概率因为migrations路径加载的内容 包含非迁移文件造成的.
import * as path from 'path';
import { DataSource, DataSourceOptions } from 'typeorm';
import { rootConfig } from '../../src/config/config.module';
import { typedConfigLoadNacos } from '../../src/utils/config.utils';
export const initializeDataSource = async () => {
const configValue = await typedConfigLoadNacos(rootConfig);
const typeOrmOptionConfig = configValue.nacosValue.typeormOption;
const db: DataSourceOptions = {
type: typeOrmOptionConfig.type,
host: typeOrmOptionConfig.host,
port: typeOrmOptionConfig.port,
username: typeOrmOptionConfig.username,
password: typeOrmOptionConfig.password,
database: typeOrmOptionConfig.database, // 移除或保留此项用于连接,不用于生成 SQL
schema: typeOrmOptionConfig.schema, // 确保指定 schema
// entities: [__dirname + '/**/*.entity{.ts,.js}'],
logging: typeOrmOptionConfig.logging,
migrations: [path.join(__dirname, '../tables/*{.ts,.js}')],
synchronize: false,
};
console.log(JSON.stringify(db));
const dataSource = new DataSource(db);
// 确保 DataSource 已经初始化
return await dataSource
.initialize()
.then((value) => {
return value;
})
.catch((error) => {
console.log(error);
process.exit(1);
});
};
run-migration.ts
迁移 run 执行
import { initializeDataSource } from './data-source';
const runMigration = async () => {
try {
const dataSource = await initializeDataSource();
await dataSource.runMigrations();
console.log('🏅 Migrations executed successfully! 数据迁移成功!');
} catch (err) {
console.error('🙋 Error running migrations:', err);
} finally {
process.exit(0); // 无论成功还是失败,都退出程序
}
};
runMigration();
revert-migration.ts
revert 执行
import { initializeDataSource } from './data-source';
const runMigration = async () => {
try {
const dataSource = await initializeDataSource();
await dataSource.undoLastMigration();
console.log('🎮 Migrations executed successfully! 数据迁移回滚成功!');
} catch (err) {
console.error('🙋 Error running migrations:', err);
} finally {
process.exit(0); // 无论成功还是失败,都退出程序
}
};
runMigration();
common.ts
辅助类
// common 通用类
import { TableColumnOptions, TableForeignKeyOptions } from 'typeorm';
/**
* 默认列
*/
export const defaultColumn = (): TableColumnOptions[] => {
return [
{
name: 'id',
type: 'varchar',
length: '50',
isPrimary: true,
},
{
name: 'enable_at',
type: 'int',
default: 1,
comment: '状态: 1 正常 0 停用',
},
{
name: 'remark',
type: 'varchar',
length: '500',
isNullable: true,
comment: '状态: 1 正常 0 停用',
},
{
name: 'created_at',
type: 'timestamp',
default: 'CURRENT_TIMESTAMP',
comment: '创建时间',
},
{
name: 'created_id',
type: 'varchar',
length: '50',
isNullable: true,
comment: '创建人',
},
{
name: 'updated_at',
type: 'timestamp',
default: 'CURRENT_TIMESTAMP',
onUpdate: 'CURRENT_TIMESTAMP',
comment: '更新时间',
},
{
name: 'updated_id',
type: 'varchar',
length: '50',
isNullable: true,
comment: '更新时间',
},
{
name: 'deleted_at',
type: 'timestamp',
isNullable: true,
comment: '删除时间',
},
];
};
/**
* 根据 process.env.NODE_ENV 只在dev环境生成 约束
* @param array
*/
export const envForeignKeys = (array: TableForeignKeyOptions[]) => {
console.log('NODE_ENV', process.env.NODE_ENV);
if (process.env.NODE_ENV !== 'production') {
return array;
}
return [];
};
/**
* 是否创建外键
*/
export const createForeignKey = process.env.NODE_ENV !== 'production';
script 命令
{
"script": {
"typeorm": "typeorm-ts-node-commonjs",
"m:create": "npm run typeorm migration:create",
"m:run": "cross-env NODE_ENV=development && ts-node migrations/config/run-migration.ts",
"m:revert": "cross-env NODE_ENV=development && ts-node migrations/config/revert-migration.ts"
}
}
demo
import { MigrationInterface, QueryRunner, Table } from 'typeorm';
import { createForeignKey, defaultColumn } from '../utils/common';
export class User1732203320222 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.createTable(
new Table({
name: 'user',
comment: '用户表',
columns: [
...defaultColumn(),
{
name: 'nickname',
type: 'varchar',
length: '50',
isNullable: true,
comment: '昵称',
},
{
name: 'account',
type: 'varchar',
length: '50',
isNullable: true,
comment: '账号',
},
{
name: 'phone_number',
type: 'varchar',
length: '20',
isNullable: true,
comment: '手机号',
},
{
name: 'password',
type: 'varchar',
length: '150',
isNullable: true,
comment: '密码',
},
{
name: 'last_time',
type: 'timestamp',
isNullable: true,
comment: '最后登录时间',
},
{
name: 'wx_openid',
type: 'varchar',
length: '50',
isNullable: true,
comment: '微信授权登录openid',
},
{
name: 'zfb_openid',
type: 'varchar',
length: '50',
isNullable: true,
comment: '支付宝授权登录openid',
},
],
}),
createForeignKey,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.dropTable('user');
}
}