关于 prisma-orm
prisma-orm 是面向 TypeScript 的数据库工具参考技能,介绍模式设计、类型安全的数据访问和迁移。内容还涵盖查询、关系、事务、Prisma Studio,以及应用集成和数据库操作模式。
主要功能
- 01以模式优先的数据库建模
- 02生成类型安全的 Prisma Client
- 03CRUD、关系与高级查询
- 04开发和生产环境迁移
- 05事务与查询优化
- 06使用 Prisma Studio 可视化浏览数据
适用场景
- 01为 TypeScript 应用建模数据表和关系
- 02为应用代码生成类型安全的数据访问
- 03管理开发与生产环境中的模式变更
- 04在 Prisma Studio 中浏览、筛选和编辑记录
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ bobmatnyc/ claude-mpm-skills --skill prisma-orm安装后在 Claude Code 中输入/prisma-orm确认已加载。
附带文件metadata.json
详细介绍
Prisma ORM - 类型安全的数据库工具包
面向 TypeScript 的现代数据库工具包,支持以架构为先的开发、自动生成类型安全的客户端,以及强大的迁移系统。
快速参考
安装
npm install prisma @prisma/client
npx prisma init
基本工作流
# 1. Define schema
# Edit prisma/schema.prisma
# 2. Create migration
npx prisma migrate dev --name init
# 3. Generate client
npx prisma generate
# 4. Open Studio
npx prisma studio
核心架构模式
// prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(cuid())
email String @unique
name String?
posts Post[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Post {
id String @id @default(cuid())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([authorId])
}
类型安全的 CRUD
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
// Create
const user = await prisma.user.create({
data: {
email: '[email protected]',
name: 'Alice',
posts: {
create: { title: 'First Post', content: 'Hello World' }
}
},
include: { posts: true }
});
// Read with filters
const users = await prisma.user.findMany({
where: { email: { contains: '@example.com' } },
include: { posts: { where: { published: true } } },
orderBy: { createdAt: 'desc' },
take: 10
});
// Update
await prisma.user.update({
where: { id: userId },
data: { name: 'Bob' }
});
// Delete
await prisma.user.delete({ where: { id: userId } });
架构设计模式
字段类型和属性
model Product {
id Int @id @default(autoincrement())
sku String @unique
name String
description String? // Optional field
price Decimal @db.Decimal(10, 2)
inStock Boolean @default(true)
quantity Int @default(0)
tags String[] // Array field (PostgreSQL)
metadata Json? // JSON field
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([sku])
@@index([name, inStock])
}
关系
一对多:
model User {
id String @id @default(cuid())
posts Post[]
}
model Post {
id String @id @default(cuid())
author User @relation(fields: [authorId], references: [id])
authorId String
@@index([authorId])
}
多对多:
model Post {
id String @id @default(cuid())
categories Category[] @relation("PostCategories")
}
model Category {
id String @id @default(cuid())
name String @unique
posts Post[] @relation("PostCategories")
}
一对一:
model User {
id String @id @default(cuid())
profile Profile?
}
model Profile {
id String @id @default(cuid())
bio String
user User @relation(fields: [userId], references: [id])
userId String @unique
}
自关联:
model User {
id String @id @default(cuid())
following User[] @relation("UserFollows")
followers User[] @relation("UserFollows")
}
客户端操作
嵌套写入
// Create with nested relations
const user = await prisma.user.create({
data: {
email: '[email protected]',
profile: {
create: { bio: 'Software Engineer' }
},
posts: {
create: [
{ title: 'Post 1', content: 'Content 1' },
{ title: 'Post 2', content: 'Content 2' }
]
}
}
});
// Update with nested operations
await prisma.user.update({
where: { id: userId },
data: {
posts: {
create: { title: 'New Post' },
update: {
where: { id: postId },
data: { published: true }
},
delete: { id: oldPostId }
}
}
});
事务
顺序(交互式):
await prisma.$transaction(async (tx) => {
const user = await tx.user.create({
data: { email: '[email protected]' }
});
await tx.post.create({
data: { title: 'Post', authorId: user.id }
});
// Rollback if error thrown
if (someCondition) {
throw new Error('Rollback transaction');
}
});
批量(并行):
const [deletedPosts, updatedUser] = await prisma.$transaction([
prisma.post.deleteMany({ where: { published: false } }),
prisma.user.update({
where: { id: userId },
data: { name: 'Updated' }
})
]);
高级查询
聚合:
const result = await prisma.post.aggregate({
_count: { id: true },
_avg: { views: true },
_sum: { likes: true },
_max: { createdAt: true },
where: { published: true }
});
const grouped = await prisma.post.groupBy({
by: ['authorId'],
_count: { id: true },
_avg: { views: true },
having: { views: { _avg: { gt: 100 } } }
});
原始 SQL:
// Raw query
const users = await prisma.$queryRaw<User[]>`
SELECT * FROM "User" WHERE email LIKE ${`%${search}%`}
`;
// Execute
await prisma.$executeRaw`
UPDATE "Post" SET views = views + 1 WHERE id = ${postId}
`;
迁移
开发工作流
# Create and apply migration
npx prisma migrate dev --name add_user_role
# Reset database (WARNING: deletes all data)
npx prisma migrate reset
# View migration status
npx prisma migrate status
生产部署
# Apply pending migrations
npx prisma migrate deploy
# Generate client (in CI/CD)
npx prisma generate
架构原型设计
# Push schema without migrations (dev only)
npx prisma db push
# Pull schema from existing database
npx prisma db pull
集成模式
Next.js App Router
// lib/prisma.ts
import { PrismaClient } from '@prisma/client';
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined;
};
export const prisma = globalForPrisma.prisma ?? new PrismaClient();
if (process.env.NODE_ENV !== 'production') {
globalForPrisma.prisma = prisma;
}
服务端组件:
// app/users/page.tsx
import { prisma } from '@/lib/prisma';
export default async function UsersPage() {
const users = await prisma.user.findMany({
include: { posts: { take: 5 } }
});
return (
<ul>
{users.map(u => (
<li key={u.id}>{u.name} - {u.posts.length} posts</li>
))}
</ul>
);
}
服务端操作:
// app/actions.ts
'use server';
import { prisma } from '@/lib/prisma';
import { revalidatePath } from 'next/cache';
export async function createPost(formData: FormData) {
const title = formData.get('title') as string;
const authorId = formData.get('authorId') as string;
await prisma.post.create({
data: { title, authorId }
});
revalidatePath('/posts');
}
Node.js 中间件
import { PrismaClient } from '@prisma/client';
import express from 'express';
const app = express();
const prisma = new PrismaClient();
app.get('/users/:id', async (req, res) => {
const user = await prisma.user.findUnique({
where: { id: req.params.id },
include: { posts: true }
});
if (!user) return res.status(404).json({ error: 'Not found' });
res.json(user);
});
app.listen(3000);
性能优化
查询优化
// ❌ N+1 queries
const users = await prisma.user.findMany();
for (const user of users) {
const posts = await prisma.post.findMany({
where: { authorId: user.id }
});
}
// ✅ Single query with include
const users = await prisma.user.findMany({
include: { posts: true }
});
// ✅ Select specific fields
const users = await prisma.user.findMany({
select: { id: true, email: true, posts: { select: { title: true } } }
});
分页
// Cursor-based (recommended for large datasets)
const posts = await prisma.post.findMany({
take: 10,
cursor: lastPostId ? { id: lastPostId } : undefined,
skip: lastPostId ? 1 : 0,
orderBy: { createdAt: 'desc' }
});
// Offset-based (simple but slower)
const posts = await prisma.post.findMany({
skip: (page - 1) * pageSize,
take: pageSize
});
连接池
// schema.prisma
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
// Connection pool settings
directUrl = env("DIRECT_URL")
// Serverless connection limit
relationMode = "prisma" // For PlanetScale, Neon
}
# .env
DATABASE_URL="postgresql://user:pass@host:5432/db?connection_limit=10&pool_timeout=20"
Prisma Studio
# Launch visual database browser
npx prisma studio
功能:
- 可视化数据浏览器和编辑器
- 创建、读取、更新和删除记录
- 筛选和搜索数据
- 可视化查看关系
- 运行于
localhost:5555
Prisma 与 Drizzle 对比
| 特性 | Prisma | Drizzle |
|---|---|---|
| 架构定义 | 自定义 DSL | TypeScript 代码 |
| 类型安全 | 生成的类型 | 推断的类型 |
| 迁移 | 内置(migrate) | drizzle-kit |
| 查询构建器 | 流式 API | 类 SQL 构建器 |
| 关系 | 自动处理 | 手动连接 |
| Studio | 内置 GUI | 无 GUI |
| 包体积 | ~300kB | ~50kB |
| 原始 SQL | 支持 | 一等支持 |
| Edge 运行时 | 支持有限 | 完全支持 |
| 学习曲线 | 适中 | 更陡峭 |
| 最适合 | 全栈应用、快速开发、团队 | Edge 函数、SQL 专家、对包体积敏感的场景 |
选择 Prisma 的情况:
- 团队偏好以架构为先的开发
- 需要可视化数据库工具(Studio)
- 希望自动处理关系
- 构建全栈单体应用
- 快速原型设计和迁移
选择 Drizzle 的情况:
- 需要最小化包体积(Edge 函数)
- 偏好类 SQL 语法
- 部署到 Edge 运行时(Cloudflare Workers)
- 希望完全控制 SQL 生成
- 团队具备扎实的 SQL 专业知识
最佳实践
- 单例模式 - 复用
PrismaClient实例(尤其是在开发环境中) - 连接管理 - 为无服务器环境配置连接池大小
- 选择特定字段 - 使用
select减少载荷大小 - 使用事务 - 用于需要原子性的多步操作
- 合理添加索引 - 在频繁查询的字段上添加
@@index - 严格管理迁移 - 部署后绝不要编辑迁移
- 架构版本管理 - 使用具有描述性的迁移名称
- 软删除 - 添加
deletedAt字段,而不是硬删除 - 保存前验证 - 在执行 Prisma 操作前使用 Zod 架构
- 监控查询 - 使用
prisma.$on('query')记录日志
常见陷阱
❌ 创建多个 PrismaClient 实例:
// WRONG - creates connection leak
function getUser() {
const prisma = new PrismaClient(); // New instance every call
return prisma.user.findMany();
}
// CORRECT - singleton pattern
const prisma = new PrismaClient();
function getUser() {
return prisma.user.findMany();
}
❌ N+1 查询:
// WRONG - multiple queries
const users = await prisma.user.findMany();
for (const user of users) {
user.posts = await prisma.post.findMany({ where: { authorId: user.id } });
}
// CORRECT - single query with include
const users = await prisma.user.findMany({ include: { posts: true } });
❌ 多步操作缺少事务:
// WRONG - not atomic, can leave inconsistent state
await prisma.user.delete({ where: { id: userId } });
await prisma.post.deleteMany({ where: { authorId: userId } }); // May fail
// CORRECT - atomic transaction
await prisma.$transaction([
prisma.post.deleteMany({ where: { authorId: userId } }),
prisma.user.delete({ where: { id: userId } })
]);
危险信号
出现以下情况时请停下来重新考虑:
- 在请求处理程序中创建新的
PrismaClient - 多步操作未使用事务
- 外键或频繁查询的字段缺少索引
- 在大型表上使用未分页的
findMany - 只需特定字段时却获取整个对象
- 未处理生产环境中的连接错误
- 在生产环境中使用
migrate dev(应使用migrate deploy)
与其他技能的集成
- typescript-core:Zod 验证、类型安全模式
- nextjs-core:Server Actions、Server Components 集成
- nextjs-v16:App Router 数据获取、缓存
- database-migration:安全演进架构的模式
资源
- 文档:https://www.prisma.io/docs
- 架构参考:https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference
- 客户端 API:https://www.prisma.io/docs/reference/api-reference/prisma-client-reference
- 示例:https://github.com/prisma/prisma-examples
相关技能
使用 Prisma 时,这些技能可提升你的工作流:
- drizzle:Drizzle ORM,作为 Prisma 的轻量级替代方案
- typescript:Prisma 生成类型的 TypeScript 最佳实践
- nextjs:Prisma 与 Next.js:连接池、边缘运行时注意事项
- test-driven-development:测试 Prisma 模型、迁移和查询
[这些技能中提供了完整文档,前提是它们已部署在你的工具包中]
评论
支持 Markdown