NestJS中集成TypeORM進(jìn)行數(shù)據(jù)庫(kù)操作
本文深入探討了如何在NestJS中集成TypeORM進(jìn)行數(shù)據(jù)庫(kù)操作,包括TypeORM的配置和集成、實(shí)體設(shè)計(jì)和關(guān)系映射、Repository模式的應(yīng)用、事務(wù)處理方案、數(shù)據(jù)庫(kù)遷移管理、性能優(yōu)化策略。
TypeORM 集成配置
1. 安裝依賴
首先安裝必要的依賴包:
npm install @nestjs/typeorm typeorm pg # 如果使用 MySQL # npm install @nestjs/typeorm typeorm mysql2
2. 數(shù)據(jù)庫(kù)配置
// src/config/database.config.ts import { TypeOrmModuleOptions } from '@nestjs/typeorm'; export const databaseConfig: TypeOrmModuleOptions = { type: 'postgres', host: process.env.DB_HOST || 'localhost', port: parseInt(process.env.DB_PORT) || 5432, username: process.env.DB_USERNAME || 'postgres', password: process.env.DB_PASSWORD || 'postgres', database: process.env.DB_DATABASE || 'nestjs_db', entities: ['dist/**/*.entity{.ts,.js}'], synchronize: process.env.NODE_ENV !== 'production', logging: process.env.NODE_ENV !== 'production', ssl: process.env.DB_SSL === 'true', }; // src/app.module.ts import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { databaseConfig } from './config/database.config'; @Module({ imports: [ TypeOrmModule.forRoot(databaseConfig), // 其他模塊 ], }) export class AppModule {}
實(shí)體設(shè)計(jì)與關(guān)系映射
1. 基礎(chǔ)實(shí)體設(shè)計(jì)
// src/entities/base.entity.ts import { PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn, DeleteDateColumn } from 'typeorm'; export abstract class BaseEntity { @PrimaryGeneratedColumn('uuid') id: string; @CreateDateColumn() createdAt: Date; @UpdateDateColumn() updatedAt: Date; @DeleteDateColumn() deletedAt: Date; } // src/users/entities/user.entity.ts import { Entity, Column, OneToMany } from 'typeorm'; import { BaseEntity } from '../entities/base.entity'; import { Post } from './post.entity'; @Entity('users') export class User extends BaseEntity { @Column({ length: 100 }) name: string; @Column({ unique: true }) email: string; @Column({ select: false }) password: string; @OneToMany(() => Post, post => post.author) posts: Post[]; } // src/posts/entities/post.entity.ts import { Entity, Column, ManyToOne, JoinColumn } from 'typeorm'; import { BaseEntity } from '../entities/base.entity'; import { User } from './user.entity'; @Entity('posts') export class Post extends BaseEntity { @Column() title: string; @Column('text') content: string; @Column({ default: false }) published: boolean; @ManyToOne(() => User, user => user.posts) @JoinColumn({ name: 'author_id' }) author: User; }
2. 關(guān)系映射策略
// src/users/entities/profile.entity.ts import { Entity, Column, OneToOne, JoinColumn } from 'typeorm'; import { BaseEntity } from '../entities/base.entity'; import { User } from './user.entity'; @Entity('profiles') export class Profile extends BaseEntity { @Column() avatar: string; @Column('text') bio: string; @OneToOne(() => User) @JoinColumn({ name: 'user_id' }) user: User; } // src/posts/entities/tag.entity.ts import { Entity, Column, ManyToMany } from 'typeorm'; import { BaseEntity } from '../entities/base.entity'; import { Post } from './post.entity'; @Entity('tags') export class Tag extends BaseEntity { @Column({ unique: true }) name: string; @ManyToMany(() => Post, post => post.tags) posts: Post[]; } // 更新 Post 實(shí)體,添加標(biāo)簽關(guān)系 @Entity('posts') export class Post extends BaseEntity { // ... 其他字段 @ManyToMany(() => Tag, tag => tag.posts) @JoinTable({ name: 'posts_tags', joinColumn: { name: 'post_id' }, inverseJoinColumn: { name: 'tag_id' } }) tags: Tag[]; }
數(shù)據(jù)庫(kù)操作實(shí)現(xiàn)
1. Repository 模式
// src/users/users.service.ts import { Injectable, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { User } from './entities/user.entity'; import { CreateUserDto, UpdateUserDto } from './dto'; @Injectable() export class UsersService { constructor( @InjectRepository(User) private usersRepository: Repository<User> ) {} async create(createUserDto: CreateUserDto): Promise<User> { const user = this.usersRepository.create(createUserDto); return await this.usersRepository.save(user); } async findAll(): Promise<User[]> { return await this.usersRepository.find({ relations: ['posts', 'profile'] }); } async findOne(id: string): Promise<User> { const user = await this.usersRepository.findOne({ where: { id }, relations: ['posts', 'profile'] }); if (!user) { throw new NotFoundException(`User with ID ${id} not found`); } return user; } async update(id: string, updateUserDto: UpdateUserDto): Promise<User> { const user = await this.findOne(id); Object.assign(user, updateUserDto); return await this.usersRepository.save(user); } async remove(id: string): Promise<void> { const user = await this.findOne(id); await this.usersRepository.softRemove(user); } }
2. 查詢構(gòu)建器
// src/posts/posts.service.ts import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { Post } from './entities/post.entity'; @Injectable() export class PostsService { constructor( @InjectRepository(Post) private postsRepository: Repository<Post> ) {} async findPublishedPosts() { return await this.postsRepository .createQueryBuilder('post') .leftJoinAndSelect('post.author', 'author') .leftJoinAndSelect('post.tags', 'tags') .where('post.published = :published', { published: true }) .orderBy('post.createdAt', 'DESC') .getMany(); } async searchPosts(query: string) { return await this.postsRepository .createQueryBuilder('post') .leftJoinAndSelect('post.author', 'author') .where('post.title ILIKE :query OR post.content ILIKE :query', { query: `%${query}%` }) .orderBy('post.createdAt', 'DESC') .getMany(); } async getPostStats() { return await this.postsRepository .createQueryBuilder('post') .select('author.name', 'authorName') .addSelect('COUNT(*)', 'postCount') .leftJoin('post.author', 'author') .groupBy('author.name') .getRawMany(); } }
事務(wù)處理
1. 事務(wù)裝飾器
// src/common/decorators/transaction.decorator.ts import { createParamDecorator, ExecutionContext } from '@nestjs/common'; import { getManager } from 'typeorm'; export const Transaction = createParamDecorator( async (data: unknown, ctx: ExecutionContext) => { const queryRunner = getManager().connection.createQueryRunner(); await queryRunner.connect(); await queryRunner.startTransaction(); return queryRunner; } ); // 使用示例 @Post('transfer') async transfer( @Transaction() queryRunner, @Body() transferDto: TransferDto ) { try { // 執(zhí)行轉(zhuǎn)賬操作 await queryRunner.manager.update(Account, transferDto.fromId, { balance: () => `balance - ${transferDto.amount}` } ); await queryRunner.manager.update(Account, transferDto.toId, { balance: () => `balance + ${transferDto.amount}` } ); await queryRunner.commitTransaction(); } catch (err) { await queryRunner.rollbackTransaction(); throw err; } finally { await queryRunner.release(); } }
2. 事務(wù)管理器
// src/common/services/transaction.service.ts import { Injectable } from '@nestjs/common'; import { Connection, QueryRunner } from 'typeorm'; @Injectable() export class TransactionService { constructor(private connection: Connection) {} async executeInTransaction<T>( callback: (queryRunner: QueryRunner) => Promise<T> ): Promise<T> { const queryRunner = this.connection.createQueryRunner(); await queryRunner.connect(); await queryRunner.startTransaction(); try { const result = await callback(queryRunner); await queryRunner.commitTransaction(); return result; } catch (err) { await queryRunner.rollbackTransaction(); throw err; } finally { await queryRunner.release(); } } } // 使用示例 @Injectable() export class PaymentService { constructor( private transactionService: TransactionService, private ordersService: OrdersService ) {} async processPayment(paymentDto: PaymentDto) { return await this.transactionService.executeInTransaction(async queryRunner => { const order = await this.ordersService.findOne(paymentDto.orderId); // 更新訂單狀態(tài) await queryRunner.manager.update(Order, order.id, { status: 'paid' }); // 創(chuàng)建支付記錄 const payment = queryRunner.manager.create(Payment, { order, amount: paymentDto.amount }); await queryRunner.manager.save(payment); return payment; }); } }
數(shù)據(jù)庫(kù)遷移
1. 遷移配置
// ormconfig.js module.exports = { type: 'postgres', host: process.env.DB_HOST, port: parseInt(process.env.DB_PORT), username: process.env.DB_USERNAME, password: process.env.DB_PASSWORD, database: process.env.DB_DATABASE, entities: ['dist/**/*.entity{.ts,.js}'], migrations: ['dist/migrations/*{.ts,.js}'], cli: { migrationsDir: 'src/migrations' } }; // package.json { "scripts": { "typeorm": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js", "migration:create": "npm run typeorm migration:create -- -n", "migration:generate": "npm run typeorm migration:generate -- -n", "migration:run": "npm run typeorm migration:run", "migration:revert": "npm run typeorm migration:revert" } }
2. 遷移示例
// src/migrations/1642340914321-CreateUsersTable.ts import { MigrationInterface, QueryRunner, Table } from 'typeorm'; export class CreateUsersTable1642340914321 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise<void> { await queryRunner.createTable( new Table({ name: 'users', columns: [ { name: 'id', type: 'uuid', isPrimary: true, generationStrategy: 'uuid', default: 'uuid_generate_v4()' }, { name: 'name', type: 'varchar', length: '100' }, { name: 'email', type: 'varchar', isUnique: true }, { name: 'password', type: 'varchar' }, { name: 'created_at', type: 'timestamp', default: 'now()' }, { name: 'updated_at', type: 'timestamp', default: 'now()' }, { name: 'deleted_at', type: 'timestamp', isNullable: true } ] }) ); } public async down(queryRunner: QueryRunner): Promise<void> { await queryRunner.dropTable('users'); } }
性能優(yōu)化
1. 查詢優(yōu)化
// src/posts/posts.service.ts @Injectable() export class PostsService { constructor( @InjectRepository(Post) private postsRepository: Repository<Post> ) {} // 使用分頁(yè)和緩存 async findAll(page = 1, limit = 10) { const [posts, total] = await this.postsRepository.findAndCount({ relations: ['author', 'tags'], skip: (page - 1) * limit, take: limit, cache: { id: `posts_page_${page}`, milliseconds: 60000 // 1分鐘緩存 } }); return { data: posts, meta: { total, page, lastPage: Math.ceil(total / limit) } }; } // 使用子查詢優(yōu)化 async findPopularPosts() { return await this.postsRepository .createQueryBuilder('post') .leftJoinAndSelect('post.author', 'author') .addSelect(subQuery => { return subQuery .select('COUNT(*)', 'commentCount') .from('comments', 'comment') .where('comment.postId = post.id'); }, 'commentCount') .orderBy('commentCount', 'DESC') .limit(10) .getMany(); } }
2. 索引優(yōu)化
// src/posts/entities/post.entity.ts @Entity('posts') @Index(['title', 'content']) // 復(fù)合索引 export class Post extends BaseEntity { @Column() @Index() // 單列索引 title: string; @Column('text') content: string; @Column() @Index() authorId: string; // ... 其他字段 }
寫(xiě)在最后
本文詳細(xì)介紹了 NestJS 中的數(shù)據(jù)庫(kù)操作實(shí)踐:
- TypeORM 的配置和集成
- 實(shí)體設(shè)計(jì)和關(guān)系映射
- Repository 模式的應(yīng)用
- 事務(wù)處理方案
- 數(shù)據(jù)庫(kù)遷移管理
- 性能優(yōu)化策略
到此這篇關(guān)于NestJS中集成TypeORM進(jìn)行數(shù)據(jù)庫(kù)操作的文章就介紹到這了,更多相關(guān)NestJS集成TypeORM數(shù)據(jù)庫(kù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
關(guān)于npm?i幾種常見(jiàn)命令的區(qū)別詳解
npm(Node.js Package Manager)是一個(gè)Node.js的包管理工具,用來(lái)解決Node.js代碼部署問(wèn)題,下面這篇文章主要給大家介紹了關(guān)于npm?i幾種常見(jiàn)命令的那點(diǎn)事,需要的朋友可以參考下2023-03-03node操作mysql數(shù)據(jù)庫(kù)實(shí)例詳解
這篇文章主要介紹了node操作mysql數(shù)據(jù)庫(kù),結(jié)合實(shí)例形式較為詳細(xì)的分析了node操作數(shù)據(jù)庫(kù)的連接、增刪改查、事務(wù)處理及錯(cuò)誤處理相關(guān)操作技巧,需要的朋友可以參考下2017-03-03Windows系統(tǒng)下Node.js安裝以及環(huán)境配置的完美教程
相信對(duì)于很多關(guān)注javascript發(fā)展的同學(xué)來(lái)說(shuō),nodejs已經(jīng)不是一個(gè)陌生的詞眼,下面這篇文章主要給大家介紹了關(guān)于Windows系統(tǒng)下Node.js安裝以及環(huán)境配置的完美教程,需要的朋友可以參考下2022-06-06總結(jié)幾道關(guān)于Node.js的面試問(wèn)題
這篇文章主要總結(jié)了幾道關(guān)于Node.js的面試問(wèn)題,通過(guò)這些問(wèn)題就來(lái)判斷一個(gè)人的Node.js水平是不太嚴(yán)謹(jǐn)?shù)?,但是它能讓你?duì)面試者在Node.js上的經(jīng)驗(yàn)如何有個(gè)大概的了解。有需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-01-01