【问题标题】:Why does ADO BeginTrans() do something different than "BEGIN TRANSACTION"?为什么 ADO BeginTrans() 做的事情与“BEGIN TRANSACTION”不同?
【发布时间】:2010-10-22 15:12:31
【问题描述】:

在将 ADO 与 C++ 和 Microsoft SQL Server 2008 (express) 一起使用时,我遇到了一些令人惊讶的行为。本质上,我有这样做的代码:

//pseudocode pseudocode pseudocode   
adoConnection->Execute("BEGIN TRANSACTION;");
Insert( adoRecordsetPtr );
SelectAll( adoRecordsetPtr  );
adoConnection->Execute("COMMIT TRANSACTION;");

但是当它试图执行 SelectAll 时,ADO 抛出了异常,并显示以下信息:

错误:ADO 错误 -2147217871:071A14D0
来源:Microsoft OLE DB Provider for SQL Server
说明:超时过期

经过一番调查,我发现如果我像正常人一样使用 ado_connection->BeginTrans(),一切都会按预期进行。虽然这篇文章主要是为了让其他可能遇到它的人可以用谷歌搜索解决方法,但我还有一个问题:

为什么这能解决问题?

这里有一些关于我的 Insert 和 SelectAll 发生了什么的更多细节。请注意,SelectAll 使用的是 ADO 命令对象(因为在实际代码中它没有执行全选)。如果我使用 Connection.Execute() 而不是 Command.Execute(),则不会发生超时。

//Insert
ADODB::_RecordsetPtr prs = NULL;
HRESULT hr = prs.CreateInstance(__uuidof(ADODB::Recordset));
prs->Open(
    table
    _variant_t((IDispatch *) acpAdoConnection),
    ADODB::adOpenUnspecified, 
    ADODB::adLockOptimistic, 
    ADODB::adCmdTable);
prs->AddNew();
//put some stuff into fields using prs->Fields->Item[]
prs->Update();
prs->Close();

//SelectAll
ADODB::_CommandPtr cmd;
cmd.CreateInstance( __uuidof( ADODB::Command ) );
cmd->ActiveConnection = acpAdoConnection;
ADODB::_RecordsetPtr prs2 = NULL;
HRESULT hr2 = prs2.CreateInstance(__uuidof(ADODB::Recordset));
prs2->Open(
    table, 
    _variant_t((IDispatch *) acpAdoConnection),
    ADODB::adOpenUnspecified, 
    ADODB::adLockOptimistic, 
    ADODB::adCmdTable);
std::string sql = "SELECT * FROM [" + table + "] ;";
cmd->CommandText = sql.c_str();
_variant_t  vtEmpty (DISP_E_PARAMNOTFOUND, VT_ERROR);
_variant_t  vtEmpty2(DISP_E_PARAMNOTFOUND, VT_ERROR);
//timeout:
ADODB::_RecordsetPtr records = 
    cmd->Execute( &vtEmpty, &vtEmpty2, ADODB::adCmdText );

【问题讨论】:

    标签: c++ sql-server ado


    【解决方案1】:

    简短的回答是BEGIN TRANSACTIONcn.BeginTrans() 的行为方式并不总是相同。这个MSDN article 告诉你更多关于这个问题的信息:


    ADO 在交易方面的表现如何

    默认情况下,ADO 在 AutoCommit 模式下运行,除非您通过执行 Connection.BeginTrans 启动隐式事务。

    Implicit_transactions 为每个语句在服务器上开始一个事务,并且在手动发出之前不会发生提交。

    所以,

    set implicit_transactions on
    go
    insert
    insert
    insert
    

    内部变成

    BEGIN TRAN
    insert
    insert
    insert
    ...
    

    除非用户发出正确的语句,否则上述事务不会被回滚或提交。

    没有隐式事务,默认情况下是 ADO(自动提交模式)的行为,以下是(概念上)发生:

    BEGIN TRAN
    insert
    COMMIT TRAN
    BEGIN TRAN
    insert
    COMMIT TRAN
    

    如您所见,适合您的情况

    BEGIN TRAN
    insert
    COMMIT TRAN
    BEGIN TRAN
    select
    COMMIT TRAN
    

    不同于:

    BEGIN TRAN
    insert
    select
    COMMIT TRAN
    

    ...也可能不是您所期望的。

    【讨论】:

    • 这意味着当我明确地开始而不使用 BeginTran() 时,会发生以下事件:(BEGIN,BEGIN,insert,COMMIT,BEGIN,select,COMMIT,COMMIT)。但是当我使用 Command->Execute 时也会发生这种情况,所以它并不能完全解释这种行为。
    • 这意味着您获得了一个嵌套事务,因此在执行您的 2 个事务时,第一个启动的事务尚未提交。我想数据仍然被锁定,因为封闭的交易还没有完成。
    • 我想了解的是“数据仍被锁定”部分。如果 SelectAll 实现为 Connection->Execute("select ") 则所描述的程序有效,但如果我使用 Command->Execute(,,"select") 则中断。
    猜你喜欢
    • 1970-01-01
    • 2014-06-16
    • 1970-01-01
    • 2011-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多