您的位置:首页 > 数据库 > Oracle

使用 ODP.NET 访问 Oracle

2012-05-02 16:26 344 查看
开发人员 ODP.NET
使用 ODP.NET 访问 Oracle

作者:Robert P. Lipschutz 和 Gregg D. Harrington

现在 .NET 开发人员可利用 Oracle 的本地数据访问来改进应用程序的性能。

要想从 Oracle 所支持的企业应用程序获得预期的响应性能和高级数据库特性,应该选择 Oracle 专用的连接而不是一般的数据库连接。Java 开发人员长期以来一直可以选择使用 Oracle 提供的专用 API 连接到 Oracle 数据库,这些 API 扩展了 JDBC 并为开发人员利用如大型对象 (LOB) 和 Ref Cursor 这样的 Oracle 高级特性提供了一条途径。

2002 年 2 月,Microsoft 推出了 Visual Studio .NET 及其三种语言(C# .NET、Visual Basic .NET 和 C++ .NET)以及底层的 .NET 框架。Microsoft .NET 为建立企业级的桌面、Web 和 client/server 应用程序提供了一个面向对象的开发平台。不幸的是,使用这些 Microsoft 语言和 .NET 框架的开发人员并没有一个现成的、专用于 Oracle 的数据库连接选项。

针对这一问题,Oracle 为 .NET (ODP.NET) 专门编写了 Oracle Data Provider,一个用于 Microsoft .NET 环境下的 Oracle 数据访问 API。在本文中,我们将讨论 ODP.NET 的用法、特性和对性能的改善。我们发现使用 ODP.NET 确实具有一些显著的优势。

ODP.NET 的基本知识

我们在 Microsoft Visual Studio .NET 中使用专用于 Oracle 数据库连接的 ODP.NET,建立了一个简单的 C# 应用程序(名为 dejavu);完整的源代码位于列表 1 和列表 2 中。这一应用程序及其附带的数据库将作为本文所有示例的一个基础。

数据库中的每条记录都包括一个员工的相关信息。这一应用程序使用带有四个字段的基本表, 表 1 对此进行了概述。

您会发现使用 ODP.NET 进行连接或使用基本的 INSERT 和 SELECT 语句相对要容易些;这些任务与 JDBC 或 OLE DB .NET 同类产品非常相似。在编写 dejavu 的时侯,我们首先执行了一些基本步骤以便可以使用 ODP.NET:

安装 ODP.NET 软件。

在 Visual Studio 的 Solution Explorer 中,添加一个对 Oracle.DataAccess DLL 的引用。

在 C# 类中,通过 在源代码文件的最上面使用 子句( using Oracle.DataAccess.Client; 和using Oracle.DataAccess.Types; )添加了两个命名空间(如果您正在使用 Visual Basic .NET 或其他与 .NET 兼容的语言,则添加命空间的语法可能会略有不同)。

一旦可以使用 ODP.NET,就可以执行下面的步骤来完成一个基本的插入操作:

创建和打开一个数据库连接。

创建一个命令。

执行命令。

首先,创建和打开一个数据库连接:

OracleConnection dbConn = new OracleConnection(
"Data Source=" + txtDataSource.Text + ";" +
"User Id=" + txtUsername.Text + ";" +
"Password=" + txtPassword.Text + ";");
dbConn.Open();


然后使用 OracleConnection object 建立对数据库的一个连接。每当创建该对象的一个实例,就要提供一个连接字符串。OracleConnection 对象使用此字符串定位到相应的 Oracle 数据库服务器,并进行认证。

在这个例子中,连接字符串有三个连接参数: Data Source、User Id 和 Password 。由于 Oracle Data Provider 使用了 Oracle 调用接口 (OCI),因此 Data Source 将是 Oracle 客户端软件所用 tnsnames.ora文件中的一个有效 TNS 名。 User Id 是指定的 Data Source 的有效用户名。最后, Password 是与所提供的 User Id 相匹配的密码值。

然后,打开连接,方法是对 OracleConnection 对象调用 Open() 方法, 并创建一个 OracleCommand 对象(该对象与已创建的 OracleConnection 对象相关联):

OracleCommand cmd = new OracleCommand(
"insert into dejavu_employee values " +
"(1, 'Gregg D. Harrington', empty_blob())", dbConn);


在创建 OracleCommand 对象的时侯,需要提供带有两个参数的构造符。第一个是希望执行的 SQL 命令;这可以是用字符串所写的任意有效的 SQL 语句。在这个例子中,准备在 DEJAVU_EMPLOYEE 表中插入一个员工。第二个参数是连接到数据库的 OracleConnection 对象。

最后,执行 OracleCommand 对象:

int rows = cmd.ExecuteNonQuery();
ShowInfo(rows + " where added to the
database");


在这个例子中,要在数据库中插入一行数据,因此我们将使用 ExecuteNonQuery() 方法。在不需要查询返回任何行时(比如 SQL 的 INSERT、UPDATE 或 DELETE 语句)就可以使用这个方法(使用ExecuteReader() 用于 SELECT 语句)。 ExecuteNonQuery() 方法接收所构造的 SQL 语句,将其送到数据库执行,并返回操作涉及到的行数。

现在,让我们来读取刚刚插入到数据库中的记录并向用户显示相应的列名。将使用现有的连接和命令目标,而不是建立新的数据库连接。

要更改 SQL 语句,可根据想要执行的新的 SQL 语句更改 OracleCommand 对象的 CommandText 属性:

cmd.CommandText = "select * from dejavu_employee where " +
"id = 1";


如果想要使用单一的数据库连接在单一的 OracleCommand 对象中执行多个 SQL 语句,这一技术就特别有用。

下面,执行该命令并读取结果:

OracleDataReader reader = cmd.ExecuteReader();
reader.Read();
ShowInfo("The selected user's name was " +
reader["name"].ToString());
dbConn.Close();


我们在此看到一个新对象, OracleDataReader 。该对象用于读取一条或多条记录。在给出的示例中,由于我们是在主关键字列中查找一个特定值,因此只返回一条记录。 ExecuteReader() 返回一个包含生成的一条或多条记录的 OracleDataReader 对象。

一旦有了 OracleDataReader 对象,就可以获得想要的数据了 — 在这个例子中,就是姓名这一列。在开始创建 OracleDataReader 的时侯,该对象不是放置在第一条记录的位置,因此有必要在读取第一条记录之前,调用 Read() 方法。 如果之后还有相应的记录,则 Read() 方法会移动到下一条记录并返回一个为真的布尔值。

下面,我们要使用 OracleDataReader 对象缺省的字符串数组属性检索姓名列的值,传递所要列的名称 — 在这个例子中,就是姓名。最后,我们另外通过对 OracleConnection 对象调用 Close() 方法关闭这个连接。

比较基本的 ODP.NET 类和与之相当的类

用于 ODP.NET 中的基本类与用于其他连接选项中的基本类非常相似。虽然方法名称和执行有时也会不太一样,但基本的逻辑都是一样的。 表 2 列出了一些典型的数据库操作,以及 ODP.NET、OLE DB .NET 和 Oracle 瘦 JDBC 驱动器处理这些操作时用到的对象。

ODP.NET 高级特性

至此我们已经了解了一些基本知识,下面让我们来仔细了解使得 ODP.NET 如此强大的一些特性:对 LOB 和 Ref Cursors 的处理。

Oracle LOB、BLOB 和 Bfile。 Oracle LOB 用于存储各种大型的字符或二进制数据,包括图片和文本文档。ODP.NET 提供本地方法检索和管理 LOB,这样就改进了处理 LOB 的代码性能,并使得编写这一代码的过程对于开发人员来说变得更为简单。

ODP.NET 包含了 OracleBlob 和 OracleClob 对象。这些对象扩展了 System.IO.Stream 对象的功能。这些数据类型使得开发人员用较少的性能开销,便可实现 LOB 到数据库的简单读写。

为了展示 ODP.NET 处理 BLOB (二进制大对象)和 CLOB (字符型大对象)的能力,我们将逐步演示一个简单的示例。在这个例子中,我们将打开一个照片文件并将其写入到数据库中。对于这一过程的前两步(创建和打开一个数据库连接,并创建一个命令)使用上面例子所示的相同方式来完成。

下面,我们必须创建一个事务:

OracleTransaction trans = dbConn.BeginTransaction();


在更新某个 BLOB 的时侯,需要执行完一个事务中的所有操作以便维护数据的完整性(如果正要准备检索一个 BLOB 数据库对象,则不需要创建一个事务)。要创建一个本地事务,调用 OracleConnection 对象的BeginTransaction() 方法。这样就返回了一个 OracleTransaction 对象,我们将该对象指定给一个变量以备以后使用。

现在,我们打开该记录并获得 OracleBlob 对象:

cmd.CommandText = "select * from dejavu_employee where " +
"id = 1 for update";

OracleDataReader reader = cmd.ExecuteReader();
reader.Read();
OracleBlob lob = reader.GetOracleBlob(2); // ordinal position of blob


我们检索 OracleBlob 对象,并将其注入照片文件中。首先,我们选择想要的记录 — 在这个例子中,就是 ID 号为 1 的员工记录。由于我们将更新这个 BLOB,所以数据库的行必须是锁定的。我们通过在 SELECT 语句中使用更新子句来执行这一操作。

下面,我们打开一个 OracleDataReader 并调用 Read() 方法移动到第一条记录。最后,我们从OracleDataReader 中获得 OracleBlob 对象。这一操作使用 OracleDataReader.GetOracleLob() 方法来完成。

现在,我们就可以将照片放到服务器上了,如列表 3 所示。应用程序会提示用户输入想要上载到数据库中的文件名。对于这个任务,我们在 Visual Studio 表单设计器中创建的一个 OpenFileDialog 对象 (在我们的例子中就是 ofdPicture )。下面,我们用 System.IO.FileStream 对象打开文件,然后继续从FileStream 对象中将数据复制到 OracleBlob 对象。后者使用一个 WHILE 循环通过 Read() 和 Write()方法 来实现。

使用 OracleClob 对象和使用 OracleBlob 对象是非常相似的。在读取 CLOB 数据库对象时,可以使用OracleClob 对象从数据库读取字符型数据。 OracleClob 对象覆盖 Read() 方法,并向开发人员提供使用 CLOB 置入一个字符数组或字符串的方法。

ODP.NET 还提供一个和 OracleBlob 类似的对象,用于从 Oracle 数据库中读取 BFile 数据库。Bfile 是存储在数据库之外文件系统中的 LOB,对于存储特别大的文件非常有用。

Ref Cursor 和存储过程。 可以通过存储过程打开 Ref Cursor,并通过一个输出参数将其传送到应用程序。

要更好地理解 Ref Cursor 在 ODP.NET 中的用法,可以迅速浏览列表 4 中的 PL/SQL 码。这段代码根据输入参数 n_empid 和 n_empid2 ,从 DEJAVU_EMPLOYEE 表中检索到两条记录。然后通过输出参数 io_cursor和 io_cursor2 返回 Ref Cursor。

开发人员会发现使用 ODP.NET 编写 Ref Cursor 非常容易,因为他们可以使用游标的输出参数(如io_cursor 和 io_cursor2 ),如同 OracleDataReaders 一样。要检索某个 Ref Cursor,需要首先打开一个数据库连接,如同第一个示例中所示。下面,我们要创建一个命令调用存储过程并传递必要的参数,如列表 5 所示。在这个列表中,我们使用一个已熟悉的对象(即 OracleCommand )来调用存储过程并获得结果 Ref Cursor。但是,我们使用存储过程的输出参数,而不是使用 SQL 语句从数据库中获得记录。

让我们逐步演示一下列表 5 中的过程。首先,在第 1 行,我们创建一个新的 OracleCommand 对象。但是,我们是通过提供存储过程的名称来调用该存储过程,而不是使用包含 SQL 语句的一个字符串 — 在我们的例子中,就是 curspkg.open_two_cursor。

下面,在第 2 行,我们将 OracleCommand 对象指定为存储过程调用,而不是 SQL 语句调用。要指定这个命令是调用存储过程,必须正确地指定 OracleCommand 对象的 CommandType 的属性。 CommandType 属性有很多可接受的值,可以通过 CommandType 枚举对象访问这些值。在这个例子中,我们使用CommandType.StoredProcedure。

第 3 行和第 4 行创建了两个必需的参数变量以便访问返回的 Ref Cursor。我们将使用这些变量从数据库的存储过程获得对 Ref Cursor 结果的引用。

第 5 行到第 8 行向 OracleCommand 对象添加四个必需的参数。我们为 cmd.Parameters.Add() 方法同一行中的两个 n_empid 参数创建 OracleParameter 对象。

OracleParameter 构造符接收两个参数。第一个是 PL/SQL存储过程参数名;这就意味着可以灵活地添加参数,而不是按照声明存储过程参数的顺序添加。第二个参数是 PL/SQL 存储过程参数(即 Value 属性)应该被解释成的数据类型; Value 参数的可接受类型位于 OracleDbType 枚举对象中。这两个构造符参数同时告诉 OracleParameter 和 OracleCommand 对象如何调用存储过程,以及如何解释从该存储过程返回的值。

接下来的步骤
下载 ODP.NET
www.oracle.com/technetwork/cn/topics/windows/index-088718-zhs.html

ODP.NET 示例代码
/global/cn/sample_code/tech/windows/odpnet

参加 ODP.NETiSeminar
oracle.com/iseminars
使用 Oracle Data Provider for .NET

在我们的示例中,我们向 OracleCommand 对象添加了四个 OracleParameter 对象。其中的两个是 n_empid 和 n_empid2 输入 参数,这两个参数是 OracleDbType.Int64 类型,用于open_two_cursor 存储过程。我们还需要使用OracleParameter 对象的 Value 属性,将参数的值分别设为 1 和 2。

第三个和第四个参数是 open_two_cursor 存储过程的输出参数。在这个例子中,就是io_cursor 和 io_cursor2 。对于 io_cursor 参数,我们并不向存储过程传递某个值,而是希望存储过程能返回一个 Ref Cursor。要处理这一步,我们添加一个 OracleParameter 对象作为OracleDbType.RefCursor 类型。要让 OracleCommand 对象知道这是一个输出参数,需要将OracleParameter 对象的 Direction 属性设置为 ParameterDirection.Output。

下面,执行存储过程并检索结果,如列表 6 所示。在该列表的第 1 行,我们对 OracleCommand 对象调用ExecuteNonQuery() 方法。该方法调用存储过程并将结果读回到正确的 OracleParameter 对象。

在第 2 行和第 3 行,我们将返回值抛入到 OracleRefCursor 对象。在 ODP.NET 执行回传某个值的存储过程时,会将那些值存储在与之相关联的 OracleParameter 对象的 Value 属性中。

从第 4 行到第 9 行,我们创建 OracleDataReader 对象,将它们设置为 Ref Cursor 结果,调用 Read() 方法移动到第一个记录,并从 OracleDataReader 对象中检索姓名列。

OracleDataReader 对象还包含了一个名为 NextResult() 的方法,该方法可以与方法ExecuteReader() 联合使用,顺序地迭代来自存储过程的所有 Ref Cursor 结果。利用 NextResult() 方法,我们仅需要使用一个 OracleDataReader 对象来访问所有的 Ref Cursor。然而,这样做会限制我们顺序地访问 Ref Cursor。访问多个 Ref Cursor 结果所使用的方法将取决于应用程序的需要。

总结

Oracle Data Provider for .NET提供一个易于使用的、强大的 API,并且与 OLE DB .NET 相比,性能得到巨大的改善。如果希望在 .NET 框架中开发应用程序访问新的和现有的 Oracle 数据库,那么熟悉这一技术会使您的工作更轻松。

Robert P. Lipschutz (rob@thing7.com) 是 Thing 7 的总裁,该公司擅长编写技术白皮书和设计原型,而Gregg D. Harrington (gregg@thing7.com ) 是 Thing 7 的高级设计师。

Table 1:DEJAVU_EMPLOYEE table

字段类型目的
IDNUMBER(10)保留员工的 ID(这是该数据库的主关键字)
NAMEVARCHAR2(32)保留员工的姓名
PHOTOBLOB保留员工的照片
BIGNUMBERNUMBER(38,37)保留一个高精度数字
Table 2:比较几种常用 Oracle 连接的
基本选项命令

操作ODP.NETOLE DB .NETORACLE 瘦 JDBC 驱动器
连接到OracleConnectionOleDbConnectionOracleConnection database
创建 SQLOracleCommandOleDbCommandOracleStatement commands
读取数据OracleDataReaderOleDbDataReaderOracleResultSet
Microsoft's .NET Framework Data Provider for Oracle
Microsoft 也发布了一个 Oracle 专用的数据库驱动器,名为 .NET Framework Data Provider for Oracle。由于针对 .NET 的 Oracle 数据库的连接极其重要,因此 Oracle 和 Microsoft 都来处理这一问题就不足为奇了。正如 Oracle 的 ODP.NET 一样,Microsoft 的产品比 OLE DB .NET提供更多 Oracle 专用的功能和更好的性能。然而,Microsoft 的提供程序总得来说还是不如 ODP.NET 的功能多。

例如,ODP.NET API 可以支持事务保存点、本地 Ref Cursor 数据类型、代理服务器用户认证和全球化特性。Microsoft 的解决方案提供了一个 OracleLob 对象,该对象以相同的方式控制所有的 Oracle LOB 数据库。然而,ODP.NET 对 Oracle BLOB 和 CLOB 数据库对象都提供特定的对象。这就意味着 ODP.NET 对这些 Oracle 数据类型提供更好的支持。例如, OracleClob 对象可覆盖 read 方法置入一组字符数组。这一特性在处理 Unicode 和其他多字节字符时特别有用。

Microsoft 的解决方案和 ODP.NET 都比 OLE DB .NET 提供更好的数据库访问性能,最显著的是写操作和 LOB 操作。Microsoft 计划将其新版本的 .NET Framework Data Provider for Oracle 包含在 Visual Studio .NET 的下一个发布版中。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: