Nest.js从0到1搭建博客系统使用TypeORM连接MySQL数据库(4)

2023年 12月 26日 90.6k 0

使用TypeORM连接MySQL数据库

TypeORM 是一个ORM框架,它可以运行在 NodeJS、Browser、Cordova、PhoneGap、Ionic、React Native、Expo 和 Electron 平台上,可以与 TypeScript 和 JavaScript (ES5,ES6,ES7,ES8)一起使用。 它的目标是始终支持最新的 JavaScript 特性并提供额外的特性以帮助你开发任何使用数据库的(不管是只有几张表的小型应用还是拥有多数据库的大型企业应用)应用程序。

TypeORM作为TypeScript中最成熟的对象关系映射器,可以很好的与Nest框架集成使用。

Repository 方法

  • save - 保存给定实体(Entity)或实体数组
  • remove - 删除给定的实体或实体数组
  • insert - 插入新实体或实体数组
  • update - 通过给定的更新选项或实体 ID 部分更新实体
  • delete -根据实体 id, ids 或给定的条件删除实体
  • count - 符合指定条件的实体数量。对分页很有用
  • increment - 增加符合条件的实体某些列值
  • decrement - 减少符合条件的实体某些列值
  • find - 查找指定条件的实体
  • findAndCount - 查找指定条件的实体
  • findByIds - 按 ID 查找多个实体
  • findOne - 查找匹配某些 ID 或查找选项的第一个实体
  • findOneOrFail - - findOneOrFail - 查找匹配某些 ID 或查找选项的第一个实体
  • query - 执行原始 SQL 查询
  • clear - 清除给定表中的所有数据(truncates/drops)

QueryBuilder 方法

  • createQueryBuilder:创建一个新的查询构建器
  • select:选择特定的列
  • where:添加查询条件
  • orderBy:添加排序条件
  • getMany:执行查询并返回多个结果
  • getOne:执行查询并返回单个结果

安装依赖

npm install --save @nestjs/typeorm typeorm mysql2
  • 推荐vscode安装数据库插件 Database Client

image.png

  • 下载apifox测试接口

image.png

配置

  • 自定义配置文件,为了方便维护配置项,故需要集中管理
  • 安装
npm i -S @nestjs/config js-yaml cross-env
npm i -D @types/js-yaml

根目录新建 .config/.dev.yml

# 项目配置
APP:
  prefix: '/api'
  host: 'localhost'
  port: 3000
  • package.json更改 "start:dev": "cross-env NODE_ENV=dev nest start --watch"
  • 新建src/utils/ymlConfig.ts
import { readFileSync } from 'fs';
import * as yaml from 'js-yaml';
import { join } from 'path';

const env = process.env.NODE_ENV
export const getYmlConfig = (key?: string) => {
    const ymlInfo = yaml.load(
        readFileSync(join(process.cwd(), `.config/.${env}.yml`), 'utf-8'),
    ) as Record<string, any>;
    if (key) {
        return ymlInfo[key];
    }
    return ymlInfo;
};
  • app.module.tsimports中添加
