假设有一个“ addUser”函数,我们需要在其中向“ Account”表和“ User”表插入一条记录,因此这两个步骤也必须在事务内,因此我们将编写以下代码:
function addUser (userName, password) {
sequelize.transaction(function () {
return AccountModel.create(...)
.then(UserModel.create(...))
})
}
但是,在另一个“ addTeam”函数中,我们需要在内部向“ Team”表中插入一条记录,并使用上述函数创建一个admin用户。该功能还需要包装在事务中。
因此问题来了,“ addUser”功能有时需要开始一个新的事务,有时需要使用传入的事务。最明显的方法如下:
function addUser (userName, password, transaction) {
let func = function (t) {
return AccountModel.create(..., t)
.then(()=>UserModel.create(..., t)));
if (transaction) func(t);
else sequelize.transaction(x=>func(t));
}
function addTeam() {
sequelize.transaction(x=> {
TeamModel.create(..., x)
.then(y=>addUser(x));
});
}
显然,这很糟糕。如何轻松处理它,这使事务对调用方完全透明,如下所示:
@Transaction
async function addUser(userName, password) {
await AccountModel.create(...);
await UserModel.create(...);
}
@Transaction
async function addTeam(...) {
await TeamModel.create(...);
await addUser(...);
}
我解决了问题,感觉很好。
我使用了sequelize提供的CLS功能,例如下面的代码:
let namespace = Sequelize.cls = cls.createNamespace('myschool');
export const db = new Sequelize(config.db.url);
export const trans = option => operation => async function () {
let t = namespace.get('transaction');
let hasTrans = !!t;
t = t || await db.transaction();
try {
let result = await operation.apply(null, arguments);
if (!hasTrans) await t.commit();
return result;
}
catch (e) {
if (!hasTrans) await t.rollback();
throw e;
}
};
上面的代码只是创建一个事务,如果本地上下文中没有事务,则提交该事务,否则将其保留。
而每个要交易的业务功能只需使用上面的高阶功能来包装,如下所示:
export const createSchool = trans()( async (name, accountProps) => {
let school = await SchoolModel.create({name});
let teacher = await createTeacher({...accountProps, schoolId: school.get('id')});
return {school, teacher};
});
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句