日拱一卒无有尽,功不唐捐终入海

在Next.js中使用Prisma代替@vercel/postgres连接MySQL数据库

vercel在《Learn Next.js》教程中提供了连接vercel平台数据库的方法。Vercel 最初是由 Next.js 背后的团队创建的,所以在教程中主推自家的云平台服务,其实无可厚非。但是对于程序员来说,自己的产品被某一平台、技术绑定,是很没安全感的事情。

下面介绍一下如何使用Prisma代替@vercel/postgres连接MySQL数据库,本篇博客包含以下内容:

  1. 前提
  2. 安装Prisma
  3. 初始化Prisma
  4. 设置数据库连接
  5. 配置ORM映射
  6. 使用Prisma Migrate
  7. 安装和生成Prisma Client
  8. 使用Prisma

什么是Prisma

Prisma 是一个开源的数据库工具和 ORM(对象关系映射)库,它旨在简化数据库交互并提高开发效率。Prisma 通过生成类型安全和自动完成的查询构建器,使得数据库操作更加直观和易于管理。它支持多种数据库,包括 PostgreSQL、MySQL、SQLite 以及 MongoDB(通过 Prisma MongoDB 连接器)。

简单的说,Prisma就是Javascript的ORM框架它简化了JavaScript的数据库操作,并支持多种数据库

设置步骤

  1. 前提
  • Next.js项目
  • 已设置好的MySQL数据库,比如名称为demo
  • 已创建好的数据库用户,名称为demo_user,密码为123456
  • 已设置好的数据库权限,demo_user可以访问demo数据库
  • 数据库的host为localhost,端口为3306

2. 安装Prisma

打开终端,在Next.js项目的根目录下完成以下操作:

npm install prisma --save-dev

这条命令的作用是在Node.js项目中安装Prisma,并将其设置为开发依赖。Prisma是一个数据库工具,可以帮助你更容易地与数据库交互。使用--save-dev参数意味着Prisma只会在开发环境中使用,不会包含在生产环境的部署中。

3. 初始化Prisma

npx prisma init

这个命令做了两件事:

  1. 创建一个名为 prisma 的新目录,其中包含一个名为 schema.prisma 的文件,该文件包含 Prisma 模式,包括数据库连接变量和模式模型。
  2. 在项目的根目录中创建一个 .env 文件,该文件用于定义环境变量(例如数据库连接)。

4. 设置数据库连接

接下来,要连接数据库,需要将 Prisma 模式中 datasource 块的 url 字段设置为的数据库连接 URL:

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

请注意,默认情况下,由 prisma init 创建的模式使用的是 PostgreSQL,所以你首先需要将provider切换为 MySQL:

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

在这种情况下,URL 是通过在 .env 文件中定义的环境变量来设置的:

DATABASE_URL="mysql://demo_user:123456@localhost:3306/demo"

所以对于生产环境,我会在Docker Compose中设置环境变量:

services:
  app:
    image: demo:latest
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=mysql://demo_user:123456@the_database_ip:3306/demo

同时会在.dockerignore文件中加上.env,即打包成镜像的时候忽略.env文件。

5. 配置ORM映射

npx prisma db pull

这个命令读取在 .env 文件中定义的 DATABASE_URL 环境变量,并连接到你的数据库。一旦连接建立,它就会对数据库进行内省 introspects(即读取数据库模式)。然后,它将数据库模式从 SQL 翻译成 Prisma 数据模型。

内省 introspects 完成后,你的schema.prisma将被更新,例如:

model Post {
  id        Int      @id @default(autoincrement())
  title     String   @db.VarChar(255)
  createdAt DateTime @default(now()) @db.Timestamp(0)
  content   String?  @db.Text
  published Boolean  @default(false)
  authorId  Int
  User      User     @relation(fields: [authorId], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "Post_ibfk_1")

  @@index([authorId], map: "authorId")
}

model Profile {
  id     Int     @id @default(autoincrement())
  bio    String? @db.Text
  userId Int     @unique(map: "userId")
  User   User    @relation(fields: [userId], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "Profile_ibfk_1")
}

model User {
  id      Int      @id @default(autoincrement())
  name    String?  @db.VarChar(255)
  email   String   @unique(map: "email") @db.VarChar(255)
  Post    Post[]
  Profile Profile?
}

这个文件基本上就是ORM中的“O”(即对象)了,之后我们使用JavaScript的时候会使用这些“O”去操作数据库。对象与数据表的映射永远是ORM框架中最核心的主题。在Prisma中是怎么进行设置的,一言两语是说不清楚的,有兴趣的同学可以去官网上看看。

6. 使用Prisma Migrate 

软件开发讲究“迭代”,系统功能会迭代,数据库表也不是一成不变的,也会迭代。当我们在开发环境对数据库表及表结构进行调整的时候,需要把调整的过程给记录下来,以便在更新生产环境功能的时候,在生产数据库“重播”调整过程,即同步更新生产环境的数据库。

Prisma Migrate简化了这一过程。

