CRUD
このページでは、生成された Prisma Client API を使って CRUD 操作を実行する方法について説明します。CRUD は、以下の頭文字を取った略語です。
各メソッドの詳細については、Prisma Client API リファレンスドキュメントを参照してください。
スキーマ例
すべての例は以下のスキーマに基づいています。
サンプルスキーマを展開
- リレーショナルデータベース
- 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
コマンドを使用して、サンプルスキーマを独自のデータベースにプッシュします。
npx prisma db push
MongoDBの場合、データが均一な形式であり、Prisma スキーマで定義されたモデルと一致していることを確認してください。
作成
単一レコードの作成
以下のクエリは、2 つのフィールドを持つ単一のユーザーを作成します (create()
)。
const user = await prisma.user.create({
data: {
email: 'elsa@prisma.io',
name: 'Elsa Prisma',
},
})
ユーザーの id
は自動生成され、どのフィールドが必須であるかはスキーマによって決定されます。
生成された型を使用した単一レコードの作成
以下の例は同じ結果を生成しますが、create()
クエリのコンテキスト**外**で UserCreateInput
型の変数 user
を作成します。簡単なチェック(「この 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 を使用して、データベースにサンプルデータをシードする方法をデモンストレーションしています。
レコードの作成と、関連レコードの接続または作成
レコードと、1 つ以上の関連レコードを同時に作成する方法については、リレーションの操作 > ネストされた書き込みを参照してください。
複数レコードの作成と返却
この機能は、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 以上の「いいね!」がある投稿を少なくとも 1 つ持つ、**最も新しく作成されたユーザー**を返します。
- ユーザーをIDの降順(大きい順)で並べ替える - 最大のIDが最新です。
- 100以上の「いいね!」がある投稿を少なくとも1つ持つユーザーを降順で最初のユーザーとして返します。
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
) が少なくとも**1つ**あるユーザーを返します。
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',
},
})
1 つ以上の投稿を持つユーザーを削除しようとするとエラーが発生します。これは、すべての Post
に作成者が必要であるためです。詳細については、カスケード削除を参照してください。
複数レコードの削除
以下のクエリは、deleteMany()
を使用して、email
に prisma.io
を含むすべての User
レコードを削除します。
const deleteUsers = await prisma.user.deleteMany({
where: {
email: {
contains: 'prisma.io',
},
},
})
1 つ以上の投稿を持つユーザーを削除しようとするとエラーが発生します。これは、すべての Post
に作成者が必要であるためです。詳細については、カスケード削除を参照してください。
全レコードの削除
以下のクエリは、deleteMany()
を使用して、すべての User
レコードを削除します。
const deleteUsers = await prisma.user.deleteMany({})
このクエリは、ユーザーに関連レコード(投稿など)がある場合、失敗することに注意してください。この場合、最初に関連レコードを削除する必要があります。
カスケード削除 (関連レコードの削除)
以下のクエリは、delete()
を使用して、単一の User
レコードを削除します。
const deleteUser = await prisma.user.delete({
where: {
email: 'bert@prisma.io',
},
})
ただし、例のスキーマには 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
} -
ユーザーを削除する前に、投稿の作者を別のユーザーに変更する。
-
トランザクション内で2つの個別のクエリ(すべてのクエリが成功する必要がある)を使って、ユーザーとそのすべての投稿を削除する。
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])
✅ 長所
- スキーマの構造を事前に把握している場合にうまく機能します
- 各テーブルのデータを同期的に削除します
❌ 短所
- リレーショナルデータベースを使用する場合、この関数は、リレーショナル制約に関係なくテーブルをルックアップして
TRUNCATE
する、より汎用的なソリューションよりもスケールしません。このスケーリングの問題は、MongoDB コネクタを使用する場合には適用されないことに注意してください。
注:
$transaction
は各モデルのテーブルでカスケード削除を実行するため、順序通りに呼び出す必要があります。
生の SQL / TRUNCATE
を使用してすべてのデータを削除する
生の SQL の扱いに慣れている場合、$executeRawUnsafe
を使用してテーブルに対して TRUNCATE
クエリを実行できます。
以下の例では、最初のタブは、Postgres データベースで $queryRaw
ルックアップを使用して TRUNCATE
を実行する方法を示しています。これはテーブルをマッピングし、単一のクエリですべてのテーブルを TRUNCATE
します。
2番目のタブは、同じ関数を 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
レコード2件 - 投稿ごとに
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',
},
},
],
},
},
],
},
},
})