最近在用Node.js练习写后端,操作数据库的方面自然用到了比较火的一款TypeORM框架,在使用迁移(migration)时,看文档查百度好容易弄出来了(感觉我点了谷歌翻译后的官方文档不够清楚啊-_-||),先记录下来。
迁移(Migration)
迁移就我的理解来说的话,就是通过我们写的代码来修改数据库的解构或者数据(用于不同环境间的数据库解构同步),好处的话:
1. 通过代码编写,不用直接操作数据库;
2. 直接通过命令来操作,简单快捷。
实体
因为TypeORM
的表生成是通过对应的实体来生成的,所以得先了解什么是实体。
在TypeORM
里面,实体其实就是一个Class
,也就是一个普通的类,只是这个类需要通过@Entity
装饰器来装饰,类里面的每一个属性,对应的都是数据表的每一个字段,例如:
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity({
name: 'article'
})
class Article {
@PrimaryGeneratedColumn()
id: number = 0;
@Column({
type: 'varchar',
length: 50,
comment: '标题'
})
title: string = '';
@Column({
type: 'varchar',
length: 100,
comment: '描述'
})
desc: string = '';
}
export default Article
这样的一个类,就可以称之为实体(或者叫实体类)。
开始迁移
开始迁移前我们需要定义一个配置文件,这里用的是ts
作为配置文件来使用:
import { resolve } from 'path';
import { ConnectionOptions } from 'typeorm';
const dbConfig: ConnectionOptions = {
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: 'password',
database: 'myblog',
entities: [
'src/model/*.ts' // 对应的实体类位置
],
migrations: [
'src/migrations/**/*.ts' // 对应的迁移文件位置
],
cli: {
entitiesDir: 'src/model', // 实体类的目录位置
migrationsDir: 'src/migrations' // 迁移文件的目录位置
}
}
export default dbConfig;
这个配置文件也是typeorm
连接数据库所需要的。
了解这些概念后,就可以通过TypeORM
开始实现迁移功能,因为使用的是ts
,所以我们需要在package.json
里面配置几条命令,这些命令可以去官网文档里面查看:
"typeorm:create": "ts-node --transpile-only ./node_modules/typeorm/cli.js --config src/config/db.config.ts migration:create -n",
"typeorm:gen": "ts-node --transpile-only ./node_modules/typeorm/cli.js --config src/config/db.config.ts migration:generate -n",
"typeorm:run": "ts-node --transpile-only ./node_modules/typeorm/cli.js migration:run --config src/config/db.config.ts"
typeorm:create
这个命令的作用就是用于生成一个migration
文件,我们需要操作数据库的代码,例如执行命令:
npm run typeorm:create UpdateArticle
就会生成下面这些代码:
import {MigrationInterface, QueryRunner} from "typeorm";
export class UpdateArticle1633872542504 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE \`article\` MODIFY \`desc\` VARCHAR(200)`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE \`article\` MODIFY \`desc\` VARCHAR(100)`);
}
}
上面的一个类就是通过create
命令生成的,这里面主要有两个方法:
- up:包含迁移所需执行的代码;
- down:包含迁移恢复的代码。
其实就是:up
是我们想要去执行操作数据库的sql
语句,如果这个语句有问题,执行不了的话,就需要执行down
方法来恢复成原来的样子,避免数据库遭到污染(系统应该会自动执行的)。
typeorm:gen
这个命令是用来生成对应实体类的migration
,通过这个migration
就可以创建对应的表,例如:
npm run typeorm:gen CreateArticle
会生成一个TIMESTAMP-CreateArticle.ts
文件,这个文件就已经包含了创建表的sql
了:
import {MigrationInterface, QueryRunner} from "typeorm";
export class CreateAritcle1633872275180 implements MigrationInterface {
name = 'CreateAritcle1633872275180'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`CREATE TABLE \`article\` (\`id\` int NOT NULL AUTO_INCREMENT, \`title\` varchar(50) NOT NULL COMMENT '标题', \`desc\` varchar(100) NOT NULL COMMENT '描述', \`view\` int NOT NULL COMMENT '浏览数量', \`good\` int NOT NULL COMMENT '点赞数量', \`message\` int NOT NULL COMMENT '留言数量', \`createTime\` varchar(13) NOT NULL COMMENT '创建时间', \`updateTime\` varchar(13) NOT NULL COMMENT '修改时间', \`userId\` int NOT NULL COMMENT '用户ID', PRIMARY KEY (\`id\`)) ENGINE=InnoDB`);
await queryRunner.query(`ALTER TABLE \`article\` ADD CONSTRAINT \`FK_636f17dadfea1ffb4a412296a28\` FOREIGN KEY (\`userId\`) REFERENCES \`user\`(\`id\`) ON DELETE NO ACTION ON UPDATE NO ACTION`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE \`article\` DROP FOREIGN KEY \`FK_636f17dadfea1ffb4a412296a28\``);
await queryRunner.query(`DROP TABLE \`article\``);
}
}
注意:上面的CreateArticle是固定的写法,表名是article,但是执行generate命令时需要在Article前加上Create表示创建表,可以是CreateUser,也可以说CreateRole,总之表名称一定要对应!
typeorm:run
表示执行迁移,上面通过create
或者gen
生成的文件都是迁移文件,通过执行命令:
npm run typeorm:run
然后就会执行对这些文件的逻辑(按道理,应该可以执行单个的,不过我暂时还未发现)。
暂时记录这些...