news 2026/9/24 6:03:25

Prisma 数据建模完全指南:基于 SDL 的 Data Model 设计、字段约束与关系建模

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Prisma 数据建模完全指南:基于 SDL 的 Data Model 设计、字段约束与关系建模
  • 后端
  • 数据库
  • GraphQL

【免费下载链接】prisma1

💾 Database Tools incl. ORM, Migrations and Admin UI (Postgres, MySQL & MongoDB) [deprecated]

项目地址:https://gitcode.com/gh_mirrors/pr/prisma1
点击查看免费下载

导读

本文以 Prisma 服务端的数据建模为核心,讲解如何使用 GraphQL Schema Definition Language(SDL)编写datamodel.graphql数据模型,并通过prisma deploy将其转换为真实的数据库 Schema 与 Prisma GraphQL API。你将掌握对象类型、标量字段与类型修饰符、@unique/@default/@relation/@rename等指令的完整用法、系统字段与迁移技巧,以及关系(Relation)的删除行为(onDelete)建模,同时结合仓库源码理解数据模型在 Prisma CLI 内部是如何被解析与执行的。

一、Prisma 数据建模总览:用 SDL 描述你的数据模型

Prisma 使用 GraphQL 的 Schema Definition Language(SDL)进行数据建模。你的数据模型写在一个或多个.graphql文件中,它是 Prisma 在底层生成真实数据库 Schema的基础。如果只使用单个文件承载类型定义,这个文件通常被命名为datamodel.graphql

包含数据模型的.graphql文件需要在prisma.yml中通过datamodel属性声明。例如:

datamodel: - types.graphql - enums.graphql

如果只有一个文件定义数据模型,可以简写为:

datamodel: datamodel.graphql

在仓库的 PrismaDefinition.ts 中,可以确认 CLI 对这一配置的解析逻辑:datamodel既支持字符串也支持字符串数组,CLI 会将其归一化为数组,然后逐个读取文件内容并拼接成一份完整的 SDL 文本(getTypesString)。如果某个路径指向的文件不存在,CLI 会直接抛出错误The types definition file "..." could not be found.。也就是说,datamodel属性指向的文件路径是相对于prisma.yml所在目录解析的(path.join(this.definitionDir, unresolvedTypesPath))。

数据模型是 Prisma 服务 GraphQL API 的基础:基于数据模型,Prisma 会生成一个强大的 GraphQL Schema(称为Prisma database schema),它为数据模型中的每个类型定义 CRUD 操作。

说明:GraphQL Schema 定义了一个 GraphQL API 的操作集合,本质上是用 SDL 编写的类型集合(SDL 还支持 interface、enum、union type 等更丰富的原语)。一个 GraphQL Schema 拥有三个特殊的根类型:QueryMutationSubscription,它们定义了 API 的入口点与可接受的操作。

一个最小示例

一个简单的datamodel.graphql文件:

type Tweet { id: ID! @unique createdAt: DateTime! text: String! owner: User! location: Location! } type User { id: ID! @unique createdAt: DateTime! updatedAt: DateTime! handle: String! @unique name: String tweets: [Tweet!]! } type Location { latitude: Float! longitude: Float! }

这个示例展示了数据建模中几个重要的核心概念:

  • 三个类型TweetUserLocation会被映射为数据库中的表(table);
  • UserTweet之间存在一个双向关系
  • TweetLocation存在一个单向关系
  • 除了User上的name字段,数据模型中所有字段都是必填的(类型后的!表示非空);
  • idcreatedAtupdatedAt字段由 Prisma 自动维护,并且在暴露出的 GraphQL API 中是只读的(无法通过 mutation 修改)。

创建和更新数据模型就像编写一个文本文件一样简单。当你对数据模型满意后,运行prisma deploy即可将变更应用到 Prisma 服务:

