如何在JavaScript中使用具有异步函数的数据库事务?

如何在JavaScript中使用具有异步函数的数据库事务?,javascript,node.js,Javascript,Node.js,考虑下面的代码,它从外部服务获取一些数据并将其插入数据库中的某个表中。它启动事务并在插入所有数据时提交,或者在发生错误时回滚事务: const mysql = require('mysql2/promise'); class SomeClass { async connect(params) { this.db = await mysql.createConnection(params); } async insertSomeRowsIntoS

考虑下面的代码,它从外部服务获取一些数据并将其插入数据库中的某个表中。它启动事务并在插入所有数据时提交,或者在发生错误时回滚事务:

const mysql = require('mysql2/promise');

class SomeClass
{
    async connect(params)
    {
        this.db = await mysql.createConnection(params);
    }

    async insertSomeRowsIntoSomeTable()
    {
        await this.db.execute('START TRANSACTION;');

        try {
            for (let i = 0; i < 100; i++) {
                //get the values to insert from an external service, for example.
                await const values = service.getValues();
                await this.db.execute('INSERT SomeTable .... <the values>...');
            }
            await this.db.execute('COMMIT;');
        } 
        catch (e) {
            await this.db.execute('ROLLBACK;');
        }
    }
}
constmysql=require('mysql2/promise');
上课
{
异步连接(参数)
{
this.db=wait mysql.createConnection(params);
}
异步insertSomeRowsIntoSomeTable()
{
等待这个.db.execute('START TRANSACTION;');
试一试{
for(设i=0;i<100;i++){
//例如,从外部服务获取要插入的值。
wait const values=service.getValues();
等待这个.db.execute('insertsometable……');
}
等待这个.db.execute('COMMIT;');
} 
捕获(e){
等待这个.db.execute('ROLLBACK;');
}
}
}
但是,如果有另一个异步方法
updatemething
使用单个更新查询更新某个表中的行,并且客户机在
insertsomerowsintosotebase
方法仍在运行时调用该方法,该怎么办?显然,如果在
insertSomeRowsIntoSomeTable
中发生错误,更新的数据将丢失

例如,如果有另一个方法启动它自己的事务,我们会意外地得到嵌套事务

目前我唯一的想法是,通过将所有事务逻辑移动到存储过程,使数据库的所有操作成为原子操作,但这看起来像是一种变通方法,而不是解决方案

出于显而易见的原因,在每个异步方法中使用单独的数据库连接不是一个选项


有人有更好的主意吗?

最简单的解决方案可能是使用池:

const mysql = require('mysql2/promise');

class SomeClass
{
    async connect(params)
    {
        this.db = mysql.createPool(params);
    }

    async insertSomeRowsIntoSomeTable()
    {
        const connection = await this.db.getConnection();
        await connection.execute('START TRANSACTION;');

        try {
            for (let i = 0; i < 100; i++) {
                //get the values to insert from an external service, for example.
                const values = await service.getValues();
                await conn.execute('INSERT SomeTable .... <the values>...');
            }
            await connection.execute('COMMIT');
            await connection.release();
        } 
        catch (e) {
            await connection.execute('ROLLBACK');
            await connection.release();
        }
    }
}
constmysql=require('mysql2/promise');
上课
{
异步连接(参数)
{
this.db=mysql.createPool(params);
}
异步insertSomeRowsIntoSomeTable()
{
const connection=wait this.db.getConnection();
等待连接。执行('START TRANSACTION;');
试一试{
for(设i=0;i<100;i++){
//例如,从外部服务获取要插入的值。
const values=await service.getValues();
等待conn.execute('INSERT SomeTable……');
}
等待连接。执行('COMMIT');
等待连接。释放();
} 
捕获(e){
等待连接。执行('ROLLBACK');
等待连接。释放();
}
}
}

在这里,您从
insertSomeRowsIntoSomeTable()中的池借用连接,如果其他异步函数遵循相同的模式,则此连接将仅由一个事务代码路径独占使用,直到发布为止

可能最简单的解决方案是使用池:

const mysql = require('mysql2/promise');

class SomeClass
{
    async connect(params)
    {
        this.db = mysql.createPool(params);
    }

    async insertSomeRowsIntoSomeTable()
    {
        const connection = await this.db.getConnection();
        await connection.execute('START TRANSACTION;');

        try {
            for (let i = 0; i < 100; i++) {
                //get the values to insert from an external service, for example.
                const values = await service.getValues();
                await conn.execute('INSERT SomeTable .... <the values>...');
            }
            await connection.execute('COMMIT');
            await connection.release();
        } 
        catch (e) {
            await connection.execute('ROLLBACK');
            await connection.release();
        }
    }
}
constmysql=require('mysql2/promise');
上课
{
异步连接(参数)
{
this.db=mysql.createPool(params);
}
异步insertSomeRowsIntoSomeTable()
{
const connection=wait this.db.getConnection();
等待连接。执行('START TRANSACTION;');
试一试{
for(设i=0;i<100;i++){
//例如,从外部服务获取要插入的值。
const values=await service.getValues();
等待conn.execute('INSERT SomeTable……');
}
等待连接。执行('COMMIT');
等待连接。释放();
} 
捕获(e){
等待连接。执行('ROLLBACK');
等待连接。释放();
}
}
}

在这里,您可以从
insertSomeRowsIntoSomeTable()中的池借用连接,如果其他异步函数遵循相同的模式,则此连接将仅由一个事务代码路径独占使用,直到释放为止

请查看不要使用单个共享连接(您称之为
db
)对于实例的所有方法。在每个事务/方法的连接上使用,并将它们池化。看一看不要对实例的所有方法使用单个共享连接(您称之为
db
)。在每个事务/方法的连接上使用,并将其池化。