通过.NET访问 Oracle数据库

enchanter 发布于2004-8-14 19:11 1033 次浏览 1 位用户参与讨论   [复制分享主题]
长期以来,我一直用的是 MS SQL Server / Access 数据库,通过.NET 访问MS自家的东西几乎没碰到过什么麻烦。最近项目中要用 Oracle 作为数据库,学习研究了一些 .NET 访问Oracle 的东西,发现问题倒真的不少。
: t% F7 ?: M' R8 ^8 r2 z5 \& D6 s
+ a: k0 }  C$ A7 J0 t  x! `% Q- Q2 O
1。System.Data.OracleClient 和 System.Data.OleDb 命名空间( C/ F7 C0 m; _7 C9 {1 s+ X

( O1 h. C/ r: L4 [" N  虽然通过这两个命名空间的类都可以访问 Oracle 数据库,但和 SQL Server 类似的(System.Data.SqlClient 命名空间的类效率要比 System.Data.OleDb 命名空间中的类高一些),System.Data.OracleClient 命名空间中的类要比 System.Data.OleDb 命名空间的类效率高一些(这一点我没有亲自验证,但大多数地方都会这么说,而且既然专门为 Oracle 作的东西理论上也应该专门作过针对性的优化)。
1 `, A5 o# }) Q& V
: ^0 V6 x  E! Q8 `6 W* b% q  当然还有另一点就是从针对性上说,System.Data.OracleClient 要更好一些:: N* I$ Z  |$ A2 t) z1 p

+ o+ O% \0 n7 l! i) z, J2 J  比如数据类型,System.Data.OleDb.OleDbType 枚举中所列的就没有 System.Data.OracleClient.OracleType 枚举中的那些有针对性;另外,Oracle 的Number 类型如果数字巨大,超出 .NET 数据类型范围的情况中,就必须使用System.Data.OracleClient 中的专门类 -- OracleNumber 类型。; t# g& J8 t7 l, L
2 l$ Z; M) C2 _% s# N, t8 u) D
  好了,不再赘述这两个的比较,下面主要讨论System.Data.OracleClient 命名空间中的类型,即 ADO.NET for Oracle Data Provider (数据提供程序)。
$ C5 E" N6 D9 t2 Y( @4 B0 Y, J& r' a( W* a
2。数据库连接:
! \# O4 c. {( g/ v7 x3 _/ z+ d2 E, X5 Z* g" }* |7 _
  无论是 System.Data.OleDb 还是 System.Data.OracleClient 访问 Oracle 都需要在 .NET 运行的机器(ASP.NET 中就是 Web 服务器)安装 Oracle 客户端组件。(这一点是和 MS 的两种数据库不同的,MS 的东西安装 MDAC: Microsoft Data Access Component 2.6 以上版本后,就无须再安装 SQL Server 客户端或者 Office 软件,就能访问。)/ `. |2 H# M" O6 m

