一些原则

  • 过早优化乃万恶之源
    • 如果没办法量化性能,就不要尝试优化性能
  • 开发效率>可读性>运行效率
  • 可用性>易用性>美观
  • 永远不要删除数据

    • 尽量软删除,删除前确认

      需求分析

  • 博客系统

    • 用户可以登录,注册,注销。但是不可以重置密码
    • 重置密码联系管理员
    • 用户可以对博客进行增删改查
    • 用户可以对博客进行评论,但不能修改评论
    • 用户不可以编辑用户名密码姓名头像
  • 可用性要求
    • 手机上也能完成操作
  • 其他要求

    • 对搜索引擎优化

      思路

  • 需求

    • 简单的增删改查
    • 主要表有users/posts/comments
  • 主要数据
    • user(id/username/password_digest)
    • posts(id/user_id/title/content)
    • comments(id/user_id/post_id/content)
  • 其他
    • 手机适配:一开始就设计两套界面PC+mobile
    • SEO:多用SSG或SSR,少用BSR

代码

删除之前的容器

$ docker ps
image.png
$ docker kill xxx
image.png
$ docker rm xxx
image.png

创建新容器

  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

image.png
$ docker exec -it xxx bash
image.png
# psql -U blog
image.png
# /l
image.png
# drop database blog_development;
image.png

创建新数据库

$ CREATE DATABASE xxx ENCODING 'UTF8' LC_COLLATE 'en_US.utf8' LC_CTYPE 'en_US.utf8';
image.png

清空src内的entity和migration,清空dist文件夹
image.png

seed.tsx

  1. import "reflect-metadata";
  2. import { createConnection } from "typeorm";
  3. createConnection().then(async connection => {
  4. connection.close()
  5. }).catch(error => console.log(error));

按两次ctrl+D退出bash
$ yarn dev
image.png

创建表

package.json写入命令
“migration:create”: “typeorm migration:create”,
或者简写成
“m:create”: “typeorm migration:create”,
$ yarn m:create -n CreateUsers
image.png
image.png

1627888…..

  1. import { MigrationInterface, QueryRunner, Table } from "typeorm";
  2. export class CreateUsers1627888334255 implements MigrationInterface {
  3. public async up(queryRunner: QueryRunner): Promise<void> {
  4. return await queryRunner.createTable(new Table({
  5. name: 'users',
  6. columns: [
  7. {
  8. name: 'id',
  9. isGenerated: true,
  10. type: 'int',
  11. generationStrategy: 'increment',
  12. isPrimary: true
  13. },
  14. {
  15. name: 'username',
  16. type: 'varchar'
  17. },
  18. {
  19. name: 'passwordDigest',
  20. type: 'varchar'
  21. }
  22. ]
  23. }))
  24. }
  25. public async down(queryRunner: QueryRunner): Promise<void> {
  26. return await queryRunner.dropTable('users')
  27. }
  28. }

image.png
$ yarn m:create -n CreatePosts

  1. import { MigrationInterface, QueryRunner,Table } from "typeorm";
  2. export class CreatePosts1627889702787 implements MigrationInterface {
  3. public async up(queryRunner: QueryRunner): Promise<void> {
  4. return await queryRunner.createTable(new Table({
  5. name: 'posts',
  6. columns: [
  7. {
  8. name: 'id',
  9. isGenerated: true,
  10. type: 'int',
  11. generationStrategy: 'increment',
  12. isPrimary: true
  13. },
  14. {
  15. name: 'title',
  16. type: 'varchar'
  17. },
  18. {
  19. name: 'content',
  20. type: 'text'
  21. },
  22. {
  23. name: 'authorId',
  24. type: 'int'
  25. }
  26. ]
  27. }))
  28. }
  29. public async down(queryRunner: QueryRunner): Promise<void> {
  30. return await queryRunner.dropTable('posts')
  31. }
  32. }

$ yarn m:create -n CreateComments

  1. import { MigrationInterface, QueryRunner, Table } from "typeorm";
  2. export class CreateComments1627890376988 implements MigrationInterface {
  3. public async up(queryRunner: QueryRunner): Promise<void> {
  4. return await queryRunner.createTable(new Table({
  5. name: 'comments',
  6. columns: [
  7. {
  8. name: 'id',
  9. isGenerated: true,
  10. type: 'int',
  11. generationStrategy: 'increment',
  12. isPrimary: true
  13. },
  14. {
  15. name: 'userId',
  16. type: 'int'
  17. },
  18. {
  19. name: 'postId',
  20. type: 'int'
  21. },
  22. {
  23. name: 'content',
  24. type: 'text'
  25. }
  26. ]
  27. }))
  28. }
  29. public async down(queryRunner: QueryRunner): Promise<void> {
  30. return await queryRunner.dropTable('comments')
  31. }
  32. }

