ORM初识

  • ORMObject Relational Mapping 的缩写,译为“对象关系映射”,它解决了对象和关系型数据库之间的数据交互问题。使用面向对象编程时,数据很多时候都存储在对象里面,具体来说是存储在对象的各个属性(也称成员变量)中。例如有一个 User 类,它的 idusernamepasswordemail 属性都可以用来记录用户信息。当我们需要把对象中的数据存储到数据库时,按照传统思路,就得手动编写 SQL 语句,将对象的属性值提取到 SQL 语句中,然后再调用相关方法执行 SQL 语句。

  • 而有了 ORM 技术以后,只要提前配置好对象和数据库之间的映射关系,ORM 就可以自动生成 SQL 语句,并将对象中的数据自动存储到数据库中,整个过程不需要人工干预。在 Java 中,ORM 一般使用 XML 或者注解来配置对象和数据库之间的映射关系。而nestjs官方推荐使用的orm工具为typeOrm

传统的sql语句

SELECT id,name,age FROM girls where  id=1
// 代码
res = db.execSql(sql)
name=res[0]['NAME']

orm代码

// 就像操作对象一样
girl= Girl.get(1) // 获取第一条数据
name=girl.name // 获取对应数据的属性

使用TypeOrm

  1. 安装
npm install --save @nestjs/typeorm typeorm mysql2
  1. 引入TypeORM
/* ------------- /src/app.module.ts --------------- */
import { TypeOrmModule } from '@nestjs/typeorm';

@Module({
imports:[ TypeOrmModule.forRoot({
type:'mysql', // 数据库类型
host:'localhost', // 数据库的连接地址host
port:3306, // 数据库的端口 3306
username:'root', // 连接账号
password:'root123', // 连接密码
database:'test_db', // 连接的表名
retryDelay:500, // 重试连接数据库间隔
retryAttempts:10, // 允许重连次数
})],
controllers: [...],
providers: [...],

})

image

看到全绿输出则证明连接数据库成功

数据库操作-TypeORM的实体操作

  • ORM中的实体其实就是把数据库映射成对象的那个类。这个类可以模拟数据库表,定义其中的字段。因为映射的过程ORM已经为我们作好了,所以我们只需要定义实体类,当实体类定义好以后,就可操作数据库了。这里所谓的实体,就是Object 。我们要想在nestjs中实现操作数据库,光连接数据库是不足够的,我们要使用typeorm去创建一个实体,去对应数据库中对应的表,建立连接关系,这样我们才可以操作数据库

创建实体

  • 我们在想要操作的模块中新建一个文件夹entities,然后在里边新建boys.entity.ts编写实体。
// 编写实体,用于一一对应数据库中的数据映射关系
import { Entity, Column, PrimaryColumn,CreateDateColumn,Generated } from 'typeorm';

// 定义一个实体类
/*
当然我们也可以自定义对应数据映射到数据库中的存储属性
我们可以设置存储的类型,长度等..
*/
@Entity()
export class Boys {
// 定义Boys的id (一般设置为自增)
@PrimaryColumn({'uuid'})// 设置自动生成不重复的数据
id: number;

// 定义Boys的名字
/*
设置数据库中的存储形式
*/
@Column({type:'varchar',length:255})
name: string;

// 定义Boys的年纪
@Column({type:'int'})
age: number;

// 自动生成时间(自动生成时间戳)
@CreateDateColumn({type:'timestamp'})
entryTime:Date;

// 生成唯一标识 uuid
@Generated('uuid')
uuid:string
}
  • 编写好类实体之后,需要在module里进行引入,打开boys.module.ts文件,先用import引入TypeOrmModule
import { Module } from '@nestjs/common';
import { BoysController } from './boys.controller';
import { BoysService } from './boys.service';
import { TypeOrmModule } from '@nestjs/typeorm'; // 引入 TypeOrmModule 操作实体
import { Boys } from './entities/boys.entities'; // 引入我们刚刚新建的实体

@Module({
imports: [TypeOrmModule.forFeature([Boys])], // 使用方法操作实体
controllers: [BoysController],
providers: [BoysService],
})
export class BoysModule {}
  • 随后我们打开数据库查看是否新建了一张表boys,若是存在说明我们成功通过实体让整个nest项目连接到数据库了!

image

下面补充一下数据库中的类型以及typeOrm中常用的装饰符模块

  1. 下面给出所有的mysql的数据类型
int, tinyint, smallint, mediumint, bigint, float, double, dec, decimal, 
numeric, date, datetime, timestamp, time, year, char, varchar, nvarchar,
text, tinytext, mediumtext, blob, longtext, tinyblob, mediumblob, longblob,
enum, json, binary, geometry, point, linestring, polygon, multipoint,
multilinestring, multipolygon, geometrycollection
  1. 自动生成时间 在开发中经常需要在保存数据时同时记录时间,这时候可以使用@CreateDateColumn( )装饰器来制作。我们先进行引入以及使用。
import {CreateDateColumn} from 'typeorm'

// 引入后就可以使用@CreateDateColumn( )装饰器来定义字段了。
@CreateDateColumn({type:"timestamp"})
entryTime:Date
  1. 自动生成列 开发时也会遇到生成一个不规则、不重复的自动编号,这时候就可以使用Generated( )装饰器。
import {CreateDateColumn} from 'typeorm'

@Generated('uuid')
uuid:string

操作数据库实现数据的增删改查

  • 经过上面的操作,我们已经成功连接到数据库且在数据库于项目中成功建立映射关系,下面我们开始编写接口,实现操作数据库,增删改查操作,业务逻辑还是在service里面写,controller中还是作路由操作

