【NestJS】ORM操作数据库

248 阅读3分钟

TypeORM操作数据库

1. Nest集成TypeORM

  • 由于datasource的配置是需要手动传入的,所以,TypeORM需要做成动态模块,支持根据传入的配置来动态产生模块内容。而动态模块的规范里就 3 种方法名: register、forRoot、forFeature。
  • 用forRoot只需要注册一次,然后这个模块会在各处被使用。
npm install --save @nestjs/typeorm typeorm mysql2
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'root',
      password: 'root',
      database: 'hello-mysql',
      entities: [],
      synchronize: true,
    }),
  ],
})
export class AppModule {}

2. 多级分类的场景

创建树形实体类

  • 通过 @TreeChildren 声明的属性里存储着它的 children 节点,通过 @TreeParent 声明的属性里存储着它的 parent 节点。
  • 并且这个 entity 要用 @Tree 声明。一般都是用 closure-table,或者 materialized-path。
  • closure-table生成了两个表,在 city 表里保存着 city 记录之间的父子关系,通过 parentId 关联。在 city_closure 表里记录了也记录了父子关系。
  • materialized-path只生成了一个表,只是这个表多了一个 mpath 字段。
// city.entity.ts
import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, Tree, TreeChildren, TreeParent, UpdateDateColumn } from "typeorm";

@Entity()
@Tree('closure-table')
export class City {
    @PrimaryGeneratedColumn()
    id: number;

    @Column({ default: 0 })
    status: number;

    @CreateDateColumn()
    createDate: Date;

    @UpdateDateColumn()
    updateDate: Date;
    
    @Column()
    name: string;

    @TreeChildren()
    children: City[];

    @TreeParent()
    parent: City;
}

获取树形数据

@InjectEntityManager()
entityManager: EntityManager;

async findAll() {
    return this.entityManager.getTreeRepository(City).findTrees();
}

获取树形数据的第一层级数据

@InjectEntityManager()
entityManager: EntityManager;

async findAll() {
    return this.entityManager.getTreeRepository(City).findRoots();
}

findDescendantsTree查询某个节点的所有后代节点

@InjectEntityManager()
entityManager: EntityManager;

async findAll() {
    const parent = await this.entityManager.findOne(City, { where: { name: '云南' } });
    
    return this.entityManager.getTreeRepository(City).findDescendantsTree(parent)
}

findAncestorsTree查询某个节点的所有祖先节点

@InjectEntityManager()
entityManager: EntityManager;

async findAll() {
    const parent = await this.entityManager.findOne(City, { where: { name: '云南' } }); 
    
    return this.entityManager.getTreeRepository(City).findAncestorsTree(parent)
}

countAncestors 和 countDescendants 来计数返回层级数

@InjectEntityManager()
entityManager: EntityManager;

async findAll() {
    const parent = await this.entityManager.findOne(City, { where: { name: '云南' } }); 
    
    return this.entityManager.getTreeRepository(City).countAncestors(parent)
}

Prisma操作数据库

1. 基本使用

初始化项目

mkdir prisma-app 
cd prisma-app
npm init -y
  • 安装 typescript 相关的包:
npm install typescript ts-node @types/node --save-dev
  • 创建 tsconfig.json
npx tsc --init
  • 安装 prisma:
npm install prisma --save-dev

创建 schema 文件

  • primsa init 创建 schema 文件
// 创建 mysql 数据库的 schema 文件
npx prisma init --datasource-provider mysql
  • 修改 .env 文件里存储着连接信息
DATABASE_URL="mysql://root:root@localhost:3306/prisma_test"

数据建表

  • 定义 model
model User {
  id    Int     @id @default(autoincrement())
  email String  @unique
  name  String?
}
  • 基于model生成 client 的代码

prisma migrate dev 根据 schema 文件生成 sql 并执行,还会生成 client 代码。

npx prisma migrate dev --name aaa
  • 在 mysql workbench 里可以看到生成了 1 个表

2. Prisma 的命令

Prisma 的全部命令

  • init:创建 schema 文件
  • generate: 根据 shcema 文件生成 client 代码
  • db:同步数据库和 schema
  • migrate:生成数据表结构更新的 sql 文件
  • studio:用于 CRUD 的图形化界面
  • validate:检查 schema 文件的语法错误
  • format:格式化 schema 文件
  • version:版本信息

创建完 schema 文件,如何定义 model 呢?

可以执行 prisma db pull 把数据库里的表同步到 schema 文件。

prisma db pull

可以执行 prisma db push 把 schema 文件同步到了 database,并且生成了 client 代码。

prisma db push

数据迁移

  • prisma migrate dev

会根据 schema 的变化生成 sql 文件,并执行这个 sql,还会生成 client 代码。

既创建了表,又插入了初始数据,还生成了 client。

prisma migrate dev --name init