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 LanguageSDL编写datamodel.graphql数据模型并通过prisma deploy将其转换为真实的数据库 Schema 与 Prisma GraphQL API。你将掌握对象类型、标量字段与类型修饰符、unique/default/relation/rename等指令的完整用法、系统字段与迁移技巧以及关系Relation的删除行为onDelete建模同时结合仓库源码理解数据模型在 Prisma CLI 内部是如何被解析与执行的。一、Prisma 数据建模总览用 SDL 描述你的数据模型Prisma 使用 GraphQL 的 Schema Definition LanguageSDL进行数据建模。你的数据模型写在一个或多个.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 拥有三个特殊的根类型Query、Mutation和Subscription它们定义了 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! }这个示例展示了数据建模中几个重要的核心概念三个类型Tweet、User、Location会被映射为数据库中的表tableUser与Tweet之间存在一个双向关系Tweet到Location存在一个单向关系除了User上的name字段数据模型中所有字段都是必填的类型后的!表示非空id、createdAt、updatedAt字段由 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输出的信息非常详尽它逐个类型列出新增的类型和新增的字段并明确展示自动创建的关系TweetToUser、LocationToTweet。关系字段owner、location、tweets在输出中的类型被标记为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 所需的其他类型外还拥有三个根类型Query、Mutation和Subscription。按照这个定义数据模型实际上并不是一个 GraphQL Schema——尽管它是以 SDL 编写的.graphql文件。它缺少根类型因此并不真正定义 API 操作Prisma 只是把数据模型当作一种方便的工具让你能够表达数据模型长什么样。随后Prisma 会生成一个真正的 GraphQL Schema其中包含Query、Mutation、Subscription根类型。这个 Schema 通常存储在项目内的prisma.graphql中被称为Prisma database schema。注意永远不要手动修改这个文件以如下极简数据模型为例datamodel.graphqltype User { id: ID! uniue name: String! }将此数据模型部署到 Prisma 服务后Prisma 会生成如下 Prisma database schema它定义了服务的 GraphQL APIprisma.graphqltype 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类型直接派生出一整套 APIusers/user查询、createUser/updateUser/deleteUser变更以及user订阅。这就是数据模型是 Prisma 服务 API 的基石的直观体现。补充如果你已经研究过如何基于 Prisma 构建自己的 GraphQL 服务器可能还会遇到另一个.graphql文件即你的application schema应用 Schema。它是另一个真正的 GraphQL Schema同样包含Query、Mutation、Subscription根类型定义了暴露给客户端应用的 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字段id、text和isPublished默认值为false从源码层面看CLI 的数据模型解析器见 parser.ts只处理 SDL AST 中的ObjectTypeDefinition与EnumTypeDefinition两类定义对于对象类型它会逐一解析字段的name、type、isList是否为 ListType、isRequired是否为 NonNullType、defaultValue、relationName以及各类指令。这解释了为何数据模型中的类型名和字段名必须符合 SDL 语法且非标量字段最终会通过resolveRelations见 parser.ts被解析为真实的对象类型引用。类型生成的 API 操作数据模型中的类型会影响 Prisma GraphQL API 中可用的操作。对每个类型而言queries查询允许你获取该类型的一个或多个节点mutations变更允许你创建、更新或删除该类型的节点subscriptions订阅允许你在该类型节点发生变化时得到通知例如新节点被创建或已有节点被更新或删除。四、字段Fields字段是类型的构建块赋予节点形状。每个字段通过其名称被引用并且要么是 标量 字段要么是 关系 字段。标量类型Scalar Types仓库中的类型标识符表见 scalar.ts列出了 Prisma 数据模型支持的核心标量String、Int、Float、Boolean、Long内部使用、DateTime、ID、UUID、Json。以下逐一说明这些标量的语义与使用规则。StringString保存文本。它适用于用户名、博客文章内容等任何适合用文本表示的数据。注意在共享 demo cluster 上String 值当前限制为 256KB。在其他集群上可以通过集群配置提高该限制。在查询或变更中String 字段必须使用双引号包裹string: some-string。IntegerInt是不能包含小数的数字。用于存储配料的重量、活动的最低年龄限制等值。注意Int的取值范围是 -2147483648 到 2147483647。在查询或变更中Int字段无需任何包裹字符int: 42。FloatFloat是包含小数的数字。用于存储商品价格或复杂计算结果等值。在查询或变更中Float字段无需包裹字符且小数点可选float: 42、float: 4.2。BooleanBoolean的值只能是true或false。适合跟踪设置项例如用户是否希望收到邮件订阅、某道菜谱是否适合素食者。在查询或变更中Boolean字段无需包裹字符boolean: true、boolean: false。DateTimeDateTime类型用于存储日期或时间值例如一个人的出生日期。在查询或变更中DateTime字段必须以 ISO 8601 格式并用双引号包裹datetime: 2015datetime: 2015-11datetime: 2015-11-22datetime: 2015-11-22T13:57:31.123ZEnum枚举在服务级别service scope上定义。和 Boolean 类似Enum 的值也只能是预定义集合中的一个。区别在于你可以自己定义可选值。例如通过创建一个可能值为COMPACT、WIDE、COVER的枚举可以规定文章应该如何排版。注意Enum 值最长不能超过 191 个字符。在查询或变更中Enum 字段无需包裹字符且只能使用你为枚举定义的值enum: COMPACT、enum: WIDE。从解析器实现看枚举类型同样会被解析成一种内部类型表示isEnum: true它的每个枚举值被当作一个字段处理见 parser.ts。Json有时你需要为松散结构的数据存储任意的 Json 值。Json类型会确保存储的内容确实是合法的 Json并返回解析后的 Json 对象/数组而非字符串。注意在共享 demo cluster 上Json 值当前限制为 256KB。其他集群可通过集群配置提高限制。在查询或变更中Json 字段必须用双引号包裹特殊字符需要转义json: {\int\: 1, \string\: \value\}。IDID 值是基于 cuid 生成的 25 位唯一字符串。ID 字段是系统字段仅用于内部使用因此不能创建新的 ID 类型字段。类型修饰符Type ModifiersList列表标量字段可以标记为列表字段类型。具有多对多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: alicegraph.cool }) { age } }从源码看unique指令由解析器中的isUniqe方法识别见 parser.ts并且id字段会被自动视为唯一const isUnique isId || this.isUniqe(field)见 parser.ts。同时指令名常量unique、default、relation等统一定义在 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) }注意即使是非字符串类型如Boolean或Int始终需要将值放在双引号中。这一点与解析器实现一致default的value参数统一按字符串字面量读取见 parser.ts 中getDefaultValue对参数值的提取方式。系统字段System Fieldsid、createdAt、updatedAt这三个字段具有特殊含义。它们在数据模型中是可选的但会始终在底层数据库中被维护。因此你可以在之后随时把字段添加到数据模型中已有节点的数据仍然可用。目前这些字段的值在 GraphQL API 中是只读的导入数据时除外未来将允许配置。重要警告你不能拥有名为id、createdAt、updatedAt的自定义字段因为这些名称被系统字段保留。以下是这三个字段唯一支持的声明形式id: ID! uniquecreatedAt: DateTime!updatedAt: DateTime!在解析器层面这三个保留字段名定义于 legacyFields.ts关系型模型解析器 relationalParser.ts 通过字段名或指令识别它们并据此将字段标记为只读isReservedReadOnlyField见 parser.ts。系统字段id节点创建时会自动获得一个全局唯一标识符存储于id字段。每当你将id字段添加到类型定义中以便在 GraphQL API 中暴露它时必须用unique指令标注。id具有以下属性由 25 个字母数字字符组成字母始终为小写始终以小写字母c开头遵循 cuidcollision resistant unique identifiers防碰撞唯一标识符方案。注意你的所有对象类型在数据库 Schema 中都会实现Node接口。Node接口如下interface Node { id: ID! unique }系统字段createdAt与updatedAt数据模型还提供两个特殊字段你可以添加到类型中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。添加必填字段需要以下步骤先以可选方式添加该字段使用updateManyXs将所有节点的该字段从null迁移为非空值现在将该字段标记为必填并正常部署。五、关系Relations关系定义了 类型 之间连接connection的语义。两个类型通过一个 关系字段 相连。当关系可能存在歧义时需要为该关系字段标注relation指令来消除歧义。关系也可以连接类型与其自身此时被称为self-relation自关系。必填关系Required Relations对于to-one关系字段你可以配置它是必填还是可选。必填标记在 GraphQL 中充当一种契约表明该字段永远不会是null。因此用户地址字段的类型应为Address或Address!。包含必填to-one关系字段的类型的节点只能通过 嵌套 mutation 创建以确保对应字段不会为null。注意to-many关系字段始终是必填的。例如一个包含多个用户地址的字段总是使用类型[Address!]!永远不能是[Address!]。原因在于当字段不包含任何节点时会返回[]空数组而[]并不是null。relation指令在类型之间定义关系时可以使用relation指令为关系提供元信息。它接受两个参数name该关系的标识符以字符串形式提供。仅当关系存在歧义时才需要此参数。注意每次使用relation指令时都必须提供name参数。onDelete指定删除行为并启用级联删除。当一个带有相关节点的节点被删除时删除行为决定相关节点的命运。该参数的值定义为一个枚举可能的取值如下SET_NULL默认将相关节点设为nullCASCADE删除相关节点。注意不能将双向关系的两端都设置为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指令。下面在User与Story之间定义了一个双向one-to-many关系。由于未提供onDelete使用默认删除行为SET_NULLtype 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参数在某些情况下数据模型可能包含歧义关系。例如你不仅想用关系表达User与Story之间的作者关系还想表达哪些Story节点被User点赞。这时User与Story之间就存在两个不同的关系为了消除歧义你需要给关系命名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.blog与Blog.owner构成的双向关系两端分别设置了CASCADE与SET_NULL——这正是文档强调的双向关系两端不能同时为CASCADE的典型应用。关系生成的 API 操作数据模型中的关系会影响 GraphQL API 中可用的操作。对每个关系而言relation queries关系查询允许你跨类型查询数据或针对关系进行聚合查询也可以使用 Relay 的 connection modelnested mutations嵌套变更允许你跨类型创建create、连接connect、更新update、upsert 和删除delete节点relation subscriptions关系订阅允许你在关系发生变化时得到通知。六、GraphQL 指令Directives指令用于为数据模型提供额外信息。它们的写法是name(argument: value)当没有参数时则简写为name。数据模型指令Data Model Directives数据模型指令描述 GraphQL Schema 中类型或字段的附加信息。唯一标量字段Unique scalar fieldsunique指令将标量字段标记为唯一。唯一字段将在底层数据库中应用唯一索引。# User 类型拥有唯一的 email 字段 type User { email: String unique }关系字段Relation fieldsrelation(name: String, onDelete: ON_DELETE! CASCADE)指令可以附加到关系字段上。详见上文 relation 指令 一节。标量字段默认值Default value for scalar fieldsdefault(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 个字符。建议使用单数形式的类型名称。类型名称在服务级别上是唯一的。示例Post、PostCategory标量字段与关系字段Scalar and relation fields标量字段的名称用于查询以及变更的查询参数中。字段名称只能包含字母数字字符并且需要以小写字母开头。它们最多可包含64 个字符。关系字段的名称遵循相同的约定并决定关系变更relation mutations的参数名称。建议只为列表字段选择复数名称。字段名称在类型级别上是唯一的。示例name、email、categoryTags关系Relations关系名称只能包含字母数字字符并且需要以大写字母开头。它们最多可包含64 个字符。关系名称在服务级别上是唯一的。示例UserOnPost、UserPosts或PostAuthor字段名为user和postsAppointments、EmployeeOnAppointment或AppointmentEmployee字段名为employee和appointments。枚举Enums枚举值只能包含字母数字字符和下划线并且需要以大写字母开头。枚举值的名称可用于查询过滤器和变更中。它们最多可包含191 个字符。枚举名称在服务级别上是唯一的。枚举值名称在枚举级别上是唯一的。示例A、ROLE_TAG、RoleTag八、更多 SDL 特性本节介绍 Prisma 数据建模尚未支持的更多 SDL 特性。接口Interfaces与许多类型系统一样GraphQL 支持接口。接口是一种抽象类型包含一组字段实现该接口的类型必须包含这些字段。——引自官方 GraphQL 文档。说明Prisma 对接口的支持处于待开发状态暂不能由用户自定义接口类型。联合类型Union Types联合类型与接口非常相似但它们不能在类型之间指定任何公共字段。——引自官方 GraphQL 文档。说明联合类型同样处于待开发状态暂不支持用于 Prisma 数据建模。九、快速上手路线从数据模型到可用 API综合以上内容一个完整的 Prisma 数据建模工作流可以概括为编写数据模型在datamodel.graphql或按prisma.yml中datamodel属性声明的多个.graphql文件中用 SDL 定义类型、字段、约束与关系声明数据模型在prisma.yml中通过datamodel属性指向上述文件CLI 解析逻辑见 PrismaDefinition.ts部署运行prisma deployPrisma 会输出变更计划并生成数据库 Schema 与prisma.graphqlPrisma database schema为每个类型自动派生查询、变更与订阅操作消费 API在应用层使用生成的 GraphQL API或在此基础上构建你自己的 application schema读写数据。在整个过程中请牢记几个关键约束id/createdAt/updatedAt是保留的系统字段且只读relation的name参数在歧义关系中必不可少双向关系的两端不能同时设置onDelete: CASCADErename用于避免重命名导致的数据丢失所有非字符串标量的默认值也需用双引号包裹。十、延伸阅读数据模型相关指令的源码定义directives.ts数据模型解析器实现指令、默认值、关系解析parser.ts关系型数据库模型解析器保留字段识别relationalParser.ts文档型数据库模型解析器内嵌类型识别documentParser.ts标量类型标识符表scalar.tsprisma.yml中datamodel属性的解析与校验PrismaDefinition.tsPrisma 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点击查看免费下载相关推荐Prisma 数据建模SDL完全指南用 GraphQL SDL 设计数据模型、字段约束与关系Prisma 数据建模SDL完全指南用 GraphQL SDL 设计数据模型、字段约束与关系 导读 本文是 Prisma 1.x 数据建模Data Mo后端数据库GraphQLPrisma 数据建模完全指南基于 GraphQL SDL 设计数据模型Data ModellingPrisma 数据建模完全指南基于 GraphQL SDL 设计数据模型Data Modelling 导读 本文以 Prisma 1.x 官方参考文档《D后端数据库GraphQLPrisma 数据建模SDL完全指南datamodel.graphql 类型、字段、关系与指令详解Prisma 数据建模SDL完全指南datamodel.graphql 类型、字段、关系与指令详解 导读 本篇指南以 docs/1.4/04 Refere后端数据库GraphQL创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考