Skip to main content

数据建模

什么是数据建模?

¥What is data modeling?

术语数据建模是指定义应用中对象的形状和结构的过程,这些对象通常称为 "应用模型"。在关系数据库(如 PostgreSQL)中,它们存储在表中。 当使用文档数据库(如 MongoDB)时,它们存储在集合中。

¥The term data modeling refers to the process of defining the shape and structure of the objects in an application, these objects are often called "application models". In relational databases (like PostgreSQL), they are stored in tables . When using document databases (like MongoDB), they are stored in collections.

根据你的应用域,模型会有所不同。例如,如果你正在编写博客应用,你可能拥有博客、作者、文章等模型。在编写汽车共享应用时,你可能会有驾驶员、汽车、路由等模型。应用模型使你能够通过创建各自的数据结构来在代码中表示这些不同的实体。

¥Depending on the domain of your application, the models will be different. For example, if you're writing a blogging application, you might have models such as blog, author, article. When writing a car-sharing app, you probably have models like driver, car, route. Application models enable you to represent these different entities in your code by creating respective data structures.

在对数据建模时,你通常会问以下问题:

¥When modeling data, you typically ask questions like:

  • 我的应用中的主要实体/概念是什么?

    ¥What are the main entities/concepts in my application?

  • 他们之间的关系如何?

    ¥How do they relate to each other?

  • 它们的主要特性/特性是什么?

    ¥What are their main characteristics/properties?

  • 他们如何用我的技术堆栈来表示?

    ¥How can they be represented with my technology stack?

没有 Prisma ORM 的数据建模

¥Data modeling without Prisma ORM

数据建模通常需要(至少)在两个层面上进行:

¥Data modeling typically needs to happen on (at least) two levels:

  • 在数据库层面

    ¥On the database level

  • 在应用级别(即,在你的编程语言中)

    ¥On the application level (i.e., in your programming language)

由于以下几个原因,应用模型在两个级别上的表示方式可能会有所不同:

¥The way that the application models are represented on both levels might differ due to a few reasons:

  • 数据库和编程语言使用不同的数据类型

    ¥Databases and programming languages use different data types

  • 关系在数据库中的表示方式与在编程语言中的表示方式不同

    ¥Relations are represented differently in a database than in a programming language

  • 数据库通常具有更强大的数据建模功能,例如索引、级联删除或各种附加约束(例如唯一、非空……)

    ¥Databases typically have more powerful data modeling capabilities, like indexes, cascading deletes, or a variety of additional constraints (e.g. unique, not null, ...)

  • 数据库和编程语言有不同的技术限制

    ¥Databases and programming languages have different technical constraints

数据库级别的数据建模

¥Data modeling on the database level

关系数据库

¥Relational databases

在关系数据库中,模型由表表示。例如,你可以定义 users 表来存储有关应用用户的信息。使用 PostgreSQL,你可以将其定义如下:

¥In relational databases, models are represented by tables. For example, you might define a users table to store information about the users of your application. Using PostgreSQL, you'd define it as follows:

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 表的直观表示可能如下所示:

¥A visual representation of the users table with some random data might look as follows:

user_idnameemailisAdmin
1Alicealice@prisma.iofalse
2Bobbob@prisma.iofalse
3Sarahsarah@prisma.iotrue

它有以下列:

