使用 Prisma 连接到 TiDB

TiDB 是一个兼容 MySQL 的数据库。Prisma 是当前流行的 Node.js ORM 框架之一。

本文档将展示如何使用 TiDB 和 Prisma 来构造一个简单的 CRUD 应用程序。

  • 配置你的环境。
  • 使用 Prisma 连接到 TiDB 集群。
  • 构建并运行你的应用程序。你也可以参考示例代码片段,完成基本的 CRUD 操作。

Prisma - 图1

注意

本文档适用于 TiDB Serverless、TiDB Dedicated 和本地部署的 TiDB。

前置需求

为了能够顺利完成本教程,你需要提前:

  • 在你的机器上安装 Node.js 16.x 或以上版本。
  • 在你的机器上安装 Git。
  • 准备一个 TiDB 集群。

如果你还没有 TiDB 集群,可以按照以下方式创建:

运行代码并连接到 TiDB

本小节演示如何运行示例应用程序的代码,并连接到 TiDB。

第 1 步:克隆示例代码仓库到本地

运行以下命令,将示例代码仓库克隆到本地:

1. git clone https://github.com/tidb-samples/tidb-nodejs-prisma-quickstart.git
2. cd tidb-nodejs-prisma-quickstart

第 2 步:安装依赖

运行以下命令,安装示例代码所需要的依赖 (包括 prisma 依赖包):

1. npm install

在你现有的项目当中,你可以通过以下命令安装所需要的依赖包:

1. npm install prisma typescript ts-node @types/node --save-dev

第 3 步:配置连接信息

根据不同的 TiDB 部署方式,使用不同的方法连接到 TiDB 集群。

  • TiDB Serverless
  • TiDB Dedicated
  • 本地部署的 TiDB

  • 在 TiDB Cloud 的 Clusters 页面中,选择你的 TiDB Serverless 集群,进入集群的 Overview 页面。

  • 点击右上角的 Connect 按钮,将会弹出连接对话框。
  • 确认对话框中的选项配置和你的运行环境一致。

  • Endpoint Type 为 Public。

  • Branch 选择 main。
  • Connect With 选择 Prisma。
  • Operating System 为运行示例代码所在的操作系统。

Prisma - 图2

注意

如果你的程序在 Windows Subsystem for Linux (WSL) 中运行,请切换为对应的 Linux 发行版。 4. 如果你还没有设置密码,点击 Generate Password 按钮生成一个随机的密码。 5. 运行以下命令,将 .env.example 复制并重命名为 .env:

1. cp .env.example .env 6. 编辑 .env 文件,按照如下格式设置环境变量 DATABASE_URL,将占位符 {} 替换为从连接对话框中复制的连接字符串:

1. DATABASE_URL='{connection_string}'

Prisma - 图3

注意

在使用 Public Endpoint 连接 TiDB Serverless 集群时,必须启用 TLS 连接,请将 sslaccept 参数设置为 strict。 7. 保存 .env 文件。 8. 在 prisma/schema.prisma 文件中,将 provider 修改为 mysql,并将 url 修改为 env("DATABASE_URL"):

1. datasource db { 2. provider = "mysql" 3. url = env("DATABASE_URL") 4. } 9. 在 TiDB Cloud 的 Clusters 页面中,选择你的 TiDB Dedicated 集群,进入集群的 Overview 页面。 10. 点击右上角的 Connect 按钮,将会出现连接对话框。 11. 在对话框中点击 Allow Access from Anywhere,然后点击 Download CA cert 下载 TiDB Cloud 提供的 CA 证书。

