VFP 客户/服务器应用程序中的事务处理

 

资料来源:foxtalk 2001    转自:http://www.gz9f.com/jiaocai/hcc/hcc9/hcc9.htm

作者: Hector J. Correa

译者:RMH

--本文出自《天堂论坛》,若欲转载,敬请注明

我们都知道事务处理在数据库世界中是多么重要. 我们也知道 VFP 提供了杰出的事务处理支持. 但是, 当你的 VFP 应用程序使用另一种象 SQL Server 这样的 DBMS 来保存信息时会发生什么情况呢? 你是否还需要事务处理? 是的, 你需要. 你需要 VFP 的事务处理还是 SQL Server 的事务处理? 两者你都需要! Hector Correa 向你展示了为什么.

一般而言, 事务处理是访问数据库的一个程序单元. 在执行时, 事务处理可以接收并可能更新数据. 一个象 VFP 这样的数据库管理系统, 有执行事务处理的责任, 这样它才是原子的(OOP 术语:组合对象中的最小单元)和正确的. 要是原子的, 事务处理必须要么完成要么不执行.

使用事务处理允许你转换你从一个一致的状态转换你的数据库到另一个一致的状态. 如果一个数据库能够确保它的完整性约束则它是处于一致的状态. 在事务处理时, 数据库可能会处于不一致的状态. 但是, 如果完整性约束在事务处理结束时不能满足要求, VFP 必须终止事务处理并使数据库回到没有进行事务处理时的状态.

事务处理的典型示例包括银行传输 (从支票取走 $500, 减少 $500 存款) 和发票系统 (添加五个行项到一张发票, 从库存中移去五个项).

使用事务处理的优势是不可数的, 但基本的概念事务处理使你的生活更轻松, 因为你现在有一个个有力的, VFP 内置的机制来帮助你的数据库处于一种一致的状态.

现在, 让我们谈谈关于现实世界应用程序中的典型情节. 当你的 VFP 应用程序使用另一种象 SQL Server 这样的数据库管理系统来保存它的数据时会发生什么事? SQL Server 也支持事务处理. 你需要 SQL Server 的事务处理吗? 回答是需要. 你可以用 SQL Server 的事务处理来代替 VFP 的事务处理吗? 不, 两个你都需要. 让我们来看看为什么.

测试情节

在本文中, 我将用 VFP 的远程视图来访问并更新一个 SQL Server 数据库. 出于简单的原故, 我使用两个虚构的的 VFP 远程视图 (rv_MyView 和 rv_MyOtherView) 来访问两个 SQL Server 中的虚构的表 (MyTable 和 MyOtherTable). 图 1 显示了该情节是如何组织的.

我用 SQL Server 和 MSDE 测试了本文中的所示例. 但是, 如果你计划使用另一种 DBMS 作为你的后端, 你可能需要进行一点小小的调整.

在所附的下载文件中, 你可以找到更多的使用远程视图来访问和更新 SQL Server 7 的示例数据库 Northwind 数据库的例子.

没有事务处理的世界

在很大程度上, 事务处理是确保数据库的正确性的一种机制. 在完美的世界中, 你可能不需要事务处理. 以下代码不用事务处理来更新后端数据:

* 更新 VFP 游标中的数据.
Replace balance With balance - 100 In rv_MyView
Replace balance With balance + 100 In rv_MyOtherView

* 更新修改到 MyTable.
lEverythingOK = Tableupdate( 2, .F., 'rv_MyView')
If lEverythingOK
    * 更新修改到 MyOtherTable.
    lEverythingOK = Tableupdate( 2, .F., 'rv_MyOtherView')
Endif

好了, 由于我们并不是生活在一个完美的世界上, 让我们看看如果更新 rv_MyView 成功而更新 rv_MyOtherView 失败会发生什么事.

第一个 TableUpdate() 将通知 VFP 执行一个 INSERT/UPDATE 命令到 SQL Server (例如, UPDATE myTable SET balance = 400 WHERE myTablePK = 'abc'). 一但该命令提交到后端, 后端处理它并告诉 VFP 命令成功地执行了. 第二个 TableUpdate() 失败—理由之一是后端不能处理所请求的命令--因为我正试图更新的记录被另一个用户删除了.

我将如何处理这种情况? 我已经更新了 MyTable 而且在 MyOtherTable 没有更新时不想保持这个修改. 当然, 我可以编写代码来保存 MyTable  的原始状态然后在发生错误时恢复它. 但是嗨, 这就是为什么正规的数据库管理系统中都有事务处理的能力. 让我们使用它!

事务处理命令

