Prisma 数据库工具使用手册

技术 · 昨天 · 访问: 3 次

介绍

Prisma 是一个现代的数据库工具,旨在简化数据库访问和操作。它提供了一种类型安全的方式来与数据库进行交互,并且可以与多种数据库(如 PostgreSQL、MySQL、SQLite 等)一起使用。Prisma 的核心组件包括 Prisma CLient、Prisma Migrate 和 Prisma Studio。

使用

安装

pnpm install prisma -D

使用

npx prisma <command>

指令:

  • init: 初始化 Prisma 项目,生成 prisma/schema.prisma 文件。
  • generate: 生成 Prisma 客户端代码。
  • migrate: 创建迁移文件,用于数据库架构的变更。
  • studio: 启动 Prisma Studio,一个图形化的数据库管理工具。
  • introspect: 生成 Prisma schema 文件,用于描述数据库架构。
  • validate: 验证 Prisma schema 文件的有效性。
  • format: 格式化 Prisma schema 文件。
prisma db pull

prisma migrate dev --name init

prisma generate

初始化

  • npx prisma init

    • 初始化 prisma 项目,创建 prisma 目录(包含 prisma.prisma 文件)和 .env 文件(用于数据库连接配置)。

数据库 schema 管理

  • npx prisma migrate dev --name <migration-name>

    • 根据 schema.prisma 的变更生成迁移文件,并应用到开发数据库。--name 用于指定迁移名称(必填)。
    • 需要添加影子数据库连接 SHADOW_DATABASE_URL,在 .envprisma.schema 中配置
    • 会在 prisma 目录中生成一个 migrations 迁移文件目录
  • npx prisma migrate deploy

    • 将迁移文件应用到生产环境数据库(不生成新迁移,仅执行已有的迁移)。
  • npx prisma migrate reset

    • 重置数据库(删除数据、重新应用所有迁移),常用于开发环境。
  • npx prisma db pull

    • 根据现有数据库结构自动生成 schema.prisma 模型(反向工程)。
  • npx prisma db push

    • 直接将 shcema.prisma 的变更推送到数据库(不生成迁移文件,适合快速原型开发)。

影子数据库

  • 在 prisma 中,影子数据库主要用于开发阶段检测模式漂移或生成迁移时可能的数据丢失问题。运行 prisma migrate dev 命令时,prisma 会自动创建和删除影子数据库。但在生产环境中,通常不需要影子数据库,生产关系命令如 prisma migrate resolveprisma migrate deploy 不会使用它。

.env

SHADOW_DATABASE_URL="mysql://remove_watermarks:123456@localhost:3306/prisma_shadow_db"

prisma.schema

datasource db {
    provider = "mysql"
    url      = env("DATABASE_URL")
    shadowDatabaseUrl = env("SHADOW_DATABASE_URL")
}

客户端生成

  • npx prisma generate

    • 根据 schema.prisma 生成 Prisma Client(类型安全的数据库访问客户端),每次修改 schema 后需执行。

