在本文中您将了解到如何在 Visual Basic NET (VBNET) 和 Visual C# NET (C#) 中使用数据库事务具体来讲您将系统学习数据库事务在 NET 程序中使用 OracleTransaction 对象以及如何设置事务保存点本文中引用的所有脚本和文件都在这里提供本文假定您大体上熟悉 C# 和 VBNET 编程
所需软件
如果您要跟随我们逐步完成本文中给出的示例那么您需要安装以下软件
Windows NT Windows Windows XP Professional 或 Windows Server
能够访问一个已安装的 Oracle 数据库(Oraclei 版本 或更高版本)
Oracle 客户机(版本 或更高版本)
Oracle Net(版本 或更高版本)
Oracle Data Providers for NET(版本 或更高版本)
Microsoft NET Framework(版本 或更高版本)
Microsoft NET 框架 SDK(版本 或更高版本)
如果您打算使用企业服务事务或分布式事务来开发和运行应用程序那么您还需要安装 Oracle Services for Microsoft Transaction Server( 或更高版本)
您需要分别下载和安装 NET 框架以及 SDK(先安装框架)您还需要下载和安装 Oracle 数据库 g它包括 Oracle Data Provider for NET (ODPNET)您可以选择在不同计算机或同一计算机上安装 ODPNET 和数据库服务器
注意ODPNET 驱动程序针对 Oracle 数据库访问进行了优化因此可以获得最佳性能并且它们还支持 Oracle 数据库的丰富特性如 BFILEBLOBCLOBXMLType 等如果您正在开发基于 Oracle 数据库的 NET 应用程序那么就特性和性能来讲ODPNET 无疑是最佳的选择
数据库模式设置
首先您需要设置数据库模式在此我们使用一个简化的 Web 商店示例您必须首先创建一个名为 store 的用户并按以下方式将所需的权限授予该用户(您必须首先以拥有 CREATE USER 权限的用户身份登录数据库才能创建用户)
CREATE USER store IDENTIFIED BY store;
GRANT connect resource TO store;
注意您会在源代码文件 dbsql 中找到前两个语句和该部分中出现的设置 store 模式的其他语句
接下的语句以 store 用户身份进行连接
CONNECT store/store;
以下语句创建了所需的两个数据库表名称分别为 product_types 和 products
CREATE TABLE product_types (
product_type_id INTEGER
CONSTRAINT product_types_pk PRIMARY KEY
name VARCHAR() NOT NULL
);
CREATE TABLE products (
product_id INTEGER
CONSTRAINT products_pk PRIMARY KEY
product_type_id INTEGER
CONSTRAINT products_fk_product_types
REFERENCES product_types(product_type_id)
name VARCHAR() NOT NULL
description VARCHAR()
price NUMBER( )
);
注意如果您在一个不同的模式中为 store 用户创建了这些数据库表那么您将需要修改示例配置文件(您稍后将看到)中的模式名称
表 product_types 用于存储示例在线商店可能库存的产品类型的名称表 products 包含了所销售产品的详细信息
下面的 INSERT 语句为表 product_types 和 products 添加行
INSERT INTO product_types (
product_type_id name
) VALUES (
Book
);
INSERT INTO product_types (
product_type_id name
) VALUES (
DVD
);
INSERT INTO products (
product_id product_type_id name description price
) VALUES (
Modern Science A description of modern science
);
INSERT INTO products (
product_id product_type_id name description price
) VALUES (
Chemistry Introduction to Chemistry
);
INSERT INTO products (
product_id product_type_id name description price
) VALUES (
Supernova A star explodes
);
INSERT INTO products (
product_id product_type_id name description price
) VALUES (
Tank War Action movie about a future war
);
COMMIT;
接下来您将了解有关数据库事务的内容
数据库事务简介
数据库事务是由一组 SQL 语句组成的一个逻辑工作单元您可以把事务看作是一组不可分的 SQL 语句这些语句作为一个整体永久记录在数据库中或一并撤销比如在银行帐户之间转移资金一条 UPDATE 语句将从一个帐户的资金总数中减去一部分另一条 UPDATE 语句将把资金加到另一个帐户中减操作和加操作必须永久记录在数据库中或者必须一并撤销 ― 否则将损失资金这个简单的示例仅使用了两条 UPDATE 语句但一个更实际的事务可能包含许多 INSERTUPDATE 和 DELETE 语句
要永久记录一个事务中的 SQL 语句的结果您可以通过 COMMIT 语句来执行提交要撤销 SQL 语句的结果您可以使用 ROLLBACK 语句来执行回滚这会把所有的行重设为它们原来的状态只要您事先没有与数据库断开则您在执行回滚之前所做的任何修改都将被撤销您还可以设置一个保存点以便将事务回滚至该特定的点同时保持事务中的其他语句原封不动
在 C# 和 VBNET 中使用数据库事务
您可以使用 OracleTransaction 类的一个对象来表示一个事务OracleTransaction 类包含多个属性其中的两个为 Connection(指定与事务关联的数据库连接)和 IsolationLevel(指定事务隔离级别)本文稍后将向您介绍更多有关事务隔离级别的内容
OracleTransaction 类包含许多操控事务的方法您可以使用 Commit() 方法永久提交 SQL 语句并可以使用 Rollback() 撤销这些语句您还可以使用 Save() 在事务中设置一个保存点
我现在将带着您逐步完成两个示例程序 ― 一个用 C# 编写 (TransExamplecs)另一个用 VBNET 编写 (TransExamplevb)这些程序演示了如何执行一个包含了两条 INSERT 语句的事务第一条 INSERT 语句将在表 product_types 中添加一行第二条将在表 products 中添加一行
导入命名空间
以下 C# 程序语句指定在程序中使用 System 和 OracleDataAcessClient 命名空间
using System;
using OracleDataAccessClient;
下面是等价的 VBNET 语句
Imports System
Imports OracleDataAccessClient
OracleDataAccessClient 命名空间是 ODPNET 的一部分它包含许多类其中有 OracleConnectionOracleCommand 和 OracleTransaction示例程序用到了这些类
第 步
创建一个 OracleConnection 对象连接到 Oracle 数据库然后打开该连接
在 C# 中
OracleConnection myOracleConnection =
new OracleConnection(
User Id=store;Password=store;Data Source=ORCL
);
myOracleConnectionOpen();
在 VBNET 中
Dim myOracleConnection As New OracleConnection( _
User Id=store;Password=store;Data Source=ORCL)
myOracleConnectionOpen()
User Id 和 Password 属性指定了您所要连接到的模式的数据库用户和口令Data Source 属性指定了数据库的 Oracle Net 服务名称初始数据库的默认服务名称为 ORCL如果您使用的不是初始数据库或者您的服务名称不同那么您需要在程序中修改 Data Source 属性的设置
第 步
创建一个 OracleTransaction 对象然后调用 OracleConnection 对象的 BeginTransaction() 方法启动事务
在 C# 中
OracleTransaction myOracleTransaction =
myOracleConnectionBeginTransaction();
In VBNET:
Dim myOracleTransaction As OracleTransaction = _
myOracleConnectionBeginTransaction()
第 步
创建一个 OracleCommand 对象用于存储 SQL 语句
在 C# 中
OracleCommand myOracleCommand = myOracleConnectionCreateCommand();
在 VBNET 中
Dim myOracleCommand As OracleCommand =
myOracleConnectionCreateCommand
因为 OracleCommand 对象使用 OracleConnection 对象的 CreateCommand() 方法创建的所以它自动使用在第 步中为 OracleConnection 对象设置的事务
第 步
将 OracleCommand 对象的 CommandText 属性设为向表 product_types 中添加一行的第一条 INSERT 语句
在 C# 中
myOracleCommandCommandText =
INSERT INTO product_types ( +
product_type_id name +
) VALUES ( +
Magazine +
);
在 VBNET 中
myOracleCommandCommandText = _
INSERT INTO product_types ( & _
product_type_id name & _
) VALUES ( & _
Magazine & _
)
第 步
使用 OracleCommand 对象的 ExecuteNonQuery() 方法运行 INSERT 语句
在 C# 中
myOracleCommandExecuteNonQuery();
在 VBNET 中
myOracleCommandExecuteNonQuery()
第 和第 步
将 OracleCommand 对象的 CommandText 属性设为向表 Products 中添加一行的第二条 INSERT 语句并运行它
在 C# 中
myOracleCommandCommandText =
INSERT INTO products ( +
product_id product_type_id name description price +
) VALUES ( +
Oracle Magazine Magazine about Oracle +
);
myOracleCommandExecuteNonQuery();
在 VBNET 中
myOracleCommandCommandText = _
INSERT INTO products ( & _
product_id product_type_id name description price & _
) VALUES ( & _
Oracle Magazine Magazine about Oracle & _
)
myOracleCommandExecuteNonQuery()
第 步
使用 OracleTransaction 对象的 Commit() 方法提交数据库中的事务
在 C# 中
myOracleTransactionCommit();
在 VBNET 中
myOracleTransactionCommit()
在完成 Commit() 方法之后由 INSERT 语句添加的两行将在数据库中永久记录
第 步
使用 Close() 方法关闭 OracleConnection 对象
在 C# 中
myOracleConnectionClose();
在 VBNET 中
myOracleConnectionClose()
编译并运行示例程序
要编译 C# 示例程序您可以使用 csc 命令运行 C# 编译器因为程序使用 Oracle Data Access DLL所以您应使用 /r 选项指定该 DLL 的完整路径例如
csc TransExamplecs /r:C:\oracle\product\\
Client_\bin\OracleDataAccessdll
注意您需要用您计算机上的相应路径来替换该 DLL 的路径此外如果您的计算机找不到 csc 编译器那么您可能需要运行 Microsoft sdkvarsbat 脚本来首先设置 NET SDK 的环境变量您可以在安装 NET SDK 的 bin 目录中找到该脚本
如果您遇到以下错误
Examplecs():error CS:The type or namespace name Oracle
could not be found (are you missing a using directive or an assembly reference?)
这说明您没有在编译命令中正确指定 Oracle Data Access DLL(有关设置的信息请参阅 John Paul Cook 的技术文章在 Oracle 数据库上构建 NET 应用程序)
下面是用于编译 VBNET 程序的等价命令
vbc TransExamplevb /r:C:\oracle\product\\
Client_\bin\OracleDataAccessdll /r:systemdll /r:systemdatadll
接下来输入以下命令运行示例
TransExample
您将看到程序的输出不过如果您遇到类似以下的异常
An exception was thrown
Message = ORA:TNS:listener does not currently know
of service requested in connect descriptor
这说明 OracleConnection 对象的连接字符串中的 Data Source 的设置不正确您应当咨询您的 DBA 或查阅 Oracle Net 文档以获得更多详细信息
如果您使用的是 VS NET那么您可以遵循以下指示来编译和运行 C# 程序 TransExamplecs
创建一个新的 C# 控制台应用程序File>New Project然后选择 Visual C# ProjectsConsole Application
将项目命名为 TransExample
用 TransExamplecs 中的代码替换 VS NET 生成的所有代码
选择 Project>Add Reference 添加对 OracleDataAccessdll 的引用然后浏览至您安装 ODPNET 的目录(在我的计算机上它是 C:\oracle\product\\Client_\bin\OracleDataAccessdll)然后双击 OracleDataAccessdll
选择 Debug>Start without Debugging 运行该程序
要编译和运行 TransExamplevb您可以执行类似的一系列步骤但第 步应选择一个 Visual Basic 控制台应用程序并在第 步用 TransExamplevb 中的代码替换生成的代码
查看程序的运行结果
当您运行完 C# 或 VB NET 程序时您可以在 SQL*Plus 中使用以下 SELECT 语句查看事务的结果
SELECT pproduct_id pproduct_type_id pt name pname pdescription pprice
FROM products p product_types pt
WHERE pproduct_type_id = ptproduct_type_id
AND pproduct_id = ;
您将看到以下结果
PRODUCT_ID PRODUCT_TYPE_ID NAME NAME
DESCRIPTION PRICE
Magazine Oracle Magazine
Magazine about Oracle
接下来您将了解如何设置事务保存点
在 NET 程序中设置事务保存点
正如本文前面所提到的那样您可以设置一个保存点以便将事务回滚至该特定的点同时保持事务中的其他语句原封不动您可以使用 OracleTransaction 类的 Save() 方法在事务中设置保存点
如果您有一个非常长的事务并且希望能够仅回滚到某个特定的时间点那么您可能要使用保存点例如您可能想对 个产品做一些更改然后设置一个保存点然后再对另 个产品做更改如果您在进行第二批更改时出现了错误那么您可以回滚至保存点使您的第一批更改原封不动
我将带您逐步完成演示如何使用保存点的 C# (TransExamplecs) 示例程序和 VBNET (TransExamplevb) 示例程序中的相关新步骤这些程序向表 products 中添加一行设置一个保存点向表 products 中添加另一行回滚至保存点然后从表 products 中读取这些行在回滚至保存点后只有添加到表 products 中的第一行保留了下来第二行将已被删除
第 到第 步与在 C# 和 VBNET 中使用数据库事务部分中所示的步骤相同因此在这里将其省略
第 步
向表 products 中添加一行该行的产品 ID 为
在 C# 中
myOracleCommandCommandText =
INSERT INTO products ( +
product_id product_type_id name description price +
) VALUES ( +
Man from Another World Man from Venus lands on Earth +
);
myOracleCommandExecuteNonQuery();
在 VBNET 中
myOracleCommandCommandText = _
INSERT INTO products ( & _
product_id product_type_id name description price & _
) VALUES ( & _
Man from Another World Man from Venus lands on Earth & _
)
myOracleCommandExecuteNonQuery()
第 步
使用 OracleTransaction 的 Save() 方法设置一个名为 SaveProduct 的保存点
在 C# 中
myOracleTransactionSave(SaveProduct);
在 VBNET 中
myOracleTransactionSave(SaveProduct)
第 步
向表 products 中添加另一行该行的产品 ID 为
在 C# 中
myOracleCommandCommandText =
INSERT INTO products ( +
product_id product_type_id name description price +
) VALUES ( +
ZFiles Mysterious stories +
);
myOracleCommandExecuteNonQuery();
在 VBNET 中
myOracleCommandCommandText = _
INSERT INTO products ( & _
product_id product_type_id name description price & _
) VALUES ( & _
ZFiles Mysterious stories & _
)
myOracleCommandExecuteNonQuery()
第 步
回滚到先前在第 步中设置的 SaveProduct 保存点
在 C# 中
myOracleTransactionRollback(SaveProduct);
在 VBNET 中
myOracleTransactionRollback(SaveProduct)
完成回滚后在第 步中添加的第二行已被删除而在第 步中添加的第一行保留了下来
TransExamplecs 和 TransExamplevb 中剩下的步骤显示表 products 的内容回滚整个事务并从数据库断开
用于 Microsoft Transaction Server 的 Oracle 事务服务的快速说明
Microsoft Transaction Server 是一个运行在互联网或网络服务器上的专有事务处理系统Microsoft Transaction Server 为客户端计算机部署和管理应用程序和数据库事务请求
Microsoft Transaction Server 是以服务器为中心的三层体系结构模型的一个组件这种方法实现了将应用程序的表示业务逻辑和数据元素清晰地分布到在一个网络中连接的不同计算机上无需专门集成您就可以在与 Oracle 数据库服务器 版或更高版本连接的 Microsoft Transaction Server 中部署一个组件但首先您必须安装 Oracle Services for Microsoft Transaction Server
结论
在本文中您系统学习了在 NET 程序中使用数据库事务您了解了如何创建 OracleTransaction 对象并用它们将事务提交给数据库如何使用保存点部分回滚一个事务以及 Oracle 数据库如何分离并发事务