$ prisma deploy Changes: Tweet (Type) + Created type `Tweet` + Created field `id` of type `GraphQLID!` + Created field `createdAt` of type `DateTime!` + Created field `text` of type `String!` + Created field `owner` of type `Relation!` + Created field `location` of type `Relation!` + Created field `updatedAt` of type `DateTime!` User (Type) + Created type `User` + Created field `id` of type `GraphQLID!` + Created field `createdAt` of type `DateTime!` + Created field `updatedAt` of type `DateTime!` + Created field `handle` of type `String!` + Created field `name` of type `String` + Created field `tweets` of type `[Relation!]!` Location (Type) + Created type `Location` + Created field `latitude` of type `Float!` + Created field `longitude` of type `Float!` + Created field `id` of type `GraphQLID!` + Created field `updatedAt` of type `DateTime!` + Created field `createdAt` of type `DateTime!` TweetToUser (Relation) + Created relation between Tweet and User LocationToTweet (Relation) + Created relation between Location and Tweet Applying changes... (22/22) Applying changes... 0.4s

注意prisma deploy输出的信息非常详尽:它逐个类型列出"新增的类型"和"新增的字段",并明确展示自动创建的关系(TweetToUserLocationToTweet)。关系字段(ownerlocationtweets)在输出中的类型被标记为Relation![Relation!]!,表明它们是关系字段而非标量字段。

数据模型的构建块

塑造数据模型有多种可用的构建块:

  • Types(对象类型):由多个fields(字段)组成,用于对相似的实体进行分组。数据模型中的每个类型都会被映射到数据库,并在 GraphQL Schema 中生成对应的 CRUD 操作。
  • Relations(关系):描述类型之间的关联语义。
  • Interfaces(接口):抽象类型,包含一组字段,实现接口的类型必须包含这些字段。当前版本中接口还不能由用户自定义(相关能力处于待开发状态)。
  • 特殊指令(directives):覆盖不同使用场景,例如类型约束或级联删除行为。

二、Prisma database schema 与 Data model 的区别

初学 GraphQL 与 Prisma 时,.graphql文件的数量容易让人困惑。理解每个文件的角色至关重要。

一般而言,一个.graphql文件可能包含以下两种内容之一:

  • GraphQL 操作(即 query、mutation 或 subscription);
  • 用 SDL 编写的 GraphQL 类型定义。

在区分 Prisma database schema 与 data model 的语境下,只有后者相关

需要特别注意:并非所有属于后者的.graphql文件都是合法的 GraphQL Schema。如前所述,GraphQL Schema 的特征在于它除了 API 所需的其他类型外,还拥有三个根类型:QueryMutationSubscription

按照这个定义,数据模型实际上并不是一个 GraphQL Schema——尽管它是以 SDL 编写的.graphql文件。它缺少根类型,因此并不真正定义 API 操作!Prisma 只是把数据模型当作一种方便的工具,让你能够表达数据模型长什么样。

随后,Prisma 会生成一个真正的 GraphQL Schema,其中包含QueryMutationSubscription根类型。这个 Schema 通常存储在项目内的prisma.graphql中,被称为Prisma database schema。注意:永远不要手动修改这个文件

以如下极简数据模型为例:

datamodel.graphql

type User { id: ID! @uniue name: String! }

将此数据模型部署到 Prisma 服务后,Prisma 会生成如下 Prisma database schema,它定义了服务的 GraphQL API:

prisma.graphql

