上节我们过了一遍 Prisma Client 做 CRUD 的 api,但那只是针对单表的,这节我们把涉及到多表的 CRUD 的 api 过一遍。
创建个新项目:
mkdir prisma-client-api2
cd prisma-client-api2
npm init -y
进入项目,执行 init 命令:
npx prisma init
生成了 .env 和 schema 文件:
然后改下 .env 文件的数据库连接信息:
DATABASE_URL="mysql://root:guang@localhost:3306/prisma_test"
改一下 datasource 的 provider 为 mysql,并且添加 model
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
model Department {
id Int @id @default(autoincrement())
name String @db.VarChar(20)
createTime DateTime @default(now())
updateTime DateTime @updatedAt
employees Employee[]
}
model Employee {
id Int @id @default(autoincrement())
name String @db.VarChar(20)
phone String @db.VarChar(30)
deaprtmentId Int
department Department @relation(fields: [deaprtmentId], references: [id])
}
之后执行 migrate reset 重置下:
npx prisma migrate reset
然后用 migrate dev 创建新的迁移:
npx prisma migrate dev --name aaa
生成了 client 代码,还有 sql 文件。
数据库中也多了这 2 个表:
然后来写下 client 的 crud 代码。
首先安装 ts、ts-node 包:
npm install typescript ts-node @types/node --save-dev
创建 tsconfig.json
npx tsc --init
把注释删掉,保留这些配置就行:
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"types": ["node"],
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
}
}
创建 src/index.ts
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient({
log: [
{
emit: 'stdout',
level: 'query'
},
],
});
async function main() {
}
main();
然后分别做下 CRUD。
首先是插入数据:
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient({
log: [
{
emit: 'stdout',
level: 'query'
},
],
});
async function test1() {
await prisma.department.create({
data: {
name: '技术部',
employees: {
create: [
{
name: '小张',
phone: '13333333333'
},
{
name: '小李',
phone: '13222222222'
}
]
}
}
})
}
test1();
插入关联 model 的数据的时候,也是用 create 指定:
测试下:
npx ts-node ./src/index.ts
在 mysql workbench 里看下结果:
确实,数据都被正确插入了。
当然,你也可以用这种写法:
async function test2() {
await prisma.department.create({
data: {
name: '技术部',
employees: {
createMany: {
data: [
{
name: '小王',
phone: '13333333333'
},
{
name: '小周',
phone: '13222222222'
}
],
}
}
}
})
}
test2();
跑一下:
效果一样:
那如何关联查询呢?
可以这样写:
async function test3() {
const res1 = await prisma.department.findUnique({
where: {
id: 1
},
include: {
employees: true
}
});
console.log(res1);
const res2 = await prisma.department.findUnique({
where: {
id: 1
},
include: {
employees: {
where: {
name: '小张'
},
select: {
name: true
}
}
}
});
console.log(res2);
const res3 = await prisma.department.findUnique({
where: {
id: 1
}
}).employees();
console.log(res3);
}
test3();
查询 department 的时候,通过 include 指定关联查询出 employees。
include 还可以指定 where 等查询的参数,进一步过滤。
此外,你也可以在查出 department 后调用 empolyees() 方法来查询。
可以看到,都能正确查出关联数据:
再就是关联更新:
async function test4() {
const res1 = await prisma.department.update({
where: {
id: 1
},
data: {
name: '销售部',
employees: {
create: [
{
name: '小刘',
phone: '13266666666'
}
]
}
}
});
console.log(res1);
}
test4();
比如我在更新 department 的时候关联插入了一条 employee 的记录。
跑一下:
在 mysql workbench 里可以看到,id 为 1 的 department 更新了:
关联插入了一条 employee 的记录:
更新 department 的时候,除了可以插入 empolyee 的数据,也可以和别的 empolyee 建立关联。
比如 id 为 4 的 empolyee:
现在他关联的是 id 为 2 的 department。
我们 update 的时候使用 connect 和它关联:
async function test5() {
const res1 = await prisma.department.update({
where: {
id: 1
},
data: {
name: '销售部',
employees: {
connect: [
{
id: 4
}
]
}
}
});
console.log(res1);
}
test5();
跑一下:
刷新可以看到,id 为 4 的 employee 关联的 department 就变了:
如果是某个 id 的数据存在就 connect,不存在就 create 呢?
可以这样写:
async function test6() {
const res1 = await prisma.department.update({
where: {
id: 1
},
data: {
name: '销售部',
employees: {
connectOrCreate: {
where: {
id: 6
},
create: {
id: 6,
name: '小张',
phone: '13256665555'
}
}
}
}
});
console.log(res1);
}
test6();
第一次跑,执行的是 insert:
第二次跑,就是 update 了:
也就是说,update 的时候可以通过 create、connect、connectOrCreate 来插入新的关联 model 的记录或者关联已有的记录。
当然,create 的时候也可以这样:
效果一样,就不一个个测试了。
再就是删除:
如果我们想删除 id 为 1 的 department 的所有 empolyee,可以这样写:
async function test7() {
await prisma.employee.deleteMany({
where: {
department: {
id: 1
}
},
});
}
test7();
这就是多个 model 关联时的 CRUD。
此外,Prisma 还可以直接执行 sql:
async function test8() {
await prisma.$executeRaw`TRUNCATE TABLE Employee`;
const res = await prisma.$queryRaw`select * from Department`;
console.log(res);
}
test8();
这样,当上面的 api 都不能满足需求的时候,你就可以直接执行 sql。
案例代码在小册仓库
总结
这节我们过了一遍多 model 关联的时候涉及到多个表的 CRUD 方法。
还是 findXxx、updateXxx、deleteXxx、createXxx 那些方法,只不过查询的时候可以通过 include 包含关联记录,新增修改的时候可以通过 create、connect、connectOrCreate 来关联或者插入记录。
此外,你还可以直接执行 sql。
通过这些 api,我们就能完成各种 CRUD 需求了。