import { getYmlConfig } from '@/utils/ymlConfig';
@Module({
  imports: [ConfigModule.forRoot({
    isGlobal: true,
    ignoreEnvFile: true,
    load: [getYmlConfig],
  }), UserModule]
  • xx控制器中使用
/*
 * @Author: vhen
 * @Date: 2023-12-20 19:28:09
 * @LastEditTime: 2023-12-25 17:28:59
 * @Description: 现在的努力是为了小时候吹过的牛逼!
 * @FilePath: nest-vhen-blogsrcapp.controller.ts
 */
import { Controller, Get } from '@nestjs/common';
import { ConfigService } from '@nestjs/config'
import { AppService } from './app.service';
import { AllowAnon } from '@/common/decorator/allow-anon.decorator'

@Controller()
export class AppController {
  constructor(private readonly appService: AppService, private readonly configService: ConfigService,) { }

  @Get()
  @AllowAnon()
  getHello(): string {
    return this.configService.get('APP')
    // return this.appService.getHello();
  }
}

image.png

新建名为nest_vhen_blog的数据库

image.png

  • 根目录.config/.dev.yml添加mysql配置
# 数据库
MYSQL:
  type: mysql # 数据库链接类型
  host: localhost
  port: 3306
  username: "root" # 数据库链接用户名
  password: "root" # 数据库链接密码
  database: "nest_vhen_blog" # 数据库名
  logging: true # 数据库打印日志
  synchronize: true # 是否开启同步数据表功能
  autoLoadEntities: true # 是否自动加载实体
  • app.module.ts 导入typeorm以及mysql配置
/*
 * @Author: vhen
 * @Date: 2023-12-20 19:28:09
 * @LastEditTime: 2023-12-25 17:48:11
 * @Description: 现在的努力是为了小时候吹过的牛逼!
 * @FilePath: nest-vhen-blogsrcapp.module.ts
 * 
 */
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm';
import { DataSource } from 'typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './modules/user/user.module';
import { getYmlConfig } from '@/utils/ymlConfig';

@Module({
  imports: [
    // 配置模块
    ConfigModule.forRoot({
      isGlobal: true,
      ignoreEnvFile: true,
      load: [getYmlConfig],
    }),
    // 数据库
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (config: ConfigService) => {
        return {
          type: 'mysql',
          // 可能不再支持这种方式,entities 将改成接收 实体类的引用
          // entities: [`${__dirname}/**/*.entity{.ts,.js}`],
          autoLoadEntities: true,
          keepConnectionAlive: true,
          ...config.get('MYSQL'),
          // cache: {
          //   type: 'ioredis',
          //   ...config.get('redis'),
          //   alwaysEnabled: true,
          //   duration: 3 * 1000, // 缓存3s
          // },
        } as TypeOrmModuleOptions
      },
      async dataSourceFactory(options) {
        if (!options) {
          throw new Error('Invalid options passed');
        }
        return new DataSource(options);
      }
    }),
    UserModule, DemoModule,],
  controllers: [AppController],
  providers: [AppService],

})
export class AppModule { }

  • user.entity.ts注册实体
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
@Entity({ name: "user" })
export class User {
    @PrimaryGeneratedColumn({
        comment: '用户主键ID'
    })
    id: number;
    @Column({ type: 'varchar', comment: '用户名' })
    username: string;
    @Column({ type: 'varchar', comment: '昵称' })
    nickname: string;
    @Column({ length: 11, comment: '手机号' })
    phone: string;
    @Column({ comment: "用户密码" })
    password: string;
    @Column({ type: 'text', comment: '备注' })
    remark: string;
    @Column({ type: 'time', comment: '创建时间' })
    createTime: string;
    @Column({ type: 'time', comment: '更新时间' })
    updateTime: string;
}

  • user.module.ts导入模块
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserService } from './user.service';
import { UserController } from './user.controller';
import { User as UserEntity } from './entities/user.entity'
@Module({
  imports: [
    TypeOrmModule.forFeature([UserEntity]),
  ],
  controllers: [UserController],
  providers: [UserService],
  exports: [UserService],
})
export class UserModule { }
  • user.service.ts注入实体
import { Injectable } from '@nestjs/common';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { User as UserEntity } from './entities/user.entity';
@Injectable()
export class UserService {
  // 通过构造函数注入
  constructor(@InjectRepository(UserEntity) private readonly userRepo: Repository<UserEntity>) { }
}
  • user.controller.ts
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiParam, ApiQuery, ApiBody, ApiResponse, ApiHeader } from "@nestjs/swagger"
import { UserService } from './user.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';

@Controller({
  path: "user",
  version: '1'
})
@ApiTags("用户管理")
// username: string
// @Controller('user')
@ApiHeader({
  name: 'authoriation',
  required: true,
  description: '本次请求请带上token',
})
export class UserController {
  constructor(private readonly userService: UserService) { }

  @Post()
  @ApiOperation({ summary: "创建用户", description: "创建用户接口" })
  create(@Body() createUserDto: CreateUserDto) {
    return this.userService.create(createUserDto);
  }
}
  • 自动创建user表

image.png

  • 由于数据库表字段命名规范 推荐下划线命名 xxx_xxx

错误写法

image.png
前端是驼峰式写法,因此安装插件

npm i -S typeorm-naming-strategies
  • app.module.ts
import { Module, ValidationPipe } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm';
import { addTransactionalDataSource } from 'typeorm-transactional';
import { SnakeNamingStrategy } from 'typeorm-naming-strategies';
import { DataSource } from 'typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './modules/user/user.module';
import { getYmlConfig } from '@/utils/ymlConfig';

