数学建模社区-数学中国

标题: 通过.NET访问 Oracle数据库 [打印本页]

作者: 韩冰    时间: 2004-10-3 21:16
标题: 通过.NET访问 Oracle数据库
长期以来,我一直用的是 MS SQL Server / Access 数据库,通过.NET 访问MS自家的东西几乎没碰到过什么麻烦。最近项目中要用 Oracle 作为数据库,学习研究了一些 .NET 访问Oracle 的东西,发现问题倒真的不少。 2 p* e8 l+ `8 g  e1 \" y3 S- ^9 i
<>
' t% }% `5 h$ i( g
  Q/ _; ~! h2 r" u/ n5 B8 x4 @flash/swflash.cab#version=5,0,0,0 height=280 width=320 classid=clsid27CDB6E-AE6D-11cf-96B8-444553540000&gt;200309/guangli_320.swf"&gt;200309/guangli_320.swf"&gt; 200309/guangli_320.swf" width=320 height=280 type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash"&gt;1。System.Data.OracleClient 和 System.Data.OleDb 命名空间</P>, Q9 M% W' u; m
<>  虽然通过这两个命名空间的类都可以访问 Oracle 数据库,但和 SQL Server 类似的(System.Data.SqlClient 命名空间的类效率要比 System.Data.OleDb 命名空间中的类高一些),System.Data.OracleClient 命名空间中的类要比 System.Data.OleDb 命名空间的类效率高一些(这一点我没有亲自验证,但大多数地方都会这么说,而且既然专门为 Oracle 作的东西理论上也应该专门作过针对性的优化)。</P>: [; l& ~$ _* `' \* r+ d( F
<>  当然还有另一点就是从针对性上说,System.Data.OracleClient 要更好一些:</P>! e9 d1 b2 V6 M9 _2 k
<>  比如数据类型,System.Data.OleDb.OleDbType 枚举中所列的就没有 System.Data.OracleClient.OracleType 枚举中的那些有针对性;另外,Oracle 的Number 类型如果数字巨大,超出 .NET 数据类型范围的情况中,就必须使用System.Data.OracleClient 中的专门类 -- OracleNumber 类型。</P>6 g$ f; s+ f7 g
<>  好了,不再赘述这两个的比较,下面主要讨论System.Data.OracleClient 命名空间中的类型,即 ADO.NET for Oracle Data Provider (数据提供程序)。</P>2 [* i& O  G. f. B; t
<>2。数据库连接:</P>' v+ p5 |9 ^  g2 y, ]) U0 ]. q
<>  无论是 System.Data.OleDb 还是 System.Data.OracleClient 访问 Oracle 都需要在 .NET 运行的机器(ASP.NET 中就是 Web 服务器)安装 Oracle 客户端组件。(这一点是和 MS 的两种数据库不同的,MS 的东西安装 MDAC: Microsoft Data Access Component 2.6 以上版本后,就无须再安装 SQL Server 客户端或者 Office 软件,就能访问。)</P>
& T) Y3 _6 }) R& |<>System Requirements:</P>
1 T2 o7 V" ~' |: s; V+ p% o<>  (1)如用 System.Data.OracleClient 访问 Oracle,客户端组件版本应在 Oracle 8i Client Release 3 (8.1.7)以上版本。MS 只确保访问 Oracle 8.1.6、Oracle 8.1.7、Oracle 9i 服务器时的情况。MDAC 2.6 以上。</P>
6 {: q0 c; T5 j& s<>  (2)如用 System.Data.OleDb 访问 Oracle,客户端组件版本 7.3.3.4.0 以上或 8.1.7.4.1 以上。MDAC 2.6 以上。</P>
. t- F  c" o6 }# F<>  如服务器为 Oracle8i 以上,客户端组件版本应为 8.0.4.1.1c。</P>- ^7 B3 e& q. ~1 I0 C& o% q0 @
<>  在 .NET 运行的机器中,安装 Oracle 客户端,然后打开 Net Manager (Oracle 9i) / Easy Config (Oracle 8i) 按你以前的经验设置本地服务的映射(这里的服务名将用于数据库连接串)。</P>8 [5 o4 ?, o( s5 K  x9 |
<>  System.Data.OracleClient 中访问 Oracle 数据库的连接串是:</P>4 @6 ?) q, g: F$ Q- e5 a
<>User ID=用户名; Password=密码; Data Source=服务名</P>& A7 S7 t5 A9 o: Y. }* J! I7 C
<>  (上述为一般的连接串,详细的连接串项目可以在 System.Data.OracleClient.OracleConnection.ConnectionString 属性的文档中找到。)</P>
8 v# @1 q+ i8 k4 J* E<>  System.Data.OleDb 中的访问 Oracle 数据库的连接串是:</P>
: G( t( Z5 M* g# A, k7 P$ `<>rovider=MSDAORA.1; User ID=用户名; Password=密码; Data Source=服务名</P>
9 ~2 S0 K: m+ H3 q: ]2 H<>3。Oracle 中的数据类型:</P>6 Z* _0 R+ U& x7 I. C! R# S
<>  Oracle 的数据类型和 SQL Server 相比,要“奇怪”一些:SQL Server 的大多数据类型很容易找到 .NET 中比较接近的类型,Oracle 中的类型就离 .NET 类型远了许多,毕竟 Oracle 是和 Java 亲近的数据库。</P>4 Q& u6 z* |9 J8 a4 M) D
number: 数字类型,一般是 Number(M,N),M是有效数字,N是小数点后的位数(默认0),这个是按十进制说的。
- s. T4 [8 L/ j4 znvarchar2: 可变长字符型(Unicode),这个比较像 SQL Server 的 nvarchar(但不知 Oracle 为什么加了个“2”)。(去掉“n”为非 Unicode 的,下同。)   ^6 E4 j6 k9 n0 p, t" O& F
nchar: 定长字符型(Unicode)。
7 `  E, \, g; J2 Z0 N% F8 D. {nclob: “写作文”的字段,存储大量字符(Unicode)时用。 , p' C# o9 ?3 t/ }- e8 l5 Y
date: 日期类型,比较接近 SQL Server 的 datetime。
4 u* h2 a* B2 I! m4 ]<>  Oracle 中字段不能是 bit 或者 bool 之类的类型,一般是 number(1) 代替的。</P>
- D$ `' F6 R; z' H% K3 P<>  和 SQL Server 一样在 SQL 命令中,字符类型需要用单引号(')隔开,两个单引号('')是单引号的字符转义(比如: I'm fat. 写入一个 SQL 命令是: UPDATE ... SET ...='I''m fat.' ...)。</P>3 G- P( z) \$ k  q; Q8 [. c% }0 b- F; \
<>  比较特殊的是日期类型:比如要写入 2004-7-20 15:20:07 这个时刻需要如下写:</P>! P: d7 ?% ]* }0 E8 U
<>UPDATE ... SET ... = TIMESTAMP '2004-7-20 15:20:07' ...</P>
7 c% X" X! M% K+ r' a. D<>注意这里使用了 TIMESTAMP 关键字,并使用单引号隔开;另外请注意日期格式,上面的格式是可识别的,Oracle 识别的格式没有 SQL Server 那般多。这是和 SQL Server 不同的地方。</P>
4 C7 u: d2 C. F* n% ?; g" m, ^# g<>顺便提一句:Access 中的日期类型是用井号(#)隔开的,UPDATE ... SET ... = #2004-7-20 15:20:07# ...</P>6 z- ?/ j" W9 V+ V4 E2 I& o3 e
<>4。访问 Oracle 过程/函数(1)</P>
. J) y4 X6 Y/ t$ X5 Z<>  SQL Server 作程序时经常使用存储过程,Oracle 里也可以使用过程,还可以使用函数。Oracle 的过程似乎是不能有返回值的,有返回值的就是函数了(这点有些像 BASIC,函数/过程区分的很细致。SQL Server 存储过程是可以有返回值的)。</P>
: D# y$ J7 M& C5 M% [<>.NET 访问 Oracle 过程/函数的方法很类似于 SQL Server,例如:</P>
8 ~. Z( z) S+ P5 }8 d& W- J<>OracleParameter[] parameters = {
3 T" ^. S2 E1 S    new OracleParameter("ReturnValue", OracleType.Int32, 0, ParameterDirection.ReturnValue, true, 0, 0, "",
0 c$ a. `8 j. d+ w; A5 L( p         DataRowVersion.Default, Convert.DBNull )7 s' @( t" Y0 `0 y7 [. u( u0 o
    new OracleParameter("参数1", OracleType.NVarChar, 10),
8 s8 @4 P: @8 L+ D* v( D, W    new OracleParameter("参数2",  OracleType.DateTime),
8 k( k9 k9 p  Y, w7 Z8 i2 Y    new OracleParameter("参数3",  OracleType.Number, 1)
& c9 ~) {% M; M! w };3 j* m& T( h. ]: h
( x" C: O% K& Q0 e# S) u
parameters[1].Value = "test";  A( l1 @3 G! m  K& ?# \
parameters[2].Value = DateTime.Now;
% z! X0 z, w* Y+ P% b- @parameters[3].Value = 1;                        // 也可以是 new OracleNumber(1);</P>
. w7 t0 `- d0 L; |. C9 a- K<P>OracleConnection connection = new OracleConnection( ConnectionString );
& ?$ J: ^' J# |( ?) uOracleCommand command = new OracleCommand("函数/程名", connection);
3 {: O! P. v' s6 ]command.CommandType = CommandType.StoredProcedure;* ?2 y8 M5 D) F; y& r  ?$ @2 P

% N. z, G4 X/ Z4 k( ]- `0 sforeach(OracleParameter parameter in parameters)
( ^$ z5 Q- s  O& z& x& z     command.Parameters.Add( parameter );" p3 D9 `* z1 K! Z# L

3 a5 Q7 s  i2 q" k+ m3 p4 oconnection.Open();$ ]& B. I$ v' ]  x
command.ExecuteNonQuery();5 q% N8 D6 [5 I( l
int returnValue = parameters[0].Value; //接收函数返回值
$ b# W- u4 E  Z" A, l) W( ~. Qconnection.Close();</P>
% b& A% a7 F* D$ w<P>  Parameter 的 DbType 设定请参见 System.Data.OracleClient.OracleType 枚举的文档,比如:Oracle 数据库中 Number 类型的参数的值可以用 .NET decimal 或 System.Data.OracleClient.OracleNumber 类型指定; Integer 类型的参数的值可以用 .NET int 或 OracleNumber 类型指定。等等。</P>) G* o6 z% N% {3 N6 ]- |! h6 g2 G
<P>  上面例子中已经看到函数返回值是用名为“ReturnValue”的参数指定的,该参数为 ParameterDirection.ReturnValue 的参数。</P>7 M. d& _- `2 N2 J! N0 T
<P>5。访问 Oracle 过程/函数 (2)</P>
$ }' l5 e6 ~' s' i- X! @8 Y<P>  不返回记录集(没有 SELECT 输出)的过程/函数,调用起来和 SQL Server 较为类似。但如果想通过过程/函数返回记录集,在 Oracle 中就比较麻烦一些了。</P>$ b9 r* b5 H* C6 w8 L
<P>在 SQL Server 中,如下的存储过程:</P>
  z# a: g5 l7 c6 a- {<P>CREATE PROCEDURE GetCategoryBooks
! v3 ^: P$ K, v$ q+ @1 v(. A8 C8 \) _5 y5 g1 g& {
    @CategoryID int
# ]$ _! J& l+ _) @; ])% O& W  G! X8 D0 f- Z3 s
AS
# Q' W( |+ B& }8 r- xSELECT * FROM Books
* q* Y$ Q/ _! e3 ?9 bWHERE CategoryID = @CategoryID
2 ~9 _  k* E: |GO</P>
3 ~( y! u+ e4 F2 R1 \<P>  在 Oracle 中,请按以下步骤操作:</P>/ u$ L. y  S6 p, o
<P>(1)创建一个包,含有一个游标类型:(一个数据库中只需作一次)</P># N8 k; Q+ O2 V  r7 A1 ^5 o
<P>CREATE OR REPLACE PACKAGE Test
0 m7 x, @, ?, G/ V6 x' N; I) T- H  AS6 ]1 t, \" A3 y; p+ [
       TYPE Test_CURSOR IS REF CURSOR;
8 l4 ]4 G( K+ t' f& e; Y/ jEND Test;</P>- y7 L$ ^4 t/ ^$ I6 ]5 J
<P>(2)过程:</P>5 R9 T' U2 F' [3 t# O( p- j* b3 J
<P>CREATE OR REPLACE PROCEDURE GetCategoryBooks
. F5 J- O' k% }- [- S(+ S; ]& L  J8 v! f, p( j: d
     p_CURSOR out Test.Test_CURSOR,    -- 这里是上面包中的类型,输出参数
; e7 j6 D8 \, s& Z/ S, ^. n     p_CatogoryID INTEGER+ t) U4 A; _/ j( n0 `7 v
)" c! f6 E7 `$ s' p: Y! B6 ?% J0 c7 D
AS% H0 l8 f) @% E3 k. |2 |
BEGIN
& Y: g* i6 F, `     OPEN p_CURSOR FOR
+ \/ p5 g! l, Z, h: E           SELECT * FROM Books# g0 \) {* m' u
           WHERE CategoryID=p_CatogoryID;  O% Q/ i( V: [
END GetCategoryBooks;</P>
7 e! O+ @% J( q1 K. `( \6 e1 p<P>(3).NET 程序中:</P>  N& M* a: M5 a! g  B% W, j
<P>OracleParameters parameters = {3 t7 k7 K4 }7 v* h
     new OracleParameter("p_CURSOR", OracleType.CURSOR, 2000, ParameterDirection.Output, true, 0, 0, "",1 v! C- v4 z/ P4 w+ m
          DataRowVersion.Default, Convert.DBNull),
1 @! L. m4 j/ p     new OracleParameter("p_CatogoryID", OracleType.Int32)
4 `) b7 e# v. U4 I( }0 d" f};- D& r0 r7 J6 P7 U( O8 Y: w

( k  R0 s; F) \parameters[1].Value = 22;8 u2 J" d: {; v' h$ L6 t7 @
# s! A* w2 c0 k. g; E8 O
OracleConnection connection = new OracleConnection( ConnectionString );+ j- N5 G2 @" {" X  l
OracleCommand command = new OracleCommand("GetCategoryBooks", connection);; g5 X. N' C8 h  N2 G* F
command.CommandType = CommandType.StoredProcedure;) m4 |- W2 L8 H* Q
. I" }1 g7 B: P9 T9 ^
foreach(OracleParameter parameter in parameters)
0 X1 J1 E# k7 |     command.Parameters.Add( parameter );& |1 r# Y3 z) I! J+ ^# T
4 S  p! B4 X6 I0 n; }) O* Z9 M
connection.Open();
- A5 |6 c/ M% eOracleDataReader dr = command.ExecuteReader();  A/ @; w. Z1 S, }6 k( Z3 L2 s

* f2 |( c: u) E( `: e( Rwhile(dr.Read()), S( B4 x/ y& n, f9 G' p. D
{
# }5 E! c/ n. V" t; Q* q3 }    // 你的具体操作。这个就不需要我教吧?' \; R2 z  k9 i
}
% H0 R. m* c$ ~' l. Y  j" y" G& m/ tconnection.Close();</P># Q' v! d  ]" ~$ k* @9 Y. M
<P>  另外有一点需要指出的是,如果使用 DataReader 取得了一个记录集,那么在 DataReader 关闭之前,程序无法访问输出参数和返回值的数据。</P>
$ V6 }$ a6 T) }, o<P>  好了,先这些,总之 .NET 访问 Oracle 还是有很多地方和 SQL Server 不同的,慢慢学习了。</P>* x. p/ f5 h" \* F: Q5 [  u; B
<P>
作者: ilikenba    时间: 2004-10-18 22:31
好文章,顶一下!




欢迎光临 数学建模社区-数学中国 (http://www.madio.net/) Powered by Discuz! X2.5