简体   繁体   中英

Transaction for prepared statements using Node mssql

I want to create an Express REST API and will use MSSQL for the database part. I use the mssql module and use the PreparedStatement class.

Sometimes my queries have to be transactional, so I have to work with the Transaction class too.

I found some information about prepared statements here and about transactions here but don't know how to execute transactional queries using prepared statements.

Would someone mind explaining how to use the mssql module with prepared statements within transactions? My query function should provide a functionality giving access to write something like this in my query file

  • begin transaction
  • run prepared statement one
  • run prepared statement two
  • end transaction

So to reproduce the problem: First I created a database with a table called "integer" and a numeric column "value".

Then I created an empty Node/Typescript project with this code:

import sql, { ConnectionPool, PreparedStatement } from 'mssql';

class App {
    private connectionPool: ConnectionPool;

    constructor() {
        this.connectionPool = new sql.ConnectionPool(databaseConfig);
    }

    public run = async (): Promise<void> => {
        try {
            await this.connectionPool.connect();

            const preparedStatement: PreparedStatement = new sql.PreparedStatement(this.connectionPool);

            preparedStatement.input('number', sql.Numeric(18, 0));

            await preparedStatement.prepare('INSERT INTO integer (value) VALUES (@number)');
            await preparedStatement.execute({ number: 1 });
            await preparedStatement.unprepare();

            console.log('done...');
        } catch (error) {
            throw error;
        }
    }
}

(async () => {
    const app: App = new App();
    await app.run();
})().catch(error => {
    throw error;
});

So far so good. Now I want to run two INSERTs within a transaction but the second one passes in a string so I would expect an error and the first one gets rolled back. My updated run function:

public run = async (): Promise<void> => {
    try {
        await this.connectionPool.connect();

        const transaction: Transaction = new sql.Transaction(this.connectionPool);

        const workingStatement: PreparedStatement = new sql.PreparedStatement(transaction);
        workingStatement.input('number', sql.Numeric(18, 0));

        const invalidStatement: PreparedStatement = new sql.PreparedStatement(transaction);
        invalidStatement.input('number', sql.Numeric(18, 0));

        try {
            await transaction.begin();

            await workingStatement.prepare('INSERT INTO integer (value) VALUES (@number)');
            await workingStatement.execute({ number: 1 });
            await workingStatement.unprepare();

            await invalidStatement.prepare('INSERT INTO integer (value) VALUES (@number)');
            await invalidStatement.execute({ number: false });
            await invalidStatement.unprepare();

            await transaction.commit();
        } catch (error) {
            await transaction.rollback();
            console.log('execution failed...');
        }

        console.log('done...');
    } catch (error) {
        throw error;
    }
}

Now I get this error

(node:18416) UnhandledPromiseRejectionWarning: TransactionError: Can't rollback transaction. There is a request in progress.

And I'm not sure if my syntax is wrong.

I believe the error you're hitting happens because you didn't call unprepare as suggested in the docs:

keep in mind you can't execute other requests in the transaction until you call unprepare

Calling unprepare on the statement before rolling back the transaction works for me:

try {
  // ...
  await invalidStatement.prepare('INSERT INTO integer (value) VALUES (@number)');
  try {
    await invalidStatement.execute({ number: false });
  } finally {
    await invalidStatement.unprepare();
  }
} catch (error) {
  // rollback here...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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