CRUD
本頁面描述瞭如何使用生成的 Prisma Client API 執行 CRUD 操作。CRUD 是以下各項的首字母縮寫:
有關每個方法的詳細說明,請參閱Prisma Client API 參考文件。
示例 Schema
所有示例均基於以下 schema:
展開檢視示例 schema
- 關係型資料庫
- MongoDB
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model ExtendedProfile {
id Int @id @default(autoincrement())
biography String
user User @relation(fields: [userId], references: [id])
userId Int @unique
}
model User {
id Int @id @default(autoincrement())
name String?
email String @unique
profileViews Int @default(0)
role Role @default(USER)
coinflips Boolean[]
posts Post[]
profile ExtendedProfile?
}
model Post {
id Int @id @default(autoincrement())
title String
published Boolean @default(true)
author User @relation(fields: [authorId], references: [id])
authorId Int
comments Json?
views Int @default(0)
likes Int @default(0)
categories Category[]
}
model Category {
id Int @id @default(autoincrement())
name String @unique
posts Post[]
}
enum Role {
USER
ADMIN
}
datasource db {
provider = "mongodb"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model ExtendedProfile {
id String @id @default(auto()) @map("_id") @db.ObjectId
biography String
user User @relation(fields: [userId], references: [id])
userId String @unique @db.ObjectId
}
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
name String?
email String @unique
profileViews Int @default(0)
role Role @default(USER)
coinflips Boolean[]
posts Post[]
profile ExtendedProfile?
}
model Post {
id String @id @default(auto()) @map("_id") @db.ObjectId
title String
published Boolean @default(true)
author User @relation(fields: [authorId], references: [id])
authorId String @db.ObjectId
comments Json?
views Int @default(0)
likes Int @default(0)
categories Category[]
}
model Category {
id String @id @default(auto()) @map("_id") @db.ObjectId
name String @unique
posts Post[]
}
enum Role {
USER
ADMIN
}
對於關係型資料庫,使用 db push 命令將示例 schema 推送到你自己的資料庫。
npx prisma db push
對於 MongoDB,請確保你的資料形狀統一併與 Prisma schema 中定義的模型匹配。
建立
建立單個記錄
以下查詢建立 (create()) 一個具有兩個欄位的單個使用者:
const user = await prisma.user.create({
data: {
email: 'elsa@prisma.io',
name: 'Elsa Prisma',
},
})
使用者的 id 是自動生成的,你的 schema 決定了哪些欄位是必需的。
使用生成的型別建立單個記錄
以下示例產生相同的結果,但建立了一個名為 user 的 UserCreateInput 變數,其位於 create() 查詢的上下文之外。在完成簡單檢查(“此 create() 查詢中是否應包含文章?”)後,user 變數被傳遞到查詢中:
import { PrismaClient, Prisma } from '@prisma/client'
const prisma = new PrismaClient()
async function main() {
let includePosts: boolean = false
let user: Prisma.UserCreateInput
// Check if posts should be included in the query
if (includePosts) {
user = {
email: 'elsa@prisma.io',
name: 'Elsa Prisma',
posts: {
create: {
title: 'Include this post!',
},
},
}
} else {
user = {
email: 'elsa@prisma.io',
name: 'Elsa Prisma',
}
}
// Pass 'user' object into query
const createUser = await prisma.user.create({ data: user })
}
main()
有關使用生成型別的更多資訊,請參閱:生成型別。
建立多個記錄
Prisma Client 支援批次插入作為 2.20.0 及更高版本中的 GA 功能。
以下 createMany() 查詢建立多個使用者並跳過任何重複項(email 必須唯一):
const createMany = await prisma.user.createMany({
data: [
{ name: 'Bob', email: 'bob@prisma.io' },
{ name: 'Bobo', email: 'bob@prisma.io' }, // Duplicate unique key!
{ name: 'Yewande', email: 'yewande@prisma.io' },
{ name: 'Angelique', email: 'angelique@prisma.io' },
],
skipDuplicates: true, // Skip 'Bobo'
})
{
count: 3
}
請注意,使用 MongoDB、SQLServer 或 SQLite 時不支援 skipDuplicates。
createMany() 使用一個帶有多個值的 INSERT INTO 語句,這通常比每行單獨一個 INSERT 更高效。
BEGIN
INSERT INTO "public"."User" ("id","name","email","profileViews","role","coinflips","testing","city","country") VALUES (DEFAULT,$1,$2,$3,$4,DEFAULT,DEFAULT,DEFAULT,$5), (DEFAULT,$6,$7,$8,$9,DEFAULT,DEFAULT,DEFAULT,$10), (DEFAULT,$11,$12,$13,$14,DEFAULT,DEFAULT,DEFAULT,$15), (DEFAULT,$16,$17,$18,$19,DEFAULT,DEFAULT,DEFAULT,$20) ON CONFLICT DO NOTHING
COMMIT
SELECT "public"."User"."country", "public"."User"."city", "public"."User"."email", SUM("public"."User"."profileViews"), COUNT(*) FROM "public"."User" WHERE 1=1 GROUP BY "public"."User"."country", "public"."User"."city", "public"."User"."email" HAVING AVG("public"."User"."profileViews") >= $1 ORDER BY "public"."User"."country" ASC OFFSET $2
注意:
$transaction內的多個create()語句會導致多個INSERT語句。
以下影片演示瞭如何使用 createMany() 和 faker.js 來為資料庫播種示例資料:
建立記錄並連線或建立相關記錄
有關同時建立記錄和一個或多個相關記錄的資訊,請參閱處理關係 > 巢狀寫入。
建立並返回多個記錄
此功能在 Prisma ORM 5.14.0 及更高版本中適用於 PostgreSQL、CockroachDB 和 SQLite。
你可以使用 createManyAndReturn() 來建立多條記錄並返回結果物件。
const users = await prisma.user.createManyAndReturn({
data: [
{ name: 'Alice', email: 'alice@prisma.io' },
{ name: 'Bob', email: 'bob@prisma.io' },
],
})
使用 createManyAndReturn() 時,relationLoadStrategy: join 不可用。
讀取
透過 ID 或唯一識別符號獲取記錄
以下查詢透過唯一識別符號或 ID 返回單個記錄 (findUnique()):
// By unique identifier
const user = await prisma.user.findUnique({
where: {
email: 'elsa@prisma.io',
},
})
// By ID
const user = await prisma.user.findUnique({
where: {
id: 99,
},
})
如果你使用 MongoDB 聯結器並且底層 ID 型別是 ObjectId,你可以使用該 ObjectId 的字串表示形式。
// By ID
const user = await prisma.user.findUnique({
where: {
id: '60d5922d00581b8f0062e3a8',
},
})
獲取所有記錄
以下 findMany() 查詢返回所有 User 記錄:
const users = await prisma.user.findMany()
你也可以對結果進行分頁。
獲取符合特定條件的第一條記錄
以下 findFirst() 查詢返回最近建立的、至少有一篇文章點贊數超過 100 的使用者:
- 按 ID 降序(最大值優先)排序使用者 — 最大的 ID 是最新的。
- 返回降序排列的第一個使用者,該使用者至少有一篇文章的點贊數超過 100。
const findUser = await prisma.user.findFirst({
where: {
posts: {
some: {
likes: {
gt: 100,
},
},
},
},
orderBy: {
id: 'desc',
},
})
獲取過濾後的記錄列表
Prisma Client 支援對記錄欄位和相關記錄欄位進行過濾。
按單個欄位值過濾
以下查詢返回所有郵箱以 "prisma.io" 結尾的 User 記錄:
const users = await prisma.user.findMany({
where: {
email: {
endsWith: 'prisma.io',
},
},
})
按多個欄位值過濾
以下查詢結合使用運算子,返回姓名以 E 開頭的使用者或至少有 1 次個人資料瀏覽量的管理員:
const users = await prisma.user.findMany({
where: {
OR: [
{
name: {
startsWith: 'E',
},
},
{
AND: {
profileViews: {
gt: 0,
},
role: {
equals: 'ADMIN',
},
},
},
],
},
})
按相關記錄欄位值過濾
以下查詢返回郵箱以 prisma.io 結尾並且至少有一篇未釋出文章(some)的使用者:
const users = await prisma.user.findMany({
where: {
email: {
endsWith: 'prisma.io',
},
posts: {
some: {
published: false,
},
},
},
})
有關按相關欄位值過濾的更多示例,請參閱處理關係。
選擇欄位子集
以下 findUnique() 查詢使用 select 返回特定 User 記錄的 email 和 name 欄位:
const user = await prisma.user.findUnique({
where: {
email: 'emma@prisma.io',
},
select: {
email: true,
name: true,
},
})
有關包含關係的更多資訊,請參閱:
選擇相關記錄欄位子集
以下查詢使用巢狀 select 返回:
- 使用者的
email - 每篇文章的
likes欄位
const user = await prisma.user.findUnique({
where: {
email: 'emma@prisma.io',
},
select: {
email: true,
posts: {
select: {
likes: true,
},
},
},
})
有關包含關係的更多資訊,請參閱選擇欄位幷包含關係。
選擇不同的欄位值
有關選擇不同欄位值的資訊,請參閱選擇 distinct。
包含相關記錄
以下查詢返回所有 ADMIN 使用者,並在結果中包含每個使用者的文章:
const users = await prisma.user.findMany({
where: {
role: 'ADMIN',
},
include: {
posts: true,
},
})
有關包含關係的更多資訊,請參閱選擇欄位幷包含關係。
包含過濾後的關係列表
請參閱處理關係,瞭解如何將include 和 where 結合起來以獲取過濾後的關係列表——例如,只包含使用者的已釋出文章。
更新
更新單個記錄
以下查詢使用 update() 根據 email 查詢並更新單個 User 記錄:
const updateUser = await prisma.user.update({
where: {
email: 'viola@prisma.io',
},
data: {
name: 'Viola the Magnificent',
},
})
更新多個記錄
以下查詢使用 updateMany() 更新所有包含 prisma.io 的 User 記錄:
const updateUsers = await prisma.user.updateMany({
where: {
email: {
contains: 'prisma.io',
},
},
data: {
role: 'ADMIN',
},
})
更新並返回多個記錄
此功能在 Prisma ORM 6.2.0 及更高版本中適用於 PostgreSQL、CockroachDB 和 SQLite。
你可以使用 updateManyAndReturn() 來更新多條記錄並返回結果物件。
const users = await prisma.user.updateManyAndReturn({
where: {
email: {
contains: 'prisma.io',
}
},
data: {
role: 'ADMIN'
}
})
使用 updateManyAndReturn() 時,relationLoadStrategy: join 不可用。
更新或建立記錄
以下查詢使用 upsert() 更新具有特定郵箱地址的 User 記錄,如果該記錄不存在則建立該 User 記錄:
const upsertUser = await prisma.user.upsert({
where: {
email: 'viola@prisma.io',
},
update: {
name: 'Viola the Magnificent',
},
create: {
email: 'viola@prisma.io',
name: 'Viola the Magnificent',
},
})
從版本 4.6.0 開始,Prisma Client 儘可能使用資料庫原生的 SQL 命令執行 upsert。瞭解更多。
Prisma Client 沒有 findOrCreate() 查詢。你可以使用 upsert() 作為一種變通方法。要使 upsert() 的行為類似於 findOrCreate() 方法,請為 upsert() 提供一個空的 update 引數。
將 upsert() 用作 findOrCreate() 的變通方法的侷限性是,upsert() 僅接受 where 條件中的唯一模型欄位。因此,如果 where 條件包含非唯一欄位,則無法使用 upsert() 來模擬 findOrCreate()。
更新數字欄位
使用原子數字操作來更新基於其當前值的數字欄位——例如,遞增或乘以。以下查詢將 views 和 likes 欄位遞增 1:
const updatePosts = await prisma.post.updateMany({
data: {
views: {
increment: 1,
},
likes: {
increment: 1,
},
},
})
連線和斷開相關記錄
有關斷開 (disconnect) 和連線 (connect) 相關記錄的資訊,請參閱處理關係。
刪除
刪除單個記錄
以下查詢使用 delete() 刪除單個 User 記錄:
const deleteUser = await prisma.user.delete({
where: {
email: 'bert@prisma.io',
},
})
嘗試刪除一個或多個帖子的使用者會導致錯誤,因為每個 Post 都需要一個作者——請參閱級聯刪除。
刪除多個記錄
以下查詢使用 deleteMany() 刪除所有 email 包含 prisma.io 的 User 記錄:
const deleteUsers = await prisma.user.deleteMany({
where: {
email: {
contains: 'prisma.io',
},
},
})
嘗試刪除一個或多個帖子的使用者會導致錯誤,因為每個 Post 都需要一個作者——請參閱級聯刪除。
刪除所有記錄
以下查詢使用 deleteMany() 刪除所有 User 記錄:
const deleteUsers = await prisma.user.deleteMany({})
請注意,如果使用者有任何相關記錄(如文章),此查詢將失敗。在這種情況下,你需要首先刪除相關記錄。
級聯刪除(刪除相關記錄)
以下查詢使用 delete() 刪除單個 User 記錄:
const deleteUser = await prisma.user.delete({
where: {
email: 'bert@prisma.io',
},
})
然而,示例 schema 中包含 Post 和 User 之間的必需關係,這意味著你無法刪除帶有文章的使用者:
The change you are trying to make would violate the required relation 'PostToUser' between the `Post` and `User` models.
要解決此錯誤,你可以:
-
使關係可選
model Post {
id Int @id @default(autoincrement())
author User? @relation(fields: [authorId], references: [id])
authorId Int?
author User @relation(fields: [authorId], references: [id])
authorId Int
} -
在刪除使用者之前,將文章的作者更改為另一個使用者。
-
透過事務中的兩個獨立查詢(所有查詢必須成功)刪除使用者及其所有文章。
const deletePosts = prisma.post.deleteMany({
where: {
authorId: 7,
},
})
const deleteUser = prisma.user.delete({
where: {
id: 7,
},
})
const transaction = await prisma.$transaction([deletePosts, deleteUser])
刪除所有表中的所有記錄
有時你希望從所有表中刪除所有資料,但保留實際的表。這在開發環境和測試時尤其有用。
以下展示瞭如何使用 Prisma Client 和 Prisma Migrate 刪除所有表中的所有記錄。
使用 deleteMany() 刪除所有資料
當你提前知道表的刪除順序時,可以使用 deleteMany 函式。這在 $transaction 中同步執行,並可與所有型別的資料庫一起使用。
const deletePosts = prisma.post.deleteMany()
const deleteProfile = prisma.profile.deleteMany()
const deleteUsers = prisma.user.deleteMany()
// The transaction runs synchronously so deleteUsers must run last.
await prisma.$transaction([deleteProfile, deletePosts, deleteUsers])
✅ 優點
- 當你提前知道 schema 結構時,效果很好。
- 同步刪除每個表的資料。
❌ 缺點
- 在使用關係型資料庫時,此函式的擴充套件性不如具有更通用的解決方案(該方案查詢並
TRUNCATE你的表,而不考慮其關係約束)。請注意,使用 MongoDB 聯結器時,此擴充套件性問題不適用。
注意:
$transaction對每個模型的表執行級聯刪除,因此必須按順序呼叫它們。
使用原始 SQL / TRUNCATE 刪除所有資料
如果你習慣使用原始 SQL,可以使用 $executeRawUnsafe 對錶執行 TRUNCATE 查詢。
在以下示例中,第一個選項卡展示瞭如何透過使用 $queryRaw 查詢來對 Postgres 資料庫執行 TRUNCATE,該查詢會遍歷表並在單個查詢中 TRUNCATE 所有表。
第二個選項卡展示了執行相同功能但使用 MySQL 資料庫。在這種情況下,必須在執行 TRUNCATE 之前刪除約束,完成後再重新設定。整個過程作為一個 $transaction 執行。
- PostgreSQL
- MySQL
const tablenames = await prisma.$queryRaw<
Array<{ tablename: string }>
>`SELECT tablename FROM pg_tables WHERE schemaname='public'`
const tables = tablenames
.map(({ tablename }) => tablename)
.filter((name) => name !== '_prisma_migrations')
.map((name) => `"public"."${name}"`)
.join(', ')
try {
await prisma.$executeRawUnsafe(`TRUNCATE TABLE ${tables} CASCADE;`)
} catch (error) {
console.log({ error })
}
const transactions: PrismaPromise<any>[] = []
transactions.push(prisma.$executeRaw`SET FOREIGN_KEY_CHECKS = 0;`)
const tablenames = await prisma.$queryRaw<
Array<{ TABLE_NAME: string }>
>`SELECT TABLE_NAME from information_schema.TABLES WHERE TABLE_SCHEMA = 'tests';`
for (const { TABLE_NAME } of tablenames) {
if (TABLE_NAME !== '_prisma_migrations') {
try {
transactions.push(prisma.$executeRawUnsafe(`TRUNCATE ${TABLE_NAME};`))
} catch (error) {
console.log({ error })
}
}
}
transactions.push(prisma.$executeRaw`SET FOREIGN_KEY_CHECKS = 1;`)
try {
await prisma.$transaction(transactions)
} catch (error) {
console.log({ error })
}
✅ 優點
- 可伸縮的
- 非常快
❌ 缺點
- 無法撤消操作
- 使用 SQL 保留關鍵字作為表名在執行原始查詢時可能導致問題
使用 Prisma Migrate 刪除所有記錄
如果你使用 Prisma Migrate,可以使用 migrate reset,這將:
- 刪除資料庫
- 建立一個新資料庫
- 應用遷移
- 用資料填充資料庫
高階查詢示例
建立深度巢狀的記錄樹
- 一個
User - 兩個新的相關
Post記錄 - 每篇文章連線或建立
Category
const u = await prisma.user.create({
include: {
posts: {
include: {
categories: true,
},
},
},
data: {
email: 'emma@prisma.io',
posts: {
create: [
{
title: 'My first post',
categories: {
connectOrCreate: [
{
create: { name: 'Introductions' },
where: {
name: 'Introductions',
},
},
{
create: { name: 'Social' },
where: {
name: 'Social',
},
},
],
},
},
{
title: 'How to make cookies',
categories: {
connectOrCreate: [
{
create: { name: 'Social' },
where: {
name: 'Social',
},
},
{
create: { name: 'Cooking' },
where: {
name: 'Cooking',
},
},
],
},
},
],
},
},
})