使用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
- 下载apifox测试接口
配置
- 自定义配置文件,为了方便维护配置项,故需要集中管理
- 安装
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.ts
下imports
中添加
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();
}
}
新建名为nest_vhen_blog
的数据库
根目录.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表
- 由于数据库表字段命名规范 推荐下划线命名 xxx_xxx
错误写法
前端是驼峰式写法,因此安装插件
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 { }
- 启动项目,刷新数据库就字段更新了,方便得一批
事务
事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。
假如A转账给B 10000 元,先从A的账户里扣除 10000 元,再在 B 的账户上加上 10000 元。如果扣完A的10000元后,还没来得及给B加上,银行系统异常了,最后导致A的余额减少了,B的余额却没有增加。所以就需要事务,将A的钱回滚回去,就是这么简单。
-
原子性:
事务作为一个整体被执行,包含在其中的对数据库的操作要么全部都执行,要么都不执行。 -
一致性:
指在事务开始之前和事务结束以后,数据不会被破坏,假如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
即可
github
项目地址:nest_vhen_blog