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}) } );