更多配置细节,可参考 [TiDB Dedicated 标准连接教程(英文)](https://docs.pingcap.com/tidbcloud/connect-via-standard-connection)。
  1. 运行以下命令,将 .env.example 复制并重命名为 .env:

    1. cp .env.example .env 13. 编辑 .env 文件,按照如下格式设置环境变量 DATABASE_URL,将占位符 {} 替换为从连接对话框中复制的参数值:

    1. DATABASE_URL='mysql://{user}:{password}@{host}:4000/test?sslaccept=strict&sslcert={downloaded_ssl_ca_path}'

    Prisma - 图4

    注意

    推荐在使用 Public Endpoint 连接 TiDB Dedicated 集群时,启用 TLS 连接。

    为了启用 TLS (SSL) 连接,将 DATABASE_URL 末尾添加 sslaccept=strict 参数,并使用 sslcert=/path/to/ca.pem 参数指定从连接对话框中下载的 CA 证书的文件路径。 14. 保存 .env 文件。 15. 在 prisma/schema.prisma 文件中,将 provider 修改为 mysql,并将 url 修改为 env("DATABASE_URL"):

    1. datasource db { 2. provider = "mysql" 3. url = env("DATABASE_URL") 4. } 16. 运行以下命令,将 .env.example 复制并重命名为 .env:

    1. cp .env.example .env 17. 编辑 .env 文件,按照如下格式设置连接信息,将占位符 {} 替换为你的 TiDB 集群的连接参数值:

    1. DATABASE_URL='mysql://{user}:{password}@{host}:4000/test'

    如果你在本地运行 TiDB 集群,默认的 Host 是 127.0.0.1, 默认用户名为 root, 密码为空。 18. 保存 .env 文件。 19. 在 prisma/schema.prisma 文件中,将 provider 修改为 mysql,并将 url 修改为 env("DATABASE_URL"):

    1. datasource db { 2. provider = "mysql" 3. url = env("DATABASE_URL") 4. }

第 4 步:初始化表结构

运行以下命令,使用 Prisma Migrate 根据 prisma/schema.prisma 文件中的数据模型定义来初始化数据库表结构:

1. npx prisma migrate dev

prisma.schema 文件中的模型定义:

1. // 定义一个 Player 模型,表示 `players` 表。
2. model Player {
3. id        Int      @id @default(autoincrement())
4. name      String   @unique(map: "uk_player_on_name") @db.VarChar(50)
5. coins     Decimal  @default(0)
6. goods     Int      @default(0)
7. createdAt DateTime @default(now()) @map("created_at")
8. profile   Profile?

10. @@map("players")
11. }

13. // 定义一个 Profile 模型,表示 `profiles` 表。
14. model Profile {
15. playerId  Int    @id @map("player_id")
16. biography String @db.Text

18. // 定义 `Player` 和 `Profile` 模型之间的 1:1 关系,并使用外键约束。
19. player    Player @relation(fields: [playerId], references: [id], onDelete: Cascade, map: "fk_profile_on_player_id")

21. @@map("profiles")
22. }

你可以通过查阅 Prisma 的 Data model 文档来了解如何在 prisma.schema 文件里定义数据模型。

预期执行结果:

1. Your database is now in sync with your schema.

3. ✔ Generated Prisma Client (5.1.1 | library) to ./node_modules/@prisma/client in 54ms

这个命令同时会根据 prisma/schema.prisma 文件中的模型定义,生成用于与数据库交互的 Prisma Client 的代码。

第 5 步:运行代码并查看结果

运行下述命令,执行示例代码:

1. npm start

示例代码中的主要逻辑:

1. // 步骤 1. 导入自动生成的 `@prisma/client` 依赖包。
2. import {Player, PrismaClient} from '@prisma/client';

4. async function main(): Promise<void> {
5. // 步骤 2. 创建一个新的 `PrismaClient` 实例。
6. const prisma = new PrismaClient();
7. try {

9. // 步骤 3. 使用 Prisma Client 执行一些 CRUD 操作。

11. } finally {
12. // 步骤 4. 断开 Prisma Client 的连接。
13. await prisma.$disconnect();
14. }
15. }

17. void main();

预期输出结果:

如果连接成功,在你的终端上会输出所连接集群的版本信息。

1. 🔌 Connected to TiDB cluster! (TiDB version: 5.7.25-TiDB-v6.6.0-serverless)
2. 🆕 Created a new player with ID 1.
3. ℹ️ Got Player 1: Player { id: 1, coins: 100, goods: 100 }
4. 🔢 Added 50 coins and 50 goods to player 1, now player 1 has 150 coins and 150 goods.
5. 🚮 Player 1 has been deleted.

示例代码片段

你可参考以下关键代码片段,完成自己的应用开发。

完整代码及其运行方式,见代码仓库 tidb-samples/tidb-nodejs-prisma-quickstart。

插入数据

下面的查询会创建一条新的 Player 记录,并返回一个包含自增 ID 的 Player 对象:

1. const player: Player = await prisma.player.create({
2. data: {
3. name: 'Alice',
4. coins: 100,
5. goods: 200,
6. createdAt: new Date(),
7. }
8. });
9. console.log(player.id);

更多信息参考插入数据。

查询数据

下面的查询会返回 ID 为 101 的 Player 记录,如果没有找到对应的记录,会返回 null:

1. const player: Player | null = prisma.player.findUnique({
2. where: {
3. id: 101,
4. }
5. });

更多信息参考查询数据。

更新数据

下面的查询会将 ID 为 101 的 Player 记录的 coins 和 goods 字段的值分别增加 50:

1. await prisma.player.update({
2. where: {
3. id: 101,
4. },
5. data: {
6. coins: {
7. increment: 50,
8. },
9. goods: {
10. increment: 50,
11. },
12. }
13. });

更多信息参考更新数据。

删除数据

下面的查询会删除 ID 为 101 的 Player 记录:

1. await prisma.player.delete({
2. where: {
3. id: 101,
4. }
5. });

更多信息参考删除数据。

注意事项

外键约束与 Prisma Relation Mode

你可以使用外键约束或 Prisma Relation Mode 来检查参照完整性:

  • 外键是 TiDB 从 v6.6.0 开始支持的实验特性,外键允许跨表交叉引用相关数据,外键约束则可以保证相关数据的一致性。

Prisma - 图5

警告

外键功能通常适用于为中小规模的数据提供完整性和一致性约束校验,但是在大数据量和分布式数据库系统下,使用外键可能会导致严重的性能问题,并对系统产生不可预知的影响。如果计划使用外键,请进行充分验证后谨慎使用。 - Prisma Relation Mode 是 Prisma Client 端对外键约束的模拟。该特性会对应用程序的性能产生一些影响,因为它需要额外的数据库查询来维护参照完整性。

下一步