• 微信:WANCOME
  • 扫码加微信,提供专业咨询
  • 服务热线
  • 13215191218
    13027920428

  • 微信扫码访问本页
desc-2
环企首页

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 字段 + 全局过滤