简体   繁体   English

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

[英]How to use database transactions in JavaScript with async functions?

Consider the code below that gets some data from an external service and inserts it into some table in a database.考虑下面的代码,它从外部服务获取一些数据并将其插入到数据库中的某个表中。 It starts the transactions and commits it when all the data is inserted or rolls it back if an error occurres:它启动事务并在插入所有数据时提交它,如果发生错误则将其回滚:

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;');
        }
    }
}

but what if there is another async method updateSomething that updates a row in some table with a single update query and the client calls it while insertSomeRowsIntoSomeTable method is still running?但是,如果有另一个异步方法updateSomething使用单个更新查询更新某个表中的一行,并且客户端在insertSomeRowsIntoSomeTable方法仍在运行时调用它,该怎么办? Obviously, the updated data will be lost if the an error occurres in insertSomeRowsIntoSomeTable .显然,如果insertSomeRowsIntoSomeTable发生错误,更新的数据将会丢失。

If there is another method that initiates its own transaction, for example, we unexpectedly get the nested transaction.如果有另一种方法发起自己的事务,例如,我们意外地得到了嵌套事务。

The only idea I have at the moment is to make all the operations with the database atomic by moving all the transaction logic to the stored procedures, for example, but it look like a workaround but not a solution.例如,我目前唯一的想法是通过将所有事务逻辑移动到存储过程来使数据库的所有操作成为原子操作,但这看起来像是一种解决方法,但不是解决方案。

Having a separate database connection in each async method is not an option, for the obvious reasons.出于显而易见的原因,在每个异步方法中都有一个单独的数据库连接不是一种选择。

Does anyone have a better idea?有没有人有更好的主意?

Probably the easiest solution is to use pool:可能最简单的解决方案是使用池:

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();
        }
    }
}

Here you borrow connection from the pool in your insertSomeRowsIntoSomeTable() and if other async functions follow the same pattern this connection will be exclusively used by one transaction code path only until released在这里,您从insertSomeRowsIntoSomeTable()中的池中借用连接,如果其他异步函数遵循相同的模式,则此连接将仅由一个事务代码路径独占使用,直到释放

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM