vercel在《Learn Next.js》教程中提供了连接vercel平台数据库的方法。Vercel 最初是由 Next.js 背后的团队创建的,所以在教程中主推自家的云平台服务,其实无可厚非。但是对于程序员来说,自己的产品被某一平台、技术绑定,是很没安全感的事情。
下面介绍一下如何使用Prisma代替@vercel/postgres连接MySQL数据库,本篇博客包含以下内容:
- 前提
- 安装Prisma
- 初始化Prisma
- 设置数据库连接
- 配置ORM映射
- 使用Prisma Migrate
- 安装和生成Prisma Client
- 使用Prisma
什么是Prisma?
Prisma 是一个开源的数据库工具和 ORM(对象关系映射)库,它旨在简化数据库交互并提高开发效率。Prisma 通过生成类型安全和自动完成的查询构建器,使得数据库操作更加直观和易于管理。它支持多种数据库,包括 PostgreSQL、MySQL、SQLite 以及 MongoDB(通过 Prisma MongoDB 连接器)。
简单的说,Prisma就是Javascript的ORM框架!它简化了JavaScript的数据库操作,并支持多种数据库。
设置步骤
- 前提
- 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
这个命令做了两件事:
- 创建一个名为 prisma 的新目录,其中包含一个名为 schema.prisma 的文件,该文件包含 Prisma 模式,包括数据库连接变量和模式模型。
- 在项目的根目录中创建一个 .env 文件,该文件用于定义环境变量(例如数据库连接)。
4. 设置数据库连接
接下来,要连接数据库,需要将 Prisma 模式中 datasource 块的 url 字段设置为的数据库连接 URL:
prisma/schema.prisma
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"
注意 在开发环境中是在.env中进行定义,但是打包成Docker镜像,并以容器的方式启动,这个URL大概率是不对的,因为你不会在一个容器里包含web服务与MySQL服务。所以这里的URL会是另一个容器,或者另一台服务器上的服务。
所以对于生产环境,我会在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将被更新,例如:
prisma/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 主要有以下功能:
- 自动生成迁移文件:根据你对 Prisma Schema 的修改,Prisma Migrate 自动生成 SQL 迁移文件。这些文件记录了数据库架构的变更,如新增或删除表、修改字段等。
- 执行迁移:它可以执行生成的迁移文件,将变更应用到数据库中,从而保持 Prisma Schema 和实际数据库结构的同步。
- 回滚迁移:如果某次迁移出现问题,Prisma Migrate 提供回滚机制,允许你恢复到之前的迁移状态。
- 跟踪迁移历史:每次迁移都会被记录下来,Prisma 会维护一张
_prisma_migrations
表来跟踪所有迁移的历史记录,确保数据库状态是可追踪和一致的。 - 跨环境同步数据库架构: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_init 为applied。
你现在有了一个当前数据库模式的基线。要对你的数据库模式进行进一步的更改,你可以更新你的 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 实例(防止连接数过多)。
- 它设置了日志配置,可以选择将查询、错误、信息和警告等日志输出到控制台或者通过事件捕获。
- 还特别监听了查询事件,可以输出查询语句、参数和执行时间,帮助开发者调试和优化查询。
发表回复