【问题标题】:read data from SYS_REFCURSOR in a Oracle stored procedure and reuse it in java在 Oracle 存储过程中从 SYS_REFCURSOR 读取数据并在 java 中重用它
【发布时间】:2012-02-15 09:39:39
【问题描述】:

我有这张桌子:

  CREATE TABLE "QMS_MODEL"."BOOKING" (
       "ID" NUMBER ( 19, 0 ) CONSTRAINT "QMS_BOOKING_NN_1" NOT NULL ENABLE
      ,"CALL_TIME" TIMESTAMP ( 6 )
  );

然后我在 Oracle 中有一个简单的存储过程: 1.从表中获取记录 2.更新找到的记录上的列 3.通过 OUT 参数返回指向找到的记录的 SYS_REFCURSOR:

CREATE OR REPLACE 
PROCEDURE GET_BOOKING
  ( 
    refCursorValue OUT SYS_REFCURSOR,
    bookingId IN QMS_MODEL.booking.id%type
  )
AS
    bookingResult QMS_MODEL.booking%ROWTYPE;
    todayAtNow QMS_MODEL.booking.booking_time%type;
BEGIN

  --********************************
  --get booking cursor....
  --********************************
  OPEN refCursorValue FOR 
  SELECT 
    bb.*
  FROM qms_model.booking bb 
  WHERE bb.id = bookingId 
  FOR UPDATE;


  --****************************************
  --from boking cursor get booking record...
  --****************************************
  FETCH refCursorValue INTO bookingResult;

  --********************************
  --update a column on found booking....
  --********************************
  SELECT SYSDATE into todayAtNow FROM DUAL;  
  UPDATE qms_model.booking SET 
          call_time = todayAtNow
  WHERE id = bookingResult.id;


  /*
  after the fetch refCursorValue is not
  valid and the client can't use it!
  */ 

END;

调用此过程找到预订 并且该字段已更新,但最后光标 无效,我不能将其用于其他操作, 在此示例中,我使用光标记录 id 字段

set serveroutput on format wrapped;
DECLARE
  REFCURSORVALUE SYS_REFCURSOR;
  BOOKINGID NUMBER;
  bookingResult QMS_MODEL.booking%ROWTYPE;
BEGIN
  BOOKINGID := 184000000084539;
  GET_BOOKING(
    REFCURSORVALUE,
    BOOKINGID
  );
  FETCH REFCURSORVALUE INTO bookingResult;
  DBMS_OUTPUT.PUT_LINE('>>>OUT , cursor fetc,id='|| bookingResult.id ); 
END;

我使用实体在 java 中对预订进行建模

@Entity
@Table(name = "BOOKING", schema = "QMS_MODEL")
@NamedNativeQueries({    
    @NamedNativeQuery(name = "booking.callNext.Oracle",
    query = "call GET_BOOKING(?,:bookingId)",
    callable = true,
    resultClass = Booking.class)
})
public class Booking implements Serializable {
..
..
}

...我通过 NamedNativeQuery 得到它:

long bookingID=...some value
Query q = entityMng.createNamedQuery("booking.callNext.Oracle");
q.setParameter("bookingId", bookingID);
List results = q.getResultList();
if (results!=null && !results.isEmpty()) {
  Booking eBooking = (Booking) results.get(0);
  ..
  ..
  ..
  ..i want use some booking data here....
  ..but i can't because the cursor is closed
}

对我的唯一要求是 -选择预订并将其在同一事务中更新为存储过程 -从java调用存储过程并以@Entity-Booking的形式检索更新的预订

提前谢谢你。

【问题讨论】:

  • 请,请不要在创建对象时使用引号。它使它们区分大小写并且不会导致麻烦。

标签: java oracle hibernate cursor


【解决方案1】:

引用光标与我们在前端语言中发现的可滚动光标不同。它是一个指向结果集的指针。这意味着,我们可以阅读一次,然后就用尽了。不可重复使用。

"当一个预订被选中时,我需要更新 call_time 来标记它 作为“已选择”。当预订具有非空 call_time 时不可选择 不再。我需要将更新的记录返回给 java 应用程序 所以我需要将它作为程序的第一个参数返回 OUT sys_refcursor 输入。

" 请注意,真正的选择可能很难,所以我不想执行 不止一次”

好的,这是一种方法。警告:这是概念证明(即未经测试的代码),不能保证有效,但它似乎是一个可行的解决方案。

CREATE OR REPLACE PROCEDURE GET_BOOKING  
 (      refCursorValue OUT SYS_REFCURSOR,     
        bookingId IN QMS_MODEL.booking.id%type   ) 
AS     
     rowids dbms_debug_vc2coll;
begin
     update  qms_model.booking bb  
     set     bb.call_time = sysdate
     where bb.id = bookingId 
     returning rowidtochar(rowid) bulk collect into rowids;

     open refCursorValue for
         select * 
         from qms_model.booking bbto
         where rowid in ( select chartorowid(column_value) from table(rowids));
end;
/

基本上:

  1. 更新您要选择的行
  2. 使用 RETURNING 子句捕获更新行的 rowid
  3. 然后使用 rowid 打开 refcursor 以仅选择更新的行。

您确实发出了两个查询,但使用 ROWID 进行选择非常快。

【讨论】:

  • 选择预订时,我需要更新 call_time 以将其标记为“已选择”。当预订有非空时 call_time 不再可选择。我需要将更新的记录返回给 java 应用程序,因此我需要将其作为 OUT“sys_refcursor”类型的过程的第一个参数返回。 (见docs.jboss.org/hibernate/orm/3.3/reference/en/html/…)。我不知道是否有办法使用选择中的 %rowtype 然后使用“sys_refcursor”进行输出。
【解决方案2】:

问题出在 (3) - '通过 OUT 参数返回指向找到的记录的 SYS_REFCURSOR'。它没有指向该记录,因为您已经 fetched 过去了。我假设无论如何您只期望一条具有该 ID 的记录;如果您有多个,则返回的光标将指向具有该 ID 的 next 记录,但您的 update 将更新所有具有该 ID 的匹配记录,而不仅仅是您获取的那个。

如果你只有一条记录,为什么要使用游标?我能看到的唯一原因是允许你使用for update,但你没有在更新中使用对应的where current of

【讨论】:

  • @user1210761 - 好的,如果返回记录类型不可行。但是您仍然无法获取并返回相同的游标;如果 ID 不是唯一的,您仍然可能会更新不止一行,因为您没有使用where current of;如果你有多个,你需要一个order by 来控制你得到的。如果您可以可靠地获得相同的记录,您可以在本地游标中更新它,然后打开 out 游标进行第二次相同的选择?
  • 我发布了我的查询的简化版本,在真实版本中有一个子句“... WHERE ROWNUM docs.jboss.org/hibernate/orm/3.3/reference/en/html/…)。请注意,真正的选择可能很难,所以我不想执行超过一次(我认为重新打开光标就像第二次重新执行选择)
猜你喜欢
  • 2012-04-19
  • 2017-08-18
  • 2010-10-12
  • 2019-05-01
  • 2013-01-31
  • 2013-08-01
  • 2021-03-26
  • 1970-01-01
  • 2017-05-22
相关资源
最近更新 更多