PrismaORMGuide PrismaORMGuide

全面指南 Prisma ORM,包括模式定义、迁移、查询和最佳实践

后端开发 0 次安装 0 次浏览 更新于 3/5/2026

Prisma ORM 指南

概览

Prisma 是一个用于 Node.js 和 TypeScript 的下一代 TypeScript ORM,它通过自动生成的类型和查询使数据库访问变得简单。这项技能涵盖了模式定义、迁移、查询模式、事务和性能优化。

前提条件

  • 理解 TypeScript
  • 了解数据库概念(SQL、关系、索引)
  • 熟悉 Node.js 和 npm
  • 基本了解数据库迁移

核心概念

Prisma 架构

  • Prisma 客户端:自动生成的类型安全数据库客户端
  • Prisma 模式:声明式模式定义语言
  • Prisma 迁移:数据库迁移工具
  • Prisma 工作室:可视化数据库 IDE
  • 类型安全:自动生成的 TypeScript 类型

模式设计哲学

Prisma 的模式允许:

  • 声明式模型:使用 Prisma 模式定义模型
  • 类型安全:自动生成的 TypeScript 类型
  • 关系:定义模型之间的关系
  • 约束:数据库级约束
  • 索引:定义索引以提高性能

实施指南

模式定义

基本模型

// 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?
  password  String
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

关系

// prisma/schema.prisma

model User {
  id        String   @id @default(cuid())
  email     String   @unique
  name      String?
  posts     Post[]
  profile   Profile?
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

model Post {
  id        String   @id @default(cuid())
  title     String
  content   String?
  published Boolean  @default(false)
  authorId  String
  author    User     @relation(fields: [authorId], references: [id])
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

model Profile {
  id        String   @id @default(cuid())
  bio       String?
  userId    String   @unique
  user      User     @relation(fields: [userId], references: [id])
}

枚举

// prisma/schema.prisma

enum Role {
  USER
  ADMIN
  MODERATOR
}

enum Status {
  DRAFT
  PUBLISHED
  ARCHIVED
}

model User {
  id        String   @id @default(cuid())
  email     String   @unique
  role      Role     @default(USER)
  status    Status  @default(DRAFT)
}

索引

// prisma/schema.prisma

model User {
  id        String   @id @default(cuid())
  email     String   @unique
  name      String?
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@index([email, name])
  @@index([createdAt])
  @@index([updatedAt])
}

model Post {
  id        String   @id @default(cuid())
  title     String
  published Boolean  @default(false)
  authorId  String
  createdAt DateTime @default(now)

  @@index([authorId, published])
  @@index([createdAt])
}

约束

// prisma/schema.prisma

model User {
  id        String   @id @default(cuid())
  email     String   @unique
  age       Int

  @@map("users")
  @@index([email], name: "user_email_name_idx")
}

model Product {
  id          String   @id @default(cuid())
  name        String
  price       Float
  quantity    Int

  @@check: "price >= 0"
  @@check: "quantity >= 0"
}

迁移工作流

创建迁移

# 创建一个新的迁移
npx prisma migrate dev --name add_user_profile

# 创建迁移,没有名称(自动生成)
npx prisma migrate dev

将迁移应用到生产环境

# 生成迁移 SQL 进行审核
npx prisma migrate dev --create-only --name add_user_profile

# 将迁移部署到生产环境
npx prisma migrate deploy

# 部署特定迁移
npx prisma migrate deploy --name add_user_profile

迁移历史

# 查看迁移历史
npx prisma migrate status

# 解决迁移冲突
npx prisma migrate resolve --applied "add_user_profile"

# 重置数据库并重新应用迁移
npx prisma migrate reset

查询模式

CRUD 操作

// 创建
const user = await prisma.user.create({
  data: {
    name: 'John Doe',
    email: 'john@example.com',
    password: 'hashed_password'
  }
})

// 读取 - 查找多个
const users = await prisma.user.findMany({
  orderBy: { createdAt: 'desc' }
})

// 读取 - 查找一个
const user = await prisma.user.findUnique({
  where: { email: 'john@example.com' }
})

// 更新
const updatedUser = await prisma.user.update({
  where: { id: userId },
  data: { name: 'John Smith' }
})

// 删除
await prisma.user.delete({
  where: { id: userId }
})

// 删除多个
await prisma.user.deleteMany({
  where: { status: 'inactive' }
})

过滤

// 字符串过滤器
const users = await prisma.user.findMany({
  where: {
    OR: [
      { email: { contains: 'example.com' } },
      { name: { startsWith: 'John' } }
    ]
  }
})

// 数字过滤器
const products = await prisma.product.findMany({
  where: {
    AND: [
      { price: { gte: 10, lte: 100 } },
      { quantity: { gt: 0 } }
    ]
  }
})

// 日期过滤器
const posts = await prisma.post.findMany({
  where: {
    createdAt: {
      gte: new Date('2024-01-01'),
      lte: new Date('2024-12-31')
    }
  }
})

// 数组过滤器
const users = await prisma.user.findMany({
  where: {
    tags: { hasEvery: ['admin', 'moderator'] }
  }
})

// 空过滤器
const usersWithProfile = await prisma.user.findMany({
  where: {
    profile: { isNot: null }
  }
})

关系

// 带嵌套关系的创建
const post = await prisma.post.create({
  data: {
    title: 'New Post',
    content: 'Post content',
    author: {
      connect: {
        id: userId,
      },
      create: {
        name: 'Author Name',
        email: 'author@example.com'
      }
    }
  }
})

// 在查询中包含关系
const postWithAuthor = await prisma.post.findUnique({
  where: { id: postId },
  include: {
    author: true
  }
})

// 包含多个关系
const postWithRelations = await prisma.post.findUnique({
  where: { id: postId },
  include: {
    author: {
      include: {
        profile: true
      }
    },
    comments: true
  }
})

分页

// 跳过和限制
const page = 1
const pageSize = 10

const users = await prisma.user.findMany({
  skip: (page - 1) * pageSize,
  take: pageSize,
  orderBy: { createdAt: 'desc' }
})

// 获取分页的总数
const total = await prisma.user.count()
const totalPages = Math.ceil(total / pageSize)

排序

// 单字段排序
const users = await prisma.user.findMany({
  orderBy: { name: 'asc' }
})

// 多字段排序
const users = await prisma.user.findMany({
  orderBy: [
  { createdAt: 'desc' },
  { name: 'asc' }
  ]
})

事务

基本事务

const session = await prisma.$transaction(async (tx) => {
  const user = await tx.user.create({
    data: { name: 'John' }
  })

  const post = await tx.post.create({
    data: {
      title: 'Post 1',
      authorId: user.id
    }
  })

  await tx.post.create({
    data: {
      title: 'Post 2',
      authorId: user.id
    }
  })
})

带多个操作的事务

const session = await prisma.$transaction(async (tx) => {
  const user = await tx.user.create({
    data: { name: 'John' }
  })

  await tx.post.createMany({
    data: [
      { title: 'Post 1', authorId: user.id },
      { title: 'Post 2', authorId: user.id }
    ]
  })

  await tx.user.update({
    where: { id: user.id },
    data: { postCount: 2 }
  })
})

带错误处理的事务

async function transferFunds(fromId: string, toId: string, amount: number) {
  const session = await prisma.$transaction(async (tx) => {
    const [fromUser, toUser] = await tx.user.findMany({
      where: { id: { in: [fromId, toId] } }
    })

    if (!fromUser || !toUser) {
      throw new Error('User not found')
    }

    if (fromUser.balance < amount) {
      throw new Error('Insufficient funds')
    }

    await tx.user.update({
      where: { id: fromId },
      data: { balance: { decrement: amount } }
    })

    await tx.user.update({
      where: { id: toId },
      data: { balance: { increment: amount } }
    })

    await tx.transaction.create({
      data: {
        fromId,
        toId,
        amount,
        type: 'transfer'
      }
    })
  })
}

交互式事务

async function createUserWithProfile(userData: any, profileData: any) {
  return await prisma.$transaction(async (tx) => {
    const user = await tx.user.create({
      data: userData
    })

    const profile = await tx.profile.create({
      data: {
        ...profileData,
        userId: user.id
      }
    })

    return { user, profile }
  })
}

性能优化

查询优化

// 好:只选择需要的字段
const users = await prisma.user.findMany({
  select: {
    id: true,
    email: true,
    name: true
  }
})

// 坏:获取所有字段
const users = await prisma.user.findMany()

连接池

// prisma/schema.prisma

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
  
  // 连接池设置
  connection_limit = 10
  pool_timeout = 20
}

批量操作

// 好:使用 createMany 进行批量插入
const users = await prisma.user.createMany({
  data: [
    { name: 'User 1', email: 'user1@example.com' },
    { name: 'User 2', email: 'user2@example.com' },
    { name: 'User 3', email: 'user3@example.com' }
  ]
})

// 坏:多个单独创建
for (const userData of userDataArray) {
  await prisma.user.create({ data: userData })
}

最佳实践

  1. 模式设计

    • 使用适合您的数据的适当字段类型
    • 正确定义关系
    • 为频繁查询的字段添加索引
    • 使用枚举表示固定值集
    • 为数据完整性添加约束
  2. 迁移

    • 在部署前始终审查生成的迁移
    • 使用描述性的迁移名称
    • 先在开发中测试迁移
    • 对数据迁移使用事务
    • 保持迁移历史清晰
  3. 查询优化

    • 只选择需要的字段
    • 使用适当的索引
    • 对大型数据集使用分页
    • 尽可能使用 findFirst 而不是 findMany
    • 使用 includes 避免 N+1 查询问题
  4. 事务

    • 对多步骤操作使用事务
    • 保持事务简短
    • 正确处理错误并回滚
    • 使用适当的隔离级别
  5. 错误处理

    • 优雅地处理已知的 Prisma 错误
    • 实施适当的错误日志记录
    • 使用类型安全的错误处理
    • 提供有意义的错误消息
  6. 测试

    • 为关键操作编写单元测试
    • 彻底测试迁移
    • 使用 Prisma 的模拟客户端进行测试
    • 测试边缘情况和错误场景

相关技能