Prisma 实践
要点
- Prisma 是最流行的 TypeScript ORM,schema 用独立的
.prisma文件定义 - Prisma Client 提供完整的类型推导,查询 API 直观
- 关系建模清晰,
include语法简化关联查询 - 迁移工具 Prisma Migrate 稳定可靠
- AI 项目可以用 Prisma,但 pgvector 支持需要额外配置
- Prisma Studio 可视化查看数据,开发调试方便
1. 安装与初始化
pnpm add prisma @prisma/client
pnpx prisma init这会生成:
prisma/
└── schema.prisma
.env # DATABASE_URL
2. Schema 定义
// prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(uuid())
email String @unique
name String
metadata Json?
conversations Conversation[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Conversation {
id String @id @default(uuid())
userId String
user User @relation(fields: [userId], references: [id])
title String
model String
messages Message[]
createdAt DateTime @default(now())
@@index([userId])
}
model Message {
id String @id @default(uuid())
conversationId String
conversation Conversation @relation(fields: [conversationId], references: [id])
role String // user, assistant, system
content String
tokens Int
createdAt DateTime @default(now())
@@index([conversationId, createdAt])
}关键字段说明:
@id:主键@default(uuid()):默认值,UUID@unique:唯一约束@relation:关系定义@@index:复合索引Json?:可选的 JSON 字段
3. 生成 Client
pnpx prisma generate这会根据 schema 生成 Prisma Client,位于 node_modules/@prisma/client。
4. 迁移
# 开发环境:生成并应用迁移
pnpx prisma migrate dev --name init
# 生产环境:应用已有的迁移
pnpx prisma migrate deploy
# 重置数据库(删除所有数据)
pnpx prisma migrate reset迁移文件存放在 prisma/migrations/ 目录,可以纳入版本控制。
5. 与 Hono 集成
// src/lib/db.ts
import { PrismaClient } from '@prisma/client'
export const prisma = new PrismaClient()
// 优雅关闭
process.on('beforeExit', async () => {
await prisma.$disconnect()
})// src/routes/users.ts
import { Hono } from 'hono'
import { prisma } from '../lib/db'
const app = new Hono()
// 查询所有用户
app.get('/users', async (c) => {
const users = await prisma.user.findMany()
return c.json(users)
})
// 查询单个用户
app.get('/users/:id', async (c) => {
const id = c.req.param('id')
const user = await prisma.user.findUnique({
where: { id },
})
if (!user) return c.json({ error: 'Not found' }, 404)
return c.json(user)
})
// 创建用户
app.post('/users', async (c) => {
const body = await c.req.json()
const user = await prisma.user.create({
data: body,
})
return c.json(user, 201)
})
// 更新用户
app.put('/users/:id', async (c) => {
const id = c.req.param('id')
const body = await c.req.json()
const user = await prisma.user.update({
where: { id },
data: body,
})
return c.json(user)
})
// 删除用户
app.delete('/users/:id', async (c) => {
const id = c.req.param('id')
await prisma.user.delete({
where: { id },
})
return c.json({ ok: true })
})
export default app6. 关联查询
Prisma 的 include 语法简化关联查询:
// 查询用户及其对话
const userWithConversations = await prisma.user.findUnique({
where: { id: 'user-1' },
include: {
conversations: true,
},
})
// 嵌套查询:用户 → 对话 → 消息
const userWithMessages = await prisma.user.findUnique({
where: { id: 'user-1' },
include: {
conversations: {
include: {
messages: {
orderBy: { createdAt: 'asc' },
},
},
},
},
})
// 只选择某些字段
const userWithEmail = await prisma.user.findUnique({
where: { id: 'user-1' },
select: {
id: true,
email: true,
},
})7. 条件查询
// 等于
await prisma.user.findMany({
where: { email: '[email protected]' },
})
// 包含
await prisma.user.findMany({
where: { name: { contains: 'Alice' } },
})
// 范围
await prisma.user.findMany({
where: { createdAt: { gte: new Date('2026-01-01') } },
})
// 多个条件(AND)
await prisma.user.findMany({
where: {
name: { contains: 'Alice' },
createdAt: { gte: new Date('2026-01-01') },
},
})
// OR 条件
await prisma.user.findMany({
where: {
OR: [
{ email: '[email protected]' },
{ email: '[email protected]' },
],
},
})
// 关联过滤
await prisma.user.findMany({
where: {
conversations: {
some: {
model: 'gpt-4',
},
},
},
})8. 事务
// 简单事务
await prisma.$transaction(async (tx) => {
const user = await tx.user.create({
data: { email: '[email protected]', name: 'Alice' },
})
await tx.conversation.create({
data: {
userId: user.id,
title: 'New Conversation',
model: 'gpt-4',
},
})
})
// 批量操作
await prisma.$transaction([
prisma.user.update({
where: { id: 'user-1' },
data: { name: 'Alice Smith' },
}),
prisma.conversation.update({
where: { id: 'conv-1' },
data: { title: 'Updated Title' },
}),
])9. JSON 字段
// 插入 JSON
await prisma.user.create({
data: {
email: '[email protected]',
name: 'Alice',
metadata: {
role: 'admin',
tags: ['vip', 'beta'],
},
},
})
// 查询 JSON 字段(PostgreSQL)
const admins = await prisma.user.findMany({
where: {
metadata: {
path: ['role'],
equals: 'admin',
},
},
})Prisma 的 JSON 查询不如 Drizzle 灵活。复杂的 JSON 查询需要用 $queryRaw:
const results = await prisma.$queryRaw`
SELECT * FROM users
WHERE metadata->>'role' = 'admin'
`10. pgvector 支持
Prisma 不原生支持 pgvector,但可以通过扩展或 $queryRaw 使用:
// prisma/schema.prisma
model DocumentEmbedding {
id String @id @default(uuid())
documentId String
chunk String
embedding Unsupported("vector(1536)") // pgvector 类型
createdAt DateTime @default(now())
}// 相似度查询
const queryEmbedding = [0.1, 0.2, ...]
const results = await prisma.$queryRaw`
SELECT id, chunk, 1 - (embedding <=> ${queryEmbedding}::vector) as similarity
FROM document_embeddings
ORDER BY embedding <=> ${queryEmbedding}::vector
LIMIT 5
`或者使用 prisma-pg-extensions 等第三方扩展。
11. Prisma Studio
Prisma Studio 是可视化的数据浏览器:
pnpx prisma studio打开浏览器,可以查看、编辑数据库里的数据。开发调试时非常方便。
12. 性能优化
12.1 选择字段
避免 selectAll,只选择需要的字段:
// ❌ 查询所有字段
const users = await prisma.user.findMany()
// ✅ 只选择需要的字段
const users = await prisma.user.findMany({
select: {
id: true,
email: true,
name: true,
},
})12.2 避免 N+1
使用 include 而不是循环查询:
// ❌ N+1 问题
const conversations = await prisma.conversation.findMany()
for (const conv of conversations) {
const messages = await prisma.message.findMany({
where: { conversationId: conv.id },
})
}
// ✅ 一次查询
const conversations = await prisma.conversation.findMany({
include: { messages: true },
})12.3 分页
// Offset 分页
const page = 1
const pageSize = 10
const conversations = await prisma.conversation.findMany({
skip: (page - 1) * pageSize,
take: pageSize,
orderBy: { createdAt: 'desc' },
})
// Cursor 分页(推荐)
const conversations = await prisma.conversation.findMany({
take: 10,
cursor: {
id: 'last-id', // 上一页的最后一条 ID
},
orderBy: { createdAt: 'desc' },
})总结
Prisma 是成熟的 TypeScript ORM,开发体验好,适合关系建模为主的项目。
这一节涉及到的几个实践:
- Schema 定义:
.prisma文件定义模型和关系 - Client 生成:
prisma generate生成类型安全的 Client - 迁移管理:
prisma migrate dev/deploy管理 schema 变更 - CRUD 操作:
findMany、create、update、delete - 关联查询:
include语法简化嵌套查询 - 事务:
$transaction支持回调和批量模式 - JSON 查询:支持基本查询,复杂查询用
$queryRaw - pgvector:需要
Unsupported类型和$queryRaw - Prisma Studio:可视化数据浏览器
- 性能优化:选择字段、避免 N+1、分页
Prisma 适合团队 ORM 经验少、需要完善工具链的项目。如果需要灵活的 pgvector 支持或追求极致性能,Drizzle 可能更合适。
下一篇看数据库连接管理——连接池、Edge 环境连接、连接泄漏预防。