最近完成了一个简易的博客系统,使用 Nextjs + TypeORM + TypeScript。功能有:登录注册、博客增删改、自动部署、Docker 容器化、Nginx gzip 配置。代码地址: github.com/Maricaya/ne… 。预览地址:http://121.36.50.175/。不得不说 SSR 真香,几乎没有白屏时间,加载非常快。 上次介绍了 如何在项目中引入 TypeORM, 这次来看看如何操作数据表。
准备工作终于完成了,接下来开始愉快地操作数据库。代码地址。
吐槽一下 node.js 工具库,加上 ts 之后很多东西都要自己手动配置,很麻烦,准备工作都做很久 =.=
设计基础表
博客系统大家都很熟悉了,我在这简单说下表结构。
- 主要的表有 users/posts/comments
- users (id/username/password_digest)
- posts (id/user_d/title/content)
- comments (id/user_id/post_id/content)
通过 migration 创建数据表
posts 表
创建 posts 表,存放我们博客系统的文章。 这个表里应该有自增的 id、文章名称 title、文章内容 content。
npx typeorm migration:create -n CreatePosts
得到 src/migrations/{TIMESTAMP}-CreatePosts.ts
├── src
│ ├── entity
│ ├── index.ts
│ └── migration
│ └── 1596542806356-CreatePosts.ts // 新增文件
来看看这个 1596542806356-CreatePosts.ts 文件,里面有两个函数 up、down。
up 执行写入数据库操作,down 恢复数据库。
import {MigrationInterface, QueryRunner, Table} from 'typeorm';
export class CreatePosts1591207370002 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
// 写入数据库
// queryRunner 语句执行期,创建 Table
return await queryRunner.createTable(new Table({
name: 'posts',
// 列 id、title、content、author_id
columns: [
{name: 'id', isGenerated: true, type: 'int', generationStrategy: 'increment', isPrimary: true},
{name: 'title', type: 'varchar'},
{name: 'content', type: 'text'},
{name: 'author_id', type: 'int'}
]
}));
}
// 恢复数据库(有时候写错了,需要恢复一下重写
public async down(queryRunner: QueryRunner): Promise<void> {
return await queryRunner.dropTable('posts')
}
}
users、comments 表也是相同的操作。
运行 migration (数据迁移)
现在运行 migration 有这些步骤:
- 先编译 ts 文件
npx babel ./src --out-dir dist --extensions .ts,.tsx- 命令行加入
-w,监听文件,每次修改代码后自动编译
- 命令行加入
- 运行 migration
npx typeorm migration:run - 如果运行失败,撤回数据库操作
npx typeorm migration:revertps:我们把所有的命令都写入 package.json 中,这样每次输入就方便多了。
如何添加数据:创建实体 entity
背景
我们刚刚只是在数据库里创建了 Post,那么代码如何读写 Post 呢?
需要将数据映射到 Entity(实体),使用命令创建实体:typeorm entity:create -n Post。
import {
Column,
CreateDateColumn,
Entity,
ManyToOne,
OneToMany,
PrimaryGeneratedColumn,
UpdateDateColumn
} from 'typeorm';
import {User} from './User';
import {Comment} from './Comment';
@Entity('posts')
export class Post {
@PrimaryGeneratedColumn('increment')
id: number;
@Column('varchar')
title: string;
@Column('text')
content: string;
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
@ManyToOne(type => User, user => user.posts)
author: User;
@OneToMany(type => Comment, comment => comment.post)
comments: Comment[];
}
使用实体
现在我们有了 Post 实体,怎么用代码操作呢?
TypeORM 为我们提供了两种使用方法:EntityManager 或 Repository
这只是两种不同的封装思路而已,需要灵活使用。
EntityManager
EntityManager 的封装思路很简单,就是把所有的操作都放在 manager 上。
然后把 Post 类、post1 对象和其他参数传递给 manager。
我们在 src/index.ts 中操作 EntityManager 试试:
import "reflect-metadata";
import {createConnection} from 'typeorm';
import {Post} from './entity/Post';
import {User} from './entity/User';
import {Comment} from './entity/Comment';
createConnection().then(async connection => {
const {manager} = connection;
// 创建 user 1
const u1 = new User();
u1.username = 'frank';
u1.passwordDigest = 'xxx';
await manager.save(u1);
// 创建 post 1
const p1 = new Post();
p1.title = 'Post 1';
p1.content = 'My First Post';
p1.author = u1;
// 存入数据库
await manager.save(p1);
const c1 = new Comment();
c1.user = u1;
c1.post = p1;
c1.content = 'Awesome!';
await manager.save(c1);
await connection.close();
console.log('OK!')
}).catch(error => console.log(error));
Repository
Repository 的特色是支持 TreeRepository 和 MongoRepository。
它是先把操作哪个仓库固定好,后面直接调用操作的 Api,不用传递是哪个仓库。
比如我们先通过 User 构造一个 repo 对象,这个 repo 对象就只操作 User 表。 可以直接在 repo 对象上调用 findeOne、save 等操作。
一个🌰 :
const userRepository = getRepository(User);
await userRepository.findOne(1);
await userRepository.save(user);
总结
总结下这篇文章提到的操作:
- migration 数据迁移:用来对数据库进行升级和降级。
- entity 实体:用类和对象来操作数据表和数据行。
- connection 连接:一个数据库连接,默认最多 10 个。
- manager / repo:两种 API 封装风格,用于操作 entity。