回答

收藏

使用游标并在带有Java / JDBC的Oracle PL / SQL中获得结果

技术问答 技术问答 188 人阅读 | 0 人回复 | 2023-09-13

我有一个像这样构造的PL / SQL查询:
# K6 C! k$ [& b7 g1 k  eDECLARE$ _6 T+ s& f5 T
a NUMBER;
1 H  b" V9 X+ |2 J# |' L2 BB NUMBER;$ ^, P5 {: v; s+ @; k: u. l
CURSOR cursor
4 ]. |# y5 C2 M# RIS
2 T0 T: I* U' M* b( ? ( SOME SELECT QUERY);! R  C5 e& p0 P, M9 O8 H9 _
BEGIN  |' L( h- c# H. x1 T. p, [
  OPEN cursor;
9 M" [& h2 O9 c; V    LOOP
% A6 d% w+ }  H; R* s2 u1 p    SOME STUFF;
  F1 Z0 q& P1 _) o    END LOOP;! @; h8 r2 Y3 P/ r, a
  CLOSE cursor;* c  F- G( p3 h0 W9 q
END* ?2 H9 D- S4 J5 e) z
如何使用jdbc从Java代码运行此查询并获取结果集?我尝试不使用游标来运行查询,并且其运行正常。我想不出一种在Java代码中执行此操作的方法。如果我直接将查询运行到oracle客户端上,它将正常工作。因此查询没有问题。
+ z3 P7 s8 e4 Z# @$ i: X& ]PS我不想将代码存储为存储过程,并由于某些限制而调用它。, j( p3 z4 i* n* ~1 x0 k* Y
                " O4 x0 A% {. e0 R. {
解决方案:6 H0 S! B- i6 u
               
! k0 @$ L3 [( }' A' k* ]' A1 A/ \5 s* n: l

: n; l# V2 g# l0 Z- |. o                这是不可能的。您无法从匿名PL / SQL块返回结果集(因此无法从JDBC获取结果集)。6 A) @; r3 q3 m% {" W) C6 A- Z
您将需要直接从JDBC运行select。
' @+ O8 }  C+ F( ?% J$ ~唯一的,非常丑陋的解决方法是使用dbms_output.put_line()和读取之后的内容。但这是一个非常丑陋的技巧,直接在JDBC中处理SELECT查询的结果要好得多。
0 \$ S$ M1 u* y
- g4 l# M4 \% J1 V$ ^编辑14 \4 l3 ~+ f1 M
这是一个使用dbms_output的小例子:+ a1 h# s2 R, S7 X  L
Connection con = ....;5 l$ P) P% S8 ?1 L( i2 c( e% ]
// turn on support for dbms_output' N+ }% c5 {, A( ^0 I4 U$ \8 o
CallableStatement cstmt = con.prepareCall("{call dbms_output.enable(32000) }");
6 W6 F4 f1 e  l7 i: L; hcstmt.execute();
" H& f3 h' p$ a' D// run your PL/SQL block/ h4 X- Q2 v# b" \* s2 ?3 E  J) A
Statement stmt = con.createStatement();
7 A9 H5 x# p4 o3 L% O2 v0 IString sql =
  D: C$ U8 q" ^% M  |1 ~/ o  w: n    "declare  \n" +
