环企首页
Prisma
概述
Prisma 是新一代 Node.js / TypeScript ORM,以类型安全和现代化开发体验著称。相比传统 ORM,Prisma 的独特之处在于:
- 类型安全的查询:生成的类型与数据库 schema 完全对应,IDE 自动补全
- 声明式 Schema:通过
schema.prisma文件定义数据模型,自动生成迁移 - 直观的 API:链式调用语法,比 SQL 更易读,比传统 ORM 更简洁
- 迁移系统:版本化管理数据库变更,支持回滚
- Studio 可视化:内置数据库 GUI 工具
核心组件:
schema.prisma— 定义数据模型和数据库连接PrismaClient— 数据库操作入口prisma migrate— 数据库迁移命令prisma studio— 可视化数据库管理
适合场景:
- TypeScript/Node.js 全栈项目(Next.js、NestJS)
- 需要强类型安全保障的中大型项目
- 快速迭代的创业项目(迁移体验好)
不支持的功能: 联表查询(需手动 join 或用 Raw SQL)、事务嵌套深度限制
schema.prisma 核心配置
// schema.prisma
generator client {
provider = "prisma-client-js" // 生成 PrismaClient
}
datasource db {
provider = "postgresql" // postgresql / mysql / sqlite / mongodb / sqlserver
url = env("DATABASE_URL")
}
// 数据模型定义
model User {
id Int @id @default(autoincrement())
email String @unique // 唯一索引
name String?
age Int?
role Role @default(USER)
posts Post[] // 一对多
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([email]) // 普通索引
@@index([name, age]) // 复合索引
@@map("users_table") // 映射到实际表名
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id]) // 多对一
authorId Int
tags Tag[]
createdAt DateTime @default(now())
}
model Tag {
id Int @id @default(autoincrement())
name String @unique
posts Post[]
}
enum Role {
USER
ADMIN
}
PrismaClient 常用方法
const { PrismaClient } = require('@prisma/client')
const prisma = new PrismaClient()
// 初始化时配置日志级别
const prisma = new PrismaClient({
log: ['query', 'info', 'warn', 'error']
})
// 关闭时
await prisma.$disconnect()
查询 — find 系列
model.findMany(options)
查询多条记录,最常用的查询方法。
// 查询所有
const users = await prisma.user.findMany()
// 带条件
const activeUsers = await prisma.user.findMany({
where: { role: 'ADMIN' },
orderBy: { createdAt: 'desc' },
take: 10,
skip: 0
})
// 字段过滤(只取需要的字段)
const names = await prisma.user.findMany({
select: { id: true, name: true, email: true }
})
// 包含查询(模糊匹配)
const posts = await prisma.post.findMany({
where: {
title: { contains: 'hello' } // 包含 hello
// startsWith: 'Hello'
// endsWith: 'world'
}
})
// 数值比较
const adults = await prisma.user.findMany({
where: { age: { gte: 18 } } // >= 18
// gte: >= gt: > lte: <= lt: <
})
// IN 查询
const users = await prisma.user.findMany({
where: { role: { in: ['ADMIN', 'USER'] } }
})
// NOT 查询
const users = await prisma.user.findMany({
where: { NOT: { role: 'ADMIN' } }
})
// AND / OR 组合
const users = await prisma.user.findMany({
where: {
AND: [
{ age: { gte: 18 } },
{ role: 'USER' }
],
OR: [
{ name: { contains: '张' } },
{ email: { contains: 'admin' } }
]
}
})
// 联表查询(嵌套读取)
const users = await prisma.user.findMany({
include: { posts: true } // 读取每个用户的文章
})
// 统计数量
const count = await prisma.user.count({
where: { role: 'USER' }
})
// 去重
const roles = await prisma.user.findMany({
distinct: ['role'],
select: { role: true }
})
// 条件计数
const { _count } = await prisma.user.findFirst({
where: { id: 1 },
select: { _count: { select: { posts: true } } }
})
model.findFirst(options)
查询满足条件的第一条记录。
const first = await prisma.user.findFirst({
where: { published: true },
orderBy: { createdAt: 'desc' }
})
// 找不到时返回 null
model.findUnique(options)
根据主键或唯一索引查询一条记录。
// 主键查询
const user = await prisma.user.findUnique({
where: { id: 1 }
})
// 唯一索引查询(email 是唯一的)
const user = await prisma.user.findUnique({
where: { email: 'test@example.com' }
})
// 嵌套读取
const user = await prisma.user.findUnique({
where: { id: 1 },
include: {
posts: {
where: { published: true },
take: 5,
orderBy: { createdAt: 'desc' }
}
}
})
// 不存在则抛异常
const user = await prisma.user.findUniqueOrThrow({
where: { id: 1 }
})
创建 — create
model.create(data)
创建单条记录。
// 简单创建
const user = await prisma.user.create({
data: {
name: '张三',
email: 'zhangsan@example.com',
age: 25
}
})
// 读取创建后的完整数据(返回完整对象)
const user = await prisma.user.create({
data: {
name: '张三',
email: 'zhangsan@example.com'
},
select: { id: true, name: true } // 只返回这些字段
})
// 批量创建(connectOrCreate)
await prisma.user.createMany({
data: [
{ name: '张三', email: 'z1@example.com' },
{ name: '李四', email: 'z2@example.com' }
],
skipDuplicates: true // 跳过已存在的(需数据库支持)
})
关联创建
// 创建用户同时创建关联的文章
const user = await prisma.user.create({
data: {
name: '王五',
email: 'wangwu@example.com',
posts: {
create: {
title: '我的第一篇文章',
content: '内容...',
published: true
}
}
},
include: { posts: true } // 包含创建的关联数据
})
// 关联已有记录(通过外键连接)
await prisma.post.create({
data: {
title: '新文章',
author: { connect: { id: 1 } } // 连接到已存在的用户
}
})
// connectOrCreate(存在则连接,不存在则创建)
await prisma.post.create({
data: {
title: '文章',
author: {
connectOrCreate: {
where: { email: 'new@example.com' },
create: { name: '新用户', email: 'new@example.com' }
}
}
}
})
更新 — update
model.update(data)
更新满足条件的一条记录。
// 按 ID 更新
const user = await prisma.user.update({
where: { id: 1 },
data: {
name: '新名字',
age: { increment: 1 } // 原子递增
// decrement: 1 递减
// multiply: 2 乘
// divide: 2 除
}
})
// 按唯一字段更新
await prisma.user.update({
where: { email: 'test@example.com' },
data: { name: '更新后的名字' }
})
// 更新并返回
const user = await prisma.user.update({
where: { id: 1 },
data: { published: true },
select: { id: true, published: true }
})
// 更新第一条匹配记录
await prisma.user.updateMany({
where: { role: 'USER' },
data: { role: 'MEMBER' }
})
// 更新关联记录(嵌套更新)
await prisma.user.update({
where: { id: 1 },
data: {
posts: {
update: {
where: { id: 2 },
data: { title: '更新后的标题' }
}
}
}
})
删除 — delete
// 按主键删除
await prisma.user.delete({
where: { id: 1 }
})
// 删除不存在会抛异常,用 deleteMany 更安全
await prisma.user.deleteMany({
where: { createdAt: { lt: lastMonth } } // 删除一个月前的用户
})
upsert — 存在则更新,不存在则创建
await prisma.user.upsert({
where: { email: 'test@example.com' },
create: { name: '张三', email: 'test@example.com' },
update: { name: '张三(已更新)' }
})
分页查询
const page = 1
const pageSize = 10
const users = await prisma.user.findMany({
where: { role: 'USER' },
orderBy: { createdAt: 'desc' },
skip: (page - 1) * pageSize,
take: pageSize
})
// 同时返回总数
const total = await prisma.user.count({ where: { role: 'USER' } })
原始 SQL 查询
// 原始查询(返回标量值)
const result = await prisma.$queryRaw`
SELECT * FROM users WHERE age > ${18}
`
// 执行原始 SQL(INSERT/UPDATE/DELETE)
await prisma.$executeRaw`
UPDATE users SET role = 'ADMIN' WHERE id = ${1}
`
// 模板字符串方式
await prisma.$queryRawUnsafe('SELECT * FROM users WHERE id = $1', [1])
事务
// 方式1:$transaction(自动回滚)
await prisma.$transaction(async (tx) => {
const user = await tx.user.create({ data: { name: '张三', email: 'z@example.com' } })
await tx.post.create({
data: { title: '第一篇', authorId: user.id }
})
return user
})
// 方式2:$transaction 传入操作数组(并行执行)
const [users, posts] = await prisma.$transaction([
prisma.user.findMany({ where: { role: 'ADMIN' } }),
prisma.post.count({ where: { published: true } })
])
// 交互式事务(可中途查询)
const result = await prisma.$transaction(async (tx) => {
const user = await tx.user.findUnique({ where: { id: 1 } })
if (!user) throw new Error('用户不存在')
return tx.post.create({
data: { title: '新文章', authorId: user.id }
})
}, { isolationLevel: 'Serializable' }) // 隔离级别
监听(Subscription / Raw)
// Raw 查询监听(调试用,非实时)
prisma.$on('query', (e) => {
console.log(e.query) // SQL 语句
console.log(e.params) // 参数
console.log(e.duration) // 执行时间(ms)
})
CLI 命令
# 初始化 Prisma(创建 schema.prisma)
npx prisma init
# 生成 PrismaClient(schema 变更后必须运行)
npx prisma generate
# 创建迁移(生成 SQL 迁移文件并执行)
npx prisma migrate dev --name add_user_table
# 仅生成 SQL 迁移文件(不执行)
npx prisma migrate dev --create-only
# 执行已有迁移
npx prisma migrate deploy
# 查看数据库状态
npx prisma migrate status
# 回滚迁移
npx prisma migrate resolve --rolled-back "20240101_add_user"
# 打开 Prisma Studio(可视化数据库)
npx prisma studio
# 仅推送 schema 到数据库(不生成迁移文件,开发用)
npx prisma db push
# 重置数据库
npx prisma migrate reset
# 从数据库拉取 schema(逆向工程)
npx prisma db pull
# 填充测试数据(需安装 @prisma/seed)
npx prisma db seed
常见问题
| 问题 | 解决方案 |
|---|---|
找不到 PrismaClient |
npx prisma generate 后重启服务 |
| 关联查询返回空 | 检查外键字段是否正确、where 条件是否排除了关联数据 |
| 性能慢 | 用 explain analyze 查 SQL,或用 $queryRaw 写原生 JOIN |
| 事务失败 | 检查是否有约束冲突(唯一索引、外键等) |
findMany 报 too many |
数据量太大,分页处理,加上 take/limit |
| 迁移报错 | 先 npx prisma migrate status 确认状态 |
| 软删除 | 用 @ignore 标记字段,或自行维护 deletedAt 字段 + 全局过滤 |