type Query { users(where: UserWhereInput, orderBy: UserOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [User]! user(where: UserWhereUniqueInput!): User } type Mutation { createUser(data: UserCreateInput!): User! updateUser(data: UserUpdateInput!, where: UserWhereUniqueInput!): User deleteUser(where: UserWhereUniqueInput!): User } type Subscription { user(where: UserSubscriptionWhereInput): UserSubscriptionPayload }

注意:这是生成 Schema 的简化版本。可以看到,一个User类型直接派生出一整套 API:users/user查询、createUser/updateUser/deleteUser变更,以及user订阅。这就是"数据模型是 Prisma 服务 API 的基石"的直观体现。

补充:如果你已经研究过如何基于 Prisma 构建自己的 GraphQL 服务器,可能还会遇到另一个.graphql文件,即你的application schema(应用 Schema)。它是另一个真正的 GraphQL Schema(同样包含QueryMutationSubscription根类型),定义了暴露给客户端应用的 API,并把底层的 Prisma GraphQL API 当作"查询引擎"来真正执行针对数据库的查询、变更与订阅。

基于 Prisma 的 GraphQL 服务器通常有两个 GraphQL API,可以理解为服务的两个层级:

  • 应用层(Application layer):由 application schema 定义(在这里实现业务逻辑、认证、与第三方服务集成等);
  • 数据库层(Database layer):由 Prisma database service 定义。

三、对象类型(Object Types)

对象类型(或简称type)定义了数据模型中某一部分具体实体的结构,用来表示来自应用领域的实体。

如果你熟悉 SQL 数据库,可以把对象类型类比为关系数据库中**表(table)**的 Schema。一个类型拥有一个名称以及一个或多个字段(fields)

一个类型的实例被称为一个node(节点)。这个术语指的是你**数据图(data graph)**中的一个节点。你在数据模型中定义的每个类型,都会作为对应的类型出现在生成的Prisma database schema中。

定义对象类型

在数据模型中使用关键字type定义对象类型:

type Article { id: ID! @unique text: String! isPublished: Boolean @default(value: "false") }

上述类型具有以下属性:

  • 名称:Article
  • 字段:idtextisPublished(默认值为false

从源码层面看,CLI 的数据模型解析器(见 parser.ts)只处理 SDL AST 中的ObjectTypeDefinitionEnumTypeDefinition两类定义;对于对象类型,它会逐一解析字段的nametypeisList(是否为 ListType)、isRequired(是否为 NonNullType)、defaultValuerelationName以及各类指令。这解释了为何数据模型中的类型名和字段名必须符合 SDL 语法,且非标量字段最终会通过resolveRelations(见 parser.ts)被解析为真实的对象类型引用。

类型生成的 API 操作

数据模型中的类型会影响 Prisma GraphQL API 中可用的操作。对每个类型而言:

  • queries(查询):允许你获取该类型的一个或多个节点;
  • mutations(变更):允许你创建、更新或删除该类型的节点;
  • subscriptions(订阅):允许你在该类型节点发生变化时得到通知(例如新节点被创建,或已有节点被更新删除)。

四、字段(Fields)

字段是类型的构建块,赋予节点形状。每个字段通过其名称被引用,并且要么是 标量 字段,要么是 关系 字段。

标量类型(Scalar Types)

仓库中的类型标识符表(见 scalar.ts)列出了 Prisma 数据模型支持的核心标量:StringIntFloatBooleanLong(内部使用)、DateTimeIDUUIDJson。以下逐一说明这些标量的语义与使用规则。

String

String保存文本。它适用于用户名、博客文章内容等任何适合用文本表示的数据。

注意:在共享 demo cluster 上,String 值当前限制为 256KB。在其他集群上可以通过集群配置提高该限制。

在查询或变更中,String 字段必须使用双引号包裹:string: "some-string"

Integer

Int是不能包含小数的数字。用于存储配料的重量、活动的最低年龄限制等值。

注意:Int的取值范围是 -2147483648 到 2147483647。

在查询或变更中,Int字段无需任何包裹字符:int: 42

Float

Float是包含小数的数字。用于存储商品价格或复杂计算结果等值。

在查询或变更中,Float字段无需包裹字符且小数点可选:float: 42float: 4.2

Boolean

Boolean的值只能是truefalse。适合跟踪设置项,例如用户是否希望收到邮件订阅、某道菜谱是否适合素食者。

在查询或变更中,Boolean字段无需包裹字符:boolean: trueboolean: false

DateTime

DateTime类型用于存储日期或时间值,例如一个人的出生日期。

在查询或变更中,DateTime字段必须以 ISO 8601 格式并用双引号包裹:

  • datetime: "2015"
  • datetime: "2015-11"
  • datetime: "2015-11-22"
  • datetime: "2015-11-22T13:57:31.123Z"
Enum

枚举在服务级别(service scope)上定义。

和 Boolean 类似,Enum 的值也只能是预定义集合中的一个。区别在于你可以自己定义可选值。例如,通过创建一个可能值为COMPACTWIDECOVER的枚举,可以规定文章应该如何排版。

注意:Enum 值最长不能超过 191 个字符。

在查询或变更中,Enum 字段无需包裹字符,且只能使用你为枚举定义的值:enum: COMPACTenum: WIDE

从解析器实现看,枚举类型同样会被解析成一种内部类型表示(isEnum: true),它的每个枚举值被当作一个字段处理(见 parser.ts)。

Json

有时你需要为松散结构的数据存储任意的 Json 值。Json类型会确保存储的内容确实是合法的 Json,并返回解析后的 Json 对象/数组(而非字符串)。

注意:在共享 demo cluster 上,Json 值当前限制为 256KB。其他集群可通过集群配置提高限制。

在查询或变更中,Json 字段必须用双引号包裹,特殊字符需要转义:json: "{\"int\": 1, \"string\": \"value\"}"

ID

ID 值是基于 cuid 生成的 25 位唯一字符串。ID 字段是系统字段,仅用于内部使用,因此不能创建新的 ID 类型字段

类型修饰符(Type Modifiers)

List(列表)

标量字段可以标记为列表字段类型。具有多对多(many)多重性的关系字段也会被标记为列表。

在查询或变更中,列表字段需要用方括号包裹,列表中的每个条目遵循上述相同的格式规则:listString: ["a string", "another string"]listInt: [12, 24]

Required(必填)

字段可以标记为必填(有时也称为"非空")。在创建新节点时,对于必填且没有默认值的字段,你必须提供值。

必填字段使用字段类型后的!标记:name: String!

字段约束(Field Constraints)

字段可以配置特定的约束条件,为数据模型增加更多语义。

Unique(唯一)

设置unique约束可确保同一类型的两个节点不能在该字段上拥有相同的值。唯一的例外是null值,即多个节点可以都为null而不会违反约束。

典型例子是User类型上的email字段——我们通常假设每个User都应拥有全局唯一的邮箱地址。

请注意:String 字段只有前 191 个字符参与唯一性检查,且唯一性检查不区分大小写。如果两个字符串的前 191 个字符相同,或者仅大小写不同,则无法同时存储。

标记字段唯一只需在其后追加@unique指令:

type User { email: String! @unique age: Int! }

对于每个标注了@unique的字段,你都可以通过为该字段提供一个值来查询对应节点。

例如,对于上述数据模型,你现在可以通过email地址获取特定的User节点:

query { user(where: { email: "alice@graph.cool" }) { age } }

从源码看,@unique指令由解析器中的isUniqe方法识别(见 parser.ts),并且id字段会被自动视为唯一(const isUnique = isId || this.isUniqe(field),见 parser.ts)。同时,指令名常量uniquedefaultrelation等统一定义在 directives.ts 中。

更多约束

更多数据库约束将根据功能需求在未来逐步加入。

默认值(Default Value)

你可以为非列表标量字段设置默认值。当创建新节点时未提供值,将使用该默认值。

为字段指定默认值使用@default指令:

type Story { isPublished: Boolean @default(value: "false") someNumber: Int! @default(value: "42") title: String! @default(value: "My New Post") publishDate: DateTime! @default(value: "2018-01-26") }

注意:即使是非字符串类型(如BooleanInt),始终需要将值放在双引号中。这一点与解析器实现一致:@defaultvalue参数统一按字符串字面量读取(见 parser.ts 中getDefaultValue对参数值的提取方式)。

系统字段(System Fields)

idcreatedAtupdatedAt这三个字段具有特殊含义。它们在数据模型中是可选的,但会始终在底层数据库中被维护。因此你可以在之后随时把字段添加到数据模型中,已有节点的数据仍然可用。

目前这些字段的值在 GraphQL API 中是只读的(导入数据时除外),未来将允许配置。

重要警告:你不能拥有名为idcreatedAtupdatedAt的自定义字段,因为这些名称被系统字段保留。以下是这三个字段唯一支持的声明形式:

  • id: ID! @unique
  • createdAt: DateTime!
  • updatedAt: DateTime!

在解析器层面,这三个保留字段名定义于 legacyFields.ts,关系型模型解析器 relationalParser.ts 通过字段名或指令识别它们,并据此将字段标记为只读(isReservedReadOnlyField,见 parser.ts)。

系统字段:id

节点创建时会自动获得一个全局唯一标识符,存储于id字段。

每当你将id字段添加到类型定义中以便在 GraphQL API 中暴露它时,必须@unique指令标注。

id具有以下属性:

  • 由 25 个字母数字字符组成(字母始终为小写);
  • 始终以(小写)字母c开头;
  • 遵循 cuid(collision resistant unique identifiers,防碰撞唯一标识符)方案。

注意:你的所有对象类型在数据库 Schema 中都会实现Node接口。Node接口如下:

interface Node { id: ID! @unique }
系统字段:createdAtupdatedAt

数据模型还提供两个特殊字段,你可以添加到类型中:

  • createdAt: DateTime!:记录该对象类型的节点被创建的确切日期和时间;
  • updatedAt: DateTime!:记录该对象类型的节点最后被更新的确切日期和时间。

如果你希望类型暴露这些字段,只需将它们添加到类型定义中,例如:

type User { id: ID! @unique createdAt: DateTime! updatedAt: DateTime! }

字段生成的 API 操作

数据模型中的字段会影响 Prisma GraphQL API 中可用的查询参数(query arguments)。

迁移标量字段的值

你可以使用updateManyXsmutation 为所有节点、或仅某一特定子集的节点迁移标量字段的值:

mutation { # 将所有没有邮箱地址的用户的 email 更新为空字符串 updateManyUsers( where: { email: null } data: { email: "" } ) }
向数据模型添加必填字段

当向已包含节点的模型中添加必填字段时,你会收到如下错误信息:

You are creating a required field but there are already nodes present that would violate that constraint.

这是因为所有节点的该字段都将为null。添加必填字段需要以下步骤:

  1. 先以可选方式添加该字段;
  2. 使用updateManyXs将所有节点的该字段从null迁移为非空值;
  3. 现在将该字段标记为必填并正常部署。

五、关系(Relations)

关系定义了 类型 之间连接(connection)的语义。两个类型通过一个 关系字段 相连。当关系可能存在歧义时,需要为该关系字段标注@relation指令来消除歧义。

关系也可以连接类型与其自身,此时被称为self-relation(自关系)

必填关系(Required Relations)

对于to-one关系字段,你可以配置它是必填还是可选。必填标记在 GraphQL 中充当一种契约,表明该字段永远不会是null。因此,用户地址字段的类型应为AddressAddress!

包含必填to-one关系字段的类型的节点,只能通过 嵌套 mutation 创建,以确保对应字段不会为null

注意:to-many关系字段始终是必填的。例如,一个包含多个用户地址的字段总是使用类型[Address!]!,永远不能是[Address!]。原因在于:当字段不包含任何节点时,会返回[](空数组),而[]并不是null

@relation指令

在类型之间定义关系时,可以使用@relation指令为关系提供元信息。它接受两个参数:

  • name:该关系的标识符(以字符串形式提供)。仅当关系存在歧义时才需要此参数。注意:每次使用@relation指令时都必须提供name参数
  • onDelete:指定删除行为并启用级联删除。当一个带有相关节点的节点被删除时,删除行为决定相关节点的命运。该参数的值定义为一个枚举,可能的取值如下:
    • SET_NULL(默认):将相关节点设为null
    • CASCADE:删除相关节点。注意:不能将双向关系的两端都设置为CASCADE

以下是使用@relation指令的数据模型示例:

type User { id: ID! @unique stories: [Story!]! @relation(name: "StoriesByUser" onDelete: CASCADE) } type Story { id: ID! @unique text: String! author: User @relation(name: "StoriesByUser") }

该示例中的删除行为如下:

  • 当一个User节点被删除时,其所有相关的Story节点也会被删除;
  • 当一个Story节点被删除时,它只会从相关User节点的stories列表中被移除。

从源码层面看,@relation指令的name参数通过getRelationName提取(见 parser.ts),并存储在字段的relationName属性中。在resolveRelations阶段,解析器会优先通过relationName配对关系字段(若名称相同则视为同一关系),再对没有指令标注、但结构上唯一可配对的关系字段进行推断连接(见 parser.ts)。这也印证了文档中"歧义关系必须命名"的规则:当同一类型对之间存在多个关系时,若不做命名,解析器无法确定字段如何配对。

省略@relation指令

在最简单的情况下——两个类型之间的关系没有歧义,且使用默认删除行为(SET_NULL)——相应的关系字段不必标注@relation指令。

下面在UserStory之间定义了一个双向one-to-many关系。由于未提供onDelete,使用默认删除行为SET_NULL

type User { id: ID! @unique stories: [Story!]! } type Story { id: ID! @unique text: String! author: User }

该示例中的删除行为如下:

  • 当一个User节点被删除时,其所有相关Story节点的author字段会被设为null。注意:如果author字段被标记为必填,该操作将导致错误;
  • 当一个Story节点被删除时,它只会从相关User节点的stories列表中被移除。
使用@relation指令的name参数

在某些情况下,数据模型可能包含歧义关系。例如,你不仅想用关系表达UserStory之间的"作者关系",还想表达哪些Story节点被User点赞

这时UserStory之间就存在两个不同的关系!为了消除歧义,你需要给关系命名:

type User { id: ID! @unique writtenStories: [Story!]! @relation(name: "WrittenStories") likedStories: [Story!]! @relation(name: "LikedStories") } type Story { id: ID! @unique text: String! author: User! @relation(name: "WrittenStories") likedBy: [User!]! @relation(name: "LikedStories") }

如果在此例中未提供name,将无法判断writtenStories应该关联author字段还是likedBy字段。

使用@relation指令的onDelete参数

如前所述,你可以为相关节点指定专门的删除行为,这正是@relation指令onDelete参数的用途。

考虑以下示例:

type User { id: ID! @unique comments: [Comment!]! @relation(name: "CommentAuthor", onDelete: CASCADE) blog: Blog @relation(name: "BlogOwner", onDelete: CASCADE) } type Blog { id: ID! @unique comments: [Comment!]! @relation(name: "Comments", onDelete: CASCADE) owner: User! @relation(name: "BlogOwner", onDelete: SET_NULL) } type Comment { id: ID! @unique blog: Blog! @relation(name: "Comments", onDelete: SET_NULL) author: User @relation(name: "CommentAuthor", onDelete: SET_NULL) }

让我们逐一分析三个类型的删除行为:

  • 当一个User节点被删除时:
    • 所有相关的Comment节点将被删除;
    • 相关的Blog节点将被删除。
  • 当一个Blog节点被删除时:
    • 所有相关的Comment节点将被删除;
    • 相关User节点的blog字段将被设为null
  • 当一个Comment节点被删除时:
    • 相关Blog节点继续存在,被删除的Comment节点从它的comments列表中移除;
    • 相关User节点继续存在,被删除的Comment节点从它的comments列表中移除。

注意示例中User.blogBlog.owner构成的双向关系两端分别设置了CASCADESET_NULL——这正是文档强调的"双向关系两端不能同时为CASCADE"的典型应用。

关系生成的 API 操作

数据模型中的关系会影响 GraphQL API 中可用的操作。对每个关系而言:

  • relation queries(关系查询):允许你跨类型查询数据,或针对关系进行聚合查询(也可以使用 Relay 的 connection model);
  • nested mutations(嵌套变更):允许你跨类型创建(create)、连接(connect)、更新(update)、upsert 和删除(delete)节点;
  • relation subscriptions(关系订阅):允许你在关系发生变化时得到通知。

六、GraphQL 指令(Directives)

指令用于为数据模型提供额外信息。它们的写法是@name(argument: "value"),当没有参数时则简写为@name

数据模型指令(Data Model Directives)

数据模型指令描述 GraphQL Schema 中类型或字段的附加信息。

唯一标量字段(Unique scalar fields)

@unique指令将标量字段标记为唯一。唯一字段将在底层数据库中应用唯一索引

# `User` 类型拥有唯一的 `email` 字段 type User { email: String @unique }
关系字段(Relation fields)

@relation(name: String, onDelete: ON_DELETE! = CASCADE)指令可以附加到关系字段上。

(详见上文 @relation 指令 一节。)

标量字段默认值(Default value for scalar fields)

@default(value: String!)指令为标量字段设置默认值。注意:对所有标量字段而言,value参数的类型都是 String(即使字段本身不是字符串):

# `title`、`published` 和 `someNumber` 字段分别具有默认值 `New Post`、`false` 和 `42` type Post { title: String! @default(value: "New Post") published: Boolean! @default(value: "false") someNumber: Int! @default(value: "42") }

临时指令(Temporary Directives)

临时指令用于执行一次性迁移操作。在部署了包含临时指令的服务之后,需要手动将其从类型定义文件中移除

重命名类型或字段(Renaming a type or field)

临时指令@rename(oldName: String!)用于重命名类型或字段。

# 将 `Post` 类型重命名为 `Story`,并将其 `text` 字段重命名为 `content` type Story @rename(oldName: "Post") { content: String @rename(oldName: "text") }

警告:如果不使用 rename 指令,Prisma 会先移除旧类型和字段,再创建新的类型和字段,从而导致数据丢失!

七、命名约定(Naming Conventions)

在 Prisma 服务中,你会遇到不同类型的对象(如类型或关系),它们遵循各自的命名约定,以帮助你区分。

类型(Types)

类型名称决定了派生的查询与变更名称,以及嵌套变更的参数名称。类型名称只能包含字母数字字符,并且需要以大写字母开头。它们最多可包含64 个字符

建议使用单数形式的类型名称。

类型名称在服务级别上是唯一的。

示例:PostPostCategory

标量字段与关系字段(Scalar and relation fields)

标量字段的名称用于查询以及变更的查询参数中。字段名称只能包含字母数字字符,并且需要以小写字母开头。它们最多可包含64 个字符

关系字段的名称遵循相同的约定,并决定关系变更(relation mutations)的参数名称。

建议只为列表字段选择复数名称。

字段名称在类型级别上是唯一的。

示例:nameemailcategoryTags

关系(Relations)

关系名称只能包含字母数字字符,并且需要以大写字母开头。它们最多可包含64 个字符

关系名称在服务级别上是唯一的。

示例:

  • UserOnPostUserPostsPostAuthor,字段名为userposts
  • AppointmentsEmployeeOnAppointmentAppointmentEmployee,字段名为employeeappointments

枚举(Enums)

枚举值只能包含字母数字字符和下划线,并且需要以大写字母开头。枚举值的名称可用于查询过滤器和变更中。它们最多可包含191 个字符

枚举名称在服务级别上是唯一的。

枚举值名称在枚举级别上是唯一的。

示例:AROLE_TAGRoleTag

八、更多 SDL 特性

本节介绍 Prisma 数据建模尚未支持的更多 SDL 特性。

接口(Interfaces)

"与许多类型系统一样,GraphQL 支持接口。接口是一种抽象类型,包含一组字段,实现该接口的类型必须包含这些字段。"——引自官方 GraphQL 文档。

说明:Prisma 对接口的支持处于待开发状态,暂不能由用户自定义接口类型。

联合类型(Union Types)

"联合类型与接口非常相似,但它们不能在类型之间指定任何公共字段。"——引自官方 GraphQL 文档。

说明:联合类型同样处于待开发状态,暂不支持用于 Prisma 数据建模。

九、快速上手路线:从数据模型到可用 API

综合以上内容,一个完整的 Prisma 数据建模工作流可以概括为:

  1. 编写数据模型:在datamodel.graphql(或按prisma.ymldatamodel属性声明的多个.graphql文件)中用 SDL 定义类型、字段、约束与关系;
  2. 声明数据模型:在prisma.yml中通过datamodel属性指向上述文件(CLI 解析逻辑见 PrismaDefinition.ts);
  3. 部署:运行prisma deploy,Prisma 会输出变更计划并生成数据库 Schema 与prisma.graphql(Prisma database schema),为每个类型自动派生查询、变更与订阅操作;
  4. 消费 API:在应用层使用生成的 GraphQL API(或在此基础上构建你自己的 application schema)读写数据。

在整个过程中,请牢记几个关键约束:id/createdAt/updatedAt是保留的系统字段且只读;@relationname参数在歧义关系中必不可少;双向关系的两端不能同时设置onDelete: CASCADE@rename用于避免重命名导致的数据丢失;所有非字符串标量的默认值也需用双引号包裹。

十、延伸阅读

  • 数据模型相关指令的源码定义:directives.ts
  • 数据模型解析器实现(指令、默认值、关系解析):parser.ts
  • 关系型数据库模型解析器(保留字段识别):relationalParser.ts
  • 文档型数据库模型解析器(内嵌类型识别):documentParser.ts
  • 标量类型标识符表:scalar.ts
  • prisma.ymldatamodel属性的解析与校验:PrismaDefinition.ts
  • Prisma GraphQL API 的查询、变更与订阅参考:03-Prisma-API

版本说明:本文内容基于当前仓库中docs/1.14版本文档,其中的版本特性(如 String/Json 256KB 限制、@rename临时指令、updateManyXs迁移 mutation 等)以该版本为准。

  • 后端
  • 数据库
  • GraphQL

【免费下载链接】prisma1

💾 Database Tools incl. ORM, Migrations and Admin UI (Postgres, MySQL & MongoDB) [deprecated]

项目地址:https://gitcode.com/gh_mirrors/pr/prisma1
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/24 5:54:50

企业专利对外宣传的法律合规风险与话术规范

摘要专利是制造企业对外宣传中的常见卖点,但专利宣传并非“想怎么说就怎么说”,而是受到《广告法》《反不正当竞争法》等法律法规的严格约束。本文系统梳理《广告法》关于专利宣传的三条硬性规定,分析五类高风险话术的法律风险点,…

作者头像 李华
网站建设 2026/9/24 5:52:40

老主板BIOS魔改实战:微代码替换、VT-d与CR3校验绕过指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/24 5:48:43

Modbus RTU通讯不稳定?CRC校验与轮询节奏的隐性陷阱排查

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/24 5:46:19

以太网速率与端口吞吐量的本质区别解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/24 5:45:26

IGBT驱动电路设计核心:栅极电阻选型与PCB布局实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华