! q3 M' B, F8 k9 w% Q" @    " a number;  \n" +
! X! z4 R/ K6 d' y    " cursor c1 is select id from foo;  \n" +
( z5 Z" I- G0 c2 C    "begin  \n" +9 J( B  m% I  }: f, K9 a
    "  open c1; \n" +4 _0 [$ j, z" S3 Y' ~& W
    "  loop \n" +
! M! k0 h% p- x" o7 P9 \/ s: R    "    fetch c1 into a;  \n" +
7 c1 `% Y7 W  K) O; @  X    "    exit when c1%notfound;  \n" +3 m  Q! U1 Y. ?+ x; V0 Q! A
    "    dbms_output.put_line('ID: '||to_char(a)); \n" +. r4 h8 |% j0 W1 m
    "  end loop; \n" +
0 F" ?) }2 T2 w9 J    "end;";
* h  X7 `3 f- f, V' W% n$ Kstmt.execute(sql);- u( H" |8 [; D7 J
// retrieve the messages written with dbms_output
1 m- m' V+ ~) e3 K( A: Mcstmt = con.prepareCall("{call dbms_output.get_line(?,?)}");* x/ a! ?5 m) \, O
cstmt.registerOutParameter(1,java.sql.Types.VARCHAR);; R7 D0 }  V* |
cstmt.registerOutParameter(2,java.sql.Types.NUMERIC);5 H- n4 A2 }4 m" U
int status = 0;/ \) F0 n& ]. q
while (status == 0)
, C2 i7 }4 U/ h{
' r& S9 s) B1 X# N% m3 U    cstmt.execute();
* z3 c" R0 Y% `( D  r    String line = cstmt.getString(1);
: Q3 k: a* ^6 R4 H8 Y    status = cstmt.getInt(2);
1 J4 m1 ^" L7 P% ]    if (line != null && status == 0)
0 j! r' h: a3 V% [    {
- J8 G, k% R# q. u- q1 @- p        System.out.println(line);
: F1 v" X, u" h) B+ h  g- }# b! d    }
0 z0 M- k* i: W" P1 h}# p9 R% ?* K& Q0 c

1 l1 ?- i% A/ _8 w编辑2(对于评论来说这太长了)
% \2 i9 M, I# q+ M嵌套循环来检索数据几乎总是一个坏主意。如果您发现自己在做这样的事情:
9 [% w! p) [0 L# G- sbegin1 [7 f7 @. E2 F' P9 f! P9 r
  for data_1 in (select id from foo_1) loop
! m( N3 s/ F# j* `) t! G/ a+ m    dbms_output.put_line(to_char(data_1.id));: b, }; t9 i# o- F* q# U' T
    for data_2 in (select f2.col1, f2.col2 from foo_2 f2 where f2.id = data_1.id) loop
8 o6 p7 B9 Y# j        ... do something else0 i) r# l( s4 v/ r* `/ X9 Z
    end loop;
- g/ h8 o! y0 l! O  t7 A6 K4 H  end loop;
& V7 y# G2 W2 l$ \3 G; H+ Gend;* D- b% S9 ~5 j2 A/ }  h$ W1 F/ D
/
" |' p' w3 [7 U) D; t这样做会更加高效:
  N% w) `9 s$ m1 t; z3 a, j3 d  obegin
. v/ x8 d, c; C1 Y' P  for data_1 in (select f2.col1, f2.col2 from foo_2 f28 T$ l! i/ i2 t- c2 e6 f
                 where f2.id in (select f1.id from foo_1 f1)) loop
" N$ F4 j4 A& P# ]  _& n     ... do something
/ x$ j9 g$ s& j  end loop;/ }+ G3 t. P9 F+ |0 H+ i0 k( @
end;' E- T0 F" Z2 V4 r- s) {+ k' v
/  x3 I" C8 z: ^' W3 _: f, m
可以使用类似以下的方法来处理此过程,而不会在JDBC中占用过多的内存:1 z% e% a9 Z/ ]9 S
String sql = "select f2.col1, f2.col2 from foo_2 f2 where f2.id in (select f1.id from foo_1 f1)";
, V& f8 Y, i1 A7 t4 c; cStatement stmt = connection.createStatement();
1 Z. ]( D* Q4 ^8 eResultSet rs = stmt.executeQuery(sql);
1 _2 {9 M1 R0 {/ S7 L) v* R1 Owhile (rs.next())
4 K" @! Z2 H2 h( x% J/ y{
& x8 o1 S# q$ @9 Y, f2 O9 Y   String col1_value = rs.getString(1);+ C1 H. O5 `/ G2 `" Y
   int    col2_value = rs.getInt(2);+ ]* P( z% R. @; M* s
   ... do something6 Y7 q4 c8 L5 v" e
}. @/ d- S2 N/ o* g
上面的代码即使处理数十亿行,也只会在内存中保留一行。准确地说:JDBC驱动程序实际上将预取多行。默认值为10,可以更改。但是即使那样,您也不会占用过多的内存。
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则