VFP 和 SQL Server 都提供了事务处理管理的相似的能力. 表 1 列出相等的命令.

表 1. VFP SQL Server 中的事务处理命令.

VFP 命令/函数  

SQL Server 中的等价物 (T-SQL)

Begin Transaction  

Begin Transaction
Set Implicit_Transactions On

End Transaction  

Commit

Rollback  

Rollback

TnxLevel()  

@@TranCount

虽然两个 DBMS 提供了事务处理管理的相似的能力, 但也有一些你应该注意到的精细的区别.

第一个区别是 VFP 提供了 End Transaction 命令, 而 SQL Server 提供了 Commit 命令.

另一个差异是在嵌套事务处理时的 Rollback. 在 VFP 中, Rollback 仅撤消当前事务处理中的改变 (也就是说, 对于每一个事务处理你需要一个 Rollback 命令). 然而在 SQL Server 中, Rollback 撤消所有嵌套的事务处理直到远离事务处理 (因此, 无论你有多少嵌套的事务处理, 只需要一个 Rollback 命令).

另一个不同是允许的嵌套层数. VFP 允许嵌套至五级深度, 而 SQL Server 没有嵌套深度的限制.

使用 VFP 的事务处理

在处理远程视图时, 使用 VFP 的事务处理是直接了当的. 以下代码演示了如何做:

* 更新 VFP 游标中的数据.
Replace balance With balance - 100 In rv_MyView
Replace balance With balance + 100 In rv_MyOtherView
* 开始 VFP 事务处理.
Begin Transaction
* 更新修改到 MyTable.
lEverythingOK = Tableupdate( 2, .F., 'rv_MyView' )
If lEverythingOK
    * 更新修改到 MyOtherTable.
    lEverythingOK = Tableupdate( 2, .F., 'rv_MyOtherView')
Endif
* 结束 VFP 事务处理.
If lEverythingOK
    End Transaction
Else
    Rollback
Endif

当发布一条 VFP begin transaction 命令时, 我请求 VFP 开始登录 VFP 游标中的每一个修改. 在最后, 我将发布一条 VFP end transaction 命令来接受那些修改, 或者用 VFP rollback 命令来废弃它们.

让我们看看与早先的示例相同的情形: 更新到 MyTable 成功, 但更新到 MyOtherTable 失败. 当这种情形发生时, lEverythingOK 将会是 .F., 因此, 我会 rollback 我的修改来撤消每一样东西到它的原始状态. 这就是所有真象, 但是让我们看看我所说的原始状态的意思.

我的两个视图在我发布 VFP begin transaction 前有一些未决的修改. 当我发布 begin transaction 时, 我请求 VFP 注意所发生的每一件事情. 然后, 当我更新 rv_MyView 后, VFP 发送那些修改到后端中的 MyTable 并标记 rv_MyView 为已更新的. 接着, 因为后端不能处理 MyOtherTable 的修改, 我的调用更新 rv_MyOtherView 失败. 在后端, 我 rolled back 事务处理, 因此, VFP 标记 rv_MyView 为未更新的 (也就是说, 它的原始状态).

现在, 让我们仔细看看在更新远程视图时 TableUpdate() 做了些什么. TableUpdate() 发送适当的 SQL 语句 (INSERT, UPDATE 或 DELETE) 到后端并且, 如果成功, 标记 VFP 游标为已更新的. 那是正确的, TableUpdate() 实际上发送了更新到后端.

你可能对修 rv_MyView 时发生了什么改感到疑惑. 我们知道它们确实发送到了后端并且后端接收了它们. 当我回滚 VFP 的事务处理时那些修改会被撤消吗? 不, 它们没有被撤消! VFP 标记 rv_MyView 为未更新的, 但它决不会告诉后端它需要忘记关于 MyTable 表中的修改.

换句话说, 使用 VFP 的事务处理, VFP 保存并撤消 VFP 游标中的原始状态, 而不是后端表的!

使用 SQL Server 的事务处理

正如你所看到的, 当你使用象 SQL Server 这样的其它的数据库管理系统时, 你需要一种机制来处理后端中的事务处理, 幸运的是, 除了 VFP 所提供的事务处理能力外, 大多数 DBMS 内置了该能力.

当你想在后端上开始一个事务处理时, 你所需要做的所有事就是发送一条命令到服务器来做该工作. 基本上有两种方法来完成该任务, 且两种方法都调用 SQL pass-through 函数.

第一种方法用 VFP SQLSetProp() 函数来启动服务器上的事务处理. 以下代码演示了如何这样做:

* 开始服务器上的事务处理.
nOldTransMode = DBGetProp( 'MyConnection', ;
    'Connection', 'Transactions' )