$ yarn m:create -n AddCreatedAtAndUpdatedAt

  1. import { MigrationInterface, QueryRunner, TableColumn } from "typeorm";
  2. export class AddCreatedAtAndUpdatedAt1627891112814 implements MigrationInterface {
  3. public async up(queryRunner: QueryRunner): Promise<void> {
  4. await queryRunner.addColumns('users', [
  5. new TableColumn({
  6. name: 'createdAt',
  7. type: 'time',
  8. isNullable: false,
  9. default: 'now()'
  10. }),
  11. new TableColumn({
  12. name: 'updatedAt',
  13. type: 'time',
  14. isNullable: false,
  15. default: 'now()'
  16. })
  17. ])
  18. await queryRunner.addColumns('posts', [
  19. new TableColumn({
  20. name: 'createdAt',
  21. type: 'time',
  22. isNullable: false,
  23. default: 'now()'
  24. }),
  25. new TableColumn({
  26. name: 'updatedAt',
  27. type: 'time',
  28. isNullable: false,
  29. default: 'now()'
  30. })
  31. ])
  32. return await queryRunner.addColumns('comments', [
  33. new TableColumn({
  34. name: 'createdAt',
  35. type: 'time',
  36. isNullable: false,
  37. default: 'now()'
  38. }),
  39. new TableColumn({
  40. name: 'updatedAt',
  41. type: 'time',
  42. isNullable: false,
  43. default: 'now()'
  44. })
  45. ])
  46. }
  47. public async down(queryRunner: QueryRunner): Promise<void> {
  48. await queryRunner.dropColumn('users', 'createdAt')
  49. await queryRunner.dropColumn('users', 'updatedAt')
  50. await queryRunner.dropColumn('posts', 'createdAt')
  51. await queryRunner.dropColumn('posts', 'updatedAt')
  52. await queryRunner.dropColumn('comments', 'createdAt')
  53. return await queryRunner.dropColumn('comments', 'updatedAt')
  54. }
  55. }

删除dits 然后$ yarn dev 会重新创建dist文件夹
$ yarn m:run

创建关联

这三张表之间的内容有这样的关系

  • users表
    • 一个user有很多posts
    • 一个user有很多comments
  • posts表
    • 一个post属于一个user
    • 一个post有很多comments
  • comments
    • 一个comment属于一个user
    • 一个commnet属于一个post

创建entity中的实体
package.json
“e:create”: “typeorm entity:create”

$ yarn e:create -n User
$ yarn e:create -n Post
$ yarn e:create -n Comment

/src/entity/User.ts

  1. import { Column, CreateDateColumn, Entity, OneToMany, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm';
  2. import { Comment } from './Comment';
  3. import { Post } from './Post'
  4. @Entity('users')
  5. export class User {
  6. @PrimaryGeneratedColumn('increment')
  7. id: string;
  8. @Column('varchar')
  9. username: string;
  10. @Column('varchar')
  11. passwordDigest: string;
  12. @CreateDateColumn()
  13. createdAt: Date;
  14. @UpdateDateColumn()
  15. updatedAt: Date;
  16. @OneToMany(type => Post, post => post.author)
  17. posts: Post[]
  18. @OneToMany(type => Comment, comment => comment.user)
  19. comments: Comment[]
  20. }

/src/entity/Post.ts

  1. import { Column, CreateDateColumn, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm';
  2. import { User } from './User';
  3. import { Comment } from './Comment';
  4. @Entity('posts')
  5. export class Post {
  6. @PrimaryGeneratedColumn('increment')
  7. id: string;
  8. @Column('varchar')
  9. title: string;
  10. @Column('text')
  11. content: string;
  12. @CreateDateColumn()
  13. createdAt: Date;
  14. @UpdateDateColumn()
  15. updatedAt: Date;
  16. @ManyToOne(type => User, user => user.posts)
  17. author: User;
  18. @OneToMany(type => Comment, comment => comment.post)
  19. comments: Comment[]
  20. }

/src/entity/Comment.ts

  1. import { Column, CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm';
  2. import { Post } from './Post';
  3. import { User } from './User';
  4. @Entity('comments')
  5. export class Comment {
  6. @PrimaryGeneratedColumn('increment')
  7. id: string;
  8. @Column('text')
  9. content: string;
  10. @CreateDateColumn()
  11. createdAt: Date;
  12. @UpdateDateColumn()
  13. updatedAt: Date;
  14. @ManyToOne(type => User, user => user.comments)
  15. user: User;
  16. @ManyToOne(type => Post, post => post.comments)
  17. post: Post;
  18. }

填充数据

/src/seeds.ts

  1. import "reflect-metadata";
  2. import { createConnection } from "typeorm";
  3. import { Comment } from "./entity/Comment";
  4. import { Post } from "./entity/Post";
  5. import { User } from './entity/User';
  6. createConnection().then(async connection => {
  7. const { manager } = connection;
  8. //创建user
  9. const u1 = new User()
  10. u1.username = 'gouson'
  11. u1.passwordDigest = 'xxx'
  12. await manager.save(u1)
  13. //创建post
  14. const p1 = new Post()
  15. p1.title = "Post 1"
  16. p1.content = "yyyyyyyyyyyyyyyyyy"
  17. p1.author = u1
  18. await manager.save(p1)
  19. //创建comment
  20. const c1 = new Comment()
  21. c1.content = 'zzzzzz'
  22. c1.user = u1
  23. c1.post = p1
  24. await manager.save(c1)
  25. console.log(u1.id)
  26. connection.close()
  27. }).catch(error => console.log(error));

$ yarn dev
$ node dist/seed.js
image.png
image.png
image.png
image.png

创建成功