【React + TypeScript】DB登録時にトランザクションを実装してみる

## はじめに
前回React + TypeScriptでのデータベースへの登録について以下の記事を書きました。

https://qiita.com/kuri_kuri/items/368cbbe7be0d4d15bf65

今回はデータベースに登録する際にトランザクションを実装したので記事にしたいと思います。

## 実装内容
TODOリストで予定リストから完了リストにタスクを移す際にデータベースの処理では以下の２つの処理が必要になります。

1.予定リストのテーブルから予定タスクを削除
（本来データは残した状態で削除フラグを変更する処理とすべきですが今回はテーブルから削除する）

2.完了リストテーブルにタスクを登録

```react:

//トランザクションを開始
  db.beginTransaction((transactionErr) => {
    if (transactionErr) {
      console.error("トランザクション開始エラー:", transactionErr);
      return res.status(500).json({ error: "トランザクションに開始に失敗しました" });
    }
    // 完了タスクをcompleted_tasksに追加
    const query = `INSERT INTO completed_tasks (task_content,category_id,actual_minutes) VALUES (?, ?, ?)`;

    db.query(query, [task_content, category_id, actual_minutes], (err, results) => {
      if (err) {
        console.error("タスク取得エラー", err);
        return res.status(500).json({ error: "完了タスクの登録に失敗しましたた" });
      }
      res.status(200).json(results);

      //完了したタスクをplanned_tasksテーブルから削除
      const deleteQuery = `
      DELETE FROM planned_tasks WHERE task_content = ? AND category_id = ?`;

      db.query(deleteQuery, [task_content, category_id], (deleteErr, deleteresults) => {
        if (deleteErr) {
          console.error("予定タスク削除エラー:", deleteErr);
          return res.status(500).json({ error: "完了タスクの登録に失敗しました" });
        }

        console.log("予定タスクが削除されました:", deleteresults);

        db.commit((commitErr) => {
          if (commitErr) {
            console.error("トランザクションコミットエラー:", commitErr);
            return db.rollback(() => {
              res.status(500).json({ error: "トランザクションのコミットに失敗しました" });
            });
          }
          res.status(200).json({ message: "完了タスクが登録され、予定タスクが削除されました" });
        })
      })
    })
  })

```

トランザクションの実装部分をわかりやすくした処理は次を参照してください。

```react:

// トランザクションを開始
  db.beginTransaction((transactionErr) => {
    if (transactionErr) {
      console.error("トランザクション開始エラー:", transactionErr);
      return res.status(500).json({ error: "トランザクションの開始に失敗しました" });
    }

    // ここで必要なDB操作を行います。
    // 例: タスクの登録、関連データの更新など

    db.commit((commitErr) => {
      if (commitErr) {
        console.error("トランザクションコミットエラー:", commitErr);
        return db.rollback(() => {
          res.status(500).json({ error: "トランザクションのコミットに失敗しました" });
        });
      }
      res.status(200).json({ message: "完了タスクが登録され、予定タスクが削除されました" });
    });
  });

```

## トランザクション処理について

### トランザクションを開始

```react:
// トランザクションを開始
    db.beginTransaction((transactionErr) => {
    if (transactionErr) {
      console.error("トランザクション開始エラー:", transactionErr);
      return res.status(500).json({ error: "トランザクションの開始に失敗しました" });
    }
```

- db.beginTransactionを使ってトランザクションを開始します。ここでエラーが発生した場合には、データベース操作を行う前に処理を中止し、エラーメッセージを返します

### コミットとロールバック

```react:
    //コミットとロールバック
    db.commit((commitErr) => {
      if (commitErr) {
        console.error("トランザクションコミットエラー:", commitErr);
        return db.rollback(() => {
          res.status(500).json({ error: "トランザクションのコミットに失敗しました" });
        });
      }
      res.status(200).json({ message: "完了タスクが登録され、予定タスクが削除されました" });
    });
```
- db.commitで全てのDB操作が正常に完了したことを確認し、データベースに反映します

- コミット時にエラーが発生した場合には、db.rollbackを使って全ての操作を取り消します。これにより、データの整合性を確保します

## トランザクションの必要性

ロールバックを実装することで、部分的なデータの登録や変更が原因でデータベースの状態が不整合になることを防ぎます。たとえば、複数のテーブルに対する操作がある場合、最初の操作は成功したが、次の操作でエラーが発生したとすると、トランザクションを使用しない場合、データが中途半端に保存されたままとなります。

トランザクションとロールバックを使うことで、こうした問題を避け、一貫性のあるデータを保つことができます。