eggjs里中使用sequelize事务
发布于 6 年前 作者 a304885433 7769 次浏览 来自 分享

按照官方文档使用如下,文档地址 http://sequelize.readthedocs.io/en/v3/docs/transactions/

return sequelize.transaction(function (t) {
  // chain all your queries here. make sure you return them.
  return User.create({
    firstName: 'Abraham',
    lastName: 'Lincoln'
  }, {transaction: t}).then(function (user) {
    return user.setShooter({
      firstName: 'John',
      lastName: 'Boothe'
    }, {transaction: t});
  });
}).then(function (result) {
  // Transaction has been committed
  // result is whatever the result of the promise chain returned to the transaction callback
}).catch(function (err) {
  // Transaction has been rolled back
  // err is whatever rejected the promise chain returned to the transaction callback
});

当如果一个事务,在多个方法中传递,会显得异常麻烦,尤其是如下场景

class AppService extends Service {
	async check ( opt ) {
		let count = this.app.model.table.count( { ...} ,  opt )
		if( count ) throw new Error('数据已存在')
	}
	
	async save ( ){
		this.app.model.transaction(function (tran) {
				 await this.check({  transaction: tran })
				 await this.app.model.table.save({...} , { transaction: tran})
		}
	}
	
	async remove(){
	    this.app.model.transaction(function (tran) {
				 await this.check({  transaction: tran })
				 await this.app.model.table.destroy({...} , { transaction: tran})
		}
	}
}

事务在传递的过程中显得很复杂,尤其是当方法横跨多个service,甚至是上述代码中 save方法有调用 remove方法的场景,事务很难平滑传递。

我的处理方式如下:

  1. 封装 baseservice.js 对所有实体操作包装一层, 所有实体操作判断当前请求是否存在事务。
  2. 拓展context,如果开启事务,将事务存储在contextcontext是请求级别的对象。
  3. 编写中间件,接管action执行,并负责提交或者回滚事务。 service/base_service.js
class AppService extends Service {
    /**
     * 查询单个实体
     * 
     * @param {any} id
     * @returns 
     * @memberof AppService
     */
    async findById(id) {
        let ret = await this.model.findById(id, { transaction: this.tran })

        return ret
    }
	get tran() {
        return this.ctx.getTran()
    }
}

extend/context.js

const tran = Symbol('Context#tran');
module.exports = {
  async tran() {
    if (!this[tran]) {
      this[tran] = await this.app.model.transaction()
    }
    return this[tran]
  },
  getTran() {
    return this[tran]
  }
};

middleware/request.js


'use strict';

module.exports = (options, app) => {
  return async function (ctx, next) {
    try {
      let ret = await next();
      //ctx.cookies.set('csrfToken', ctx.csrf);
      ctx.rotateCsrfSecret();

      if (!ctx.body) {
        ctx.body = {
          success: true,
          data: ret,
        }
      }

      if (ctx.getTran()) {
        ctx.getTran().commit()
      }

    } catch (e) {
      ctx.body = {
        success: false,
        stack: app.config.env == 'local' ? e.stack : undefined,
        message: e.message,
      }

      if (ctx.getTran()) {
        ctx.getTran().rollback()
      }
    }
  };
};

封装base_service, 实体操作不止是事务,而且在数据操作的时候,通过ctx.state自动获取创建人、修改人等等。 上述request.js,最初的目的是用来,在controller中的action,习惯写返回值 return,不喜欢写 this.ctx.body = 'xxxx',即便是封装 后调用 this.success,也不喜欢,因此通过中间件来完成。 应用程需要开启事务,只需要 通过 this.ctx.tran() 开启事务,所有的实体操作,通过base_service访问,当然也可以手动获取事务,自行绑定事务执行数据操作。 请求完毕后,如果存在事务,自动提交或者回滚即可。

3 回复

我发现nodejs的mysql库,最麻烦的地方就是对事务的处理,没有发现一个orm用起来方便。。。

@arden 使用事务层面,orm设计,它很难结合具体的应用场景给一个合适的方案,基本上都是自行封装。

回到顶部