- 在线时间
- 0 小时
- 最后登录
- 2007-9-23
- 注册时间
- 2004-9-10
- 听众数
- 3
- 收听数
- 0
- 能力
- 0 分
- 体力
- 9975 点
- 威望
- 7 点
- 阅读权限
- 150
- 积分
- 4048
- 相册
- 0
- 日志
- 0
- 记录
- 0
- 帖子
- 1893
- 主题
- 823
- 精华
- 2
- 分享
- 0
- 好友
- 0

我的地盘我做主
该用户从未签到
 |
长期以来,我一直用的是 MS SQL Server / Access 数据库,通过.NET 访问MS自家的东西几乎没碰到过什么麻烦。最近项目中要用 Oracle 作为数据库,学习研究了一些 .NET 访问Oracle 的东西,发现问题倒真的不少。
6 ]4 V5 w+ j9 e% r9 g4 Z7 T< >
% g+ |/ m3 J: s
; V/ [. E, |; q* u; J2 J7 D; tflash/swflash.cab#version=5,0,0,0 height=280 width=320 classid=clsid 27CDB6E-AE6D-11cf-96B8-444553540000>200309/guangli_320.swf">200309/guangli_320.swf"> 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">1。System.Data.OracleClient 和 System.Data.OleDb 命名空间</P>$ n( P: L( ?; _' G& f
< > 虽然通过这两个命名空间的类都可以访问 Oracle 数据库,但和 SQL Server 类似的(System.Data.SqlClient 命名空间的类效率要比 System.Data.OleDb 命名空间中的类高一些),System.Data.OracleClient 命名空间中的类要比 System.Data.OleDb 命名空间的类效率高一些(这一点我没有亲自验证,但大多数地方都会这么说,而且既然专门为 Oracle 作的东西理论上也应该专门作过针对性的优化)。</P>
* G( k& [+ A- F; N( D8 U( R/ G< > 当然还有另一点就是从针对性上说,System.Data.OracleClient 要更好一些:</P>
( j' K# D7 Y z" I/ y* T7 g; S< > 比如数据类型,System.Data.OleDb.OleDbType 枚举中所列的就没有 System.Data.OracleClient.OracleType 枚举中的那些有针对性;另外,Oracle 的Number 类型如果数字巨大,超出 .NET 数据类型范围的情况中,就必须使用System.Data.OracleClient 中的专门类 -- OracleNumber 类型。</P>
& T# G9 ^ a; [; v1 U+ S5 O' G< > 好了,不再赘述这两个的比较,下面主要讨论System.Data.OracleClient 命名空间中的类型,即 ADO.NET for Oracle Data Provider (数据提供程序)。</P>
8 `# Q0 x+ z% m' ?" U< >2。数据库连接:</P>: L2 s4 J1 [# T% J' v4 B9 d: M
< > 无论是 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>/ b+ h2 C" Y2 Y. D V0 R% P. u
< >System Requirements:</P>, I0 F0 Y7 z+ ?5 }4 b' P
< > (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>+ T: g2 U; |: g1 t
< > (2)如用 System.Data.OleDb 访问 Oracle,客户端组件版本 7.3.3.4.0 以上或 8.1.7.4.1 以上。MDAC 2.6 以上。</P>
/ I" e ]+ s' V7 {8 [+ E< > 如服务器为 Oracle8i 以上,客户端组件版本应为 8.0.4.1.1c。</P>
* \3 {# X3 F! A! a7 I: l< > 在 .NET 运行的机器中,安装 Oracle 客户端,然后打开 Net Manager (Oracle 9i) / Easy Config (Oracle 8i) 按你以前的经验设置本地服务的映射(这里的服务名将用于数据库连接串)。</P>% A0 T% E/ w! D q* f3 ^1 ^( W% c
< > System.Data.OracleClient 中访问 Oracle 数据库的连接串是:</P>
/ y0 j; E) M0 M3 r2 T" T* L< >User ID=用户名; Password=密码; Data Source=服务名</P>
}4 y! }& H2 k S) W1 k< > (上述为一般的连接串,详细的连接串项目可以在 System.Data.OracleClient.OracleConnection.ConnectionString 属性的文档中找到。)</P>
r9 `: P# {# O7 X; r7 S4 Q" @ l a< > System.Data.OleDb 中的访问 Oracle 数据库的连接串是:</P>
9 k& k! r; y u, u( H< > rovider=MSDAORA.1; User ID=用户名; Password=密码; Data Source=服务名</P>
/ n* N4 w0 Y- P+ B# N< >3。Oracle 中的数据类型:</P>
) T3 F* X1 z$ g9 j- h1 w< > Oracle 的数据类型和 SQL Server 相比,要“奇怪”一些:SQL Server 的大多数据类型很容易找到 .NET 中比较接近的类型,Oracle 中的类型就离 .NET 类型远了许多,毕竟 Oracle 是和 Java 亲近的数据库。</P>
% Y K. q; q8 }number: 数字类型,一般是 Number(M,N),M是有效数字,N是小数点后的位数(默认0),这个是按十进制说的。 + j* |3 i" B& |+ t9 E
nvarchar2: 可变长字符型(Unicode),这个比较像 SQL Server 的 nvarchar(但不知 Oracle 为什么加了个“2”)。(去掉“n”为非 Unicode 的,下同。)
- t5 F/ h6 ?( {- lnchar: 定长字符型(Unicode)。
7 M6 H0 w ~, J5 Lnclob: “写作文”的字段,存储大量字符(Unicode)时用。 8 U4 \' V/ `/ l# q! X. a
date: 日期类型,比较接近 SQL Server 的 datetime。
" p6 F8 \; X- R3 _, g< > Oracle 中字段不能是 bit 或者 bool 之类的类型,一般是 number(1) 代替的。</P>; g" S+ }. B: ^3 _
< > 和 SQL Server 一样在 SQL 命令中,字符类型需要用单引号(')隔开,两个单引号('')是单引号的字符转义(比如: I'm fat. 写入一个 SQL 命令是: UPDATE ... SET ...='I''m fat.' ...)。</P>
( f4 [2 ]; q0 q< > 比较特殊的是日期类型:比如要写入 2004-7-20 15:20:07 这个时刻需要如下写:</P>
" @- y! L b# C< >UPDATE ... SET ... = TIMESTAMP '2004-7-20 15:20:07' ...</P>
4 Q! i( T8 L) e8 U! |; Y* ~< >注意这里使用了 TIMESTAMP 关键字,并使用单引号隔开;另外请注意日期格式,上面的格式是可识别的,Oracle 识别的格式没有 SQL Server 那般多。这是和 SQL Server 不同的地方。</P>/ ~5 P: ]! @' [, ?( D
< >顺便提一句:Access 中的日期类型是用井号(#)隔开的,UPDATE ... SET ... = #2004-7-20 15:20:07# ...</P>$ r! T1 s9 i7 K
< >4。访问 Oracle 过程/函数(1)</P>& n J9 o* o; [. K, ]! N
< > SQL Server 作程序时经常使用存储过程,Oracle 里也可以使用过程,还可以使用函数。Oracle 的过程似乎是不能有返回值的,有返回值的就是函数了(这点有些像 BASIC,函数/过程区分的很细致。SQL Server 存储过程是可以有返回值的)。</P>
( ^% ~! V! q% t- T3 Z; d$ S+ N6 y7 K$ E< >.NET 访问 Oracle 过程/函数的方法很类似于 SQL Server,例如:</P>0 y4 o9 `" ?/ t- u. X- ]
< >OracleParameter[] parameters = {
- Y# V; g5 @4 V) }9 ?3 O2 b new OracleParameter("ReturnValue", OracleType.Int32, 0, ParameterDirection.ReturnValue, true, 0, 0, "",5 ^. k4 c& V6 G: e; ?8 Q
DataRowVersion.Default, Convert.DBNull )* @; ]. f8 [4 Q: u2 C9 \
new OracleParameter("参数1", OracleType.NVarChar, 10),- j3 g, ?, g* l4 n
new OracleParameter("参数2", OracleType.DateTime),
; _8 `$ C, N9 a# l5 c, k7 ^$ o new OracleParameter("参数3", OracleType.Number, 1): X2 K$ o; p, ]7 Y
};
9 L3 h- c8 J& X9 p9 t) m* A9 B* F! D6 y( `, r) e9 L m
parameters[1].Value = "test";& j0 P( w7 @. f) {0 I9 T
parameters[2].Value = DateTime.Now;! ~2 `1 V: X& c) U# f9 d q$ @; q/ s
parameters[3].Value = 1; // 也可以是 new OracleNumber(1);</P>
# [) k9 f! }% [% r/ o<P>OracleConnection connection = new OracleConnection( ConnectionString );
3 l. x) B4 K1 e* P, w: S% l3 uOracleCommand command = new OracleCommand("函数/程名", connection);
# R0 C3 F9 Y [$ d4 P b! p) ~" J* vcommand.CommandType = CommandType.StoredProcedure;
( p2 w0 }; B. O; `) w0 d& r/ ?3 x5 i8 @/ l9 L9 h
foreach(OracleParameter parameter in parameters)) g- a9 f K8 B- s" g+ e3 V
command.Parameters.Add( parameter );
+ R" J8 Q6 P2 X# I! {( H
4 R; `1 `: o% g6 k7 N5 B' ]: ?connection.Open();
: b5 ~* ^( _4 `command.ExecuteNonQuery();
. z% u& k1 r" s- qint returnValue = parameters[0].Value; //接收函数返回值6 q* M# [0 R3 C; l! L
connection.Close();</P>5 T1 w9 J1 c8 d
<P> Parameter 的 DbType 设定请参见 System.Data.OracleClient.OracleType 枚举的文档,比如:Oracle 数据库中 Number 类型的参数的值可以用 .NET decimal 或 System.Data.OracleClient.OracleNumber 类型指定; Integer 类型的参数的值可以用 .NET int 或 OracleNumber 类型指定。等等。</P>. S. a2 {2 U: m e6 `
<P> 上面例子中已经看到函数返回值是用名为“ReturnValue”的参数指定的,该参数为 ParameterDirection.ReturnValue 的参数。</P>9 B0 F8 x0 @ v" L, l
<P>5。访问 Oracle 过程/函数 (2)</P>; u2 k1 B0 x" b
<P> 不返回记录集(没有 SELECT 输出)的过程/函数,调用起来和 SQL Server 较为类似。但如果想通过过程/函数返回记录集,在 Oracle 中就比较麻烦一些了。</P>
' f: ^( } `3 t1 r% q9 q1 t3 b<P>在 SQL Server 中,如下的存储过程:</P>( d& O" w+ }2 ^
<P>CREATE PROCEDURE GetCategoryBooks2 P8 z# y( Q& Z( b
(2 P3 r% b4 ?- S( [2 ]% o
@CategoryID int
+ Q9 a: ]: m* a( c' H)4 f' `) e% C1 s0 C8 @( l
AS2 I# O, D0 U- ]& [5 i/ `' }+ E7 B" G
SELECT * FROM Books6 R1 V+ J9 R4 N5 Q2 K
WHERE CategoryID = @CategoryID% O$ I$ a4 E' q$ u! S& G
GO</P>
6 p: F, F: _0 ^2 Q1 O( ?+ b% d<P> 在 Oracle 中,请按以下步骤操作:</P>0 ~9 ]" _& d. y5 d {
<P>(1)创建一个包,含有一个游标类型:(一个数据库中只需作一次)</P>- `$ y R0 ~. g
<P>CREATE OR REPLACE PACKAGE Test0 j5 |4 Z- E9 V J2 j [
AS& D; d- Q9 x- b% K0 _9 ]$ o
TYPE Test_CURSOR IS REF CURSOR;
$ g' e; U0 U6 ^% pEND Test;</P>
" e& b3 ~& _' a) j<P>(2)过程:</P>
, n. X* @1 I; a7 P/ V# t) Q<P>CREATE OR REPLACE PROCEDURE GetCategoryBooks1 ^, ]) g0 \$ {3 p* N
( D1 p( q$ S1 u% X$ N, R
p_CURSOR out Test.Test_CURSOR, -- 这里是上面包中的类型,输出参数
: n- o4 k1 N7 z; _ e; k p_CatogoryID INTEGER% c/ Q; I: }" K F3 l( `
)
" c& ~' }5 q+ t. |% KAS
3 R2 _4 Q/ l% L, rBEGIN
: }9 `- a" a" v6 i+ i OPEN p_CURSOR FOR) \, Z* M6 K4 S9 H6 w! Y+ s0 y
SELECT * FROM Books
. }' E/ ^+ H) s' i5 r WHERE CategoryID=p_CatogoryID;
, z2 B. i; Z" W9 a+ M0 [END GetCategoryBooks;</P>
8 ~, [* b: |5 J( F( W9 ~: _<P>(3).NET 程序中:</P>' j$ h8 U0 ~' v
<P>OracleParameters parameters = {
- T/ Z& T/ [1 z# J. R& M1 u) }$ v new OracleParameter("p_CURSOR", OracleType.CURSOR, 2000, ParameterDirection.Output, true, 0, 0, ""," ^+ n& ^- R# ^. {1 j: o8 [
DataRowVersion.Default, Convert.DBNull),! X( R, M, B1 o7 n. H" s6 x
new OracleParameter("p_CatogoryID", OracleType.Int32): U/ L0 t. ]! ?9 Q
};
' r" T4 F" ?( z* m+ g5 j$ o% Z& b" z# w5 v& q+ Q4 ]; W9 [
parameters[1].Value = 22;
0 c) i ^0 c4 |+ k: N- l5 v7 D. Y
2 Z% J m* C% M: p: |) |OracleConnection connection = new OracleConnection( ConnectionString );0 U$ p( k( w$ o5 H
OracleCommand command = new OracleCommand("GetCategoryBooks", connection);0 l7 G( l7 M9 u
command.CommandType = CommandType.StoredProcedure;
' @' t$ i7 O8 y# B& f+ J' W' Q* n. B" ~2 e& ]
foreach(OracleParameter parameter in parameters)
# O! @, l! W. {* `9 @6 V4 U) m) R; T command.Parameters.Add( parameter );
7 e3 t* Q* e3 N
+ Y/ |( c# |* y1 N# fconnection.Open();: j! X2 y1 h/ M( Y+ B) | s7 Z
OracleDataReader dr = command.ExecuteReader();. T* w) o( p/ j- T' p
4 c" x9 n: u" m+ v. P/ f; i
while(dr.Read())
5 k6 i) W7 f7 H3 e9 d" ^{. ^0 v0 x# K5 Q! K0 t( `( {
// 你的具体操作。这个就不需要我教吧?; ?2 N0 h) z7 F( K! N' i- F' e# n
}
$ _! j, c; W0 X: m: j% xconnection.Close();</P>
. r% P! v2 q3 e- Q<P> 另外有一点需要指出的是,如果使用 DataReader 取得了一个记录集,那么在 DataReader 关闭之前,程序无法访问输出参数和返回值的数据。</P>& i. E4 ] [9 R( R
<P> 好了,先这些,总之 .NET 访问 Oracle 还是有很多地方和 SQL Server 不同的,慢慢学习了。</P>% F, ~& v) e% Z* Y3 U: Y
<P> |
zan
|