2 C! }& i9 m3 D& [! Q; w- ~; Y5 m$ \/ z; VSystem Requirements:7 n7 R; t5 o8 H, C

. ?' r( [$ h0 @& t# T  (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 以上。% B5 B# F2 j7 P; ^
4 I! V2 h, H: c# N
  (2)如用 System.Data.OleDb 访问 Oracle,客户端组件版本 7.3.3.4.0 以上或 8.1.7.4.1 以上。MDAC 2.6 以上。4 h# l( ]" Q- b3 I0 `% U0 [
7 d" `: u7 y$ z$ T# S
  如服务器为 Oracle8i 以上,客户端组件版本应为 8.0.4.1.1c。" f% h. j3 ]. F

' y- Q1 P: ^9 X% R0 B  在 .NET 运行的机器中,安装 Oracle 客户端,然后打开 Net Manager (Oracle 9i) / Easy Config (Oracle 8i) 按你以前的经验设置本地服务的映射(这里的服务名将用于数据库连接串)。3 M) d3 J+ [7 B* |0 K) \9 H) B7 E

# G4 g, Q2 u& n, s  System.Data.OracleClient 中访问 Oracle 数据库的连接串是:
* a- D4 U; R+ K# |& Z7 D7 |
2 a: i+ ^6 B0 B: P: X0 X  HUser ID=用户名; Password=密码; Data Source=服务名
* @7 W  `- i' O; p0 s0 o6 [
6 n8 P3 t, q1 s+ C" p& [- }  (上述为一般的连接串,详细的连接串项目可以在 System.Data.OracleClient.OracleConnection.ConnectionString 属性的文档中找到。)
5 @4 n1 @4 w. @3 L5 p
" T/ \, n3 Y: c2 J! m& a  System.Data.OleDb 中的访问 Oracle 数据库的连接串是:
" ]4 E- Q3 {8 V( r2 s- o/ b- y8 y$ \; J1 E2 P2 Y! j
Provider=MSDAORA.1; User ID=用户名; Password=密码; Data Source=服务名
8 l6 @$ L7 J2 h
* @* {+ Z" a- Y3。Oracle 中的数据类型:
0 N$ n$ j! ^; a* v6 U1 L( W* V% Q9 V; M8 X; l
  Oracle 的数据类型和 SQL Server 相比,要“奇怪”一些:SQL Server 的大多数据类型很容易找到 .NET 中比较接近的类型,Oracle 中的类型就离 .NET 类型远了许多,毕竟 Oracle 是和 Java 亲近的数据库。
3 O5 K+ ?. S2 B, c
) N4 a( h1 a& ^& x% |0 X! dnumber: 数字类型,一般是 Number(M,N),M是有效数字,N是小数点后的位数(默认0),这个是按十进制说的。 6 Y, j) _" q6 e( i
nvarchar2: 可变长字符型(Unicode),这个比较像 SQL Server 的 nvarchar(但不知 Oracle 为什么加了个“2”)。(去掉“n”为非 Unicode 的,下同。)
  z: z' Q# g. i' `: xnchar: 定长字符型(Unicode)。
1 @3 m( P2 a8 q+ `- R4 r. M- vnclob: “写作文”的字段,存储大量字符(Unicode)时用。 ! W/ n# d$ Y) j% k. U4 w
date: 日期类型,比较接近 SQL Server 的 datetime。, i1 J+ F  X  z' w# b! a4 D7 O
  Oracle 中字段不能是 bit 或者 bool 之类的类型,一般是 number(1) 代替的。8 |9 J0 m; {% ?
- S" Q3 y1 f! }+ c
  和 SQL Server 一样在 SQL 命令中,字符类型需要用单引号(')隔开,两个单引号('')是单引号的字符转义(比如: I'm fat. 写入一个 SQL 命令是: UPDATE ... SET ...='I''m fat.' ...)。9 D' x: H' u+ T! w9 S' |2 D/ [

7 s) P# ?) U3 W1 M: o  o8 G  比较特殊的是日期类型:比如要写入 2004-7-20 15:20:07 这个时刻需要如下写:; l# F& I7 k, [8 W! _7 O0 f
/ C" t# S9 b2 E5 p3 q: `
UPDATE ... SET ... = TIMESTAMP '2004-7-20 15:20:07' ...1 w9 V  L( F  }1 M" k- }# d

* d: M) ]9 {4 w) a. M$ n注意这里使用了 TIMESTAMP 关键字,并使用单引号隔开;另外请注意日期格式,上面的格式是可识别的,Oracle 识别的格式没有 SQL Server 那般多。这是和 SQL Server 不同的地方。0 m- p$ U& R6 x# m2 n; t. ~
7 U6 V" G# l4 `
顺便提一句:Access 中的日期类型是用井号(#)隔开的,UPDATE ... SET ... = #2004-7-20 15:20:07# ...
8 ?3 {9 q) O6 `4 o7 h" g' g" M/ h  `' I- o8 q
4。访问 Oracle 过程/函数(1)8 f, T) i9 F" _

# }& |( L; A" D  SQL Server 作程序时经常使用存储过程,Oracle 里也可以使用过程,还可以使用函数。Oracle 的过程似乎是不能有返回值的,有返回值的就是函数了(这点有些像 BASIC,函数/过程区分的很细致。SQL Server 存储过程是可以有返回值的)。
( p/ n' Y1 t$ L) a' O/ X- v& |$ i
$ x- l9 u! I$ F) Z* g, T4 D; D- n.NET 访问 Oracle 过程/函数的方法很类似于 SQL Server,例如:
4 k% w  ?/ v9 ^8 o' @* o) ]0 L- k0 W5 }# X5 y, K6 \/ \0 {
OracleParameter[] parameters = {
1 ^/ Z0 F$ n2 g) Q2 _' P, y    new OracleParameter("ReturnValue", OracleType.Int32, 0, ParameterDirection.ReturnValue, true, 0, 0, "",: x) F- V; c9 H3 [
         DataRowVersion.Default, Convert.DBNull ), C5 M; b1 n# [& z
    new OracleParameter("参数1", OracleType.NVarChar, 10),
