🙆

【Prisma】【Typescript】外部キー制約のある関連テーブルの複数クエリのトランザクション

2023/01/17に公開

Prismaとは

セットアップも含めこちらの記事がとても参考になります。
https://reffect.co.jp/node-js/prisma-basic

実現したいこと

  • トランザクション形式でAテーブルとBテーブルにそれぞれレコードを追加したい
  • Bテーブルには、Aテーブルの外部キー制約がついている

よくあるケースですが、これが、prismaでは、現在(2023/01/17)未対応のようです。
https://www.prisma.io/docs/guides/performance-and-optimization/prisma-client-transactions-guide#nested-writes-faqs

私の環境でも同様のケースが発生したので、その際の解決法を記載します。

全容

※私の環境ではPostgresqlを使用しているので以下の素のSQLに関してはPostgresqlの仕様に準じます。

service.ts
async _create(): Promise<void> {
    try {
      const id = await prisma.$queryRaw<
        [
          {
            nextval: number
          },
        ]
      >`SELECT nextval('a_id_seq')`.then((result) => result[0].nextval)

      const aQuery = prisma.a.create({
        data: {
          a_id: id,
          a_name: 'hoge',
        },
      })

      const bQuery = prisma.b.create({
        data: {
          a_id: id,
          b_name: 'huga',
        },
      })

      const res = await prisma.$transaction([aQuery, bQuery])

    } catch (e) {
	~~省略~~
    }
  }

解説

const id = await prisma.$queryRaw<
        [
          {
            nextval: number
          },
        ]
      >`SELECT nextval('a_id_seq')`.then((result) => result[0].nextval)

連続したシーケンス値を取得するメソッドが、prismaでは現時点で無いため生のSQLを書ける「$queryRaw」関数を使用して、Postgressqlの関数を利用しシーケンス値(ID)を取得します。

const aQuery = prisma.a.create({
	data: {
	  a_id: id,
	  a_name: 'hoge',
	},
})

const bQuery = prisma.b.create({
	data: {
	  a_id: id,
	  b_name: 'huga',
	},
})

const res = await prisma.$transaction([aQuery, bQuery])

取得したIDを指定し、トランザクションを実行します。
これでDBのデータの整合性を担保したトランザクション関数が作成できます。

※ロールバックした場合、シーケンス値は連続性が担保されません。

最後に

比較的新しいプロジェクトですし、バージョンアップのサイクルも早いのでそのうち上記の機能も組み込まれると思います。(公式ドキュメントにも「まだ、対応していない」と書かれている)

Discussion