完整的源码展示:

  1. app.module.ts(项目的整体模块)
import { Module } from '@nestjs/common';
import { BoysModule } from './boys/boys.module';
import { TypeOrmModule } from '@nestjs/typeorm';

@Module({
imports: [
// 连接数据库
TypeOrmModule.forRoot({
type: 'mysql', // 数据库类型
host: 'localhost', // 数据库的连接地址host
port: 3306, // 数据库的端口 3306
username: 'root', // 连接账号
password: 'Zpl13189417387', // 连接密码
database: 'test1', // 连接的数据库名
retryDelay: 500, // 重试连接数据库间隔
retryAttempts: 10, // 允许重连次数

synchronize: true, // 设置实体同步到数据库中生成映射关系
autoLoadEntities: true, // 自动加载实体,,forFeature()注册的每个实体都自己动加载
}),
BoysModule,
],
controllers: [],
providers: [],
})
export class AppModule {}
  1. boys.module.ts(操作对应的模块)
import { Module } from '@nestjs/common';
import { BoysController } from './boys.controller';
import { BoysService } from './boys.service';
import { TypeOrmModule } from '@nestjs/typeorm'; // 引入 TypeOrmModule 操作实体
import { Boys } from './entities/boys.entities'; // 引入我们刚刚新建的实体

@Module({
imports: [TypeOrmModule.forFeature([Boys])], // 使用方法操作实体
controllers: [BoysController],
providers: [BoysService],
})
export class BoysModule {}
  1. boys.controller.ts模块控制层
/* controller层主要编写接口,不写业务逻辑 */
import {
Controller,
Get,
Request,
Post,
Query,
Body,
Param,
Headers,
} from '@nestjs/common';
import { BoysService } from './boys.service';
import { log } from 'console';

/*
我们可以认为 Controller 是用户配置路由的地方
根据以下代码我们如果想要获取数据就需要输入路径为: /boys/getBoys
当然我们要想在全局中设置初识路径,可以在main.ts中设置 setGlobalPrefix('全局路径')
*/
@Controller('boys')
export class BoysController {
// 创建一个构造函数
constructor(private BoysService: BoysService) {
/*
BoysService: BoysService 等价于 this.BoysService = new BoysService()
*/
}

// 增加一条数据
@Post('/add')
add(@Body() body): any {
console.log('数据请求体:', body); // post请求体携带过来的参数传给service
return this.BoysService.addBoys(body);
}

// 修改一条数据
@Post('/upload')
update(@Body() body): any {
console.log('数据请求体:', body); // post请求体携带过来的参数传给service
return this.BoysService.updateBoys(body.id, body.firstName, {
id: Number(body.id),
name: body.name,
age: body.age,
});
}

// 查找数据
@Post('/find')
find(@Body() body): any {
return this.BoysService.findBoys(body.name);
}

// 删除数据
@Get('/del')
del(@Query() query): any {
console.log('数据请求体(id):', query);
return this.BoysService.delBoys(Number(query.id));
}
}
  1. boys.service.ts(service层主要编写接口的业务逻辑,操作数据库)
import { Injectable } from '@nestjs/common';

/*
操作数据库引入模块
1. Repository 在typeorm中引入可操作数据库实体的方法
2. InjectRepository 注入仓库,将实体中的数据注入到数据库仓库中去
3. 我们新建的实体,一一对应数据库中的数据
*/
import { Like, Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { Boys } from './entities/boys.entities';

// 设置接口限制传参数据
interface Person {
id: number;
name: string;
age: number;
}

@Injectable()
export class BoysService {
// 注入依赖以及绑定实体数据库
constructor(
// 基本是固定写法
@InjectRepository(Boys) private readonly boys: Repository<Boys>,
) {}

// 新增数据
addBoys(data: Person): any {
// console.log('service接收到的数据:',data);
this.boys.save(data);
}

// 修改数据(对应名字,对应id)
updateBoys(id: number, name: string, data: Person): any {
console.log('要修改对应的id,name以及数据为:', id, name, data);
this.boys.update({ id, name }, data);
}

// 查询数据
findBoys(name: string): any {
this.boys.find().then(res=>{
console.log('查询所有数据:',res);

}); // 查询所有数据
this.boys.find({
// 条件查询
where: {
// 模糊查询
name: Like(`%${name}%`),
},
}).then(res=>{
console.log('查询对应name的数据:',res);

});
}

// 删除数据
delBoys(id: number): any {
console.log('要删除对应id的数据为:', id);
this.boys.delete(id);
}
}
  1. boys.entities.ts(entities层,主要用于建立数据库与对应实体的映射关系)
// 编写实体,用于一一对应数据库中的数据映射关系
import {
Entity,
Column,
PrimaryColumn,
CreateDateColumn,
Generated,
} from 'typeorm';

// 定义一个实体类
/*
当然我们也可以自定义对应数据映射到数据库中的存储属性
我们可以设置存储的类型,长度等..
*/
@Entity()
export class Boys {
// 定义Boys的id (一般设置为自增)
@PrimaryColumn() // 设置自动生成不重复的数据
id: number;

// 定义Boys的名字
/*
设置数据库中的存储形式
*/
@Column({ type: 'varchar', length: 255 })
name: string;

// 定义Boys的年纪
@Column({ type: 'int' })
age: number;

// 自动生成时间(自动生成时间戳)
@CreateDateColumn({ type: 'timestamp' })
entryTime: Date;

// 生成唯一标识 uuid
// @Generated('uuid')
// uuid:string
}

结果展示:

  1. 新增数据

image

  1. 修改数据

image

  1. 查找数据

image

  1. 删除数据

image