为什么 ADO BeginTrans() 执行的操作与“BEGIN TRANSACTION”不同?

发布于 2024-09-29 03:11:22 字数 1851 浏览 1 评论 0原文

在将 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(),就像一个理智的人会做的那样,一切都会按预期进行。虽然这篇文章主要是为了让其他可能遇到该问题的人可以在谷歌上找到解决方法,但我也有一个问题:

为什么这能解决问题?

这里有一些关于我的问题的更多细节插入并全选。请注意,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 );

I've run into some surprising behavior while using ADO with C++ and Microsoft SQL Server 2008 (express). Essentially, I had code that did this:

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

But when it tried to perform the SelectAll, ADO threw an exception with the following information:

Error: ADO Error -2147217871: 071A14D0
From source: Microsoft OLE DB Provider for SQL Server
Description: Timeout expired

After a bit of sleuthing, I discovered that if I used ado_connection->BeginTrans(), like a sane person would, everything worked as expected. And while this post is mostly here to make the workaround googleable for other people who might encounter it, I also have a question:

Why did this fix the problem?

Here's a little more detail about what's happening with my Insert and SelectAll. Note that SelectAll is using an ADO command object (because in the actual code it isn't doing a select all). The timeout doesn't occur if I use Connection.Execute() instead of 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 );

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

一向肩并 2024-10-06 03:11:22

简而言之,BEGIN TRANSACTIONcn.BeginTrans() 的行为并不总是相同。这篇MSDN 文章告诉您有关此问题的更多信息:


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

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

The short answer is that BEGIN TRANSACTION and cn.BeginTrans() don't behave always the same way. This MSDN article tells you more about this problem:


How Does ADO Behave with Respect to Transactions

By default ADO operates in AutoCommit mode, unless you start a implicit transaction by executing Connection.BeginTrans.

Implicit_transactions begin a transaction on the server for each statement, and commits do not occur until they are manually issued.

So,

set implicit_transactions on
go
insert
insert
insert

is internally turned into

BEGIN TRAN
insert
insert
insert
...

The above transaction will not be rolled back or committed unless the user issues the correct statement.

Without implicit transaction, which by default is the behavior of ADO (Auto Commit mode), the following is (conceptually) occurring:

BEGIN TRAN
insert
COMMIT TRAN
BEGIN TRAN
insert
COMMIT TRAN

As you can easy see, for your case

BEGIN TRAN
insert
COMMIT TRAN
BEGIN TRAN
select
COMMIT TRAN

is different from:

BEGIN TRAN
insert
select
COMMIT TRAN

... and also maybe not what you are expecting.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文