Транзакции¶
Транзакция — это последовательность операций чтения/записи, которые обрабатываются как единое целое, т. е. либо все операции завершаются успешно, либо все операции отклоняются с ошибкой.
Prisma позволяет использовать транзакции тремя способами:
вложенные запросы (см. выше): операции с родительскими и связанными записями выполняются в контексте одной транзакции
const newUserWithProfile = await prisma.user.create({
data: {
email,
profile: {
// !
create: {
first_name,
last_name,
},
},
},
});
пакетированные/массовые (batch/bulk) транзакции: выполнение нескольких операций за один раз с помощью таких запросов, как createMany
, updateMany
и deleteMany
const removedUser = await prisma.user.delete({
where: {
email,
},
});
// !
await prisma.post.deleteMany({
where: {
author_id: removedUser.id,
},
});
вызов метода \$transaction.
\$transaction¶
Интерфейс $transaction
может быть использован в двух формах:
$transaction([ query1, query2, ...queryN ])
— принимает массив последовательно выполняемых запросов;$transaction(fn)
— принимает функцию, которая может включать запросы и другой код.
Пример транзакции, возвращающей посты, в заголовке которых встречается слово TypeScript и общее количество постов:
const [
postsAboutTypeScript,
totalPostCount,
] = await prisma.$transaction([
prisma.post.findMany({
where: { title: { contains: 'TypeScript' } },
}),
prisma.post.count(),
]);
В $transaction
допускается использование SQL:
const [userNames, updatedUser] = await prisma.$transaction([
prisma.$queryRaw`SELECT 'user_name' FROM users`,
prisma.$executeRaw`UPDATE users SET user_name = 'Harry' WHERE id = 42`,
]);
Интерактивные транзакции¶
Интерактивные транзакции предоставляют разработчикам больший контроль над выполняемыми в контексте транзакции операциями. В данный момент они имеют статус экспериментальной возможности, которую можно включить следующим образом:
generator client {
provider = "prisma-client-js"
previewFeatures = ["interactiveTransactions"]
}
Рассмотрим пример совершения платежа.
Предположим, что у Alice и Bob имеется по 100$ на счетах (account), и Alice хочет отправить Bob свои 100$.
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function transfer(from, to, amount) {
try {
await prisma.$transaction(async (prisma) => {
// 1. Уменьшаем баланс отправителя
const sender = await prisma.account.update({
data: {
balance: {
decrement: amount,
},
},
where: {
email: from,
},
});
// 2. Проверяем, что баланс отправителя после уменьшения >= 0
if (sender.balance < 0) {
throw new Error(
`${from} имеет недостаточно средств для отправки ${amount}`
);
}
// 3. Увеличиваем баланс получателя
const recipient = await prisma.account.update({
data: {
balance: {
increment: amount,
},
},
where: {
email: to,
},
});
return recipient;
});
} catch (e) {
// обрабатываем ошибку
}
}
async function main() {
// эта транзакция разрешится
await transfer('[email protected]', '[email protected]', 100);
// а эта провалится
await transfer('[email protected]', '[email protected]', 100);
}
main().finally(() => {
prisma.$disconnect();
});
Подробнее о транзакциях можно почитать здесь.