初始化
- 新建项目文件夹
- npm初始化一个Typescript项目
npm init -y
npm install typescript tsx @types/node --save-dev
- 创建 package.json、
- 初始化 TypeScript
npx tsc --init - 在项目中将 Prisma CLI 安装为开发依赖
npm install prisma --save-dev - 使用 Prisma CLI 的
init命令设置 Prisma ORM
npx prisma init --datasource-provider sqlite --output ../generated/prisma
## 在 Prisma 模式中建模数据
- prisma/schema.prisma
- 用途
- 它在
prisma/migrations目录中为此次迁移创建了一个新的 SQL 迁移文件。 - 它针对数据库执行了 SQL 迁移文件。
- 它在后台运行了
prisma generate(这会安装@prisma/client包并根据您的模型生成定制的 Prisma Client API)。
- 它在
## 运行迁移以使用 Prisma Migrate 创建数据库表
- 加入有一个Prisma模式,但是还没有数据库,执行以下命令以创建表
- npx prisma migrate dev --name init
- 以上命令干了三件事
- 它在prisma/migrations
## 探索如何使用 Prisma Client 向数据库发送查询
要开始使用 Prisma Client,您需要安装 @prisma/client 包
npm install @prisma/client
Prisma核心命令
安装
- npm install prisma --save-dev
- npm install @prisma/client
初始化
- npx prisma init
生成 Client
- npx prisma generate
数据库迁移
- npx prisma migrate dev --name init
打开 Prisma Studio(可视化工具)
- npx prisma studio
基本CRUD
- 假设schema.prisma 里面有数据模型
model User {
id Int @id @default(autoincrement())
name String
email String @unique
posts Post[]
createdAt DateTime @default(now())
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
authorId Int
author User @relation(fields: [authorId], references: [id])
}
创建(create)
- 语法
prisma.model.create({
data:{...},
select?:{...},
include?:{...}
})
| **属性 | 作用 |
|---|---|
data | 必填,指定要创建的字段和值,可以包含关联的 create、connect、connectOrCreate |
select | 可选,指定返回哪些字段(true/false),不能和 include 同时使用 |
include | 可选,指定返回关联的模型数据 |
skipDuplicates | 只在 createMany 时有效,忽略重复主键/唯一值记录** |
// 创建单条
await prisma.user.create({
data:{
name: 'Alice',
email: 'alice@example.com',
}
})
// 创建并关联(发文关联作者)
await prisma.post.create({
data:{
title: 'Hello World',
content: 'This is my first post',
author:{
connect:{id:1} // 关联已有用户
}
}
})
// 批量创建
await prisma.user.createMany({
data:[
{ name: 'Bob', email: 'bob@example.com' },
{ name: 'Charlie', email: 'charlie@example.com' },
]
})
查询(findUnique / findFirst / findMany)
- 语法
prisma.model.findUnique({
where:{},
select?: { ... }, // 返回指定字段
include?: { ... },// 返回关联数据
rejectOnNotFound?: boolean,// 找不到记录就抛异常
})
| 属性 | 作用 |
|---|---|
where | 必填,指定唯一条件(主键或唯一字段) |
select | 返回特定字段 |
include | 返回关联数据 |
rejectOnNotFound | 如果找不到记录就抛异常 |
orderBy | 排序,可单字段或多字段 |
skip | 跳过多少条(分页用) |
take | 取多少条(分页用) |
distinct | 去重,按指定字段 |
// 查询单条
await prisma.user.findUnique({
where:{id:1}
})
// 匹配第一条
await prisma.user.findFirst({
where:{name:{contains:'A'}}
})
// 查多条
await prisma.user.findMany({
where: {
name: { startsWith: 'A' },
email: { endsWith: '@example.com' }
},
orderBy: { createdAt: 'desc' },
take: 10,
skip: 0,
})
// 关联查询
await prisma.user.findUnique({
where:{
id:1
},
include: {
posts: true
}
})
// 只查部分字段
await prisma.user.findMany({
select:{id:true,name:true}
})
更新(update,updateMany)
// 更新单条
await prisma.user.update({
where: { id: 1 },
data: { name: 'Alice Updated' }
})
// 批量更新
await prisma.user.updateMany({
where: { email: { endsWith: '@example.com' } },
data: { name: 'Bulk Updated' }
})
删除(delete , deleteMany)
// 删除单条
await prisma.user.delete({
where: { id: 1 }
})
// 批量删除
await prisma.user.deleteMany({
where: { name: 'Bulk Updated' }
})
| 操作符 | 用法示例 |
|---|---|
equals | { age: { equals: 18 } } |
not | { age: { not: 18 } } |
in | { id: { in: [1, 2, 3] } } |
notIn | { id: { notIn: [1, 2] } } |
lt / lte | { age: { lt: 30 } } |
gt / gte | { age: { gte: 18 } } |
contains | { name: { contains: 'A' } } |
startsWith | { name: { startsWith: 'Al' } } |
endsWith | { email: { endsWith: '.com' } } |
OR | { OR: [{ name: 'Alice' }, { name: 'Bob' }] } |
AND | { AND: [{ age: { gt: 18 } }, { age: { lt: 30 } }] } |
关联操作
创建关联记录
// 创建用户得同时创建文章
await prisma.user.create({
data:{
name: 'David',
email: 'david@example.com',
posts:{
create:{
{ title: 'Post 1' },
{ title: 'Post 2' }
}
}
}
})
// 关联已有记录
await prisma.post.update({
where:{id:2},
data:{
author: { connect: { id: 2 } }
}
})
// 断开关联
await prisma.post.update({
where: { id: 1 },
data: { author: { disconnect: true } }
})
// 删除关联记录
await prisma.user.update({
where: { id: 1 },
data: { posts: { deleteMany: {} } }
})
批量&事务
// 批量操作
await prisma.user.createMany({
data: new Array(100).fill(0).map((_, i) => ({
name: `User${i}`,
email: `user${i}@example.com`
}))
})
// 事务(要么批量成功,要么批量失败)
await prisma.$transation([
prisma.user.create({ data: { name: 'TX1', email: 'tx1@example.com' } }),
prisma.user.create({ data: { name: 'TX2', email: 'tx2@example.com' } })
])
分页
const page = 1
const pageSize = 10
await prisma.user.findMany({
skip: (page-1) * pageSize,
take: pageSize,
orderBy: { id: 'asc' }
})
聚合 & 分组
- 聚合函数: 用来对多行数据进行统计计算并返回单一值的函数,比如:
- COUNT()
- SUM()
- AVG()
- MIN()
- MAX()
// 返回表中用户总数
SELECT COUNT(*) AS total_users
FROM users;
- 分组:把数据按照某个字段分组,然后每组数据执行聚合计算
// 按部门统计员工数量
SELECT department, COUNT(*) AS employee_count
FROM employees
GROUP BY department;
- 聚合 + 分组 的执行顺序
- SQL执行时,GROUP BY 会先分组,再对每组应用聚合函数
- FROM->WHERE->GROUP BY->HAVING->SELECT->ORDER BY
- 注意:
WHERE过滤的是分组前的数据, HAVING过滤的是分组后的结果。
- 注意:
常用的 CRUD 扩展参数
- where 查询条件运算符
where :{
age:{gte:18}, // >=18
name:{contains:'A',node:'insensitive'},// 模糊匹配不区分大小写
OR: [
{ name: { startsWith: 'Al' } },
{ email: { endsWith: '.com' } }
]
}
| 运算符 | 作用 |
|---|---|
equals | 等于 |
not | 不等于 |
in | 在数组中 |
notIn | 不在数组中 |
lt / lte | 小于 / 小于等于 |
gt / gte | 大于 / 大于等于 |
contains | 包含(模糊搜索) |
startsWith | 以…开头 |
endsWith | 以…结尾 |
OR | 或 |
AND | 且 |
NOT | 非 |
- ORDER BY 排序
orderBy:[
{createAt:'desc'},
{name:'asc'}
]
select/include区别select:只返回指定字段include:返回关联模型的内容
connect/disconnect/set
// 关联已有记录
author: { connect: { id: 1 } }
// 断开关联
author: { disconnect: true }
// 替换关联
posts: { set: [{ id: 1 }, { id: 2 }] }
为什么要设置关联
- 保证数据的完整性
- 关联会在数据库里生成外键(foreign key)约束,比如 Post 必须有一个
authorId,而且这个authorId必须在 User 表里存在。 - 避免出现“文章作者不存在”这种脏数据。
- 关联会在数据库里生成外键(foreign key)约束,比如 Post 必须有一个
model Post{
id Int @id @default(autoincrement())
title String
authorId Int
author User @relation(field : [authorId], reference:[id])
}
- 这样数据库会帮你保证:
- 新增POST(文章)的时候,如果authorId不存在->会报错
- 删除User的时候,如果关联有Post->会限制/级联处理(取决于设置)。
简化代码操作
有了关联,你可以直接一条语句查询/创建/更新多个表的数据,而不是自己写多次 SQL。
例子:没有关联
// 要查文章 + 作者,需要手写两条 SQL 再拼结果
const post = await prisma.post.findUnique({ where: { id: 1 } })
const author = await prisma.user.findUnique({ where: { id: post.authorId } })
有了关联
// 一次性查询
await prisma.post.findUnique({
where: { id: 1 },
include: { author: true }
})
支持关联写入
有了关联,你可以在一次操作中创建/更新多个模型的数据。
// 创建文章的同时创建作者
await prisma.post.create({
data: {
title: 'Hello',
author: {
create: { name: 'Alice', email: 'alice@example.com' }
}
}
})
(4) 自动类型提示
Prisma 会根据 @relation 生成带有类型安全的 API,TS 会帮你校验字段、关联名、嵌套结构。
2. 关联是必须的吗?
不是必须的,要看业务场景:
-
必须的情况
- 两张表确实有业务上的关系,比如“文章属于某个用户”,“订单属于某个客户”。
- 需要保证数据的一致性和完整性。
- 想用 Prisma 提供的
include、connect、create这种关联操作。
-
不需要的情况
- 表之间完全独立,没有业务关系。
- 你不想数据库加外键(可能是为了性能,或者是微服务分库的设计)。
- 你打算自己用字段去维护关系,不依赖数据库外键。
3. 如果不设置关联,会怎样?
- Prisma 依然能创建表,只是不会生成外键约束,也不能用
include、connect等 API。 - 查询关联数据需要自己多写几次查询,或用
$queryRaw手动写 SQL。 - 更容易出现数据不一致,比如
post.authorId指向一个不存在的用户。
4. 总结
能用关联的地方尽量用,因为它:
- 帮你在数据库层面保证数据正确性
- 简化代码
- 提供类型安全和自动补全
但如果是为了性能优化或特殊业务架构(如无外键设计、分布式数据库),就可以不设置。