使用 TypeORM

特性

  • 默认支持 TypeScript
  • 我们来打算用 Sequelize.js,发现他 对 TS 支持不够好
  • 支持关联(Associations)
  • 支持事务(Transaction)
  • 支持数据库迁移(Migration)

启动数据库 postgresql

新版 docker(额外)

  • 在项目目录中创建 blog-data 目录
  • .gitignore 里添加 /blog-data/

启动 PostgreSQL

  • 一句话启动
  • 新版: docker run -v "$PWD/blog-data":/var/lib/postgresql/data -p 5432:5432 -e POSTGRES_USER=blog -e POSTGRES_HOST_AUTH_METHOD=trust -d postgres:12.2
  • 旧版: docker run -v "blog-data":/var/lib/postgresql/data -p 5432:5432 -e POSTGRES_USER=blog -e POSTGRES_HOST_AUTH_METHOD=trust -d postgres:12.2
  • docker ps -a 这句话可以查看容器的运行状态
  • docker logs 容器id 这句话可以查看启动日志

验证 pg

进入 docker 容器

  • docker exec -it 容器id bash

进入 pg 命令行

  • psql -U blog -W
  • 由于上面没有设置密码,所以直接回车即可
  • 如果需要密码,可在docker run 选项里的 -e POSTGRES_HOST_AUTH_METHOD=trust 替换成 -e POSTGRES_PASSWORD=123456

一些简单的命令

  • \l 用于 list databases,目前有一个 blog 数据库
  • \c 用于 connect to a database
  • \d 用于 display
  • \dt 用于 display tables,目前还没有

创建数据库

用 SQL 来创建数据库

  • CREATE DATABASE xxx ENCODING 'UTF8' LC_COLLATE 'en_US.utf8' LC_CTYPE 'en_US.utf8';
  • 因为 Type ORM 没有提供单纯创建数据库的 API
  • 创建三个数据库:开发、测试、生产
  • 对应英文 blog_development、blog_test、 blog_production
  • 得到三个数据库

安装 TypeORM

  • 打开官网,点击 Getting Started
  • 安装该安装的依赖(typeorm reflect-metadata @types/node pg)
  • 不要使用 Quick Start 里面的 typeorm init 命令,因为他们改写你的现有项目的文件(后面自己改)
  • 在 tsconfig.json 中加入 "emitDecoratorMetadata": true, "experimentalDecorators": true, 并更改成 "module": "commonjs"
  • 创建 ormconfig.json,并加入内容(官网上有)

运行 TypeORM

吐槽

  • Next.js 默认使用 babel 来将 TS 编译为JS(内置功能)
  • TypeORM 推荐使用 ts-node 来编译(没有内置)
  • babel 和 ts-node 对 TS 的支持并非完全一致
  • 所以我们必须进行统一,全部都用 babel

安装 babel

  • 安装 @babel/cli
  • 创建 src/index.ts
// index.ts
import "reflect-metadata";
import {createConnection} from 'typeorm';

createConnection().then(async connection => {
  console.log(connection)
  await connection.close()
}).catch(error => console.log(error));
  • npx babel ./src --out-dir dist --extensions ".ts,.tsx"
  • node dist/index.js
  • 控制台成功打印出 connection 对象,连接数据库成功!

此时项目运行流程

  • 统一让 Next.js 和 TypeORM 使用 babel 翻译 TS
  • 每次修改 src 的 TS 代码后,翻译为 dist 里的 JS
  • 使用 node 运行 dist 里的 JS,执行 TypeORM 任务
  • 也可使用 Next.js 执行 TypeORM 任务(后面弄)

重要配置:禁用 sync

ormconfig

  • "synchronize": true => false
  • 如果为 true,那么在连接数据库时,typeorm 会自动根据 entity 目录来修改数据表
  • 假设 entity 里面有 User,就会自动创建 User 表

看起来很方便,为什么要禁用

  • 因为 sync 功能可能会在我们修改User 时直接删除数据
  • 假设你把 user 表中的 name 字段改为了 nickname,他可能会误解你删除了 name,并新增了 nickname,此时表中 name 数据已经丢失
  • 这种行为绝对不能发生在生产环境
  • 所以我们要一开始就杜绝 sync 功能

创建表

posts表

  • 使用命令行来创建
  • 首先在 ormconfig 中加入以下代码,控制文件生成的目录
"cli": {
  "migrationsDir": "src/migration"
}
  • npx typeorm migration:create -n CreatePosts
  • 在新生成的文件中 up 方法代表升级数据库,down 代表降级数据库
import {MigrationInterface, QueryRunner, Table} from 'typeorm';

