Ron*_*Ron 7 node.js sequelize.js
假设有一个“addUser”函数,里面我们需要向“Account”表和“User”表插入一条记录,所以这两个步骤也必须在一个事务中,所以我们将编写如下代码:
function addUser (userName, password) {
sequelize.transaction(function () {
return AccountModel.create(...)
.then(UserModel.create(...))
})
}
Run Code Online (Sandbox Code Playgroud)
但是,在另一个“addTeam”函数中,我们需要在“Team”表中插入一条记录并使用上述函数创建一个管理员用户。该函数还需要包含在事务中。
那么问题来了,“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));
});
}
Run Code Online (Sandbox Code Playgroud)
显然,这很可怕。如何轻松处理它,让交易对调用者完全透明,如下所示:
@Transaction
async function addUser(userName, password) {
await AccountModel.create(...);
await UserModel.create(...);
}
@Transaction
async function addTeam(...) {
await TeamModel.create(...);
await addUser(...);
}
Run Code Online (Sandbox Code Playgroud)
sequelize.transaction接受选项对象 - 如果options.transaction设置,这将在事务中创建一个保存点(前提是 SQL 方言支持它),否则它将创建一个新事务
http://docs.sequelizejs.com/en/latest/api/sequelize/#transactionoptions-promise
所以你应该能够简单地做
sequelize.transaction({ transaction }, x=>func(t));
Run Code Online (Sandbox Code Playgroud)
如果您使用CLS,一个非常简单的辅助函数就可以完成这项工作。
const cls = require('continuation-local-storage');
const Sequelize = require('sequelize');
const NAMESPACE = 'your-namespace';
// Use CLS for Sequelize
Sequelize.cls = cls.createNamespace(NAMESPACE);
const sequelize = new Sequelize(...);
/* * * * * * * * * * * * * * * * * * * * *
* THE MAGIC: Create a transaction wrapper
* * * * * * * * * * * * * * * * * * * * */
function transaction(task) {
return cls.getNamespace(NAMESPACE).get('transaction') ? task() : sequelize.transaction(task);
};
/* * * * * * * * * * * * * * * * * * * * *
* Your code below
* * * * * * * * * * * * * * * * * * * * */
function addUser(userName, password) {
return transaction(function() {
return AccountModel
.create(...)
.then(() => UserModel.create(...));
});
}
function addTeam() {
return transaction(function() {
return TeamModel
.create(...)
.then(() => addUser(...));
});
}Run Code Online (Sandbox Code Playgroud)
我解决了问题,感觉很棒。
我使用了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;
}
};
Run Code Online (Sandbox Code Playgroud)
上面的代码只是创建一个事务,如果本地上下文中没有事务则提交它,否则就离开它。
每个业务功能想要一个交易只需要使用上面的高阶函数来包装如下:
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};
});
Run Code Online (Sandbox Code Playgroud)