启动数据库postgresql

  • 创建数据目录(Winodws Toolbox不用做这一步)
    • 在项目目录创建blog-data目录
    • .gitignore里添加/blog-data/
  • 启动PostgreSQL

    • 一句命令启动pg:

      1. 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
    • 以下是 Windows 旧版 Docker 客户端(Toolbox)的命令(推荐 Windows 用户使用这一版客户端,很稳)

      1. 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 查看容器运行状态

image.png

  • $ docker logs 容器id 查看启动日志

    验证pg

    进入docker容器

    $ docker exec -it 容器id bash
    image.png

    进入pg命令行

    $ psql -U blog -W

  • 没设置密码直接回车

image.png

  • 如果需要密码,可以在docker run 选项中

    -e POSTGRES_HOST_AUTH_ME THOD=trust 替换成
    -e POSTGRES_PASSWORD=123456

    执行pg命令

  • \l 用于 list databases,目前有一个blog数据库

image.png

  • \c 用于 connect to a database

image.png

  • \dt 用于 display tables,目前没有tables

image.png

创建数据库

用SQL来创建数据库

  • 因为TypeORM没有单纯提供创建数据库的API
  • $ CREATE DATABASE xxx ENCODING 'UTF8' LC_COLLATE 'en_US.utf8' LC_CTYPE 'en_US.utf8';
  • xxx换成表名
  • 我们需要创建三个数据库:开发、测试、生产
  • 对应英文:Development、test、production
  • 最终结果得到三个数据库

image.png

image.png

安装TypeORM

步骤

  • 打开官网,点击Getting Started

  • $ yarn add typeorm

  • $ yarn add reflect-metadata
  • $ yarn add --dev @types/node
  • $ yarn add pg

  • 配置tsconfig.json

    1. "emitDecoratorMetadata": true,
    2. "experimentalDecorators": true,

这时候先 git commit一下,非常重要

  • $ ./node_modules/.bin/typeorm init --database postgres

或者

  • $ npx typeorm init --database postgres

image.png
image.png

  • 这个命令会覆盖很多东西,撤销一部分
  • $ git checkout HEAD -- .gitignore 撤销对.gitiggnore 的修改
  • $ git checkout HEAD -- package.json 撤销对package.json的修改
  • $ git checkout HEAD -- tsconfig.json 撤销对tsconfig.json的修改
  • 修改ormconfig.json的内容

    1. {
    2. "type": "postgres",
    3. "host": "localhost",
    4. // 如果是toolbox用户,输入 docker-machine.exe ip
    5. // 将这里修改为ip
    6. "port": 5432,
    7. "username": "blog",
    8. "password": "",
    9. "database": "blog_development",
    10. "synchronize": true,
    11. "logging": false,
    12. "entities": [
    13. "src/entity/**/*.ts"
    14. ],
    15. "migrations": [
    16. "src/migration/**/*.ts"
    17. ],
    18. "subscribers": [
    19. "src/subscriber/**/*.ts"
    20. ],
    21. "cli": {
    22. "entitiesDir": "src/entity",
    23. "migrationsDir": "src/migration",
    24. "subscribersDir": "src/subscriber"
    25. }
    26. }
  • User.ts中会报错

image.png

  • 如果在tsconfig.json中关闭严格模式就不会报错
  • image.png
  • 删掉/src/index.ts中的内容 ```json import “reflect-metadata”; import { createConnection } from “typeorm”;

createConnection().then(async connection => {

  1. console.log(connection)
  2. connection.close()

}).catch(error => console.log(error));

  1. <a name="lmGnN"></a>
  2. # 运行index.ts
  3. <a name="txZ6K"></a>
  4. ## 如何运行TypeScript
  5. - Next.js默认使用babel来将TS编译为JS(内置功能)
  6. - TypeORM推介使用ts-node来编译(没有内置)
  7. - babel和ts-node对TS的支持并非完全一致
  8. - 统一,全都用babel
  9. <a name="eTv2l"></a>
  10. ## 连接数据库
  11. `$ yarn add @babel/cli`<br />`$ yarn add --dev @babel/plugin-proposal-decorators`<br />创建.babelrc
  12. ```json
  13. {
  14. "presets": [
  15. "next/babel"
  16. ],
  17. "plugins": [
  18. [
  19. "@babel/plugin-proposal-decorators",
  20. {
  21. "legacy": true
  22. }
  23. ]
  24. ]
  25. }

$ npx babel ./src --out-dir dist --extensions ".ts,.tsx"
image.png

  • 修改ormconfig.json的内容 “entities”: [“dist/entity/*/.js”]
  • 删除dist,删掉/src/entity/User.ts
  • 重新babel
  • $ node dist/index.js

image.png

禁用sync

ormconfig.json

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

  • 看起来方便但是sync可能会在我们修改User时删除数据

  • 绝不要在生产环境使用sync

通过migration创建表

确保现在没有任何表,有就 drop table xxx

posts表

  • $ npx typeorm migration:create -n CreatePost

得到src/migration/{TIMESTAMP}-CreatePosts.ts

  1. import { MigrationInterface, QueryRunner, Table } from "typeorm";
  2. export class CreatePost1626854000792 implements MigrationInterface {
  3. public async up(queryRunner: QueryRunner): Promise<void> {
  4. //升级数据库
  5. return await queryRunner.createTable(new Table({
  6. name: 'posts',
  7. columns: [{
  8. name: 'id',
  9. type: 'int',
  10. isPrimary: true,
  11. isGenerated: true,
  12. generationStrategy: 'increment'
  13. }, {
  14. name: 'title',
  15. type: 'varchar'
  16. }, {
  17. name: 'content',
  18. type: 'text'
  19. }]
  20. }))
  21. }
  22. public async down(queryRunner: QueryRunner): Promise<void> {
  23. //降级数据库
  24. return await queryRunner.dropTable('posts')
  25. }
  26. }
  • 用babel编译成js

$ npx babel ./src --out-dir dist --extensions ".ts,.tsx"

  • 修改ormconfig.json的内容 “migrations”: [“dist/migration/*/.js”],
  • 重新babel编译
  • 运行

$ typeorm migration:run
image.png

  • 如果表设计错了,这时候就可以降级,package.json中加个命令image.png

$ typeorm migration:revert
image.png

  • 为了不每次运行babel,可以将package.json中的命令改为

“typeorm:build”: “babel -w ./src —out-dir dist —extensions .ts,.tsx”,

  • 同时也可以加入到dev中,把两个命令写到一起

    • 安装concurrently $ yarn add --dev concurrently
    • “dev”: “concurrently \”next dev\” \”babel -w ./src —out-dir dist —extensions .ts,.tsx\””,

      数据映射到实体entity

  • $ typeorm entity:create -n Post

image.png

  • /src/entity/Post.ts ```json import { Column, Entity, PrimaryGeneratedColumn } from ‘typeorm’;

@Entity(‘posts’) export class Post { @PrimaryGeneratedColumn(‘increment’) id: number; @Column(‘varchar’) title: string; @Column(‘text’) content: string

}

  1. <a name="pGRKd"></a>
  2. ## 如何使用实体
  3. 两种方法
  4. <a name="QHA9a"></a>
  5. ### [EntityManager](https://typeorm.biunav.com/zh/#%E4%BD%BF%E7%94%A8-entity-manager)
  6. /scr/index.ts
  7. ```json
  8. import "reflect-metadata";
  9. import { createConnection } from "typeorm";
  10. import { Post } from "./entity/Post";
  11. createConnection().then(async connection => {
  12. const posts = await connection.manager.find(Post)
  13. console.log(posts)
  14. const p = new Post()
  15. p.title = 'Post 1'
  16. p.content = '我的第一篇文章'
  17. await connection.manager.save(p)
  18. const posts2 = await connection.manager.find(Post)
  19. console.log(posts2)
  20. connection.close()
  21. }).catch(error => console.log(error));

$ node dist/index.js
image.png
image.png

EntityManagerApi

  • await manager.find(User,{name:”gouson”});
  • await manager.create(User,{name:”gouson”});
  • await manager.save(user1);
  • await manager.save([user1,user2,user3]);
  • await manager.remove(user1);
  • await manager.update(User,1,{name:”gouson”});
  • await manager.delete(User,1);
  • await manager.findOne(User,1);

    封装思路

    把所有的操作都放在manager上
    把User类、user1对象和其他参数传给manager

    Repository

    Repository Api

    cont userRepository =getRepository(User);
    await userRepository.findOne(1);
    await userRepository.save(user);

    封装思路

    先通过User构造一个repo对象
    这个对象就只操作User表

使用seed填充数据

  • 也叫数据填充
    • 有了数据库,数据表post,但是没数据
    • 可以通过seed脚本来构造数据
    • 一般不在生产环境运行seed脚本
  • 好处
    • 别人可以快速运行项目

/src/entity/Post.ts

  1. import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
  2. @Entity('posts')
  3. export class Post {
  4. @PrimaryGeneratedColumn('increment')
  5. id: number;
  6. @Column('varchar')
  7. title: string;
  8. @Column('text')
  9. content: string
  10. constructor(title: string, content: string) {
  11. this.title = title;
  12. this.content = content;
  13. }
  14. }

/src/seed.tsx

  1. import "reflect-metadata";
  2. import { createConnection } from "typeorm";
  3. import { Post } from "./entity/Post";
  4. createConnection().then(async connection => {
  5. const posts = await connection.manager.find(Post)
  6. if (posts.length === 0) {
  7. let dataArray = []
  8. for (let i = 0; i < 10; i++) {
  9. dataArray.push(new Post(`Post ${i}`, `第${i}篇文章`))
  10. }
  11. await connection.manager.save(dataArray)
  12. }
  13. console.log('posts 数据填充了')
  14. connection.close()
  15. }).catch(error => console.log(error));

$ yarn dev
$ yarn migaration:revert
$ yarn migaration:run
$ node dist/seed.js
image.png
image.png