SQLSetprop( nConnection, 'Transactions', DB_TransManual )
* 更新修改到 MyTable.
lEverythingOK = Tableupdate( 2, .F., 'rv_MyView' )
If lEverythingOK
    * 更新修改到 MyOtherTable.
    lEverythingOK = Tableupdate( 2, .F., 'rv_MyOtherView' )
Endif
* 结束服务器上的事务处理.
If lEverythingOK
    Sqlcommit( nConnection )
Else
    Sqlrollback( nConnection )
Endif
* 恢复原始的事务处理模式.
SQLSetprop( nConnection, 'Transactions', nOldTransMode )

虽然它不是很直观, 但调用 SQLSetProp() 函数是一种实际启动 SQL Server 的事务处理方法. 设置 'Transactions' 属性为 DB_TRANSMANUAL 告诉 SQL Server 事务处理将手动地处理. 也就是说, 程序员钭发布一个明确的 rollback 或 commit 来指示结束事务处理. 当你发布该调用时, VFP 发送一个 SET IMPLICIT_TRANSACTIONS ON 命令到 SQL Server. 该命令选择一个轻快的版的 BEGIN TRANSACTION, 它延迟事务处理的开始直到真正被请求时 (例如, 当侦测到一个 UPDATE 命令时).

有另一种非直观的调用你必须用 VFP 内置的函数来处理服务器上的事务处理. 一但结果了你的事务处理, 必需确保设置事务处理模式回到它的原始状态 (最可能是 DB_MANUAL). 忘记这一步会在你的应用程序中产生大问题 (如, 锁定问题).

代替使用这些非直观调用和猜测 VFP 请求服务器做了什么, 我更喜欢自己发送事务处理命令到后端. 以下代码演示了该方法:

* 开始服务器上的事务处理.
SQLExec( nConnection, 'BEGIN TRANSACTION' )
* 更新修改到 MyTable.
lEverythingOK = Tableupdate( 2, .F., 'rv_MyView' )
If lEverythingOK
    * 更新修改到 MyOtherTable.
    lEverythingOK = Tableupdate( 2, .F., 'rv_MyOtherView' )
Endif
* 结束服务器上的事务处理.
If lEverythingOK
    SQLExec( nConnection, 'IF @@TRANCOUNT > 0 COMMIT' )
Else
    SQLExec( nConnection, 'IF @@TRANCOUNT > 0 ROLLBACK' )
Endif

以上代码一直运行到明确地发布 T-SQL 命令来开始和结束后端上的事务处理. 我发现该方法比用 VFP 内置的函数更易于阅读.

现在, 让我们看看如果 rv_View 更新成功而 rv_MyOtherView 失败时以上代码会发生什么情况. 我的第一个调用 SQLExec() 在后端上开始一个事务处理. 也就是说, 我现在请求后端保持跟踪所有的后端表气所发生的事. 然后, TableUpdate() 发送 rv_MyView 的修改到后端并标记游标为已更新的. 接着, 因为后端不能处理对 MyOtherTable 的修改, 我的第二个更新失败. 在结束时, 我请示后端回滚任何后端表中的已经执行了的修改.

两个最好的世界

以下示例代码演示了我的代码在同时使用 VFP 和 SQL Server 的事务处理时是如何做的:

* 开始一个 VFP 事务处理和一个服务器上的事务处理.
Begin Transaction
SQLExec( nConnection, 'BEGIN TRANSACTION' )
* 更新修改到 MyTable.
lEverythingOK = Tableupdate( 2, .F., 'rv_MyView' )
If lEverythingOK
    * 更新修改到 MyOtherTable.
    lEverythingOK = Tableupdate( 2,.F.,'rv_MyOtherView')
Endif
* 结束服务器上的和 VFP 的事务处理.
If lEverythingOK
    SQLExec( nConnection, 'IF @@TRANCOUNT > 0 COMMIT' )
    End Transaction
Else
    SQLExec( nConnection, 'IF @@TRANCOUNT > 0 ROLLBACK' )
    Rollback
Endif

正如你在最后的示例代码中看到的一样, 在 VFP 客户/服务器应用程序中处理事务处理是相当直接了当的. 作为一个黄金规则你应该意识到你是在处理两个 (不是一个) 数据库管理系统, 因此你的代码必须确保两个 DBMS 明白你对基本数据库的行为. 一但你这样做了, 确保你的数据库的正确性只是小菜一碟? 那怕你是生活在一个不太完美的世界中.

 

转载于:https://www.cnblogs.com/hylan/archive/2008/09/26/1299833.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值