TypeORM
本页比较 Prisma ORM 和 TypeORM。如果你想了解如何从 TypeORM 迁移到 Prisma ORM,请查看此 guide。
¥This page compares Prisma ORM and TypeORM. If you want to learn how to migrate from TypeORM to Prisma ORM, check out this guide.
TypeORM 与 Prisma ORM
¥TypeORM vs Prisma ORM
虽然 Prisma ORM 和 TypeORM 解决类似的问题,但它们的工作方式却截然不同。
¥While Prisma ORM and TypeORM solve similar problems, they work in very different ways.
TypeORM 是一种传统的 ORM,它将表映射到模型类。这些模型类可用于生成 SQL 迁移。然后,模型类的实例在运行时为应用提供 CRUD 查询的接口。
¥TypeORM is a traditional ORM which maps tables to model classes. These model classes can be used to generate SQL migrations. Instances of the model classes then provide an interface for CRUD queries to an application at runtime.
Prisma ORM 是一种新型 ORM,它缓解了传统 ORM 的许多问题,例如模型实例臃肿、业务与存储逻辑混合、缺乏类型安全或导致不可预测的查询(例如)。 通过延迟加载。
¥Prisma ORM is a new kind of ORM that mitigates many problems of traditional ORMs, such as bloated model instances, mixing business with storage logic, lack of type-safety or unpredictable queries caused e.g. by lazy loading.
它使用 Prisma 架构 以声明性方式定义应用模型。然后,Prisma Migrate 允许从 Prisma 架构生成 SQL 迁移并针对数据库执行它们。CRUD 查询由 Prisma Client 提供,这是一个适用于 Node.js 和 TypeScript 的轻量级且完全类型安全的数据库客户端。
¥It uses the Prisma schema to define application models in a declarative way. Prisma Migrate then allows to generate SQL migrations from the Prisma schema and executes them against the database. CRUD queries are provided by Prisma Client, a lightweight and entirely type-safe database client for Node.js and TypeScript.
API 设计和抽象级别
¥API design & Level of abstraction
TypeORM 和 Prisma ORM 在不同的抽象级别上运行。TypeORM 更接近于在其 API 中镜像 SQL,而 Prisma Client 提供了更高级别的抽象,该抽象是根据应用开发者的常见任务精心设计的。Prisma ORM 的 API 设计很大程度上依赖于 让正确的事情变得容易 的思想。
¥TypeORM and Prisma ORM operate on different levels of abstraction. TypeORM is closer to mirroring SQL in its API while Prisma Client provides a higher-level abstraction that was carefully designed with the common tasks of application developers in mind. Prisma ORM's API design heavily leans on the idea of making the right thing easy.
虽然 Prisma Client 在更高的抽象级别上运行,但它致力于展示底层数据库的全部功能,允许你在用例需要时随时降级到 原始 SQL。
¥While Prisma Client operates at a higher level of abstraction, it strives to expose the full power of the underlying database, allowing you to drop down to raw SQL at any time if your use case requires it.
以下部分通过几个示例来说明 Prisma ORM 和 TypeORM 的 API 在某些情况下有何不同,以及在这些情况下 Prisma ORM 的 API 设计的基本原理是什么。
¥The following sections examine a few examples for how Prisma ORM's and TypeORM's APIs differ in certain scenarios and what the rationale of Prisma ORM's API design is in these cases.
过滤
¥Filtering
TypeORM 主要依靠 SQL 运算符来过滤列表或记录,例如 用 find
方法。另一方面,Prisma ORM 提供了更加直观易用的 通用运算符集。还应该注意的是,正如类型安全部分 below 中所解释的,TypeORM 在许多场景下在过滤查询中失去了类型安全性。
¥TypeORM primarily leans on SQL operators for filtering lists or records, e.g. with the find
method. Prisma ORM on the other hand, provides a more generic set of operators that are intuitive to use. It should also be noted that, as explained in the type-safety section below, TypeORM loses type-safety in filter queries in many scenarios.
TypeORM 和 Prisma ORM 的过滤 API 有何不同的一个很好的例子是查看 string
过滤器。虽然 TypeORM 主要提供基于直接来自 SQL 的 ILike
运算符的过滤器,但 Prisma ORM 提供了开发者可以使用的更具体的运算符,例如:contains
、startsWith
和 endsWith
。
¥A good example of how the filtering APIs of both TypeORM and Prisma ORM differ is by looking at string
filters. While TypeORM primarily provides the filter based on the ILike
operator which comes directly from SQL, Prisma ORM provides more specific operators that developers can use, e.g.: contains
, startsWith
and endsWith
.
const posts = await prisma.post.findMany({
where: {
title: 'Hello World',
},
})
const posts = await postRepository.find({
where: {
title: ILike('Hello World'),
},
})
const posts = await prisma.post.findMany({
where: {
title: { contains: 'Hello World' },
},
})
const posts = await postRepository.find({
where: {
title: ILike('%Hello World%'),
},
})
const posts = await prisma.post.findMany({
where: {
title: { startsWith: 'Hello World' },
},
})
const posts = await postRepository.find({
where: {
title: ILike('Hello World%'),
},
})
const posts = await prisma.post.findMany({
where: {
title: { endsWith: 'Hello World' },
},
})
const posts = await postRepository.find({
where: {
title: ILike('%Hello World'),
},
})
分页
¥Pagination
TypeORM 仅提供限制偏移分页,而 Prisma ORM 方便地为限制偏移和基于游标的分页提供专用 API。你可以在文档的 分页 部分或 API 比较 below 中了解有关这两种方法的更多信息。
¥TypeORM only offers limit-offset pagination while Prisma ORM conveniently provides dedicated APIs for both limit-offset but also cursor-based. You can learn more about both approaches in the Pagination section of the docs or in the API comparison below.
关系
¥Relations
在 SQL 中,处理通过外键连接的记录可能会变得非常复杂。Prisma ORM 的 虚拟关系场 概念为应用开发者提供了一种直观、便捷的方式来处理相关数据。Prisma ORM 方法的一些优点是:
¥Working with records that are connected via foreign keys can become very complex in SQL. Prisma ORM's concept of virtual relation field enables an intuitive and convenient way for application developers to work with related data. Some benefits of Prisma ORM's approach are:
-
通过 Fluent API 遍历关系 (docs)
¥traversing relationships via the fluent API (docs)
-
允许更新/创建连接记录的嵌套写入 (docs)
¥nested writes that enable updating/creating connected records (docs)
-
对相关记录应用过滤器 (docs)
¥applying filters on related records (docs)
-
轻松且类型安全地查询嵌套数据,无需担心 JOIN (docs)
¥easy and type-safe querying of nested data without worrying about JOINs (docs)
-
基于模型及其关系创建嵌套 TypeScript 类型 (docs)
¥creating nested TypeScript typings based on models and their relations (docs)
-
通过关系字段对数据模型中的关系进行直观建模 (docs)
¥intuitive modeling of relations in the data model via relation fields (docs)
-
关系表的隐式处理(有时也称为 JOIN、链接、数据透视表或联结表)(docs)
¥implicit handling of relation tables (also sometimes called JOIN, link, pivot or junction tables) (docs)
数据建模和迁移
¥Data modeling and migrations
Prisma 模型在 Prisma 架构 中定义,而 TypeORM 使用类和实验性 TypeScript 装饰器进行模型定义。使用 Active Record ORM 模式,TypeORM 的方法通常会导致复杂的模型实例,随着应用的增长,这些实例变得难以维护。
¥Prisma models are defined in the Prisma schema while TypeORM uses classes and experimental TypeScript decorators for model definitions. With the Active Record ORM pattern, TypeORM's approach often leads to complex model instances that are becoming hard to maintain as an application grows.
另一方面,Prisma ORM 生成一个轻量级数据库客户端,该客户端公开定制且完全类型安全的 API,以便为 Prisma 架构中定义的模型读取和写入数据,遵循 DataMapper ORM 模式而不是 Active Record。
¥Prisma ORM on the other hand generates a lightweight database client that exposes a tailored and fully type-safe API to read and write data for the models that are defined in the Prisma schema, following the DataMapper ORM pattern rather than Active Record.
Prisma ORM 的数据建模 DSL 精益、简单且直观易用。在 VS Code 中对数据进行建模时,你可以进一步利用 Prisma ORM 强大的 VS Code 扩展,其功能包括自动补齐、快速修复、跳转到定义以及其他可提高开发者工作效率的优势。
¥Prisma ORM's DSL for data modeling is lean, simple and intuitive to use. When modeling data in VS Code, you can further take advantage of Prisma ORM's powerful VS Code extension with features like autocompletion, quick fixes, jump to definition and other benefits that increase developer productivity.
model User {
id Int @id @default(autoincrement())
name String?
email String @unique
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
authorId Int?
author User? @relation(fields: [authorId], references: [id])
}
import {
Entity,
PrimaryGeneratedColumn,
Column,
OneToMany,
ManyToOne,
} from 'typeorm'
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number
@Column({ nullable: true })
name: string
@Column({ unique: true })
email: string
@OneToMany((type) => Post, (post) => post.author)
posts: Post[]
}
@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number
@Column()
title: string
@Column({ nullable: true })
content: string
@Column({ default: false })
published: boolean
@ManyToOne((type) => User, (user) => user.posts)
author: User
}
TypeORM 和 Prisma ORM 中的迁移工作方式类似。这两个工具都遵循根据提供的模型定义生成 SQL 文件的方法,并提供 CLI 来针对数据库执行它们。可以在执行迁移之前修改 SQL 文件,以便可以使用任一迁移系统执行任何自定义数据库操作。
¥Migrations work in similar fashions in TypeORM and Prisma ORM. Both tools follow the approach of generating SQL files based on the provided model definitions and provide a CLI to execute them against the database. The SQL files can be modified before the migrations are executed so that any custom database operation can be performed with either migration system.
类型安全
¥Type safety
TypeORM 是 Node.js 生态系统中最早完全拥抱 TypeScript 的 ORM 之一,并且在使开发者能够为其数据库查询获得一定程度的类型安全性方面做得非常出色。
¥TypeORM has been one of the first ORMs in the Node.js ecosystem to fully embrace TypeScript and has done a great job in enabling developers to get a certain level of type safety for their database queries.
然而,在很多情况下,TypeORM 的类型安全保证都达不到要求。以下章节描述了 Prisma ORM 可以为查询结果类型提供更强保证的场景。
¥However, there are numerous situations where the type safety guarantees of TypeORM fall short. The following sections describe the scenarios where Prisma ORM can provide stronger guarantees for the types of query results.
选择字段
¥Selecting fields
本节介绍在查询中选择模型字段的子集时类型安全性的差异。
¥This section explains the differences in type safety when selecting a subset of a model's fields in a query.
TypeORM
TypeORM 为其 find
方法提供了 select
选项(例如 find
、findByIds
、findOne
...),例如:
¥TypeORM provides a select
option for its find
methods (e.g. find
, findByIds
, findOne
, ...), for example:
- `find` with `select`
- Model
const postRepository = getManager().getRepository(Post)
const publishedPosts: Post[] = await postRepository.find({
where: { published: true },
select: ['id', 'title'],
})
@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number
@Column()
title: string
@Column({ nullable: true })
content: string
@Column({ default: false })
published: boolean
@ManyToOne((type) => User, (user) => user.posts)
author: User
}
虽然返回的 publishedPosts
数组中的每个对象在运行时仅携带选定的 id
和 title
属性,但 TypeScript 编译器对此一无所知。它将允许你在查询后访问 Post
实体上定义的任何其他属性,例如:
¥While each object in the returned publishedPosts
array only carries the selected id
and title
properties at runtime, the TypeScript compiler doesn't have any knowledge of this. It will allow you to access any other properties defined on the Post
entity after the query, for example:
const post = publishedPosts[0]
// The TypeScript compiler has no issue with this
if (post.content.length > 0) {
console.log(`This post has some content.`)
}
这段代码在运行时会导致错误:
¥This code will result in an error at runtime:
TypeError: Cannot read property 'length' of undefined
TypeScript 编译器只能看到返回对象的 Post
类型,但它不知道这些对象在运行时实际携带的字段。因此,它无法保护你访问未在数据库查询中检索的字段,从而导致运行时错误。
¥The TypeScript compiler only sees the Post
type of the returned objects, but it doesn't know about the fields that these objects actually carry at runtime. It therefore can't protect you from accessing fields that have not been retrieved in the database query, resulting in a runtime error.
Prisma ORM
Prisma Client 可以保证相同情况下的完全类型安全,并保护你免于访问未从数据库检索的字段。
¥Prisma Client can guarantee full type safety in the same situation and protects you from accessing fields that were not retrieved from the database.
考虑 Prisma 客户端查询的相同示例:
¥Consider the same example with a Prisma Client query:
- `findMany` with `select`
- Model
const publishedPosts = await prisma.post.findMany({
where: { published: true },
select: {
id: true,
title: true,
},
})
const post = publishedPosts[0]
// The TypeScript compiler will not allow this
if (post.content.length > 0) {
console.log(`This post has some content.`)
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
authorId Int?
author User? @relation(fields: [authorId], references: [id])
}
在这种情况下,TypeScript 编译器将在编译时抛出以下错误:
¥In this case, the TypeScript compiler will throw the following error already at compile-time:
[ERROR] 14:03:39 ⨯ Unable to compile TypeScript:
src/index.ts:36:12 - error TS2339: Property 'content' does not exist on type '{ id: number; title: string; }'.
42 if (post.content.length > 0) {
这是因为 Prisma 客户端会动态生成其查询的返回类型。在本例中,publishedPosts
输入如下:
¥This is because Prisma Client generates the return type for its queries on the fly. In this case, publishedPosts
is typed as follows:
const publishedPosts: {
id: number
title: string
}[]
因此,你不可能意外访问尚未在查询中检索到的模型上的属性。
¥It therefore is impossible for you to accidentally access a property on a model that has not been retrieved in a query.
加载关系
¥Loading relations
本节介绍在查询中加载模型关系时类型安全性的差异。在传统的 ORM 中,这有时称为预加载。
¥This section explains the differences in type safety when loading relations of a model in a query. In traditional ORMs, this is sometimes called eager loading.
TypeORM
TypeORM 允许通过可传递给其 find
方法的 relations
选项从数据库预加载关系。
¥TypeORM allows to eagerly load relations from the database via the relations
option that can be passed to its find
methods.
考虑这个例子:
¥Consider this example:
- `find` with `relations`
- Models
const postRepository = getManager().getRepository(Post)
const publishedPosts: Post[] = await postRepository.find({
where: { published: true },
relations: ['author'],
})
@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number
@Column()
title: string
@Column({ nullable: true })
content: string
@Column({ default: false })
published: boolean
@ManyToOne((type) => User, (user) => user.posts)
author: User
}
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number
@Column({ nullable: true })
name: string
@Column({ unique: true })
email: string
@OneToMany((type) => Post, (post) => post.author)
posts: Post[]
}
与 select
不同,TypeORM 不提供自动补齐功能,也不为传递给 relations
选项的字符串提供任何类型安全性。这意味着,TypeScript 编译器无法捕获查询这些关系时出现的任何拼写错误。例如,它将允许以下查询:
¥Unlike with select
, TypeORM does not provide autocompletion, nor any type-safety for the strings that are passed to the relations
option. This means, the TypeScript compiler is not able to catch any typos that are made when querying these relations. For example, it would allow for the following query:
const publishedPosts: Post[] = await postRepository.find({
where: { published: true },
// this query would lead to a runtime error because of a typo
relations: ['authors'],
})
这种微妙的拼写错误现在会导致以下运行时错误:
¥This subtle typo would now lead to the following runtime error:
UnhandledPromiseRejectionWarning: Error: Relation "authors" was not found; please check if it is correct and really exists in your entity.
Prisma ORM
Prisma ORM 可以保护你免受此类错误的影响,从而消除应用在运行时可能发生的一整类错误。当使用 include
在 Prisma 客户端查询中加载关系时,你不仅可以利用自动补齐功能来指定查询,而且查询结果也将被正确键入:
¥Prisma ORM protects you from mistakes like this and thus eliminates a whole class of errors that can occur in your application at runtime. When using include
to load a relation in a Prisma Client query, you can not only take advantage of autocompletion to specify the query, but the result of the query will also be properly typed:
- `find` with `relations`
- Models
const publishedPosts = await prisma.post.findMany({
where: { published: true },
include: { author: true },
})
model User {
id Int @id @default(autoincrement())
name String?
email String @unique
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
authorId Int?
author User? @relation(fields: [authorId], references: [id])
}
同样,publishedPosts
的类型是动态生成的,如下所示:
¥Again, the type of publishedPosts
is generated on the fly and looks as follows:
const publishedPosts: (Post & {
author: User
})[]
作为参考,这是 Prisma 客户端为你的 Prisma 模型生成的 User
和 Post
类型:
¥For reference, this is what the User
and Post
types look like that Prisma Client generates for your Prisma models:
- `User`
- `Post`
// Generated by Prisma ORM
export type User = {
id: number
name: string | null
email: string
}
// Generated by Prisma ORM
export type Post = {
id: number
title: string
content: string | null
published: boolean
authorId: number | null
}
过滤
¥Filtering
本节说明使用 where
过滤记录列表时类型安全性的差异。
¥This section explains the differences in type safety when filtering a list of records using where
.
TypeORM
TypeORM 允许将 where
选项传递给其 find
方法,以根据特定条件过滤返回记录的列表。这些标准可以根据模型的属性来定义。
¥TypeORM allows to pass a where
option to its find
methods to filter the list of returned records according to specific criteria. These criteria can be defined with respect to a model's properties.
使用运算符失去类型安全
¥Loosing type-safety using operators
考虑这个例子:
¥Consider this example:
- `find` with `select`
- Model
const postRepository = getManager().getRepository(Post)
const publishedPosts: Post[] = await postRepository.find({
where: {
published: true,
title: ILike('Hello World'),
views: MoreThan(0),
},
})
@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number
@Column()
title: string
@Column({ nullable: true })
content: string
@Column({ nullable: true })
views: number
@Column({ default: false })
published: boolean
@ManyToOne((type) => User, (user) => user.posts)
author: User
}
此代码运行正常并在运行时生成有效的查询。然而,where
选项在各种不同的场景中并不是真正类型安全的。当使用仅适用于特定类型的 FindOperator
(如 ILike
或 MoreThan
)(ILike
适用于字符串,MoreThan
适用于数字)时,你将失去为模型字段提供正确类型的保证。
¥This code runs properly and produces a valid query at runtime. However, the where
option is not really type-safe in various different scenarios. When using a FindOperator
like ILike
or MoreThan
that only work for specific types (ILike
works for strings, MoreThan
for numbers), you're losing the guarantee of providing the correct type for the model's field.
例如,你可以向 MoreThan
运算符提供一个字符串。TypeScript 编译器不会抱怨,你的应用只会在运行时失败:
¥For example, you can provide a string to the MoreThan
operator. The TypeScript compiler will not complain and your application will only fail at runtime:
const postRepository = getManager().getRepository(Post)
const publishedPosts: Post[] = await postRepository.find({
where: {
published: true,
title: ILike('Hello World'),
views: MoreThan('test'),
},
})
上面的代码会导致 TypeScript 编译器无法捕获的运行时错误:
¥The code above results in a runtime error that the TypeScript compiler doesn't catch for you:
error: error: invalid input syntax for type integer: "test"
指定不存在的属性
¥Specifying non-existing properties
另请注意,TypeScript 编译器允许你在 where
选项上指定模型中不存在的属性 - 再次导致运行时错误:
¥Also note that the TypeScript compiler allows you to specify properties on the where
option that don't exist on your models – again resulting in runtime errors:
const publishedPosts: Post[] = await postRepository.find({
where: {
published: true,
title: ILike('Hello World'),
viewCount: 1,
},
})
在这种情况下,你的应用在运行时再次失败,并出现以下错误:
¥In this case, your application again fails at runtime with the following error:
EntityColumnNotFound: No entity column "viewCount" was found.
Prisma ORM
Prisma ORM 以完全类型安全的方式涵盖了 TypeORM 在类型安全方面存在问题的两种过滤场景。
¥Both filtering scenarios that are problematic with TypeORM in terms of type-safety are covered by Prisma ORM in a fully type-safe way.
运算符的类型安全使用
¥Type-safe usage of operators
使用 Prisma ORM,TypeScript 编译器强制每个字段正确使用运算符:
¥With Prisma ORM, the TypeScript compiler enforces the correct usage of an operator per field:
const publishedPosts = await prisma.post.findMany({
where: {
published: true,
title: { contains: 'Hello World' },
views: { gt: 0 },
},
})
不允许使用 Prisma 客户端指定上面显示的相同有问题的查询:
¥It would not be allowed to specify the same problematic query shown above with Prisma Client:
const publishedPosts = await prisma.post.findMany({
where: {
published: true,
title: { contains: 'Hello World' },
views: { gt: 'test' }, // Caught by the TypeScript compiler
},
})
TypeScript 编译器会捕获此错误并抛出以下错误,以保护你免受应用运行时故障的影响:
¥The TypeScript compiler would catch this and throw the following error to protect you from a runtime failure of the app:
[ERROR] 16:13:50 ⨯ Unable to compile TypeScript:
src/index.ts:39:5 - error TS2322: Type '{ gt: string; }' is not assignable to type 'number | IntNullableFilter'.
Type '{ gt: string; }' is not assignable to type 'IntNullableFilter'.
Types of property 'gt' are incompatible.
Type 'string' is not assignable to type 'number'.
42 views: { gt: "test" }
将过滤器的类型安全定义为模型属性
¥Type-safe definition of filters as model properties
使用 TypeORM,你可以在 where
选项上指定不映射到模型字段的属性。在上面的示例中,过滤 viewCount
因此会导致运行时错误,因为该字段实际上称为 views
。
¥With TypeORM, you are able to specify a property on the where
option that doesn't map to a model's field. In the above example, filtering for viewCount
therefore led to a runtime error because the field actually is called views
.
使用 Prisma ORM,TypeScript 编译器将不允许引用模型中不存在的 where
内部的任何属性:
¥With Prisma ORM, the TypeScript compiler will not allow to reference any properties inside of where
that don't exist on the model:
const publishedPosts = await prisma.post.findMany({
where: {
published: true,
title: { contains: 'Hello World' },
viewCount: { gt: 0 }, // Caught by the TypeScript compiler
},
})
同样,TypeScript 编译器会触发以下消息,以保护你免受自己的错误影响:
¥Again, the TypeScript compiler complains with the following message to protect you from your own mistakes:
[ERROR] 16:16:16 ⨯ Unable to compile TypeScript:
src/index.ts:39:5 - error TS2322: Type '{ published: boolean; title: { contains: string; }; viewCount: { gt: number; }; }' is not assignable to type 'PostWhereInput'.
Object literal may only specify known properties, and 'viewCount' does not exist in type 'PostWhereInput'.
42 viewCount: { gt: 0 }
创造新记录
¥Creating new records
本节介绍创建新记录时类型安全性的差异。
¥This section explains the differences in type safety when creating new records.
TypeORM
使用 TypeORM,有两种主要方式在数据库中创建新记录:insert
和 save
。这两种方法都允许开发者提交在未提供必填字段时可能导致运行时错误的数据。
¥With TypeORM, there are two main ways to create new records in the database: insert
and save
. Both methods allow developers to submit data that can lead to runtime errors when required fields are not provided.
考虑这个例子:
¥Consider this example:
- Create with `save`
- Create with `insert`
- Model
const userRepository = getManager().getRepository(User)
const newUser = new User()
newUser.name = 'Alice'
userRepository.save(newUser)
const userRepository = getManager().getRepository(User)
userRepository.insert({
name: 'Alice',
})
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number
@Column({ nullable: true })
name: string
@Column({ unique: true })
email: string
@OneToMany((type) => Post, (post) => post.author)
posts: Post[]
}
无论你使用 save
还是 insert
通过 TypeORM 创建记录,如果你忘记提供必填字段的值,你都会收到以下运行时错误:
¥No matter if you're using save
or insert
for record creation with TypeORM, you will get the following runtime error if you forget to provide the value for a required field:
QueryFailedError: null value in column "email" of relation "user" violates not-null constraint
email
字段根据 User
实体的要求进行定义(由数据库中的 NOT NULL
约束强制执行)。
¥The email
field is defined as required on the User
entity (which is enforced by a NOT NULL
constraint in the database).
Prisma ORM
Prisma ORM 通过强制你提交模型的所有必填字段的值来保护你免受此类错误的影响。
¥Prisma ORM protects you from these kind of mistakes by enforcing that you submit values for all required fields of a model.
例如,以下尝试创建新的 User
且缺少所需的 email
字段将被 TypeScript 编译器捕获:
¥For example, the following attempt to create a new User
where the required email
field is missing would be caught by the TypeScript compiler:
- Create with `create`
- Model
const newUser = await prisma.user.create({
data: {
name: 'Alice',
},
})
model User {
id Int @id @default(autoincrement())
name String?
email String @unique
}
这会导致以下编译时错误:
¥It would lead to the following compile-time error:
[ERROR] 10:39:07 ⨯ Unable to compile TypeScript:
src/index.ts:39:5 - error TS2741: Property 'email' is missing in type '{ name: string; }' but required in type 'UserCreateInput'.
API 比较
¥API comparison
获取单个对象
¥Fetching single objects
Prisma ORM
const user = await prisma.user.findUnique({
where: {
id: 1,
},
})
TypeORM
const userRepository = getRepository(User)
const user = await userRepository.findOne(id)
获取单个对象的选定标量
¥Fetching selected scalars of single objects
Prisma ORM
const user = await prisma.user.findUnique({
where: {
id: 1,
},
select: {
name: true,
},
})
TypeORM
const userRepository = getRepository(User)
const user = await userRepository.findOne(id, {
select: ['id', 'email'],
})
获取关系
¥Fetching relations
Prisma ORM
- Using include
- Fluent API
const posts = await prisma.user.findUnique({
where: {
id: 2,
},
include: {
post: true,
},
})
const posts = await prisma.user
.findUnique({
where: {
id: 2,
},
})
.post()
注意:
select
返回包含post
数组的user
对象,而 Fluent API 仅返回post
数组。¥Note:
select
return auser
object that includes apost
array, whereas the fluent API only returns apost
array.
TypeORM
- Using `relations`
- Using `JOIN`
- Using earger relations
const userRepository = getRepository(User)
const user = await userRepository.findOne(id, {
relations: ['posts'],
})
const userRepository = getRepository(User)
const user = await userRepository.findOne(id, {
join: {
alias: 'user',
leftJoinAndSelect: {
posts: 'user.posts',
},
},
})
const userRepository = getRepository(User)
const user = await userRepository.findOne(id)
过滤具体值
¥Filtering for concrete values
Prisma ORM
const posts = await prisma.post.findMany({
where: {
title: {
contains: 'Hello',
},
},
})
TypeORM
const userRepository = getRepository(User)
const users = await userRepository.find({
where: {
name: 'Alice',
},
})
其他过滤条件
¥Other filter criteria
Prisma ORM
Prisma ORM 生成许多现代应用开发中常用的 附加过滤器。
¥Prisma ORM generates many additional filters that are commonly used in modern application development.
TypeORM
TypeORM 提供了 内置运算符,可用于创建更复杂的比较
¥TypeORM provides built-in operators that can be used to create more complex comparisons
关系过滤器
¥Relation filters
Prisma ORM
Prisma ORM 允许你根据条件过滤列表,该条件不仅适用于正在检索的列表的模型,还适用于该模型的关系。
¥Prisma ORM lets you filter a list based on a criteria that applies not only to the models of the list being retrieved, but to a relation of that model.
例如,以下查询返回具有一篇或多篇标题中包含 "你好" 的帖子的用户:
¥For example, the following query returns users with one or more posts with "Hello" in the title:
const posts = await prisma.user.findMany({
where: {
Post: {
some: {
title: {
contains: 'Hello',
},
},
},
},
})
TypeORM
TypeORM 不提供关系过滤器的专用 API。你可以通过使用 QueryBuilder
或手动编写查询来获得类似的功能。
¥TypeORM doesn't offer a dedicated API for relation filters. You can get similar functionality by using the QueryBuilder
or writing the queries by hand.
分页
¥Pagination
Prisma ORM
光标式分页:
¥Cursor-style pagination:
const page = await prisma.post.findMany({
before: {
id: 242,
},
last: 20,
})
偏移分页:
¥Offset pagination:
const cc = await prisma.post.findMany({
skip: 200,
first: 20,
})
TypeORM
const postRepository = getRepository(Post)
const posts = await postRepository.find({
skip: 5,
take: 10,
})
创建对象
¥Creating objects
Prisma ORM
const user = await prisma.user.create({
data: {
email: 'alice@prisma.io',
},
})
TypeORM
- Using `save`
- Using `create`
- Using `insert`
const user = new User()
user.name = 'Alice'
user.email = 'alice@prisma.io'
await user.save()
const userRepository = getRepository(User)
const user = await userRepository.create({
name: 'Alice',
email: 'alice@prisma.io',
})
await user.save()
const userRepository = getRepository(User)
await userRepository.insert({
name: 'Alice',
email: 'alice@prisma.io',
})
更新对象
¥Updating objects
Prisma ORM
const user = await prisma.user.update({
data: {
name: 'Alicia',
},
where: {
id: 2,
},
})
TypeORM
const userRepository = getRepository(User)
const updatedUser = await userRepository.update(id, {
name: 'James',
email: 'james@prisma.io',
})
删除对象
¥Deleting objects
Prisma ORM
const deletedUser = await prisma.user.delete({
where: {
id: 10,
},
})
TypeORM
- Using `delete`
- Using `remove`
const userRepository = getRepository(User)
await userRepository.delete(id)
const userRepository = getRepository(User)
const deletedUser = await userRepository.remove(user)
批量更新
¥Batch updates
Prisma ORM
const user = await prisma.user.updateMany({
data: {
name: 'Published author!',
},
where: {
Post: {
some: {
published: true,
},
},
},
})
TypeORM
你可以使用 查询构建器来更新数据库中的实体。
¥You can use the query builder to update entities in your database.
批量删除
¥Batch deletes
Prisma ORM
const users = await prisma.user.deleteMany({
where: {
id: {
in: [1, 2, 6, 6, 22, 21, 25],
},
},
})
TypeORM
- Using `delete`
- Using `remove`
const userRepository = getRepository(User)
await userRepository.delete([id1, id2, id3])
const userRepository = getRepository(User)
const deleteUsers = await userRepository.remove([user1, user2, user3])
事务
¥Transactions
Prisma ORM
const user = await prisma.user.create({
data: {
email: 'bob.rufus@prisma.io',
name: 'Bob Rufus',
Post: {
create: [
{ title: 'Working at Prisma' },
{ title: 'All about databases' },
],
},
},
})
TypeORM
await getConnection().$transaction(async (transactionalEntityManager) => {
const user = getRepository(User).create({
name: 'Bob',
email: 'bob@prisma.io',
})
const post1 = getRepository(Post).create({
title: 'Join us for GraphQL Conf in 2019',
})
const post2 = getRepository(Post).create({
title: 'Subscribe to GraphQL Weekly for GraphQL news',
})
user.posts = [post1, post2]
await transactionalEntityManager.save(post1)
await transactionalEntityManager.save(post2)
await transactionalEntityManager.save(user)
})
Stay connected with Prisma
Continue your Prisma journey by connecting with our active community. Stay informed, get involved, and collaborate with other developers:
- Follow us on X for announcements, live events and useful tips.
- Join our Discord to ask questions, talk to the community, and get active support through conversations.
- Subscribe on YouTube for tutorials, demos, and streams.
- Engage on GitHub by starring the repository, reporting issues, or contributing to an issue.