数学建模社区-数学中国

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

作者: 韩冰    时间: 2004-10-3 21:16
标题: 通过.NET访问 Oracle数据库
长期以来,我一直用的是 MS SQL Server / Access 数据库,通过.NET 访问MS自家的东西几乎没碰到过什么麻烦。最近项目中要用 Oracle 作为数据库,学习研究了一些 .NET 访问Oracle 的东西,发现问题倒真的不少。
/ t! f1 \* d5 v2 k5 J, \- B<>' v2 Y& d6 G/ ^5 |3 H: K& u+ l

; Z/ ~7 ~- S. }0 o; A, d3 A( \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>
. ^( d% j) r$ f8 K1 J2 W<>  虽然通过这两个命名空间的类都可以访问 Oracle 数据库,但和 SQL Server 类似的(System.Data.SqlClient 命名空间的类效率要比 System.Data.OleDb 命名空间中的类高一些),System.Data.OracleClient 命名空间中的类要比 System.Data.OleDb 命名空间的类效率高一些(这一点我没有亲自验证,但大多数地方都会这么说,而且既然专门为 Oracle 作的东西理论上也应该专门作过针对性的优化)。</P>
! q& F3 p2 N- n<>  当然还有另一点就是从针对性上说,System.Data.OracleClient 要更好一些:</P>
6 s6 n+ a: y- {/ @, j$ U8 M+ g. x<>  比如数据类型,System.Data.OleDb.OleDbType 枚举中所列的就没有 System.Data.OracleClient.OracleType 枚举中的那些有针对性;另外,Oracle 的Number 类型如果数字巨大,超出 .NET 数据类型范围的情况中,就必须使用System.Data.OracleClient 中的专门类 -- OracleNumber 类型。</P>1 H2 Z" }5 t9 g4 c2 q. N% S
<>  好了,不再赘述这两个的比较,下面主要讨论System.Data.OracleClient 命名空间中的类型,即 ADO.NET for Oracle Data Provider (数据提供程序)。</P>
, v6 a- h, C  {, n" |% W8 r& Z<>2。数据库连接:</P>
$ b2 h6 f% i4 M: I! C, _- y8 o<>  无论是 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>
9 c6 F$ W2 \7 x# O<>System Requirements:</P>3 Q$ ~- I, s. Q' w$ r( e
<>  (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>0 s' A* C- ?: @' L0 V# Z
<>  (2)如用 System.Data.OleDb 访问 Oracle,客户端组件版本 7.3.3.4.0 以上或 8.1.7.4.1 以上。MDAC 2.6 以上。</P>& _! V% ?& `2 p1 `% u5 L
<>  如服务器为 Oracle8i 以上,客户端组件版本应为 8.0.4.1.1c。</P>4 N, X7 i- L+ r9 v- R3 o0 Q
<>  在 .NET 运行的机器中,安装 Oracle 客户端,然后打开 Net Manager (Oracle 9i) / Easy Config (Oracle 8i) 按你以前的经验设置本地服务的映射(这里的服务名将用于数据库连接串)。</P>
, [& P$ I" U3 p<>  System.Data.OracleClient 中访问 Oracle 数据库的连接串是:</P>
! }  L: T! O; t" e<>User ID=用户名; Password=密码; Data Source=服务名</P>
5 T- T& I, s5 p* A2 U2 N<>  (上述为一般的连接串,详细的连接串项目可以在 System.Data.OracleClient.OracleConnection.ConnectionString 属性的文档中找到。)</P>% S" k8 x! C; o
<>  System.Data.OleDb 中的访问 Oracle 数据库的连接串是:</P>
1 a0 b$ a% u, M' Q5 [9 Z<>rovider=MSDAORA.1; User ID=用户名; Password=密码; Data Source=服务名</P>$ o, T4 t' |$ W- V
<>3。Oracle 中的数据类型:</P>
! U+ o* M* q. g$ F5 h7 O9 P<>  Oracle 的数据类型和 SQL Server 相比,要“奇怪”一些:SQL Server 的大多数据类型很容易找到 .NET 中比较接近的类型,Oracle 中的类型就离 .NET 类型远了许多,毕竟 Oracle 是和 Java 亲近的数据库。</P>1 c7 p( N; N9 G# {
number: 数字类型,一般是 Number(M,N),M是有效数字,N是小数点后的位数(默认0),这个是按十进制说的。   ?# @: ?; b5 }
nvarchar2: 可变长字符型(Unicode),这个比较像 SQL Server 的 nvarchar(但不知 Oracle 为什么加了个“2”)。(去掉“n”为非 Unicode 的,下同。)
* n2 R+ B& a. i* n: Q0 @& ?+ F3 t0 ]9 _nchar: 定长字符型(Unicode)。
( w4 l1 ~) t1 r/ R* d8 T( }. U+ Wnclob: “写作文”的字段,存储大量字符(Unicode)时用。 0 \3 A. |  [5 ~8 A) _- ~2 T7 E( v
date: 日期类型,比较接近 SQL Server 的 datetime。1 v) O" x: z# h; L2 [
<>  Oracle 中字段不能是 bit 或者 bool 之类的类型,一般是 number(1) 代替的。</P>9 i2 `- J$ ~- i  i4 M' K
<>  和 SQL Server 一样在 SQL 命令中,字符类型需要用单引号(')隔开,两个单引号('')是单引号的字符转义(比如: I'm fat. 写入一个 SQL 命令是: UPDATE ... SET ...='I''m fat.' ...)。</P>
- J" V4 x4 ?. [/ G1 v* }<>  比较特殊的是日期类型:比如要写入 2004-7-20 15:20:07 这个时刻需要如下写:</P>
' @) E" a& D9 S+ `4 W<>UPDATE ... SET ... = TIMESTAMP '2004-7-20 15:20:07' ...</P>
5 Q8 R7 ^8 p# K9 A/ j$ e<>注意这里使用了 TIMESTAMP 关键字,并使用单引号隔开;另外请注意日期格式,上面的格式是可识别的,Oracle 识别的格式没有 SQL Server 那般多。这是和 SQL Server 不同的地方。</P>) A* R2 E8 S3 [/ p2 D
<>顺便提一句:Access 中的日期类型是用井号(#)隔开的,UPDATE ... SET ... = #2004-7-20 15:20:07# ...</P>) J' h2 K$ h$ f2 u6 S3 B
<>4。访问 Oracle 过程/函数(1)</P>
3 m* |* l1 {, v8 I<>  SQL Server 作程序时经常使用存储过程,Oracle 里也可以使用过程,还可以使用函数。Oracle 的过程似乎是不能有返回值的,有返回值的就是函数了(这点有些像 BASIC,函数/过程区分的很细致。SQL Server 存储过程是可以有返回值的)。</P>9 m! L( j2 P3 z( b  i9 O1 p: @
<>.NET 访问 Oracle 过程/函数的方法很类似于 SQL Server,例如:</P>  ]  l; T& K# s
<>OracleParameter[] parameters = {
3 o0 ]+ n! T! s! e( M    new OracleParameter("ReturnValue", OracleType.Int32, 0, ParameterDirection.ReturnValue, true, 0, 0, "",
# S. l8 F$ U9 g- m7 u5 G. |         DataRowVersion.Default, Convert.DBNull )8 K" l+ n* m9 F
    new OracleParameter("参数1", OracleType.NVarChar, 10),
' N' f+ o9 y% m& i1 Y    new OracleParameter("参数2",  OracleType.DateTime),
: R, x1 [# p) x0 `& w    new OracleParameter("参数3",  OracleType.Number, 1)
4 ~# g; Z( @/ `6 E. X) v };
; `, _* E3 s9 t' F1 g" E
( t+ A; j4 x1 j+ a4 l/ T6 ~parameters[1].Value = "test";
& C/ g( ~5 t/ Dparameters[2].Value = DateTime.Now;
. V. H5 F9 }' W( Z/ v: V) t( {parameters[3].Value = 1;                        // 也可以是 new OracleNumber(1);</P>
# w) E! N2 o$ M" u3 j5 u<P>OracleConnection connection = new OracleConnection( ConnectionString );' d& \& ~+ r) v+ L1 m0 ~5 k
OracleCommand command = new OracleCommand("函数/程名", connection);
$ u* ~( H1 H: o0 T5 ^command.CommandType = CommandType.StoredProcedure;  X3 O! m: F! W

! W+ {2 w& S3 dforeach(OracleParameter parameter in parameters)
7 A4 r. U5 m& q     command.Parameters.Add( parameter );
* R: J- [! I1 w- b- E/ d9 t/ H
6 }; z. a% ^; e7 R$ Hconnection.Open();
8 I; i+ }: [: H1 h0 ^command.ExecuteNonQuery();
2 N. }% S8 Z+ X/ a: a- yint returnValue = parameters[0].Value; //接收函数返回值1 f3 j. R% m5 ~8 y
connection.Close();</P>
5 ]" p6 a7 ^9 O) H& c5 i6 ^3 H<P>  Parameter 的 DbType 设定请参见 System.Data.OracleClient.OracleType 枚举的文档,比如:Oracle 数据库中 Number 类型的参数的值可以用 .NET decimal 或 System.Data.OracleClient.OracleNumber 类型指定; Integer 类型的参数的值可以用 .NET int 或 OracleNumber 类型指定。等等。</P>
" c5 y$ J0 {; B& R<P>  上面例子中已经看到函数返回值是用名为“ReturnValue”的参数指定的,该参数为 ParameterDirection.ReturnValue 的参数。</P>7 q( E* K* _8 c7 u
<P>5。访问 Oracle 过程/函数 (2)</P>' v8 j3 @& `4 z1 j% [
<P>  不返回记录集(没有 SELECT 输出)的过程/函数,调用起来和 SQL Server 较为类似。但如果想通过过程/函数返回记录集,在 Oracle 中就比较麻烦一些了。</P>& w. y) p. q  @  {- H
<P>在 SQL Server 中,如下的存储过程:</P>( e( ^1 W; @% q( ]1 w$ S
<P>CREATE PROCEDURE GetCategoryBooks
# G/ l( b6 Z0 X% O! F(
. O' ]5 ~7 w3 v% h1 K3 B$ P    @CategoryID int6 d. n2 T: X/ F9 Z2 H! F
)* |% q$ }, Y' q! U) z2 d  l
AS& k3 r, ^" E- q+ O4 }; y9 N" d: C/ y; S
SELECT * FROM Books
- R' Z' w/ ?  H" P, J! s6 S; n1 J9 mWHERE CategoryID = @CategoryID
, d+ h3 p2 N% w& wGO</P>* F4 u* H/ l% R$ r
<P>  在 Oracle 中,请按以下步骤操作:</P>
+ \1 V6 ]! Z" z9 w<P>(1)创建一个包,含有一个游标类型:(一个数据库中只需作一次)</P>
4 Q7 v7 j" _  b# E<P>CREATE OR REPLACE PACKAGE Test* c, j: N! P3 ~! h* n# V
  AS
3 R  {3 N& x2 J! f- s       TYPE Test_CURSOR IS REF CURSOR;- E) \) M" j5 t( y
END Test;</P>
" Y$ S% T  E* i7 e2 \<P>(2)过程:</P>& k5 ?7 ^6 h( O  S) o0 \$ b4 z
<P>CREATE OR REPLACE PROCEDURE GetCategoryBooks
2 j# @4 L, F; k; y/ H4 c, s/ O. ?(* ?* h" v9 h" V" M: S
     p_CURSOR out Test.Test_CURSOR,    -- 这里是上面包中的类型,输出参数
# {7 H) _/ V  Y( I     p_CatogoryID INTEGER
4 O% }& n' h) J) v7 H/ N: a# D)
1 v3 I7 ^! O# G$ c1 AAS, j+ z; j/ l' L) S. A8 N/ N( s
BEGIN% F# N, D! f* e& B
     OPEN p_CURSOR FOR
4 K3 G, g  S3 z6 r           SELECT * FROM Books- k' @2 R( c3 D5 {! o: k
           WHERE CategoryID=p_CatogoryID;4 S: y$ s0 R# Z* s5 K
END GetCategoryBooks;</P>
( y1 B" ]) O* f<P>(3).NET 程序中:</P>
. p) w; }# M+ ]3 o/ p<P>OracleParameters parameters = {8 [+ y; L4 g- x4 x+ P7 R% `5 L
     new OracleParameter("p_CURSOR", OracleType.CURSOR, 2000, ParameterDirection.Output, true, 0, 0, "",( V4 L0 k% c4 f( H
          DataRowVersion.Default, Convert.DBNull),' S% p" n8 ~7 b+ Z4 ?8 P. z* u
     new OracleParameter("p_CatogoryID", OracleType.Int32)
) x" w0 b! Y5 O" x# `};/ o' |' n4 A8 c
- S1 c: i# G; P8 C. x7 M& C
parameters[1].Value = 22;- ]4 R# G; n$ {" I" T
: Q* S( {# A& q/ z+ v
OracleConnection connection = new OracleConnection( ConnectionString );! B; O& c+ U0 ~
OracleCommand command = new OracleCommand("GetCategoryBooks", connection);
' d, U, c: M) {command.CommandType = CommandType.StoredProcedure;
) e$ {" p0 f1 z4 m0 n$ Z8 r2 x& p5 l7 F8 ~) p. X2 j4 k. z7 h% M' K- T
foreach(OracleParameter parameter in parameters)
) b& ]# ?9 V  e* `1 [& x& v0 v& N     command.Parameters.Add( parameter );
  a. Z! Z) a4 u1 B% I. y
* _, V+ o$ K8 m/ y+ f/ @( _connection.Open();
% O) T8 s4 t9 D3 S9 eOracleDataReader dr = command.ExecuteReader();9 _  O* J( a7 O# D/ S4 _" a

! }% i3 W7 C4 ^while(dr.Read())( J: H5 \8 c9 e7 ~8 x, I
{+ K: J* s( n% p% E
    // 你的具体操作。这个就不需要我教吧?
& [* r4 A4 K5 v- m6 t}: [# V$ J* B* X8 b
connection.Close();</P>
/ S8 j" X* B& d& T% q7 ^<P>  另外有一点需要指出的是,如果使用 DataReader 取得了一个记录集,那么在 DataReader 关闭之前,程序无法访问输出参数和返回值的数据。</P>: V+ ~$ f, R% `# z! Y" |8 R& k
<P>  好了,先这些,总之 .NET 访问 Oracle 还是有很多地方和 SQL Server 不同的,慢慢学习了。</P>+ Y  S( X9 @  V5 V  e0 v
<P>
作者: ilikenba    时间: 2004-10-18 22:31
好文章,顶一下!




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