Sequelize 中事务的使用-启动、提交、回滚

 2016年08月09日    366     声明


数据库中的事务是指单个逻辑所包含的一系列数据操作,要么全部执行,要么全部不执行。在一个事务中,可能会包含开始(START)、提交(COMMIT)、回滚(ROLLBACK)等操作,Sequelize 通过Transaction类来实现事务相关功能。Sequelize 中的事务有两种使用:可以基于Promise结果链进行自动提交或回滚,也可以由用户控制提交或回滚。

  1. Transaction类的构造函数
  2. 实例化
  3. 事务控制

1. Transaction类的构造函数

Transaction类的构造函数结构如下:

new Transaction(sequelize, [options])

构造函数参数:

  • sequelize - 一个已配置的 Sequlize 实例
  • options是一个包含以下可选值的对象:
    • autocommit - Boolean,是否自动提交
    • type - String,事务类型
    • isolationLevel - String,事务的隔离级别
    • autocommit - String,立即或延迟检查约束


2. 实例化

虽然Transaction类可以直接访问(通过require('sequelize').Transactionsequelize.Transaction),但一般不需要直接实例化。

其创建对象(实例化)可以使用Sequlize实例的sequelize.transaction()方法,创建Transaction对象后,被创建的对象回调到then回调链中。

如,创建一个事务并在其中执行一些操作:

var Sequelize = require('sequelize');

var sequelize = new Sequelize('modelTest', 'root', '111111', {host: 'localhost', port:3306, logging:console.log});
var User = sequelize.import('./user.js');
var UserCheckin = sequelize.import('./userCheckin.js');

// 创建事务
sequelize.transaction(function (t) {
  // 在事务中执行操作
  return User.create({username: 'itbilu.com', password: 'pwd', active: true}, {transaction:t})
  .then(function(user){
  return UserCheckin.create({userId: user.id, loginIp:'localhost'}, {transaction:t})
  });
}).then(function (results){
  /* 操作成功,事务会自动提交 */
}).catch(function(err){
  /* 操作失败,事件会自动回滚 */
});

以上示例执行结果类似如下:

Executing (3e756dc6-4f73-40f6-aa1c-d21ae832a302): START TRANSACTION;
Executing (3e756dc6-4f73-40f6-aa1c-d21ae832a302): SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
Executing (3e756dc6-4f73-40f6-aa1c-d21ae832a302): SET autocommit = 1;
Executing (3e756dc6-4f73-40f6-aa1c-d21ae832a302): INSERT INTO `user` (`id`,`username`,`password`,`active`,`created_at`,`updated_at`) VALUES (DEFAULT,'itbilu.com','pwd',true,'2016-08-09 09:34:46','2016-08-09 09:34:46');
Executing (3e756dc6-4f73-40f6-aa1c-d21ae832a302): INSERT INTO `userCheckin` (`id`,`user_id`,`login_ip`,`created_at`,`updated_at`) VALUES (DEFAULT,2,'127.0.0.1','2016-08-09 09:34:46','2016-08-09 09:34:46');
Executing (3e756dc6-4f73-40f6-aa1c-d21ae832a302): COMMIT;


3. 事务控制

一个事务实例(创建事务对象)就表示一个正在执行事务,即:启动一个事务。Sequelize 中的事务可以基于Promise结果链进行自动提交或回滚。也可以由用户控制,通过调用事务实例的commit()rollback()来手工提交或回滚。

在一个关系型数据库的事务中,可能会包含开始(START)、提交(COMMIT)、回滚(ROLLBACK)等操作三个操作。Sequelize中同样会有这三种操作,创建一个Transaction类实例就是“开始”一个事务。事务启动后,对于受管理的事务(auto-callback)可以基于Promise回调链由Sequelize自动“提交”或“回滚”事务;而对于不受管理的事务(then-callback),我们可以手工调用commit()方法来“提交”事务,或调用rollback()来“回滚”事务。

3.1 受管理事务的提交与回滚

在使用sequelize.transaction()创建类实列时(启动事务),可以向其传递一个回调函数。通过这种方式创建的事务,是受管理的事务:

// 启动一个受管理的事务
return sequelize.transaction(function (t) {
	// 一些在事务中进行的操作

}).then(function (result) {
  // Transaction 会自动提交
  // result 是事务回调中使用promise链中执行结果
}).catch(function (err) {
  // Transaction 会自动回滚
  // err 是事务回调中使用promise链中的异常结果
});

创建受管理事务后,传递给回调函数的transaction会返回一个promise链,在promise返回的thencatch方法中,并不能调用t.commit()t.rollback()来控制事务。在这种方式下,如果使用事务的所有promise链都执行成功,则自动提交;如果其中之一执行失败(即:catch()方法被调用),则自动回滚。

注意:也不建议回调函数内部调t.commit()t.rollback()提交或回滚事务,这样会造成阻塞。

强制回调

虽然不能在promise返回的thencatch方法中手工提交或回滚方法,但可以基于promise通过抛出异常来强制回滚事务:

return sequelize.transaction(function (t) {
  return User.create({
    firstName: 'Abraham',
    lastName: 'Lincoln'
  }, {transaction: t}).then(function (user) {
    // 注意,虽然所有操作成功但仍会回滚
    throw new Error();
  });
});


3.2 不受管理事务的提交与回滚

如果调用sequelize.transaction()创建事务实例时不传入回调函数,会启动一个不受管理事务。不受管理的事务需要你强制(手工)提交或回滚,如果不进行这些操作,事务会一直保持挂起状态直到超时。

不受管理的事务对象会被传递至then方法的回调函数中,在这里我们可以通过调用t.commit()t.rollback()提交或回滚事务:

// 启动一个不受管理的事务
return sequelize.transaction().then(function (t) {
	// 一些在事务中进行的操作
  return User.create({
    firstName: 'Homer',
    lastName: 'Simpson'
  }, {transaction: t}).then(function (user) {
    return user.addSibling({
      firstName: 'Lisa',
      lastName: 'Simpson'
    }, {transaction: t});
  }).then(function () {
  	// 手工提交事务
    return t.commit();
  }).catch(function (err) {
  	// 手工回滚事务
    return t.rollback();
  });
});


关于事务的详细介绍:事务的使用与Transaction类