前语:上篇nest 中也讲到了一些简单的步骤和操作,这篇主要是想深入将prisma的一些操作
1.orm
orm (object relational mappping) 对象关系映射 就是操作对象的方式操作数据库。
常见的ORM有prisma TypeORM Mongooseq 前两者支持多种数据库 后者为MongoDB的ORM.
prisma优点直观的数据模型、自动化迁移、类型安全、自动补全
数据模型,prisma把数据库中的表映射成model。 进行Migrate迁移prisma,就会根据你定义的数据模型,修改数据库。
运行npx prisma studio,会打开一个网页,展示数据库
2.prisma使用
//安装prisma
pnpm install prisma --save-dev
或者 npx prisma
//创建Prisma Schema
npx prisma init
会有schema.prisma文件和.env 文件 在schema.prisma可以定义自己数据模型 .env中有要连接的数据信息,可以用docker 跑一个数据库,然后连接
npx prisma migrate dev --name init
会有针对数据库运行SQL迁移文件
之后安装@prisma/client
pnpm install @prisma/client
连接我们的数据库也会有这些prisma模型生成的表
完成这些步骤之后就可以进行数据库查询了
初始化的时候 想插入一些初始数据 可以执行seed操作
npx prisma db seed
首先在prisma文件夹中创建文件 写一些初始化的数据
import { hash } from '@node-rs/bcrypt';
import { Prisma, PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
const ADMIN_USERNAME = 'admin';
const ADMIN_NICKNAME = 'Ivy-Rong';
const ADMIN_PASSWORD = '123456';
async function main() {
const adminUserInfo = Prisma.validator<Prisma.UserCreateInput>()({
username: ADMIN_USERNAME,
nickName: ADMIN_NICKNAME,
password: await hash(ADMIN_PASSWORD),
enabled: true,
});
const adminUser = await prisma.user.findUnique({
where: {
username: ADMIN_USERNAME,
},
});
if (!adminUser) {
await prisma.user.create({
data: {
...adminUserInfo,
},
});
}
}
main()
.then(async () => {
await prisma.$disconnect();
})
.catch(async (e) => {
console.error(e);
await prisma.$disconnect();
process.exit(1);
});
可以在package.json 写入脚本
"prisma": {
"seed": "ts-node prisma/seed/seed.ts"
},
最后执行命令npx prisma db seed
可以看到数据库中有数据了,并且将密码使用hash进行加密了
3.Prisma 深入了解
1.Prisma schema
enum AuthTypeEnum {
USER
COOGLE
GITHUB
}
model User {
id Int @id @default(autoincrement())
authType AuthTypeEnum @default(USER) @map("auth_type")
username String @unique @db.VarChar(30)
nickName String? @map("nick_name") @db.VarChar(50)
password String @db.VarChar(100)
avatarUrl String? @map("avatar_url") @db.VarChar(100)
gender String? @db.VarChar(50)
enabled Boolean @default(true)
accessToken String? @map("access_token")
refreshToken String? @map("refresh_token")
UserRole UserRole[]
createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(3)
createdBy Int? @map("created_by")
updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamp(3)
updatedBy Int? @map("updated_by")
deletedAt DateTime? @map("deleted_at") @db.Timestamp(3)
deletedBy Int? @map("deleted_by")
@@map("system_user")
}
- 影响字段的属性用
@作为前缀,影响块的属性用@@作为前缀@@map("system_user")映射数据库表user - @id 唯一字段 (设置主键
PRIMARY KEY) - @default 默认值
- @unique唯一约束 字段唯一
- @relation设置外键 用于建立表与表之间的关联
- @map映射数据库中的字段
- @updateAt 自动存储记录更新的时间
修改完更新数据库 可以看到增加了新的字段
2.关系
一个用户可以有多种鉴权方式,就是一个User 和 Auth 之间一对多 则可以建立两张表之间的联系
model User {
id Int @id @default(uuid())
auth Auth[]
}
model Auth {
id Int @id @default(cuid())
user User @relation(fields: [userId], references: [id])
userId Int @map("user_id")
}
其中 @relation(fields: [userId], references: [id])表示 Auth 的 userId 字段与 User 的 id 字段建立关系,这两个字段的值应该是一致。
3.创建记录
user.controller文件
@ApiTags('用户')
@Controller('user')
export class UserController {
constructor(private readonly userService: UserService) {}
@ApiOperation({ summary: '创建用户' })
@Post()
async create(@Body() createUserDto: CreateUserDto) {
return new R({
data: await this.userService.create(createUserDto),
msg: '创建成功',
});
}
@ApiOperation({ summary: '用户列表' })
@Get()
async findMany(@Query() pageUserDto: PageUserDto) {
return new R({
data: await this.userService.findMany(pageUserDto),
msg: '获取用户列表成功',
});
}
@ApiOperation({ summary: '更新用户' })
@Put(':id')
async update(
@Param('id', new ParseIntPipe()) id: number,
@Body() updateUserDto: UpdateUserDto,
) {
return new R({
data: await this.userService.update(id, updateUserDto),
msg: '更新成功',
});
}
@ApiOperation({ summary: '部分更新' })
@Patch(':id')
async patch(
@Param('id', new ParseIntPipe()) id: number,
@Body() patchUserDto: PatchUserDto,
) {
return new R({
data: await this.userService.update(id, patchUserDto),
msg: '更新成功',
});
}
@ApiOperation({ summary: '删除用户' })
@Delete(':id')
async remove(@Param('id', new ParseIntPipe()) id: number) {
await this.userService.remove(id);
return new R({
msg: '删除成功',
});
}
}
user.service文件
@Injectable()
export class UserService {
constructor(private readonly prismaService: PrismaService) {}
async create(createUserDto: CreateUserDto) {
const hashedPassword = await hash(createUserDto.password);
const user = await this.prismaService.user.create({
data: {
...createUserDto,
password: hashedPassword,
},
omit: {
password: true,
},
});
const userVo = plainToClass(UserVo, user);
return userVo;
}
async findMany(pageUserDto: PageUserDto) {
const { page, pageSize, keywords, startTime, endTime, enabled, id } =
pageUserDto;
const where: Prisma.UserWhereInput = {
deletedAt: null,
AND: [
{
createdAt: {
...(startTime && { gte: startTime }),
...(endTime && { lte: endTime }),
},
id: {
...(id && { equals: id }),
},
enabled: {
...(enabled && { equals: enabled }),
},
},
],
OR: keywords
? [
{
id: {
equals:
_.toNumber(keywords) < 100000 ? _.toNumber(keywords) : 0,
},
},
{ username: { contains: keywords } },
]
: undefined,
};
const records = await this.prismaService.user.findMany({
where,
skip: (page - 1) * pageSize,
take: pageSize,
});
const total = await this.prismaService.user.count({ where });
return plainToClass(PageUserVo, {
records,
total,
page,
pageSize,
});
}
async findOneById(id: number) {
const user = await this.prismaService.user.findUnique({
where: {
id,
deletedAt: null,
},
});
if (!user) {
throw new NotFoundException(`User with id ${id} not found`);
}
const userVo = plainToClass(UserVo, user);
return userVo;
}
async update(
id: number,
updateOrPatchUserDto: UpdateUserDto | PatchUserDto,
updatedBy?: number,
) {
const userVo = plainToClass(
UserVo,
await this.prismaService.user.update({
where: {
id,
deletedAt: null,
},
data: {
...updateOrPatchUserDto,
updatedBy,
},
}),
);
return userVo;
}
async remove(id: number, deletedBy?: number) {
await this.prismaService.user.update({
where: {
id,
deletedAt: null,
},
data: {
deletedAt: new Date().toISOString(),
deletedBy,
},
});
}
}
操作过程中遇见了这个问题 经过找原因发现 当初定义模型的时候 username是唯一值,所有修改username的时候,要先判断 用户名是否被占用
if (username) {
const existingUserByUsername = await this.prismaService.user.findUnique({
where: { username },
});
if (existingUserByUsername && existingUserByUsername.id !== id) {
throw new NotFoundException(`用户名 '${username}' 已被占用.`);
}
}