前端 | 嘿,Nest.js實(shí)戰(zhàn)開(kāi)發(fā)系列之使用TypeORM操作數(shù)據(jù)庫(kù)
寫(xiě)在前面
在上一篇文章《【前端】嘿,Nest.js實(shí)戰(zhàn)開(kāi)發(fā)系列 01 ── Nest.js初體驗(yàn)》中介紹了如何上手nest.js,同時(shí)詳細(xì)介紹了如何進(jìn)行項(xiàng)目創(chuàng)建、路由訪問(wèn)和創(chuàng)建模塊,這些都是項(xiàng)目實(shí)踐的基礎(chǔ)。隨著項(xiàng)目的推進(jìn),我們就要考慮如何實(shí)現(xiàn)數(shù)據(jù)庫(kù)的連接和使用,這樣可以用來(lái)實(shí)現(xiàn)前后端數(shù)據(jù)交互的數(shù)據(jù)存儲(chǔ)。
環(huán)境準(zhǔn)備:
- Mysql 5.7
- TypeORM 0.2.34
TypeORM集成
在Nest.js中可以使用任意數(shù)據(jù)庫(kù),且內(nèi)部集成提供了TypeORM 和 Sequelize ,開(kāi)箱即用@nestjs/typeorm和@nestjs/sequelize包。Nest 使用TypeORM,因?yàn)樗强捎糜?TypeScript 的最成熟的對(duì)象關(guān)系映射器 (ORM)。由于它是用 TypeScript 編寫(xiě)的,因此它可以很好地與 Nest 框架集成。
要開(kāi)始使用它,我們首先安裝所需的依賴項(xiàng)。在命令行中輸入:
- $ npm install --save @nestjs/typeorm typeorm mysql2
安裝完畢后,可以將TypeOrmModule導(dǎo)入根目錄AppModule。
app.module.ts
- 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: 'test',
- entities: [],
- synchronize: true,
- }),
- ],
- })
- export class AppModule {}
切記:synchronize: true不應(yīng)在生產(chǎn)中使用設(shè)置- 否則您可能會(huì)丟失生產(chǎn)數(shù)據(jù)。
該forRoot()方法支持TypeORM包中的createConnection()函數(shù)公開(kāi)的所有配置屬性。此外,還有幾個(gè)額外的配置屬性如下所述。
當(dāng)然,也可以在根目錄下創(chuàng)建ormconfig.json文件,在文件中進(jìn)行數(shù)據(jù)庫(kù)信息的設(shè)置。
ormconfig.json
- {
- "type": "mysql",
- "host": "localhost",
- "port": 3306,
- "username": "root",
- "password": "root",
- "database": "test",
- "entities": ["dist/**/*.entity{.ts,.js}"],
- "synchronize": true
- }
然后,我們可以在forRoot()沒(méi)有任何選項(xiàng)的情況下調(diào)用:
app.module.ts
- import { Module } from '@nestjs/common';
- import { TypeOrmModule } from '@nestjs/typeorm';
- @Module({
- imports: [TypeOrmModule.forRoot()],
- })
- export class AppModule {}
注意:靜態(tài) glob 路徑(例如,dist/**/*.entity{ .ts,.js})將無(wú)法與webpack正常工作。
其實(shí),ormconfig.json文件是由typeorm庫(kù)加載的,因此不會(huì)應(yīng)用額外的屬性設(shè)置。TypeORM 提供了getConnectionOptions從ormconfig文件或環(huán)境變量中讀取連接選項(xiàng)的函數(shù)。
完成以上操作后,TypeORMConnection和EntityManager對(duì)象將可用于在整個(gè)項(xiàng)目中注入(無(wú)需導(dǎo)入任何模塊)。
app.module.ts
- import { TypeOrmModule } from '@nestjs/typeorm';
- import { Connection } from "typeorm";
- import { UsersModule } from './users/users.module';
- @Module({
- imports: [
- TypeOrmModule.forRoot(),
- UsersModule
- ],
- controllers: [AppController],
- providers: [AppService],
- })
- export class AppModule {
- constructor(private connection: Connection){}
- }
存儲(chǔ)庫(kù)模式
TypeORM支持存儲(chǔ)庫(kù)設(shè)計(jì)模式,因此每個(gè)實(shí)體都有自己的存儲(chǔ)庫(kù)。這些存儲(chǔ)庫(kù)可以從數(shù)據(jù)庫(kù)連接中獲得。
下面創(chuàng)建一個(gè)用戶實(shí)體,users.entity.ts在users目錄下,
users.entity.ts
- import {Entity, PrimaryGeneratedColumn, Column, BeforeInsert, JoinTable, ManyToMany, OneToMany} from 'typeorm';
- import { IsEmail } from 'class-validator';
- import * as argon2 from 'argon2';
- import { ArticleEntity } from '../article/article.entity';
- @Entity('user')
- export class UserEntity {
- @PrimaryGeneratedColumn()
- id: number;
- @Column()
- username: string;
- @Column()
- @IsEmail()
- email: string;
- @Column({default: ''})
- bio: string;
- @Column({default: ''})
- image: string;
- @Column()
- password: string;
- @BeforeInsert()
- async hashPassword() {
- this.password = await argon2.hash(this.password);
- }
- @ManyToMany(type => ArticleEntity)
- @JoinTable()
- favorites: ArticleEntity[];
- @OneToMany(type => ArticleEntity, article => article.author)
- articles: ArticleEntity[];
- }
現(xiàn)在開(kāi)始使用Users實(shí)體,只需要在users.module.ts文件中通過(guò)entities模塊forFeature()方法選項(xiàng)中的數(shù)組來(lái)進(jìn)行導(dǎo)入。
users.module.ts
- import { Module } from '@nestjs/common';
- import { UsersController } from './users.controller';
- import { UsersService } from './users.service';
- import {UsersEntity} from "./users.entity";
- import { TypeOrmModule } from '@nestjs/typeorm';
- @Module({
- imports: [TypeOrmModule.forFeature([UsersEntity])],
- providers: [UsersService],
- controllers: [
- UsersController
- ],
- exports: [UsersService]
- })
- export class UsersModule {}
此模塊使用forFeature()來(lái)定義在當(dāng)前范圍內(nèi)注冊(cè)了那些存儲(chǔ)庫(kù),此時(shí)將可以使用裝飾器將UsersRepository注入到`UsersService @InjectRepository().
users.service.ts
- import {Get, Post, Body, Put, Delete, Query, Param, Controller} from '@nestjs/common';
- import { UsersService } from './users.service';
- @Controller('user')
- export class UsersController {
- constructor(private readonly usersService: UsersService){}
- // 查找指定用戶
- @Get("find/:id")
- async findById(@Query("id") id: number){
- return this.usersService.findById(id);
- }
- }
數(shù)據(jù)表間的關(guān)系
關(guān)系是在兩個(gè)或多個(gè)表之間建立的關(guān)聯(lián),是基于每張表的公共字段,通常是主鍵和外鍵。
數(shù)據(jù)表之間有三種關(guān)系:
因此,在實(shí)體中定義關(guān)系可以使用相應(yīng)的裝飾器。
測(cè)試代碼
users.controller.ts
- import {Get, Post, Body, Put, Delete, Query, Param, Controller} from '@nestjs/common';
- import { UsersService } from './users.service';
- @Controller('user')
- export class UsersController {
- constructor(private readonly usersService: UsersService){}
- // 查找指定用戶
- @Get("find/:id")
- async findById(@Query("id") id: number){
- return this.usersService.findById(id);
- }
當(dāng)我們運(yùn)行代碼時(shí),數(shù)據(jù)庫(kù)自動(dòng)生成了users表。
而當(dāng)我們?cè)趐ostman向服務(wù)器請(qǐng)求指定id的用戶信息時(shí),請(qǐng)求結(jié)果如下所示:
后臺(tái)顯示結(jié)果如下:
我們看到以上代碼測(cè)試是正確的。
小結(jié)
本篇文章介紹了mysql和typeorm之間的關(guān)系,typeorm的配置,nest是如何通過(guò)typeorm連接數(shù)據(jù)庫(kù),以及簡(jiǎn)單的用戶表數(shù)據(jù)查詢。
其實(shí)筆者之前也用過(guò) Sequelize ,現(xiàn)在想要嘗試typeorm和nest的搭配,所以文章寫(xiě)的有些亂,建議諸位多加查看官方文檔:《Nest官方文檔》和《Typeorm官方文檔》