[Node.js] SequelizeでMySQLを利用する - その3「トランザクション編」

Node.jsの代表的なO/RMであるSequelizeの第三弾。

第1回ではインストールから基本的な利用方法、第2回ではSELECT文の使い方を取り上げました。今回はINSERTやUPDATEなど更新系の処理に欠かせない「トランザクション」です。

インストール

事前準備や基本的な利用方法については第1回を参照ください。 blog.katsubemakito.net

準備

今回はMakerモデルを作成しMySQLへ反映しておきます。SQL実行時にかんたんにエラーを発生させるためnameにunique制約を付けました。

const Maker = sequelize.define('Maker', {
  name:      { type: DataTypes.STRING(128), unique: true },
  CountryId: { type: DataTypes.STRING(2) }
},{timestamps: false})
await Maker.sync();

2種類のトランザクション

Sequelizeには次の2種類のトランザクションが用意されています。どちらが良いという話でも無いと思いますのでお好みでどうぞ。

UnManaged Transactions
手動モード。自分でコミットやロールバックを書く必要があります。
Managed Transactions
自動モード。正常に処理が終了すると自動的にコミットされ、エラーが発生すると自動的にロールバックします。

UnManaged Transactions

まずは手動モードから。こちらが他のフレームワークなどでも見かけるので個人的には馴染み深いですね。

// トランザクションを作成
const t = await sequelize.transaction();

try {
  // 同じデータを2回挿入
  // nameにはunique制約があるので2回目でエラーになる
  await Maker.create({name: 'Honda', CountryId: 'JP'}, {transaction: t})
  await Maker.create({name: 'Honda', CountryId: 'JP'}, {transaction: t})

  await t.commit()    // コミット
}
catch(error) {
  await t.rollback()  // ロールバック 
}

// MySQLから切断
await sequelize.close()

Managed Transactions

こちらが自動モード。どこにもcommitやrollbackの文字が出てきませんが裏側ではしっかり行われています。

try {
  await sequelize.transaction(async (t) => {
    // 同じデータを2回挿入
    // nameにはunique制約があるので2回目でエラーになる
    await Maker.create({name: 'Honda', CountryId: 'JP'}, {transaction: t})
    await Maker.create({name: 'Honda', CountryId: 'JP'}, {transaction: t})
  })
}
catch (error) {
  console.error(error)
}

トランザクション分離レベル

MySQL(InnoDB)のデフォルト値は「REPEATABLE READ」ですが、SQL実行時にSequelizeでも変更することが可能です。次の定数が用意されていますので、これをインスタンスを生成時か実行時のどちらかでセットします。

const { Transaction } = require('sequelize');

Transaction.ISOLATION_LEVELS.READ_UNCOMMITTED
Transaction.ISOLATION_LEVELS.READ_COMMITTED
Transaction.ISOLATION_LEVELS.REPEATABLE_READ
Transaction.ISOLATION_LEVELS.SERIALIZABLE

インスタンス生成時に指定

すべてのトランザクションで変更したい場合はこちらを利用します。まぁ全部変えるならDBの設定変えた方が早い気もしますが何らかの事情で出来ない場合にどうぞ。

const { Sequelize, Transaction } = require('sequelize')

const sequelize = new Sequelize('database_development', 'root', null, {
  host: 'localhost',
  dialect: 'mysql',
  isolationLevel: Transaction.ISOLATION_LEVELS.SERIALIZABLE
})

実行時に指定

手動モードも自動モードもどちらもsequelize.transaction()の引数として渡してやります。

const { Transaction } = require('sequelize')

// 手動モード
const t = await sequelize.transaction({
  isolationLevel: Transaction.ISOLATION_LEVELS.SERIALIZABLE
})
const { Transaction } = require('sequelize')

// 自動モード
await sequelize.transaction({
    isolationLevel: Transaction.ISOLATION_LEVELS.SERIALIZABLE
  },
  async (t) => {
    // 実際の処理
    await Maker.create({name: 'Honda', CountryId: 'JP'}, {transaction: t})
  }
);

続き

blog.katsubemakito.net

参考ページ