TS + TypeORM 踩坑实践 (二) 操作数据表

3,953 阅读3分钟

最近完成了一个简易的博客系统,使用 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:revert ps:我们把所有的命令都写入 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 为我们提供了两种使用方法:EntityManagerRepository

这只是两种不同的封装思路而已,需要灵活使用。

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。