海外AI项目-最佳实践数据库和Typeorm

584 阅读7分钟

引言

书接上文,这一节主要讲prisma ormpostgresnext中的完整实现。

1、什么是orm

简单的理解就是,ORM对数据库进行了抽象,提供更高级和贴近语言的方式,进行数据库各种操作和查询以及管理迁移等,且统一了各种数据库,实现上层语言操作。

Ex:

// 原始sql伪代码
INSERT INTO "Conversation" ("id", "userId")
VALUES (:id, :userId);

UPDATE "User"
SET "settings" = :settings
WHERE "id" = :userId;

await prisma.conversation.create({
    data: {
      id,
      userId: session.user.id
    }
  })

await prisma.user.update({
    where: { id: userId },
    data: {
      settings
    }
 })

ORM通过提供更高级的抽象,简化了这些操作,同时保持了对底层 SQL 的控制,在更复杂的场景下会体现得更好。

2. 数据库创建

我们直接通过dockerdocker-compose来创建数据库。我写过一篇有关容器的文章(删了后面一部分,那后面是go的容器扩展开发,有兴趣的也可以来讨论一下)可以简单看一下。

首先我们在根目录下创建一个.env 文件包含以下内容:

POSTGRES_DB=you_db_name
POSTGRES_USER=you_db_user
POSTGRES_PASSWORD=your_strong_password_here
POSTGRES_PORT=5988

默认行为: Docker Compose 默认会查找当前目录中名为 .env 的文件,并自动加载其中定义的环境变量。这是内置的行为。

关于密码、用户名、端口的问题:请不要使用默认这种postgresdb nameuser。以及最重要的端口,请不要默认5432:5432。都是一些安全教训了。

紧接着让我们在根目录下创建 docker-compose.db.yml 文件:

Ex:

version: '3.8'

services:
  db:
    image: postgres:15
    command:
      - 'postgres'
      - '-c'
      - 'max_connections=1000'
    shm_size: '4gb'
    container_name: postgres-container
    restart: unless-stopped
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "${POSTGRES_PORT}:5432"

volumes:
  postgres_data:

就让我们跑起来吧!

docker-compose -f docker-compose.db.yml up -d

这个命令指定使用 docker-compose.db.yml 文件,并在后台启动 PostgreSQL 容器。

当我们输入 docker ps可以看到这个容器

Ex:

紧接着我们就可以在数据库工具中链接啦。

当我们需要停止或重启数据库时,使用以下命令:

# 停止数据库
docker-compose -f docker-compose.db.yml down

# 重启数据库
docker-compose -f docker-compose.db.yml up -d

当我们已经配置好数据库,接下来的一步就来到了Prisma链接数据库,创建表结构了~。

2、prisma使用

依赖安装

首先,让我们安装必要的依赖:

pnpm add @prisma/client
pnpm add prisma -D

这个命令会安装 Prisma CLIPrisma Client

Schema 和数据库的关系

Prisma schemaPrisma 的核心概念之一。它是一个声明性文件,用于定义我们的数据库和 Prisma Client 的生成。

  1. Schema 定义
    Schema 文件通常命名为 schema.prisma,它定义了你的数据模型、数据源(数据库连接)和生成器(用于生成 Prisma Client)。
  2. 数据模型映射
    在 schema 中定义的模型直接映射到数据库中的表。例如,如果你定义了一个 User 模型,Prisma 会在数据库中创建一个对应的 User 表。
  3. 字段和列
    模型中的每个字段对应数据库表中的一列。Prisma 会根据字段的类型自动选择适当的数据库列类型。
  4. 关系
    Prisma schema 允许你定义模型之间的关系,这些关系会被转换为数据库中的外键关系。
  5. 数据库迁移
    当你修改 schema 文件时,Prisma 可以生成和应用数据库迁移,以保持数据库结构与你的 schema 定义同步。

让我们创建一个叫做prisma的文件夹,然后再创建schema.prisma文件,和client文件。

schema.prisma可以如下

Ex:

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

generator client {
  provider = "prisma-client-js"
}

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

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

这个 schema 定义了两个模型:UserPost,它们之间有一个一对多的关系。

接下来,你需要设置数据库连接。在你的 .env 文件中添加:

