eggjs里中使用sequelize事务
按照官方文档使用如下,文档地址 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方法的场景,事务很难平滑传递。
我的处理方式如下:
- 封装 baseservice.js 对所有实体操作包装一层, 所有实体操作判断当前请求是否存在事务。
- 拓展context,如果开启事务,将事务存储在
context
,context
是请求级别的对象。 - 编写中间件,接管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访问,当然也可以手动获取事务,自行绑定事务执行数据操作。
请求完毕后,如果存在事务,自动提交或者回滚即可。