@Module({
  imports: [
    // 配置模块
    ConfigModule.forRoot({
      isGlobal: true,
      ignoreEnvFile: true,
      load: [getYmlConfig],
    }),
    // 数据库
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (config: ConfigService) => {
        return {
          type: 'mysql',
          // 可能不再支持这种方式,entities 将改成接收 实体类的引用
          // entities: [`${__dirname}/**/*.entity{.ts,.js}`],
          autoLoadEntities: true,
          keepConnectionAlive: true,
          ...config.get('MYSQL'),
          namingStrategy: new SnakeNamingStrategy(),
          // cache: {
          //   type: 'ioredis',
          //   ...config.get('redis'),
          //   alwaysEnabled: true,
          //   duration: 3 * 1000, // 缓存3s
          // },
        } as TypeOrmModuleOptions
      },
      async dataSourceFactory(options) {
        if (!options) {
          throw new Error('Invalid options passed');
        }
        return addTransactionalDataSource(new DataSource(options));
      }
    }),
    UserModule, DemoModule,],
  controllers: [AppController],
  providers: [AppService],

})
export class AppModule { }
  • 启动项目,刷新数据库就字段更新了,方便得一批

image.png

image.png

事务

事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。

假如A转账给B 10000 元,先从A的账户里扣除 10000 元,再在 B 的账户上加上 10000 元。如果扣完A的10000元后,还没来得及给B加上,银行系统异常了,最后导致A的余额减少了,B的余额却没有增加。所以就需要事务,将A的钱回滚回去,就是这么简单。

image.png

  • 原子性: 事务作为一个整体被执行,包含在其中的对数据库的操作要么全部都执行,要么都不执行。

  • 一致性: 指在事务开始之前和事务结束以后,数据不会被破坏,假如A账户给B账户转1000块钱,不管成功与否,A和B的总金额是不变的。

  • 隔离性: 多个事务并发访问时,事务之间是相互隔离的,一个事务不应该被其他事务干扰,多个并发事务之间要相互隔离。。

  • 持久性: 表示事务完成提交后,该事务对数据库所作的操作更改,将持久地保存在数据库之中。

安装

npm i -S typeorm-transactional
  • main.ts
import { NestFactory } from '@nestjs/core';
import { initializeTransactionalContext } from 'typeorm-transactional';
import { AppModule } from '@/app.module';

async function bootstrap() {
  // 开启事务
  initializeTransactionalContext();
  const app = await NestFactory.create(AppModule);
  const config = app.get(ConfigService)
  await app.listen(config.get('APP').port);
}
bootstrap();
  • app.module.ts
/*
 * @Author: vhen
 * @Date: 2023-12-20 19:28:09
 * @LastEditTime: 2023-12-25 20:34:55
 * @Description: 现在的努力是为了小时候吹过的牛逼!
 * @FilePath: nest-vhen-blogsrcapp.module.ts
 * 
 */
import { Module, ValidationPipe } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm';
import { addTransactionalDataSource } from 'typeorm-transactional';
import { DataSource } from 'typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './modules/user/user.module';
import { getYmlConfig } from '@/utils/ymlConfig';

@Module({
  imports: [
    // 配置模块
    ConfigModule.forRoot({
      isGlobal: true,
      ignoreEnvFile: true,
      load: [getYmlConfig],
    }),
    // 数据库
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (config: ConfigService) => {
        return {
          type: 'mysql',
          // 可能不再支持这种方式,entities 将改成接收 实体类的引用
          // entities: [`${__dirname}/**/*.entity{.ts,.js}`],
          autoLoadEntities: true,
          keepConnectionAlive: true,
          ...config.get('MYSQL'),
          // cache: {
          //   type: 'ioredis',
          //   ...config.get('redis'),
          //   alwaysEnabled: true,
          //   duration: 3 * 1000, // 缓存3s
          // },
        } as TypeOrmModuleOptions
      },
      async dataSourceFactory(options) {
        if (!options) {
          throw new Error('Invalid options passed');
        }
        return addTransactionalDataSource(new DataSource(options));
      }
    }),
    UserModule, DemoModule,],
  controllers: [AppController],
  providers: [AppService],

})
export class AppModule { }
  • 在方法上添加注解Transactional即可
    image.png

github

项目地址:nest_vhen_blog

相关文章

Oracle如何使用授予和撤销权限的语法和示例
Awesome Project: 探索 MatrixOrigin 云原生分布式数据库
下载丨66页PDF,云和恩墨技术通讯(2024年7月刊)
社区版oceanbase安装
Oracle 导出CSV工具-sqluldr2
ETL数据集成丨快速将MySQL数据迁移至Doris数据库

发布评论