3

为了阐明我让 nodejs/mssql 应用程序正常工作的问题,我尝试编写一个包含在事务中的简单(准备好的)INSERT 语句的两个功能等效版本。

回调版本有效 - 在我的 Sql Server 数据库中插入一行。

async / await 版本抛出错误 -

TransactionError: Can't commit transaction. There is a request in progress.

我尝试了失败版本的许多变体(在合理的情况下重新排序语句),但下面包含的版本是最接近模仿工作回调版本逻辑的版本。

谢谢!

var sql = require('mssql');  // mssql: 4.1.0; tedious: 2.2.4; node: v8.4.0

var cfg = {
    "db": "sqlserver",
    "domain": "XXXXXX",
    "user": "cseelig",
    "password": "xxxxxx",
    "server": "xxxxxx.xxxxxx.xxxxxx.xxxxxx",
    "port": 1433,
    "stream": false,
    "options": { 
        "trustedConnection": true
    },
    "requestTimeout": 900000,
    "connectionTimeout": 30000,
    "pool": {
        "max": 3,
        "min": 0,
        "idleTimeoutMillis": 30000
    }
};

var statement = "insert into wng_dw.dbo.D_LIB_Google_Search_Query (query, LastUpdateDate) values (@query, GetDate())";

// I only run one or the other -

main1("12347");   // fails
main2("98765:);   // works

async function main1(val) {

    try {
        const conn = await new sql.connect(cfg);
        const transaction = new sql.Transaction();
        await transaction.begin();
        const ps = new sql.PreparedStatement(transaction);
        ps.input('query', sql.VarChar(200));
        await ps.prepare(statement);
        await ps.execute( {"query": val} );
        await ps.unprepare();
        await transaction.commit();
        sql.close;
    } catch(err){
        console.log("Error: " + err);
    };

    process.exit(0);

}


async function main2(val) {

    sql.connect(cfg, err => {
        const transaction = new sql.Transaction();
        transaction.begin(err => {
            const ps = new sql.PreparedStatement(transaction);
            ps.input('query', sql.VarChar(200));
            ps.prepare(statement, err => {
                ps.execute( {"query": val}, (err, result) => {
                    ps.unprepare(err => { 
                        transaction.commit(err => {
                            sql.close();
                        });
                    });
                });
            });
        });
    });

}
4

2 回答 2

6

transaction.begin 不返回 Promise。你可以简单地答应它。类似于以下内容:

await new Promise(resolve => transaction.begin(resolve));
const request = new sql.Request(transaction);
//...
await transaction.commit();

在提交和回滚之后,“请求”对象就不能再使用了。否则它将显示有关交易未开始的错误....

希望这有帮助。

于 2018-03-15T02:31:26.943 回答
2

提交回滚事务之前,必须先准备好所有语句。

您也必须等待unprepare语句,否则请求仍在进行中并且执行承诺尚未解决。

使用一个小包装器让事情变得简单:

import * as dotenv from 'dotenv'
import mssql from 'mssql'

dotenv.config()

const sqlServerConfig = {
  server: process.env.SQL_SERVER,
  user: process.env.QS_USER,
  password: process.env.QS_PASS,
  options: { enableArithAbort: false },
}

let pool: mssql.ConnectionPool
let transaction: mssql.Transaction
const statements: mssql.PreparedStatement[] = []

export const connect = async (): Promise<void> => {
  pool = new mssql.ConnectionPool({ ...sqlServerConfig, database: process.env.DATABASE })
  await pool.connect()
}

export const disconnect = async (): Promise<void> => {
  if (typeof pool == 'undefined') return
  if (pool.connected) await pool.close()
}

export const begin = async (): Promise<void> => {
  transaction = new mssql.Transaction(pool)
  await transaction.begin()
}

export const unprepare = async (statement: mssql.PreparedStatement): Promise<void> => {
  if (typeof statement == 'undefined') return
  if (statement.prepared) await statement.unprepare()
}

export const commit = async (): Promise<void> => {
  await transaction.commit()
}

export const rollback = async (): Promise<void> => {
  for (const statement of statements) {
    await unprepare(statement)
  }
  if (typeof transaction == 'undefined') return
  await transaction.rollback()
}

export const createStatement = (): mssql.PreparedStatement => {
  const statement = new mssql.PreparedStatement(transaction)
  statements.push(statement)
  return statement
}

用法:

try {
  await connect()
  await begin()

  const myStatement = createStatement()

  ..... bind parameters
  ..... prepare statement

  for ( ..... ) {
    await myStatement.execute( ..... )
  }

  await unprepare(myStatement)

  await commit()
  await disconnect()
  exit(0)
}
catch(e) {
  log.error(e)
  await rollback()
  await disconnect()
  exit(1)
}

您使用createStatement()创建一个准备好的语句。createStatement 会跟踪语句,因此在您回滚时,当您调用rollback时,它们将不会为您做好准备。

于 2021-02-13T20:48:35.880 回答