数据库交互

  • npx prisma studio

    • 启动 prisma 可视化界面(默认地址 http://localhost:5555),可直观查看和编辑数据库数据。
  • npx prisma db seed

    • 执行种子脚本(需在 package.json 中配置 prisma.seed 指向脚本),用于初始化测试数据。

其他实用指令

  • npx prisma migrate status

    • 查看迁移文件的应用状态(哪些已应用,哪些未应用)。
  • npx prisma migrate resolve --applied <migration-folder>

    • 手动标记某个迁移为已应用(解决迁移状态不一致问题)。
  • npx prisma version

    • 查看当前 prisma 相关包的版本信息。

常用指令

初始化

npx prisma init

schema.prisma 文件介绍

schema.prisma 文件是 Prisma 的配置文件,用于定义数据库连接和模型。它是一个 Prisma schema 文件,使用 Prisma 的 DSL(领域特定语言)来描述数据库架构和模型。
generator client {
    provider = "prisma-client-js"
}

// 声明数据源
datasource db {
    // 连接类型
    provider = "postgresql"
    // 链接字符串
    url      = env("DATABASE_URL")  // env 函数会去读取 .env 文件中的 DATABASE_URL
}

创建模型

schema.prisma 文件中,可以定义模型,每个模型代表数据库中的一个表。

model User {
    // id 字段,类型为 Int,并且是主键,默认值是自增的
    id    Int      @id @default(autoincrement())
    // name 字段,类型为 String
    name  String
    // age 字段,类型为 Int
    age   Int
    // email 字段,类型为 String,并且是唯一约束
    email String   @unique
    // createTime 字段,类型为 DateTime,默认值为当前时间
    createTime DateTime @default(now()) @map("create_time")  // 使用 map 函数将模型对象设置别名映射到数据库中的字段名(数据表中的字段名就使用 map 函数设置的参数)
    // updateTime 字段,类型为 DateTime,默认值为当前时间,并且每次更新时自动更新
    updateTime DateTime @updatedAt

    // 通过 @@map 函数将模型对象设置别名映射到数据库中的表名(数据表中的名就使用 map 函数设置的参数)
    @@map("users")  
}

上面方式定义创建时间和更新时间会有八小时的时差,使用以下方式进行创建

createdAt DateTime @default(dbgenerated("NOW()")) @db.DateTime
updatedAt DateTime @default(dbgenerated("NOW() ON UPDATE NOW()")) @db.Timestamp(0)

将 prisma 模型映射到数据库创建表

npx prisma db push

服务端使用 prisma

实例化 prisma 对象

const { PrismaClient } = require("@prisma/client")
const prisma = new PrismaClient()

通过 prisma 实例进行一系列的数据库操作。

增删改查操作

语法:

prisma.<Model>.create({})
prisma.<Model>.createMany({})

单条

prisma.user.create({
    data: {
        // 需要添加的数据
    },
    // 设置返回的字段,默认返回所有字段
    select: {
        id: true,
        name: true
    }
}).then(res => {
    // res 返回生成的数据
})

参数:

  • select: 设置返回的字段,默认返回所有字段。{select: {id: true, name: true}}

多条

prisma.user.createMany({
    data: [
        {...}
    ],
    skipDuplicates: true  // 跳过重复数据
}).then(res => {
    // res 返回 { count: 2 } 总数
})

参数:

  • skipDuplicates: true: 跳过重复数据,原本如果数据是一个唯一值,那么会报错,设置这个参数后,如果数据重复,那么会跳过这条数据,不会报错。

单条

const result = await prisma.model.delete({
    // 查询条件
    where: {
        id: 1
    },
    select: {
        id: true
    }
})

返回的是一个被删除对象数据的参数,和新增数据返回参数一致

多条

const result = await prisma.model.deleteMany({
    where: {
        name: '张三'
    }
})

返回的参数是一个 {count: 1} 对象,表示删除了 1 条数据

单条

const result = await prisma.model.update({
    where: {
        id: 1
    },
    data: {
        name: '李四'
    },
    select: {
        id: true,
        name: true
    }
})

返回的也是一个被修改对象数据的参数,和新增数据返回参数一致

多条

const result = await prisma.model.updateMany({
    where: {
        name: '张三'
    },
    data: {
        name: '李四'
    }
})

返回的参数是一个 {count: 1} 对象,表示修改了 1 条数据

单条

const result = await prisma.model.findFirst({
    where: {
        id: 1
    },
    select: {
        id: true,
        name: true
    }
})

返回的也是一个被修改对象数据的参数,和新增数据返回参数一致

多条

const result = await prisma.model.findMany({
    where: {
        name: '张三'
    },
    select: {
        id: true,
        name: true
    }
})

返回的是一个数组,里面包含着多条查询到的数据

分页查询

  • skip: 偏移量
  • take: 查询数量
const result = await prisma.model.findMany({
    where: {
        name: '张三'
    },
    select: {
        id: true,
        name: true
    },
    skip: 0,
    take: 10
})

查询过滤条件

排序

  • orderBy
const result = await prisma.model.findMany({
    where: {
        name: '张三'
    },
    select: {
        id: true,
        name: true
    },
    orderBy: {
        id: 'asc' // asc 升序,desc 降序
    }
})

关联字段

一对一关系

如下示例,文章需要记录用户id

model User {
    id Int @id @default(autoincrement())
    name String
    // profile 设置需要进行关联的表
    // profile 模型名?
    profile Article?
}

model Article {
    id Int @id @default(autocrement())
    title String
    // user 可以自定义
    // user User 表示 Article 模型中的一个字段 user,它的类型是 User。这意味着每篇文章 (Article) 都与一个用户 (User) 相关联。
    // @relation(fields: [userId], references: [id]) 这是 Prisma 的关系注解,用于指定两个模型之间的关联方式。
        // fields: [userId]: 这表示在 Article 模型中,userId 字段用于存储关联用户的 ID。
        // references: [id]: 这表示 userId 字段的值应该与 User 模型中的 id 字段相匹配。
    user User @relation(fields: [userId], references: [id])
    userId Int @unique @map("user_id")
}

User 是关系的持有者,而 Article 是关系的“被持有者”

关系的持有者 vs 被持有者

在 Prisma 中,关系的“持有者”是指定义关系的模型,而“被持有者”是指通过外键引用其他模型的模型。

  • 持有者:定义了关系的方向(即谁“拥有”谁)。
  • 被持有者:通过外键存储关联的 ID。

https://www.bilibili.com/video/BV1Lh41157nS/?spm_id_from=333.788.recommend_more_video.2&vd_source=a2b0f47f24da2cde5c601e9d05136937

返回的数据结构大致如下:

默认是不返回关联字段的,需要加上一个配置
const user = await prisma.user.findFirst({
    where: { id: "某个用户ID" },
    include: { profile: true } // 必须显式包含关联字段才会返回
});
{
    id: 1,
    name: "张三",
    profile: {
        // 是单个对象(而非数组) 
        id: 1,
        bio: "热爱编程",
        userId: 1
    } 
    // 或如果没有关联的profile,则为: 
    // profile: null
}

一对多关系

model User {
  id            String    @id @default(cuid())
  name          String?
  email         String    @unique
  password      String?
  emailVerified DateTime?
  image         String?
  createdAt     DateTime  @default(now())
  updatedAt     DateTime  @updatedAt
  accounts      Account[]
  sessions      Session[]
}

model Account {
  id                String  @id @default(cuid())
  userId            String
  type              String
  provider          String
  providerAccountId String
  refresh_token     String? @db.Text
  access_token      String? @db.Text
  expires_at        Int?
  token_type        String?
  scope             String?
  id_token          String? @db.Text
  session_state     String?
  user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  @@unique([provider, providerAccountId])
}

model Session {
  id           String   @id @default(cuid())
  sessionToken String   @unique
  userId       String
  expires      DateTime
  user         User     @relation(fields: [userId], references: [id], onDelete: Cascade)
}

返回的数据结构大致如下:

默认是不返回关联字段的,需要加上一个配置
const user = await prisma.user.findFirst({
    where: { id: "某个用户ID" },
    include: { accounts: true } // 必须显式包含关联字段才会返回
});
{
    id: "用户ID",
    name: "用户名",
    email: "用户邮箱", 
    // 其他用户字段... 
    accounts: [ 
        // 数组形式,包含所有关联的Account记录 
        {
            id: "账户1ID",
            userId: "关联的用户ID",
            type: "oauth",
            provider: "github",
            providerAccountId: "github的用户ID",
            // 其他Account字段...
        }, 
        {
            id: "账户2ID",
            userId: "关联的用户ID",
            type: "email",
            provider: "email",
            providerAccountId: "用户邮箱",
            // 其他Account字段...
        }
        // 可能有更多关联账户...
    ]
}

onDelete: Cascade 说明:

  • onDelete: Cascade 是一种级联删除配置,用于定义当主表(被引用的表)中的记录被删除时,从表(引用主表的表)中关联的记录应该如何处理。
  • 默认 onDelete: Restrict

@@unique([provider, providerAccountId]) 说明:

  • @@unique 是模型级别的复合唯一约束,写在模型的最后,用于保证多个字段的组合值唯一。
数据库 node.js orm prisma
icon_mrgreen.gificon_neutral.gificon_twisted.gificon_arrow.gificon_eek.gificon_smile.gificon_confused.gificon_cool.gificon_evil.gificon_biggrin.gificon_idea.gificon_redface.gificon_razz.gificon_rolleyes.gificon_wink.gificon_cry.gificon_surprised.gificon_lol.gificon_mad.gificon_sad.gificon_exclaim.gificon_question.gif
Theme Jasmine by Kent Liao