处理嵌套事务的简单方法

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)

Jan*_*ier 7

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)


Tim*_*ong 5

如果您使用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)


Ron*_*Ron 1

我解决了问题,感觉很棒。

我使用了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)