Prisma 多重创建查询嵌套 connectOrCreate 抛出唯一约束失败
Prisma multiple create queries with nested connectOrCreate throws unique constraint failed
我正在尝试创建一个模型,其中电影和流派这两种类型之间存在多对多关系。
我从外部数据源异步检索电影列表,create
每个电影都在我自己的数据库中(如果尚未创建),return 电影。
const retrieveList = async () => {
const results = await API.getMovies();
return results.map((item) => getMovieItem(item.id));
}
const getMovieItem = async (movieId) => {
// see if is already in database
const movie = await prisma.movie.findUnique({
where: { tmdbId: movieId },
});
if (movie) {
return movie;
} else {
// create and return if doesn't exist
const details = await API.getDetails(movieId);
const genresData = details.genres.map((genre) => ({
create: {
name: genre.name,
},
where: {
name: genre.name
},
}));
return prisma.movie.create({
data: {
title: details.title,
description: details.overview,
genres: {
connectOrCreate: genresData,
},
},
select: {
tmdbId: true,
id: true,
title: true,
description: true,
//...
},
});
}
};
问题是似乎存在某种不一致,如果在某些运行中与外部 API 的连接足够慢,一切似乎都运行良好;但如果它足够快,它也会抛出错误:
Invalid `prisma.movie.create()` invocation:
Unique constraint failed on the fields: (`name`)"
可能是因为它正在尝试创建一个已经创建的 流派,而不是连接它。
Multiple connectOrCreate
queries that run as concurrent transactions can result in a race condition. ...
因此,如果您同时请求同一类型的 2 部电影,而该类型尚不存在,那么两个查询都会尝试创建该类型,只有第一个会成功。
文档建议捕获特定错误:
To work around this scenario, we recommend catching the unique violation exception (PrismaClientKnownRequestError
, error P2002
) and retrying failed queries.
也可以先创建所有流派,例如:
// Some abstract function which extracts all the genres
const genres = gatherGenres(results);
await prisma.genres.createMany({
data: genres,
// Do nothing on duplicates
skipDuplicates: true,
});
然后创建所有电影并将它们与流派联系起来,因为流派已经创建。
此外,小吹毛求疵,你的函数 retrieveList
是不可等待的,因为它 returns 一个 promises 数组,要真正等到所有 promises 完成你需要使用 Promise.all
在上面
const retrieveList = async () => {
const results = await API.getMovies();
return results.map((item) => getMovieItem(item.id));
}
// ...
// You can just await it
await retrieveList()
// Need to use Promise.all
await Promise.all(retrieveList())
我正在尝试创建一个模型,其中电影和流派这两种类型之间存在多对多关系。
我从外部数据源异步检索电影列表,create
每个电影都在我自己的数据库中(如果尚未创建),return 电影。
const retrieveList = async () => {
const results = await API.getMovies();
return results.map((item) => getMovieItem(item.id));
}
const getMovieItem = async (movieId) => {
// see if is already in database
const movie = await prisma.movie.findUnique({
where: { tmdbId: movieId },
});
if (movie) {
return movie;
} else {
// create and return if doesn't exist
const details = await API.getDetails(movieId);
const genresData = details.genres.map((genre) => ({
create: {
name: genre.name,
},
where: {
name: genre.name
},
}));
return prisma.movie.create({
data: {
title: details.title,
description: details.overview,
genres: {
connectOrCreate: genresData,
},
},
select: {
tmdbId: true,
id: true,
title: true,
description: true,
//...
},
});
}
};
问题是似乎存在某种不一致,如果在某些运行中与外部 API 的连接足够慢,一切似乎都运行良好;但如果它足够快,它也会抛出错误:
Invalid `prisma.movie.create()` invocation:
Unique constraint failed on the fields: (`name`)"
可能是因为它正在尝试创建一个已经创建的 流派,而不是连接它。
Multiple
connectOrCreate
queries that run as concurrent transactions can result in a race condition. ...
因此,如果您同时请求同一类型的 2 部电影,而该类型尚不存在,那么两个查询都会尝试创建该类型,只有第一个会成功。
文档建议捕获特定错误:
To work around this scenario, we recommend catching the unique violation exception (
PrismaClientKnownRequestError
, errorP2002
) and retrying failed queries.
也可以先创建所有流派,例如:
// Some abstract function which extracts all the genres
const genres = gatherGenres(results);
await prisma.genres.createMany({
data: genres,
// Do nothing on duplicates
skipDuplicates: true,
});
然后创建所有电影并将它们与流派联系起来,因为流派已经创建。
此外,小吹毛求疵,你的函数 retrieveList
是不可等待的,因为它 returns 一个 promises 数组,要真正等到所有 promises 完成你需要使用 Promise.all
在上面
const retrieveList = async () => {
const results = await API.getMovies();
return results.map((item) => getMovieItem(item.id));
}
// ...
// You can just await it
await retrieveList()
// Need to use Promise.all
await Promise.all(retrieveList())