( L* @- S/ s: H$ b5 b6 I    new OracleParameter("参数2",  OracleType.DateTime),. v' Y  O7 o+ d8 c3 Z  C$ \! N
    new OracleParameter("参数3",  OracleType.Number, 1)% t# }$ i6 w/ V2 X; U
};
- x' `9 Z! b2 L" o, n- U, Z+ K
% s$ T" S2 ], q8 i* pparameters[1].Value = "test";6 t* B% k, D2 z! o4 P
parameters[2].Value = DateTime.Now;
4 U& y, D) ?% k' t" Nparameters[3].Value = 1;                        // 也可以是 new OracleNumber(1);
) ]! j) b+ `5 o2 \/ ~1 _8 E* S2 k1 U* _0 v: @
OracleConnection connection = new OracleConnection( ConnectionString );/ f- l: K' I8 x( c3 W
OracleCommand command = new OracleCommand("函数/程名", connection);8 i; P# Z$ l/ R' H* W6 F: c6 {' Q
command.CommandType = CommandType.StoredProcedure;* f% M% Q4 [2 |+ b7 g

- R6 i6 \0 {* G, n( s' u0 s6 M" L; Z3 h. Iforeach(OracleParameter parameter in parameters)+ t6 i( J5 O. @" w$ g6 S
     command.Parameters.Add( parameter );! i* B. C. n7 \5 a/ ?

" _  K5 e! Q0 \% Sconnection.Open();
) E! K/ K- y# E; Y2 \command.ExecuteNonQuery();
7 K0 _3 {9 P1 A: I' A, Zint returnValue = parameters[0].Value; //接收函数返回值
* h! q6 f1 ?% R3 gconnection.Close();
5 |+ d% u7 N9 C% S0 Z6 a
4 y7 U! w3 D0 T  Parameter 的 DbType 设定请参见 System.Data.OracleClient.OracleType 枚举的文档,比如:Oracle 数据库中 Number 类型的参数的值可以用 .NET decimal 或 System.Data.OracleClient.OracleNumber 类型指定; Integer 类型的参数的值可以用 .NET int 或 OracleNumber 类型指定。等等。
; I7 W  E- ^3 `/ R* B9 s, `+ T9 Y* D! @
  上面例子中已经看到函数返回值是用名为“ReturnValue”的参数指定的,该参数为 ParameterDirection.ReturnValue 的参数。8 b7 N  p+ @' c
0 k9 r& k0 _# |4 X" b
5。访问 Oracle 过程/函数 (2)
7 P) i0 B! Z$ P) i3 p
  P. O2 c/ g- k9 @3 l  不返回记录集(没有 SELECT 输出)的过程/函数,调用起来和 SQL Server 较为类似。但如果想通过过程/函数返回记录集,在 Oracle 中就比较麻烦一些了。
) Z. [* z# y' e9 f; ~5 L$ @. R( N) R  K* X$ V
在 SQL Server 中,如下的存储过程:
# ^/ g1 M2 I$ _1 M* s' [+ O4 y- W
CREATE PROCEDURE GetCategoryBooks
, e, l8 {" L. ~; e$ e$ H  |(" B: a  S  G$ [6 e
    @CategoryID int
2 U% K; }5 q$ h  S! S7 f! e)+ c& H! [' e" H6 i
AS
9 i8 x3 t0 p* b  C6 pSELECT * FROM Books
1 d& G" J- E1 N6 T& H5 UWHERE CategoryID = @CategoryID% C+ V! z4 P& A8 V
GO
( z# g* e# J2 P' b' V3 [2 Z
, G  q+ J% G' d7 r  在 Oracle 中,请按以下步骤操作:. X3 m2 T/ x# ]5 N  ?6 \) n3 \+ E
$ t% _) j0 q( b5 W3 j0 E7 N7 e
(1)创建一个包,含有一个游标类型:(一个数据库中只需作一次)
" ]* F& `1 L! [4 t1 @2 S) q$ I2 Y$ p  i! z
CREATE OR REPLACE PACKAGE Test( E0 }  d6 z5 ^; W2 k- w8 r
  AS
