数据建模
什么是数据建模?
术语 数据建模 指的是 定义应用程序中对象的形状和结构的过程,这些对象通常被称为“应用程序模型”。在关系型数据库(如 PostgreSQL)中,它们存储在 表中。在使用文档数据库(如 MongoDB)时,它们存储在 集合中。
根据您的应用程序领域,模型将有所不同。例如,如果您正在编写一个博客应用程序,您可能拥有诸如 博客、作者、文章 等模型。当编写汽车共享应用程序时,您可能拥有诸如 司机、汽车、路线 等模型。应用程序模型使您能够通过创建各自的 数据结构 在代码中表示这些不同的实体。
在建模数据时,您通常会问以下问题:
- 我的应用程序中的主要实体/概念是什么?
- 它们之间如何关联?
- 它们的主要特征/属性是什么?
- 如何使用我的技术栈表示它们?
不使用 Prisma ORM 的数据建模
数据建模通常需要在(至少)两个层面进行:
- 在数据库层面
- 在应用程序层面(即,在您的编程语言中)
应用程序模型在这两个层面上的表示方式可能因以下几个原因而不同:
- 数据库和编程语言使用不同的数据类型
- 关系在数据库中的表示方式与在编程语言中的表示方式不同
- 数据库通常具有更强大的数据建模能力,例如索引、级联删除或各种附加约束(例如 unique, not null 等)
- 数据库和编程语言具有不同的技术约束
数据库层面的数据建模
关系型数据库
在关系型数据库中,模型由 表 表示。例如,您可以定义一个 users
表来存储关于应用程序用户的信息。使用 PostgreSQL,您将按如下方式定义它:
CREATE TABLE users (
user_id SERIAL PRIMARY KEY NOT NULL,
name VARCHAR(255),
email VARCHAR(255) UNIQUE NOT NULL,
isAdmin BOOLEAN NOT NULL DEFAULT false
);
包含一些随机数据的 users
表的可视化表示可能如下所示:
user_id | name | email | isAdmin |
---|---|---|---|
1 | Alice | alice@prisma.io | false |
2 | Bob | bob@prisma.io | false |
3 | Sarah | sarah@prisma.io | true |
它包含以下列:
user_id
: 一个整数,随着users
表中的每条新记录而递增。它也代表每条记录的主键。name
: 一个最多 255 个字符的字符串。email
: 一个最多 255 个字符的字符串。此外,添加的约束表明,对于email
列,不能有两条记录具有重复的值,并且 每条 记录都需要有一个值。isAdmin
: 一个布尔值,指示用户是否拥有管理员权限(默认值:false
)
MongoDB
在 MongoDB 数据库中,模型由 集合 表示,并包含可以具有任何结构的 文档
{
_id: '607ee94800bbe41f001fd568',
slug: 'prisma-loves-mongodb',
title: 'Prisma <3 MongoDB',
body: "This is my first post. Isn't MongoDB + Prisma awesome?!"
}
Prisma Client 当前期望一个一致的模型和规范化的模型设计。这意味着:
- 如果模型或字段在 Prisma schema 中不存在,则会被忽略
- 如果一个字段是强制性的但在 MongoDB 数据集中不存在,您将收到错误
应用程序层面的数据建模
除了创建表示应用程序领域实体的表之外,您还需要在编程语言中创建应用程序模型。在面向对象的语言中,这通常通过创建 类 来表示您的模型。根据编程语言的不同,这也可以通过 接口 或 结构体 来完成。
您的数据库中的表与您在代码中定义的模型之间通常存在很强的相关性。例如,为了在应用程序中表示上述 users
表中的记录,您可以定义一个类似于此的 JavaScript (ES6) 类:
class User {
constructor(user_id, name, email, isAdmin) {
this.user_id = user_id
this.name = name
this.email = email
this.isAdmin = isAdmin
}
}
使用 TypeScript 时,您可能改为定义一个接口:
interface User {
user_id: number
name: string
email: string
isAdmin: boolean
}
注意,在这两种情况下,User
模型都具有与上例中 users
表相同的属性。虽然数据库表和应用程序模型之间通常存在 1:1 的映射关系,但也可能出现模型在数据库和应用程序中的表示方式完全不同的情况。
通过这种设置,您可以从 users
表中检索记录,并将其存储为您 User
类型的实例。以下代码片段使用 pg
作为 PostgreSQL 驱动程序,并基于上面定义的 JavaScript 类创建一个 User
实例:
const resultRows = await client.query('SELECT * FROM users WHERE user_id = 1')
const userData = resultRows[0]
const user = new User(
userData.user_id,
userData.name,
userData.email,
userData.isAdmin
)
// user = {
// user_id: 1,
// name: "Alice",
// email: "alice@prisma.io",
// isAdmin: false
// }
请注意,在这些示例中,应用程序模型是“愚笨”的,这意味着它们不实现任何逻辑,其唯一目的是作为 普通旧 JavaScript 对象 来承载数据。
使用 ORM 进行数据建模
ORM 通常用于面向对象的语言中,以使开发人员更容易使用数据库。ORM 的关键特征在于它允许您以 类 的形式对应用程序数据进行建模,这些类映射到底层数据库中的 表。
与上面解释的方法相比,主要区别在于这些类不仅承载数据,还实现了大量的逻辑。主要用于存储、检索、序列化和反序列化,但有时它们也实现特定于您应用程序的业务逻辑。
这意味着,您无需编写 SQL 语句来读取和写入数据库中的数据,而是由您的模型类实例提供用于存储和检索数据的 API。
Sequelize 是 Node.js 生态系统中流行的 ORM,这是如何使用 Sequelize 的建模方法定义与前面部分相同的 User
模型:
class User extends Model {}
User.init(
{
user_id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
},
name: Sequelize.STRING(255),
email: {
type: Sequelize.STRING(255),
unique: true,
},
isAdmin: Sequelize.BOOLEAN,
},
{ sequelize, modelName: 'user' }
)
为了让这个 User
类示例正常工作,您仍然需要在数据库中创建相应的表。使用 Sequelize,您有两种方法可以做到这一点:
- 运行
User.sync()
(通常不建议用于生产环境) - 使用Sequelize 迁移来更改您的数据库 schema
请注意,您不会像前一节所示那样手动实例化 User
类(使用 new User(...)
),而是调用 User
类上的 静态 方法,这些方法随后返回 User
模型实例:
const user = await User.findByPk(42)
调用 findByPk
会创建一个 SQL 语句,用于检索由 ID 值 42
标识的 User
记录。
结果的 user
对象是 Sequelize 的 Model
类的一个实例(因为 User
继承自 Model
)。它不是 POJO,而是一个实现了 Sequelize 附加行为的对象。
使用 Prisma ORM 进行数据建模
根据您希望在应用程序中使用 Prisma ORM 的哪些部分,数据建模流程会略有不同。以下两节解释了仅使用Prisma Client和使用Prisma Client 和 Prisma Migrate的工作流程。
无论采用哪种方法,使用 Prisma ORM,您都无需通过手动定义类、接口或结构体来在编程语言中创建应用程序模型。相反,应用程序模型在您的Prisma schema中定义:
- 仅使用 Prisma Client: Prisma schema 中的应用程序模型是 基于数据库 schema 的内省生成的。数据建模主要在数据库层面进行。
- Prisma Client 和 Prisma Migrate: 数据建模通过在 Prisma schema 中 手动添加应用程序模型 来进行。Prisma Migrate 将这些应用程序模型映射到底层数据库中的表(目前仅支持关系型数据库)。
例如,前面示例中的 User
模型在 Prisma schema 中将表示如下:
model User {
user_id Int @id @default(autoincrement())
name String?
email String @unique
isAdmin Boolean @default(false)
}
一旦应用程序模型进入您的 Prisma schema(无论是通过内省还是您手动添加的),下一步通常是生成 Prisma Client,它提供了程序化且类型安全的 API,用于以应用程序模型的形状读写数据。
Prisma Client 使用 TypeScript 类型别名来在代码中表示您的应用程序模型。例如,User
模型在生成的 Prisma Client 库中将表示如下:
export type User = {
id: number
name: string | null
email: string
isAdmin: boolean
}
除了生成的类型之外,安装 @prisma/client
包后,Prisma Client 还提供了一个数据访问 API,您可以用来读写数据:
import { PrismaClient } from '@prisma/client'
// or
// const { PrismaClient } = require('@prisma/client')
const prisma = new PrismaClient()
// use inside an `async` function to `await` the result
await prisma.user.findUnique(...)
await prisma.user.findMany(...)
await prisma.user.create(...)
await prisma.user.update(...)
await prisma.user.delete(...)
await prisma.user.upsert(...)
仅使用 Prisma Client
当您的应用程序中仅使用 Prisma Client 而 不 使用 Prisma Migrate 时,数据建模需要在数据库层面通过 SQL 进行。一旦您的 SQL schema 准备就绪,您就可以使用 Prisma 的内省功能将应用程序模型添加到您的 Prisma schema 中。最后,您生成 Prisma Client,它会为您创建类型以及用于在数据库中读写数据的程序化 API。
主要工作流程概述如下:
- 使用 SQL 更改您的数据库 schema(例如
CREATE TABLE
,ALTER TABLE
等) - 运行
prisma db pull
以内省数据库并将应用程序模型添加到 Prisma schema 中 - 运行
prisma generate
以更新您的 Prisma Client API
使用 Prisma Client 和 Prisma Migrate
使用Prisma Migrate时,您在 Prisma schema 中定义您的应用程序模型,对于关系型数据库,使用 prisma migrate
子命令生成纯 SQL 迁移文件,您可以在应用之前进行编辑。对于 MongoDB,您可以使用 prisma db push
来代替,它会直接将更改应用到您的数据库。
主要工作流程概述如下:
- 手动更改 Prisma schema 中的应用程序模型(例如添加新模型、删除现有模型等)
- 运行
prisma migrate dev
以创建和应用迁移,或运行prisma db push
以直接应用更改(在这两种情况下,Prisma Client 都会自动生成)