DATABASE_URL="postgresql://username:password@localhost:5988/your_database_name"

确保替换 usernamepasswordyour_database_name 为你实际的数据库配置。

然后,你可以使用 Prisma CLI 来创建数据库表:

pnpm prisma migrate dev --name init
// 或者
pnpm prisma db push

db push它直接将你的 Prisma schema 变更推送到数据库,而不创建迁移文件, 适用于开发阶段,特别是在频繁修改数据模型时, 因为不创建迁移文件,所以无法回滚更改。 第一次创建的init数据库的时候其实也可以直接db push

migrate dev 会创建一个新的迁移,应用它到数据库,并自动执行 prisma generate 生成 Prisma Client

Now, 你的数据库结构已经与 Prisma schema 同步,你可以开始使用 Prisma Client 来进行数据库操作了。

记住,每次你修改 schema 文件后,都需要运行 pnpm prisma migrate dev --name your_change 来更新 Prisma Client,以反映最新的 schema 更改。

这就是 Prisma schema 和数据库之间关系。Prisma 通过 schema 文件提供了一种强大而直观的方式来定义和管理你的数据库结构。

有关deploy

前面顺手讲了一些脚手架命令,但还有一个比较重要的命令是:prisma migrate deploy

pnpm prisma migrate deployPrisma 提供的一个用于生产环境的数据库迁移命令。这个命令的主要目的是将你的数据库schema更新到最新版本,通常在生产环境或其他非开发环境中使用。

  1. 用途:
    • 应用所有待处理的迁移到数据库。
    • 通常用于生产环境或任何需要以可预测和可控方式更新数据库的环境。
  1. 工作原理:
    • 读取 prisma/migrations 目录中的所有迁移文件。
    • 检查数据库中的 _prisma_migrations 表,确定哪些迁移尚未应用。
    • 按顺序应用所有未应用的迁移。
  1. 特点:
    • 不会生成新的迁移文件。
    • 不会修改你的 Prisma schema 文件。
    • 不会重新生成 Prisma Client。
  1. 安全性:
    • 在应用迁移之前,会检查数据库的当前状态是否与预期一致。
    • 如果检测到数据库状态与预期不符,会停止迁移过程并报错。
  1. 使用场景:
    • 在 CI/CD 流程中自动更新数据库schema。
    • 在生产服务器上手动更新数据库schema。
  1. 使用方法:
pnpm prisma migrate deploy

7. 最佳实践:

    • 在应用迁移之前,始终备份你的数据库。
    • 在生产环境应用迁移之前,先在类似的测试环境中测试。
    • 确保所有的迁移都已经在开发环境中经过测试和验证。
  1. 注意事项:
    • 这个命令不会创建新的迁移或修改现有的迁移。
    • 如果你需要在生产环境中创建新的迁移,应该先在开发环境中创建和测试,然后将迁移文件提交到版本控制系统。
  1. prisma migrate dev 的区别:
    • migrate dev 用于开发环境,会创建新的迁移文件。
    • migrate deploy 只应用现有的迁移,不创建新的迁移文件。

使用 pnpm prisma migrate deploy 可以确保你的生产数据库schema始终与你的应用程序代码同步,同时提供了一种安全和可控的方式来管理数据库变更。

Prisma客户端使用和基础

让我们遵循官网的最佳实践,这可以解决,Next.js 的热重载功能会频繁地重新加载模块,以立即反映代码更改。但是,这会导致创建 Prisma Client 的多个实例,从而导致意外行为。

而下面的代码也就是prisma/client.ts的内容,解决了这个问题,并且抛出了prisma,也是我们后面使用的。

SourceCode:

import { PrismaClient } from '@prisma/client'

const prismaClientSingleton = () => {
  return new PrismaClient()
}

declare global {
  // eslint-disable-next-line no-var
  var prisma: undefined | ReturnType<typeof prismaClientSingleton>
}

const prisma = globalThis.prisma ?? prismaClientSingleton()

export default prisma

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

接下来我们就可以使用prisma进行操作了

Ex:

await prisma.user.update({
  where: { email: token.email },
  data: {
    subscriptions: {
      create: FreePlan
    }
  }
})

结束

项目在github上。

也可以加群大家一起闲聊、探讨、赚钱都挺好的。