了解在 Visual Basic .NET 和 C# 編寫(xiě)數(shù)據(jù)庫(kù)事務(wù)的基礎(chǔ)知識(shí)。
本文相關(guān)下載: · 示例代碼 · ODP.NET · Oracle 數(shù)據(jù)庫(kù) 10g · Microsoft .NET 框架和 SDK |
在本文中,您將了解到如何在 Visual Basic .NET (VB.NET) 和 Visual C# .NET (C#) 中使用數(shù)據(jù)庫(kù)事務(wù)。具體來(lái)講,您將系統(tǒng)學(xué)習(xí)數(shù)據(jù)庫(kù)事務(wù)、在 .NET 程序中使用 OracleTransaction 對(duì)象以及如何設(shè)置事務(wù)保存點(diǎn)。本文中引用的所有腳本和文件都在這里提供。本文假定您大體上熟悉 C# 和 VB.NET 編程。
所需軟件
如果您要跟隨我們逐步完成本文中給出的示例,那么您需要安裝以下軟件:
- Windows NT 4.0、Windows 2000、Windows XP Professional 或 Windows Server 2003
- 能夠訪問(wèn)一個(gè)已安裝的 Oracle 數(shù)據(jù)庫(kù)(Oracle8i 版本 3 8.1.7 或更高版本)
- Oracle 客戶機(jī)(版本 10.1.0.2.0 或更高版本)
- Oracle Net(版本 10.1.0.2.0 或更高版本)
- Oracle Data Providers for .NET(版本 10.1.0.2.0 或更高版本)
- Microsoft .NET Framework(版本 1.0 或更高版本)
- Microsoft .NET 框架 SDK(版本 1.0 或更高版本)
如果您打算使用企業(yè)服務(wù)事務(wù)或分布式事務(wù)來(lái)開(kāi)發(fā)和運(yùn)行應(yīng)用程序,那么您還需要安裝 Oracle Services for Microsoft Transaction Server(10.1.0.2.0 或更高版本)。
您需要分別下載和安裝 .NET 框架以及 SDK(先安裝框架)。您還需要下載和安裝 Oracle 數(shù)據(jù)庫(kù) 10g,它包括 Oracle Data Provider for .NET (ODP.NET)。您可以選擇在不同計(jì)算機(jī)或同一計(jì)算機(jī)上安裝 ODP.NET 和數(shù)據(jù)庫(kù)服務(wù)器。
注意:ODP.NET 驅(qū)動(dòng)程序針對(duì) Oracle 數(shù)據(jù)庫(kù)訪問(wèn)進(jìn)行了優(yōu)化,因此可以獲得最佳性能,并且它們還支持 Oracle 數(shù)據(jù)庫(kù)的豐富特性,如 BFILE、BLOB、CLOB、XMLType 等。如果您正在開(kāi)發(fā)基于 Oracle 數(shù)據(jù)庫(kù)的 .NET 應(yīng)用程序,那么就特性和性能來(lái)講,ODP.NET 無(wú)疑是最佳的選擇。
數(shù)據(jù)庫(kù)模式設(shè)置
首先,您需要設(shè)置數(shù)據(jù)庫(kù)模式,在此我們使用一個(gè)簡(jiǎn)化的 Web 商店示例。您必須首先創(chuàng)建一個(gè)名為 store 的用戶并按以下方式將所需的權(quán)限授予該用戶(您必須首先以擁有 CREATE USER 權(quán)限的用戶身份登錄數(shù)據(jù)庫(kù)才能創(chuàng)建用戶): CREATE USER store IDENTIFIED BY store;
GRANT connect, resource TO store;
注意:您會(huì)在源代碼文件 db1.sql 中找到前兩個(gè)語(yǔ)句和該部分中出現(xiàn)的設(shè)置 store 模式的其他語(yǔ)句。
接下的語(yǔ)句以 store 用戶身份進(jìn)行連接: CONNECT store/store;
以下語(yǔ)句創(chuàng)建了所需的兩個(gè)數(shù)據(jù)庫(kù)表,名稱分別為 product_types 和 products: CREATE TABLE product_types (
product_type_id INTEGER
CONSTRAINT product_types_pk PRIMARY KEY,
name VARCHAR2(10) 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 VARCHAR2(30) NOT NULL,
description VARCHAR2(50),
price NUMBER(5, 2)
);
注意:如果您在一個(gè)不同的模式中為 store 用戶創(chuàng)建了這些數(shù)據(jù)庫(kù)表,那么您將需要修改示例配置文件(您稍后將看到)中的模式名稱。
表 product_types 用于存儲(chǔ)示例在線商店可能庫(kù)存的產(chǎn)品類型的名稱,表 products 包含了所銷售產(chǎn)品的詳細(xì)信息。
下面的 INSERT 語(yǔ)句為表 product_types 和 products 添加行: INSERT INTO product_types (
product_type_id, name
) VALUES (
1, 'Book'
);
INSERT INTO product_types (
product_type_id, name
) VALUES (
2, 'DVD'
);
INSERT INTO products (
product_id, product_type_id, name, description, price
) VALUES (
1, 1, 'Modern Science', 'A description of modern science', 19.95
);
INSERT INTO products (
product_id, product_type_id, name, description, price
) VALUES (
2, 1, 'Chemistry', 'Introduction to Chemistry', 30.00
);
INSERT INTO products (
product_id, product_type_id, name, description, price
) VALUES (
3, 2, 'Supernova', 'A star explodes', 25.99
);
INSERT INTO products (
product_id, product_type_id, name, description, price
) VALUES (
4, 2, 'Tank War', 'Action movie about a future war', 13.95
);
COMMIT;
接下來(lái),您將了解有關(guān)數(shù)據(jù)庫(kù)事務(wù)的內(nèi)容。
數(shù)據(jù)庫(kù)事務(wù)簡(jiǎn)介
數(shù)據(jù)庫(kù)事務(wù)是由一組 SQL 語(yǔ)句組成的一個(gè)邏輯工作單元。您可以把事務(wù)看作是一組不可分的 SQL 語(yǔ)句,這些語(yǔ)句作為一個(gè)整體永久記錄在數(shù)據(jù)庫(kù)中或一并撤銷。比如在銀行帳戶之間轉(zhuǎn)移資金:一條 UPDATE 語(yǔ)句將從一個(gè)帳戶的資金總數(shù)中減去一部分,另一條 UPDATE 語(yǔ)句將把資金加到另一個(gè)帳戶中。減操作和加操作必須永久記錄在數(shù)據(jù)庫(kù)中,或者必須一并撤銷 — 否則將損失資金。這個(gè)簡(jiǎn)單的示例僅使用了兩條 UPDATE 語(yǔ)句,但一個(gè)更實(shí)際的事務(wù)可能包含許多 INSERT、UPDATE 和 DELETE 語(yǔ)句。
要永久記錄一個(gè)事務(wù)中的 SQL 語(yǔ)句的結(jié)果,您可以通過(guò) COMMIT 語(yǔ)句來(lái)執(zhí)行提交。要撤銷 SQL 語(yǔ)句的結(jié)果,您可以使用 ROLLBACK 語(yǔ)句來(lái)執(zhí)行回滾,這會(huì)把所有的行重設(shè)為它們?cè)瓉?lái)的狀態(tài)。只要您事先沒(méi)有與數(shù)據(jù)庫(kù)斷開(kāi),則您在執(zhí)行回滾之前所做的任何修改都將被撤銷。您還可以設(shè)置一個(gè)保存點(diǎn),以便將事務(wù)回滾至該特定的點(diǎn),同時(shí)保持事務(wù)中的其他語(yǔ)句原封不動(dòng)。
在 C# 和 VB.NET 中使用數(shù)據(jù)庫(kù)事務(wù)
您可以使用 OracleTransaction 類的一個(gè)對(duì)象來(lái)表示一個(gè)事務(wù)。OracleTransaction 類包含多個(gè)屬性,其中的兩個(gè)為 Connection(指定與事務(wù)關(guān)聯(lián)的數(shù)據(jù)庫(kù)連接)和 IsolationLevel(指定事務(wù)隔離級(jí)別);本文稍后將向您介紹更多有關(guān)事務(wù)隔離級(jí)別的內(nèi)容。
OracleTransaction 類包含許多操控事務(wù)的方法。您可以使用 Commit() 方法永久提交 SQL 語(yǔ)句,并可以使用 Rollback() 撤銷這些語(yǔ)句。您還可以使用 Save() 在事務(wù)中設(shè)置一個(gè)保存點(diǎn)。
我現(xiàn)在將帶著您逐步完成兩個(gè)示例程序 — 一個(gè)用 C# 編寫(xiě) (TransExample1.cs),另一個(gè)用 VB.NET 編寫(xiě) (TransExample1.vb)。這些程序演示了如何執(zhí)行一個(gè)包含了兩條 INSERT 語(yǔ)句的事務(wù)。第一條 INSERT 語(yǔ)句將在表 product_types 中添加一行,第二條將在表 products 中添加一行。
導(dǎo)入命名空間
以下 C# 程序語(yǔ)句指定在程序中使用 System 和 Oracle.DataAcess.Client 命名空間: using System;
using Oracle.DataAccess.Client;
下面是等價(jià)的 VB.NET 語(yǔ)句: Imports System
Imports Oracle.DataAccess.Client
Oracle.DataAccess.Client 命名空間是 ODP.NET 的一部分,它包含許多類,其中有 OracleConnection、OracleCommand 和 OracleTransaction。示例程序用到了這些類。
第 1 步
創(chuàng)建一個(gè) OracleConnection 對(duì)象連接到 Oracle 數(shù)據(jù)庫(kù),然后打開(kāi)該連接。
在 C# 中: OracleConnection myOracleConnection =
new OracleConnection(
"User Id=store;Password=store;Data Source=ORCL"
);
myOracleConnection.Open();
在 VB.NET 中: Dim myOracleConnection As New OracleConnection( _
"User Id=store;Password=store;Data Source=ORCL")
myOracleConnection.Open()
User Id 和 Password 屬性指定了您所要連接到的模式的數(shù)據(jù)庫(kù)用戶和口令。Data Source 屬性指定了數(shù)據(jù)庫(kù)的 Oracle Net 服務(wù)名稱;初始數(shù)據(jù)庫(kù)的默認(rèn)服務(wù)名稱為 ORCL。如果您使用的不是初始數(shù)據(jù)庫(kù),或者您的服務(wù)名稱不同,那么您需要在程序中修改 Data Source 屬性的設(shè)置。
第 2 步
創(chuàng)建一個(gè) OracleTransaction 對(duì)象,然后調(diào)用 OracleConnection 對(duì)象的 BeginTransaction() 方法啟動(dòng)事務(wù)。
在 C# 中: OracleTransaction myOracleTransaction =
myOracleConnection.BeginTransaction();
In VB.NET: Dim myOracleTransaction As OracleTransaction = _
myOracleConnection.BeginTransaction()
第 3 步
創(chuàng)建一個(gè) OracleCommand 對(duì)象,用于存儲(chǔ) SQL 語(yǔ)句。
在 C# 中: OracleCommand myOracleCommand = myOracleConnection.CreateCommand();
在 VB.NET 中: Dim myOracleCommand As OracleCommand =
myOracleConnection.CreateCommand
因?yàn)?OracleCommand 對(duì)象使用 OracleConnection 對(duì)象的 CreateCommand() 方法創(chuàng)建的,所以它自動(dòng)使用在第 2 步中為 OracleConnection 對(duì)象設(shè)置的事務(wù)。
第 4 步
將 OracleCommand 對(duì)象的 CommandText 屬性設(shè)為向表 product_types 中添加一行的第一條 INSERT 語(yǔ)句。
在 C# 中: 在 VB.NET 中: 第 5 步 使用 OracleCommand 對(duì)象的 ExecuteNonQuery() 方法運(yùn)行 INSERT 語(yǔ)句。 在 C# 中: 在 VB.NET 中: 第 6 和第 7 步 將 OracleCommand 對(duì)象的 CommandText 屬性設(shè)為向表 Products 中添加一行的第二條 INSERT 語(yǔ)句,并運(yùn)行它。 在 C# 中: 在 VB.NET 中: 第 8 步 使用 OracleTransaction 對(duì)象的 Commit() 方法提交數(shù)據(jù)庫(kù)中的事務(wù)。 在 C# 中: 在 VB.NET 中: 在完成 Commit() 方法之后,由 INSERT 語(yǔ)句添加的兩行將在數(shù)據(jù)庫(kù)中永久記錄。 第 9 步 使用 Close() 方法關(guān)閉 OracleConnection 對(duì)象。 在 C# 中: 在 VB.NET 中: 編譯并運(yùn)行示例程序 要編譯 C# 示例程序,您可以使用 csc 命令運(yùn)行 C# 編譯器。因?yàn)槌绦蚴褂?Oracle Data Access DLL,所以您應(yīng)使用 /r 選項(xiàng)指定該 DLL 的完整路徑,例如: 注意:您需要用您計(jì)算機(jī)上的相應(yīng)路徑來(lái)替換該 DLL 的路徑。此外,如果您的計(jì)算機(jī)找不到 csc 編譯器,那么您可能需要運(yùn)行 Microsoft sdkvars.bat 腳本來(lái)首先設(shè)置 .NET SDK 的環(huán)境變量;您可以在安裝 .NET SDK 的 bin 目錄中找到該腳本。 如果您遇到以下錯(cuò)誤: 這說(shuō)明您沒(méi)有在編譯命令中正確指定 Oracle Data Access DLL。(有關(guān)設(shè)置的信息,請(qǐng)參閱 John Paul Cook 的技術(shù)文章“在 Oracle 數(shù)據(jù)庫(kù)上構(gòu)建 .NET 應(yīng)用程序”。) 下面是用于編譯 VB.NET 程序的等價(jià)命令: 接下來(lái),輸入以下命令,運(yùn)行示例: 您將看到程序的輸出。不過(guò),如果您遇到類似以下的異常: 這說(shuō)明 OracleConnection 對(duì)象的連接字符串中的 Data Source 的設(shè)置不正確。您應(yīng)當(dāng)咨詢您的 DBA 或查閱 Oracle Net 文檔以獲得更多詳細(xì)信息。 如果您使用的是 VS .NET,那么您可以遵循以下指示來(lái)編譯和運(yùn)行 C# 程序 TransExample1.cs:
要編譯和運(yùn)行 TransExample1.vb,您可以執(zhí)行類似的一系列步驟,但第 1 步應(yīng)選擇一個(gè) Visual Basic 控制臺(tái)應(yīng)用程序,并在第 3 步用 TransExample1.vb 中的代碼替換生成的代碼。 查看程序的運(yùn)行結(jié)果 當(dāng)您運(yùn)行完 C# 或 VB .NET 程序時(shí),您可以在 SQL*Plus 中使用以下 SELECT 語(yǔ)句查看事務(wù)的結(jié)果: myOracleCommand.CommandText =
"INSERT INTO product_types (" +
" product_type_id, name" +
") VALUES (" +
" 3, 'Magazine'" +
")";
myOracleCommand.CommandText = _
"INSERT INTO product_types (" & _
" product_type_id, name" & _
") VALUES (" & _
" 3, 'Magazine'" & _
")"
myOracleCommand.ExecuteNonQuery();
myOracleCommand.ExecuteNonQuery()
myOracleCommand.CommandText =
"INSERT INTO products (" +
" product_id, product_type_id, name, description, price" +
") VALUES (" +
" 5, 3, 'Oracle Magazine', 'Magazine about Oracle', 4.99" +
")";
myOracleCommand.ExecuteNonQuery();
myOracleCommand.CommandText = _
"INSERT INTO products (" & _
" product_id, product_type_id, name, description, price" & _
") VALUES (" & _
" 5, 3, 'Oracle Magazine', 'Magazine about Oracle', 4.99" & _
")"
myOracleCommand.ExecuteNonQuery()
myOracleTransaction.Commit();
myOracleTransaction.Commit()
myOracleConnection.Close();
myOracleConnection.Close()
csc TransExample1.cs /r:C:\oracle\product\10.1.0\
Client_1\bin\Oracle.DataAccess.dll
Example1.cs(10,7):error CS0246:The type or namespace name 'Oracle'
could not be found (are you missing a using directive or an assembly reference?)
vbc TransExample1.vb /r:C:\oracle\product\10.1.0\
Client_1\bin\Oracle.DataAccess.dll /r:system.dll /r:system.data.dll
TransExample1
An exception was thrown
Message = ORA-12514:TNS:listener does not currently know
of service requested in connect descriptor
SELECT p.product_id, p.product_type_id, pt. name, p.name, p.description, p.price
FROM products p, product_types pt
WHERE p.product_type_id = pt.product_type_id
AND p.product_id = 5;
您將看到以下結(jié)果:
PRODUCT_ID PRODUCT_TYPE_ID NAME NAME ---------- --------------- ---------- ----------------------- DESCRIPTION PRICE -------------------------------------------------- ---------- 5 3 Magazine Oracle Magazine Magazine about Oracle 4.99
接下來(lái),您將了解如何設(shè)置事務(wù)保存點(diǎn)。
在 .NET 程序中設(shè)置事務(wù)保存點(diǎn)
正如本文前面所提到的那樣,您可以設(shè)置一個(gè)保存點(diǎn),以便將事務(wù)回滾至該特定的點(diǎn),同時(shí)保持事務(wù)中的其他語(yǔ)句原封不動(dòng)。您可以使用 OracleTransaction 類的 Save() 方法在事務(wù)中設(shè)置保存點(diǎn)。
如果您有一個(gè)非常長(zhǎng)的事務(wù)并且希望能夠僅回滾到某個(gè)特定的時(shí)間點(diǎn),那么您可能要使用保存點(diǎn)。例如,您可能想對(duì) 10 個(gè)產(chǎn)品做一些更改,然后設(shè)置一個(gè)保存點(diǎn),然后再對(duì)另 10 個(gè)產(chǎn)品做更改;如果您在進(jìn)行第二批更改時(shí)出現(xiàn)了錯(cuò)誤,那么您可以回滾至保存點(diǎn),使您的第一批更改原封不動(dòng)。
我將帶您逐步完成演示如何使用保存點(diǎn)的 C# (TransExample2.cs) 示例程序和 VB.NET (TransExample2.vb) 示例程序中的相關(guān)新步驟。這些程序向表 products 中添加一行,設(shè)置一個(gè)保存點(diǎn),向表 products 中添加另一行,回滾至保存點(diǎn),然后從表 products 中讀取這些行。在回滾至保存點(diǎn)后,只有添加到表 products 中的第一行保留了下來(lái):第二行將已被刪除。
第 1 到第 3 步與“在 C# 和 VB.NET 中使用數(shù)據(jù)庫(kù)事務(wù)”部分中所示的步驟相同,因此在這里將其省略。
第 4 步
向表 products 中添加一行,該行的產(chǎn)品 ID 為 6。
在 C# 中: myOracleCommand.CommandText =
"INSERT INTO products (" +
" product_id, product_type_id, name, description, price" +
") VALUES (" +
" 6, 2, 'Man from Another World', 'Man from Venus lands on Earth', 24.99" +
")";
myOracleCommand.ExecuteNonQuery();
在 VB.NET 中: myOracleCommand.CommandText = _
"INSERT INTO products (" & _
" product_id, product_type_id, name, description, price" & _
") VALUES (" & _
" 6, 2, 'Man from Another World', 'Man from Venus lands on Earth', 24.99" & _
")"
myOracleCommand.ExecuteNonQuery()
第 5 步
使用 OracleTransaction 的 Save() 方法設(shè)置一個(gè)名為 SaveProduct 的保存點(diǎn)。
在 C# 中: myOracleTransaction.Save("SaveProduct");
在 VB.NET 中: myOracleTransaction.Save("SaveProduct")
第 6 步
向表 products 中添加另一行,該行的產(chǎn)品 ID 為 7。
在 C# 中: myOracleCommand.CommandText =
"INSERT INTO products (" +
" product_id, product_type_id, name, description, price" +
") VALUES (" +
" 7, 2, 'Z-Files', 'Mysterious stories', 14.99" +
")";
myOracleCommand.ExecuteNonQuery();
在 VB.NET 中: myOracleCommand.CommandText = _
"INSERT INTO products (" & _
" product_id, product_type_id, name, description, price" & _
") VALUES (" & _
" 7, 2, 'Z-Files', 'Mysterious stories', 14.99" & _
")"
myOracleCommand.ExecuteNonQuery()
第 7 步
回滾到先前在第 5 步中設(shè)置的 SaveProduct 保存點(diǎn)。
在 C# 中: myOracleTransaction.Rollback("SaveProduct");
在 VB.NET 中: myOracleTransaction.Rollback("SaveProduct")
完成回滾后,在第 6 步中添加的第二行已被刪除,而在第 4 步中添加的第一行保留了下來(lái)。
TransExample2.cs 和 TransExample2.vb 中剩下的步驟顯示表 products 的內(nèi)容,回滾整個(gè)事務(wù)并從數(shù)據(jù)庫(kù)斷開(kāi)。
用于 Microsoft Transaction Server 的 Oracle 事務(wù)服務(wù)的快速說(shuō)明
Microsoft Transaction Server 是一個(gè)運(yùn)行在互聯(lián)網(wǎng)或網(wǎng)絡(luò)服務(wù)器上的專有事務(wù)處理系統(tǒng)。Microsoft Transaction Server 為客戶端計(jì)算機(jī)部署和管理應(yīng)用程序和數(shù)據(jù)庫(kù)事務(wù)請(qǐng)求。
Microsoft Transaction Server 是以服務(wù)器為中心的三層體系結(jié)構(gòu)模型的一個(gè)組件。這種方法實(shí)現(xiàn)了將應(yīng)用程序的表示、業(yè)務(wù)邏輯和數(shù)據(jù)元素清晰地分布到在一個(gè)網(wǎng)絡(luò)中連接的不同計(jì)算機(jī)上。無(wú)需專門(mén)集成,您就可以在與 Oracle 數(shù)據(jù)庫(kù)服務(wù)器 8.0.6 版或更高版本連接的 Microsoft Transaction Server 中部署一個(gè)組件,但首先您必須安裝 Oracle Services for Microsoft Transaction Server。
結(jié)論
在本文中,您系統(tǒng)學(xué)習(xí)了在 .NET 程序中使用數(shù)據(jù)庫(kù)事務(wù)。您了解了如何創(chuàng)建 OracleTransaction 對(duì)象并用它們將事務(wù)提交給數(shù)據(jù)庫(kù),如何使用保存點(diǎn)部分回滾一個(gè)事務(wù),以及 Oracle 數(shù)據(jù)庫(kù)如何分離并發(fā)事務(wù)。
技術(shù)顧問(wèn)兼作家 Jason Price 是 Microsoft 認(rèn)證專家、Oracle 認(rèn)證數(shù)據(jù)庫(kù)管理員和應(yīng)用程序開(kāi)發(fā)人員,具有十多年的軟件行業(yè)從業(yè)經(jīng)驗(yàn)。他撰寫(xiě)了《Oracle 數(shù)據(jù)庫(kù) 10g SQL》(McGraw-Hill/Osborne,2004)、《精通 C# 數(shù)據(jù)庫(kù)編程》(Sybex,2004)、《精通 Visual C# .NET 編程》(Sybex,2003)、《Oracle9i JDBC 編程》(McGraw-Hill/Osborne,2002)和《使用 Oracle SQLJ 進(jìn)行 Java 編程》(O'Reilly,2001)。