Prisma Migrate 是一种数据库迁移工具,用于在项目开发过程中管理和执行数据库架构的变更。它能够根据 Prisma Schema 的变化生成、应用和回滚数据库迁移。Prisma Migrate 主要有以下功能:

  1. 自动生成迁移文件:根据你对 Prisma Schema 的修改,Prisma Migrate 自动生成 SQL 迁移文件。这些文件记录了数据库架构的变更,如新增或删除表、修改字段等。
  2. 执行迁移:它可以执行生成的迁移文件,将变更应用到数据库中,从而保持 Prisma Schema 和实际数据库结构的同步。
  3. 回滚迁移:如果某次迁移出现问题,Prisma Migrate 提供回滚机制,允许你恢复到之前的迁移状态。
  4. 跟踪迁移历史:每次迁移都会被记录下来,Prisma 会维护一张 _prisma_migrations 表来跟踪所有迁移的历史记录,确保数据库状态是可追踪和一致的。
  5. 跨环境同步数据库架构:Prisma Migrate 支持在不同的环境(如开发、测试、生产)中同步数据库架构,通过共享迁移文件,确保所有环境的数据库架构一致。

它对于需要频繁更新数据库结构的项目特别有用,可以帮助开发者更好地管理和控制数据库变更的过程。

要使用 Prisma Migrate 与上一节中你内省的数据库一起使用,你需要为你的数据库建立基线。

基线化是指为你的数据库初始化迁移历史,这个数据库可能已经包含数据且不能重置,比如你的生产数据库。基线化告诉 Prisma Migrate 假设已经有一到多个迁移已经应用到你的数据库上。

要为你的数据库建立基线,使用 prisma migrate diff 来比较你的模式和数据库,并将输出保存到一个 SQL 文件中。

首先,创建一个迁移目录,并在其中添加一个你为迁移命名的目录。在这个例子中,我们将使用 0_init 作为迁移名称:

mkdir -p prisma/migrations/0_init

接下来,使用 prisma migrate diff 生成迁移文件:

npx prisma migrate diff --from-empty --to-schema-datamodel prisma/schema.prisma --script > prisma/migrations/0_init/migration.sql
  • –from-empty:假设你正在迁移的数据模型是空的
  • –to-schema-datamodel:使用 datasource 块中的 URL 表示的当前数据库状态
  • –script:输出一个 SQL 脚本

检查你的SQL 迁移文件(即prisma/migrations/0_init/migration.sql)以确保一切正确。

如果SQL迁移文件正确,接下来,使用 prisma migrate resolve 并带上 --applied 参数来标记迁移为已应用。

npx prisma migrate resolve --applied 0_init

该命令将通过将其添加到 _prisma_migrations 表来标记 0_initapplied

你现在有了一个当前数据库模式的基线。要对你的数据库模式进行进一步的更改,你可以更新你的 Prisma 模式,并使用 prisma migrate dev 将更改应用到你的数据库。

以下是使用Prisma Migrate特别特别要注意的注意事项

  • 备份数据库:在生产环境应用任何数据库变更之前,务必备份你的数据库,以防迁移失败或产生不可预期的效果;
  • 避免破坏性变更:生产环境中需要格外注意不要进行破坏性的变更(如删除表或字段),这些操作可能会导致数据丢失;
  • 请测试:确保在开发、测试环境中反复验证后再推送到生产环境。Prisma Migrate不是万能的,不能完全依赖它, 必要时还是需要手动操作数据库。

7. 安装和生成Prisma Client


Prisma Client是一个用于与数据库进行交互的自动生成的 TypeScript/JavaScript ORM(对象关系映射)库。Prisma Client 根据你的 Prisma Schema 自动生成强类型的数据库查询 API,使得你可以用 TypeScript/JavaScript 代码以类型安全的方式访问和操作数据库中的数据。

Prisma Schema定义了数据与对象的关系,Prisma Client基于Prisma Schema生成数据库查询 API,这个API是类型安全的。

要开始使用 Prisma Client,你需要安装 @prisma/client 包:

npm install @prisma/client

请注意,@prisma/client 节点模块引用了一个名为 .prisma/client 的文件夹。.prisma/client 文件夹包含了你生成的 Prisma Client,使用以下命令Prisma Client:

npx prisma generate

Prisma Client基于Prisma Schema,因此每次更新Prisma Schema后都需要重新运行这个命令,重新生成Prisma Client

8. 使用Prisma

Prisma Client生成后,就可以在Next.js中连接数据库并执行增删改查等操作了。具体的方法就不展开说了,有兴趣的同学可以查看官方文档,很详细。

在这里,给出我的一个工具代码,以便使用Prisma,供大家参考:

import { Prisma, PrismaClient } from '@prisma/client';

const globalForPrisma = globalThis as unknown as { prisma: PrismaClient };

let prisma: PrismaClient;

if (globalForPrisma.prisma) {
  prisma = globalForPrisma.prisma;
} else {
  let p = new PrismaClient({
    log: [
      {
        emit: 'event',
        level: 'query',
      },
      {
        emit: 'stdout',
        level: 'error',
      },
      {
        emit: 'stdout',
        level: 'info',
      },
      {
        emit: 'stdout',
        level: 'warn',
      },
    ],
  });

  p.$on('query', (e: Prisma.QueryEvent) => {
    console.log('Query: ' + e.query);
    console.log('Params: ' + e.params);
    console.log('Duration: ' + e.duration + 'ms');
  });

  prisma = p;
}

if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;

export default prisma;
  • 这个代码用于创建并配置 Prisma Client,并且使用全局缓存机制来避免在开发环境中多次创建 Prisma Client 实例(防止连接数过多)。
  • 它设置了日志配置,可以选择将查询、错误、信息和警告等日志输出到控制台或者通过事件捕获。
  • 还特别监听了查询事件,可以输出查询语句、参数和执行时间,帮助开发者调试和优化查询。


评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注