export class CreatePosts1595341120888 implements MigrationInterface {

    public async up(queryRunner: QueryRunner): Promise {
        await queryRunner.createTable(new Table({
            name: 'posts',
            columns: [
                {name: 'id', isGenerated: true, type: 'int', isPrimary: true, generationStrategy: "increment"},
                {name: 'title', type: 'varchar'},
                {name: 'content', type: 'text'},
                {name: 'author_id', type:'int'}
            ]
        }))
    }

    public async down(queryRunner: QueryRunner): Promise {
        await queryRunner.dropTable('posts')
    }

}

  • npx babel ./src --out-dir dist --extensions ".ts,.tsx"
  • 由于我们使用 babel,与 TypeORM 官方建议用的 ts-node 不一样,所以我们还需要修改 ormconfig.json 文件,把 entities、migrations、subscribers,里面的路径都替换为 dist/xxx/*/.js
  "entities": [
    "dist/entity/**/*.js"
  ],
  "migrations": [
    "dist/migration/**/*.js"
  ],
  "subscribers": [
    "dist/subscriber/**/*.js"
  ],
  • npx typeorm migration:run
  • 运行成功
  • 我们就可以看到数据库中已经有 posts 表了

每次都要运行 babel 不傻吗?

  • npx babel --help 可以看到有 -w 选项
  • 这样我们每次更改文件 babel 就会自动编译
  • 但是此时我们需要开三个窗口来运行我们的项目,第一跑 next dev,第二个跑 babel,第三个输入当前命令
  • 所以有没有办法让第一个窗口和第二个窗口合并呢?
  • Linux / Mac 用户直接使用 & 即可, next dev & babel -w ....
  • 但是 Windows 不支持(&& 的意思是如果前一个命令成功了,就执行下一个命令,此时不适用)
  • 通过搜索关键词 npm run tasks in paraller
  • 发现 concurrently 可以代替 & 操作,安装根据文档操作即可

数据映射到实体

背景

  • 刚刚只是在数据库里创建了 posts,代码如何读写 posts 呢?
  • 答案:将数据映射到 Entity(实体)
  • 和 migration 一样首先在 ormconfig 中加入以下代码,控制文件生成的目录
"cli": {
  "entitiesDir": "src/entity",
}
  • npx typeorm entity:create -n Post
import {Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn} from 'typeorm';

@Entity('posts')
export class Post {
  @PrimaryGeneratedColumn('increment')
  id: number;
  @Column('varchar')
  title: string;
  @Column('text')
  content: string;
  @Column('int')
  authorId: number;
  @CreateDateColumn()
  createdAt: Date;
  @UpdateDateColumn()
  updatedAt: Date;
}

  • 编译时遇到一个报错 syntax 'decorators-legacy'
  • 搜索以后,安装 yarn add -D @babel/plugin-proposal-decorators
  • 根据 Next.js 的要求,新建 .babelrc 文件,并加入上面安装的插件
{
  "presets": [
    "next/babel"
  ],
  "plugins": [
    [
      "@babel/plugin-proposal-decorators",
      {
        "legacy": true
      }
    ]
  ]
}

知识点

  • @PrimaryGeneratedColumn('increment') // 自增主键
  • @Column('varchar') // varchar 类型
  • @Column('text') // text 类型

如何使用实体

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

EntityManager API

举例

  • await manager.find(Post, { title: '第一篇博客' })
  • await manager.create(Post, { title: '.....' })
  • await manager.save(post1)
  • await manager.save([post1, post2, post3])
  • await manager.remove(post1)
  • await manager.update(Post, 1, { title: '修改后的标题' })
  • await manager.delete(Post, 1)
  • await manager.findOne(Post, 1)

封装思路

  • 把所有操作都放在 manager 上
  • 把 Post 类、post1 对象和其他参数传给 manager

Repository API

举例

  • const postRepository = getRepository(Post)
  • await postRepository .findOne(1)
  • await postRepository .save(post)

封装思路

  • 先通过Post 构造一个 repo 对象
  • 这个 repo 对象就只操作 posts 表了

特色

  • TreeRepository 和 MongoRepository
  • 目前用不到这两个功能,所以就先不用Repo API

总结

migration 数据迁移

  • 用来对数据库升级和降级

entity 实体

  • 用类和对象操作数据表和数据行

connection 连接

  • 一个数据库连接,默认最多 10 个连接
  • 这种模式也叫做连接池,可以参考这篇文章

manager / repo

  • 两种 API 封装风格,用于操作 entity

你可能感兴趣的:(使用 TypeORM)