¥It has the following columns:

  • user_id:一个整数,随着 users 表中的每个新记录而递增。它还代表每条记录的 首要的关键

    ¥user_id: An integer that increments with every new record in the users table. It also represents the primary key for each record.

  • name:最多包含 255 个字符的字符串。

    ¥name: A string with at most 255 characters.

  • email:最多包含 255 个字符的字符串。此外,添加的约束表示任何两个记录都不能具有 email 列的重复值,并且每个记录都需要有一个值。

    ¥email: A string with at most 255 characters. Additionally, the added constraints express that no two records can have duplicate values for the email column, and that every record needs to have a value for it.

  • isAdmin:一个布尔值,指示用户是否具有管理员权限(默认值:false

    ¥isAdmin: A boolean that indicates whether the user has admin rights (default value: false)

MongoDB

在 MongoDB 数据库中,模型由集合表示,并包含可以具有任何结构的文档:

¥In MongoDB databases, models are represented by collections and contain documents that can have any structure:

{
_id: '607ee94800bbe41f001fd568',
slug: 'prisma-loves-mongodb',
title: 'Prisma <3 MongoDB',
body: "This is my first post. Isn't MongoDB + Prisma awesome?!"
}

Prisma 客户端目前期望模型与 标准化模型设计 一致。这意味着:

¥Prisma Client currently expects a consistent model and normalized model design. This means that:

  • 如果模型或字段不存在于 Prisma 架构中,则会被忽略

    ¥If a model or field is not present in the Prisma schema, it is ignored

  • 如果某个字段是必填字段,但 MongoDB 数据集中不存在,则会收到错误消息

    ¥If a field is mandatory but not present in the MongoDB dataset, you will get an error

应用级别的数据建模

¥Data modeling on the application level

除了创建表示应用域中的实体的表之外,你还需要使用编程语言创建应用模型。在面向对象语言中,这通常是通过创建类来表示模型来完成的。根据编程语言,这也可以通过接口或结构来完成。

¥In addition to creating the tables that represent the entities from your application domain, you also need to create application models in your programming language. In object-oriented languages, this is often done by creating classes to represent your models. Depending on the programming language, this might also be done with interfaces or structs.

数据库中的表与代码中定义的模型之间通常存在很强的相关性。例如,要表示应用中上述 users 表中的记录,你可以定义一个类似于以下内容的 JavaScript (ES6) 类:

¥There often is a strong correlation between the tables in your database and the models you define in your code. For example, to represent records from the aforementioned users table in your application, you might define a JavaScript (ES6) class looking similar to this:

class User {
constructor(user_id, name, email, isAdmin) {
this.user_id = user_id
this.name = name
this.email = email
this.isAdmin = isAdmin
}
}

使用 TypeScript 时,你可以定义一个接口:

¥When using TypeScript, you might define an interface instead:

interface User {
user_id: number
name: string
email: string
isAdmin: boolean
}

请注意,两种情况下的 User 模型如何具有与上一示例中的 users 表相同的属性。虽然数据库表和应用模型之间通常存在 1:1 映射,但模型在数据库和应用中的表示方式也可能完全不同。

¥Notice how the User model in both cases has the same properties as the users table in the previous example. While it's often the case that there's a 1:1 mapping between database tables and application models, it can also happen that models are represented completely differently in the database and your application.

通过此设置,你可以从 users 表中检索记录并将它们存储为 User 类型的实例。以下示例代码片段使用 pg 作为 PostgreSQL 的驱动程序,并基于上面定义的 JavaScript 类创建 User 实例:

¥With this setup, you can retrieve records from the users table and store them as instances of your User type. The following example code snippet uses pg as the driver for PostgreSQL and creates a User instance based on the above defined JavaScript class:

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
// }

请注意,在这些示例中,应用模型是 "dumb",这意味着它们不实现任何逻辑,但它们的唯一目的是将数据作为普通的旧 JavaScript 对象进行传输。

¥Notice that in these examples, the application models are "dumb", meaning they don't implement any logic but their sole purpose is to carry data as plain old JavaScript objects.

使用 ORM 进行数据建模

¥Data modeling with ORMs

ORM 通常用在面向对象语言中,以使开发者更轻松地使用数据库。ORM 的关键特性是,它允许你根据映射到底层数据库中的表的类来对应用数据进行建模。

¥ORMs are commonly used in object-oriented languages to make it easier for developers to work with a database. The key characteristic of an ORM is that it lets you model your application data in terms of classes which are mapped to tables in the underlying database.

与上述方法相比,主要区别在于这些类不仅携带数据,而且还实现大量逻辑。主要用于存储、检索、序列化和反序列化,但有时它们也实现特定于你的应用的业务逻辑。

¥The main difference compared to the approaches explained above is these classes not only carry data but also implement a substantial amount of logic. Mostly for storage, retrieval, serialization, and deserialization, but sometimes they also implement business logic that's specific to your application.

这意味着,你不需要编写 SQL 语句来读取和写入数据库中的数据,而是模型类的实例提供 API 来存储和检索数据。

¥This means, you don't write SQL statements to read and write data in the database, but instead the instances of your model classes provide an API to store and retrieve data.

Sequelize 是 Node.js 生态系统中流行的 ORM,在使用 Sequelize 的建模方法之前,你可以通过以下部分定义相同的 User 模型:

¥Sequelize is a popular ORM in the Node.js ecosystem, this is how you'd define the same User model from the sections before using Sequelize's modeling approach:

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,你有两种方法可以做到这一点:

¥To get an example with this User class to work, you still need to create the corresponding table in the database. With Sequelize, you have two ways of doing this:

  • 运行 User.sync()(通常不建议用于生产)

    ¥Run User.sync() (typically not recommended for production)

  • 使用 连续迁移 更改数据库架构

    ¥Use Sequelize migrations to change your database schema

请注意,你永远不会像上一节中所示手动实例化 User 类(使用 new User(...)),而是调用 User 类上的静态方法,然后返回 User 模型实例:

¥Note that you'll never instantiate the User class manually (using new User(...)) as was shown in the previous section, but rather call static methods on the User class which then return the User model instances:

const user = await User.findByPk(42)

findByPk 的调用创建一个 SQL 语句来检索由 ID 值 42 标识的 User 记录。

¥The call to findByPk creates a SQL statement to retrieve the User record that's identified by the ID value 42.

生成的 user 对象是 Sequelize 的 Model 类的实例(因为 User 继承自 Model)。它不是 POJO,而是一个实现 Sequelize 附加行为的对象。

¥The resulting user object is an instance of Sequelize's Model class (because User inherits from Model). It's not a POJO, but an object that implements additional behavior from Sequelize.

使用 Prisma ORM 进行数据建模

¥Data modeling with Prisma ORM

根据你想要在应用中使用 Prisma ORM 的哪些部分,数据建模流程看起来略有不同。以下两节介绍使用 仅 Prisma 客户端 和使用 Prisma 客户端和 Prisma Migrate 的工作流程。

¥Depending on which parts of Prisma ORM you want to use in your application, the data modeling flow looks slightly different. The following two sections explain the workflows for using only Prisma Client and using Prisma Client and Prisma Migrate.

无论采用哪种方法,使用 Prisma ORM,你都无需通过手动定义类、接口或结构来用编程语言创建应用模型。相反,应用模型是在 Prisma 架构 中定义的:

¥No matter which approach though, with Prisma ORM you never create application models in your programming language by manually defining classes, interfaces, or structs. Instead, the application models are defined in your Prisma schema:

  • 仅 Prisma 客户端:Prisma 架构中的应用模型是根据数据库架构的内省生成的。数据建模主要发生在数据库级别。

    ¥Only Prisma Client: Application models in the Prisma schema are generated based on the introspection of your database schema. Data modeling happens primarily on the database-level.

  • Prisma 客户端和 Prisma 迁移:数据建模通过手动添加应用模型在 Prisma 模式中进行。Prisma Migrate 将这些应用模型映射到底层数据库中的表(当前仅支持关系数据库)。

    ¥Prisma Client and Prisma Migrate: Data modeling happens in the Prisma schema by manually adding application models to it. Prisma Migrate maps these application models to tables in the underlying database (currently only supported for relational databases).

例如,上一示例中的 User 模型在 Prisma 架构中表示如下:

¥As an example, the User model from the previous example would be represented as follows in the Prisma schema:

model User {
user_id Int @id @default(autoincrement())
name String?
email String @unique
isAdmin Boolean @default(false)
}

一旦应用模型位于你的 Prisma 架构中(无论它们是通过内省添加还是由你手动添加),下一步通常是生成 Prisma 客户端,它提供编程和类型安全的 API,用于以应用模型的形式读取和写入数据。

¥Once the application models are in your Prisma schema (whether they were added through introspection or manually by you), the next step typically is to generate Prisma Client which provides a programmatic and type-safe API to read and write data in the shape of your application models.

Prisma Client 使用 TypeScript 类型别名 来表示代码中的应用模型。例如,User 模型在生成的 Prisma 客户端库中将表示如下:

¥Prisma Client uses TypeScript type aliases to represent your application models in your code. For example, the User model would be represented as follows in the generated Prisma Client library:

export type User = {
id: number
name: string | null
email: string
isAdmin: boolean
}

除了生成的类型之外,Prisma Client 还提供了一个数据访问 API,你可以在安装 @prisma/client 软件包后使用该 API:

¥In addition to the generated types, Prisma Client also provides a data access API that you can use once you've installed the @prisma/client package:

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 客户端

¥Using only Prisma Client

当你的应用中仅使用 Prisma Client 而未使用 Prisma Migrate 时,需要通过 SQL 在数据库级别进行数据建模。一旦你的 SQL 架构准备就绪,你就可以使用 Prisma 的自省功能将应用模型添加到你的 Prisma 架构中。最后,你生成 Prisma 客户端,它创建类型以及编程 API,供你在数据库中读取和写入数据。

¥When using only Prisma Client and not using Prisma Migrate in your application, data modeling needs to happen on the database level via SQL. Once your SQL schema is ready, you use Prisma's introspection feature to add the application models to your Prisma schema. Finally, you generate Prisma Client which creates the types as well as the programmatic API for you to read and write data in your database.

以下是主要工作流程的概述:

¥Here is an overview of the main workflow:

  1. 使用 SQL 更改数据库架构(例如 CREATE TABLEALTER TABLE...)

    ¥Change your database schema using SQL (e.g. CREATE TABLE, ALTER TABLE, ...)

  2. 运行 prisma db pull 以内省数据库并将应用模型添加到 Prisma 架构

    ¥Run prisma db pull to introspect the database and add application models to the Prisma schema

  3. 运行 prisma generate 来更新你的 Prisma 客户端 API

    ¥Run prisma generate to update your Prisma Client API

使用 Prisma 客户端和 Prisma Migrate

¥Using Prisma Client and Prisma Migrate

使用 Prisma 迁移 时,你可以在 Prisma 模式中定义应用模型,对于关系数据库,使用 prisma migrate 子命令生成纯 SQL 迁移文件,你可以在应用之前对其进行编辑。对于 MongoDB,你可以使用 prisma db push,它将更改直接应用到数据库。

¥When using Prisma Migrate, you define your application model in the Prisma schema and with relational databases use the prisma migrate subcommand to generate plain SQL migration files, which you can edit before applying. With MongoDB, you use prisma db push instead which applies the changes to your database directly.

以下是主要工作流程的概述:

¥Here is an overview of the main workflow:

  1. 手动更改 Prisma 架构中的应用模型(例如添加新模型、删除现有模型……)

    ¥Manually change your application models in the Prisma schema (e.g. add a new model, remove an existing one, ...)

  2. 运行 prisma migrate dev 创建并应用迁移或运行 prisma db push 直接应用更改(在这两种情况下,Prisma 客户端都会自动生成)

    ¥Run prisma migrate dev to create and apply a migration or run prisma db push to apply the changes directly (in both cases Prisma Client is automatically generated)