8 l* L# ?, L& U* X( Z( V       TYPE Test_CURSOR IS REF CURSOR;
' S0 r$ d* R" `& B3 k8 @END Test;! o3 }' {; j2 V- C

2 E( c/ i3 x# f2 N4 C: {(2)过程:3 m3 \3 @$ Q6 D7 D" {* o; V, t/ X

4 h7 x  q; g: BCREATE OR REPLACE PROCEDURE GetCategoryBooks
7 M) }* j  E) a7 j! @(( |2 K# M8 W, j
     p_CURSOR out Test.Test_CURSOR,    -- 这里是上面包中的类型,输出参数7 k7 x1 a& [& L5 n) ]* C
     p_CatogoryID INTEGER, p% z$ n- C, I. R0 R$ E
)" S9 B9 ?! h& i4 y: i2 \6 u& t
AS
9 u$ J1 Z5 }5 N2 mBEGIN  c* A7 T, H4 f& I
     OPEN p_CURSOR FOR. S1 `& [) Z2 c8 k) Q2 ?" r7 g
           SELECT * FROM Books
: c) e0 a' K4 u4 `) y5 A+ z           WHERE CategoryID=p_CatogoryID;
( f7 _+ Z! U# yEND GetCategoryBooks;2 `7 T; P3 ^: C1 E1 w: b, W

1 y9 X3 s% y. S# v" _- I* `(3).NET 程序中:
% V0 a+ c4 A9 a* C% g% \( g& C" d. v/ x& O2 p
OracleParameters parameters = {6 u" F. f" r/ l9 t$ s* G
     new OracleParameter("p_CURSOR", OracleType.CURSOR, 2000, ParameterDirection.Output, true, 0, 0, "",  Y$ T4 v" i% f8 T8 p
          DataRowVersion.Default, Convert.DBNull),
; \' V2 r% P: d: z% w9 c* @: D5 r     new OracleParameter("p_CatogoryID", OracleType.Int32)$ X- C  e: V0 L; F9 `- x# a
};
3 U6 g% s. f% {( S; D" R6 k0 f# O7 S) ^3 f3 h
parameters[1].Value = 22;% C  h% m9 C7 Z5 ~! L6 j1 U
0 h& V  Q; N8 g5 y5 I3 o
OracleConnection connection = new OracleConnection( ConnectionString );
- }# e* ]( Z- e, b, J, \4 U# U- HOracleCommand command = new OracleCommand("GetCategoryBooks", connection);4 ?7 Q& u. G4 h$ J$ o5 D) `+ |% S
command.CommandType = CommandType.StoredProcedure;
: W; v4 h: D8 N( v2 O' v. e" S8 D/ d5 }8 W2 l3 {* Z
foreach(OracleParameter parameter in parameters)$ G+ F. L  E" J: j6 L- X6 O
     command.Parameters.Add( parameter );) V6 s' X+ P, T) a7 `# N8 \
/ M" {" b7 \# h* O
connection.Open();/ O& B# ]/ u9 M9 Y( I2 s. B
OracleDataReader dr = command.ExecuteReader();
4 f$ u4 J4 [, r5 W- N0 x$ \9 D1 \: V+ o1 F8 R
while(dr.Read())& [! `, l! k6 b/ l
{; r$ H6 ^0 H0 a; J9 n
    // 你的具体操作。这个就不需要我教吧?# e& w/ a0 J  ?* m; D' F; b/ ~  P3 A+ l
}1 b/ T8 y& k  T8 Y) s/ V1 ?8 W7 E
connection.Close();0 Z# V+ w: @( ^2 T
  ?/ y8 N$ X4 s; E
  另外有一点需要指出的是,如果使用 DataReader 取得了一个记录集,那么在 DataReader 关闭之前,程序无法访问输出参数和返回值的数据。
7 i/ x0 ~0 _4 v9 E
* Q8 }, Q! I( i( @) g+ D4 o' s  好了,先这些,总之 .NET 访问 Oracle 还是有很多地方和 SQL Server 不同的,慢慢学习了。

已有(1)人评论

enchanter 发表于 2004-8-20 04:31:50 | 显示全部楼层
没人顶我自己来~~~~~~~~~~~~`
您需要登录后才可以回帖 登录 | 註冊

本版积分规则

快速
回复
返回
列表
返回
顶部