背景
最近在学习NestJs,但是不从实际需求出发,没有项目的依托,感觉总是个入门而已,只有在解决一个个项目上的问题才能进一步加深和巩固所学知识,故想将搭建一个博客后台系统的需求为出发点,巩固所学知识,同时也作为学习新知识、难题解决及注意事项的记录。
一、安装依赖
yarn add @nestjs/typeorm typeorm mysql
二、连接数据库(MySQL)
在app.modules中用TypeOrmModule实现数据库连接
// src/app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
imports: [
TypeOrmModule.forRootAsync({
useFactory: () => ({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: '',
database: 'blogs',
timezone: 'UTC',
charset: 'utf8mb4',
entities: ['./**/*.entity.js'],
synchronize: true,
logging: true,
})}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
三、创建entity
entity(实体对象)映射数据库中的表,entity中的Column与表中的字段相对应
// src/entities/article.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn, DeleteDateColumn } from 'typeorm';
@Entity('article')
export class ArticleEntity {
@PrimaryGeneratedColumn({
type: 'int',
comment: '主键id',
})
id: number;
@Column('varchar', {
nullable: false,
comment: '文章标题',
})
title: string;
@Column('varchar', {
nullable: false,
comment: '文章内容',
})
content: string;
@Column({
nullable: false,
name: 'category_id',
comment: '文章类别',
})
categoryID: string;
@Column('varchar', {
nullable: true,
comment: '文章简介',
})
intro: string;
@Column('varchar', {
nullable: true,
comment: '文章封面',
})
cover: string;
@Column('varchar', {
nullable: true,
comment: '文章标签',
})
tags: string;
@Column('enum', {
nullable: false,
default: 0,
enum: [0, 1, 2],
comment: '文章状态,0为编辑中,1为已发布,2为不可用',
})
status: number;
@CreateDateColumn({
type: 'timestamp',
name: 'created_at',
comment: '创建时间',
})
createdAt: Date;
@UpdateDateColumn({
type: 'timestamp',
name: 'updated_at',
comment: '最后更新时间',
})
updatedAt: Date;
@DeleteDateColumn({
type: 'timestamp',
name: 'delete_at',
comment: '删除',
})
deleteAt: Date;
}
// src/entities/article-category.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn, DeleteDateColumn } from 'typeorm';
@Entity('article_category')
export class ArticleCategoryEntity {
@PrimaryGeneratedColumn({
type: 'int',
comment: '主键id',
})
id: number;
@Column('varchar', {
nullable: false,
unique: true,
name: 'category_name',
comment: '文章类别名称',
})
categoryName: string;
@CreateDateColumn({
type: 'timestamp',
name: 'created_at',
comment: '创建时间',
})
createdAt: Date;
@UpdateDateColumn({
type: 'timestamp',
name: 'updated_at',
comment: '最后更新时间',
})
updatedAt: Date;
@DeleteDateColumn({
type: 'timestamp',
name: 'delete_at',
comment: '删除',
})
deleteAt: Date;
}
Tips:entity要放在/src/entities目录下,才能在dist目录下生成.entity.js格式的文件,直接加载.entity.ts文件则会报语法错误,之前试过新建libs,然后将数据库模块放到libs下,希望将数据库这部分独立出来,方便维护;不过发现最后编译只会在dist生成一个main.js文件,缺少.entity.js,查看main.js内容发现*.entity.ts编译的结果也直接放入main.js中。
四、数据库的CURD
EntityManager: 像放一个实体存储库的集合的地方,你可以管理(insert, update, delete, load等)任何实体;可以通过getManager()或Connection访问实体管理器。
Repository: 像EntityManager一样,但其操作仅限于具体实体;可以通过getRepository(Entity),Connection#getRepository访问存储库。
下面通过Repository提供的API来实现对数据库的增删改查,以及左联查询
// src/service/article/article.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { ArticleCategoryEntity } from 'src/entities/article-category.entity';
import { ArticleEntity } from 'src/entities/article.entity';
import { Repository, createQueryBuilder, getConnection, getRepository } from 'typeorm';
@Injectable()
export class ArticleService {
constructor(
@InjectRepository(ArticleCategoryEntity)
private readonly articleCategoryRepo: Repository,
@InjectRepository(ArticleEntity)
private readonly articleRepo: Repository,
) {
}
/**
* 获取文章分类
*/
getArticleCate() {
return this.articleCategoryRepo.find();
}
/**
* 创建文章分类
* @param body
*/
createArticleCate(body): Promise {
const userEntity = this.articleCategoryRepo.create(body);
return this.articleCategoryRepo.save(userEntity);
}
/**
* 删除文章分类
* @param id 文章分类ID
*/
delArticleCate(id): Promise {
return this.articleCategoryRepo.delete(id);
}
/**
* 获取文章
* @param id 文章ID
*/
getArticle(id) {
// 利用leftJoinAndMapOne方法进行左联查询
// 两个未关联的实体,通过getRawMany获取原始查询数据
if (id) {
return createQueryBuilder(ArticleEntity, 'article')
.leftJoinAndMapOne('article.cate_name', ArticleCategoryEntity, 'cate', 'cate.id = article.category_id')
.where('article.id = :id', { id })
.select(['article.*', 'cate.category_name'])
.getRawMany(); // 获得原始结果
} else {
return createQueryBuilder(ArticleEntity, 'article')
.leftJoinAndMapOne('article.cate_name', ArticleCategoryEntity, 'cate', 'cate.id = article.category_id')
.getRawMany(); // 获得原始结果
}
}
/**
* 创建文章
* @param body
*/
createArticle(body) {
const articleEntity = this.articleRepo.create(body);
return this.articleRepo.save(articleEntity);
}
/**
* 更新文章
* @param id
* @param body
*/
updateArticle(id, body) {
return this.articleRepo.update(id, body);
}
/**
* 删除文章
* @param id 文章ID
*/
delArticle(id) {
return this.articleRepo.delete(id);
}
}