4

我想让一个 oracle 对象返回自己并能够链接这些调用。我怎么做?

我尝试返回相同的类型,但它不起作用,我还尝试添加一个由函数调用的过程,但它也不起作用。总是抱怨修改宽度成员的值。看起来函数不会承认副作用?,它们是按照更数学的函数原理建模的吗?这可以实现吗?我想我可以编写这个函数,以便它用 SELF 构建一个新的矩形,但工作量太大了。

我的目标是能够链接 jQuery 或一些 java 类(单例?)之类的调用。就像是:

r := r.setWidth(0).setWidth(1).setWidth(2);

当然,它会有更多的方法,它不会是一个矩形。这是错误:

Error: PLS-00363: expression 'SELF' cannot be used as an assignment target
Line: 18
Text: stWidth(w);

-

CREATE OR REPLACE TYPE rectangle AS OBJECT
(
-- The type has 3 attributes.
  length NUMBER,
  width NUMBER,
  area NUMBER,
-- Define a constructor that has only 2 parameters.
  CONSTRUCTOR FUNCTION rectangle(length NUMBER, width NUMBER)
    RETURN SELF AS RESULT,
  MEMBER FUNCTION setWidth(w NUMBER) RETURN rectangle,
  MEMBER PROCEDURE stWidth(w NUMBER)
)

-

CREATE OR REPLACE TYPE BODY rectangle AS
  CONSTRUCTOR FUNCTION rectangle(length NUMBER, width NUMBER)
    RETURN SELF AS RESULT
  AS
  BEGIN
    SELF.length := length;
    SELF.width := width;
-- We compute the area rather than accepting it as a parameter.
    SELF.area := length * width;
    RETURN;
  END;
  MEMBER PROCEDURE stWidth(w NUMBER) IS
  BEGIN
    self.width := w;
  END;
  MEMBER FUNCTION setWidth(w NUMBER) RETURN rectangle IS
    BEGIN
        stWidth(w);

        RETURN SELF;
  END;
END;

提前致谢。

4

4 回答 4

6

您不能同时更改对象并分配给它。您已经知道解决方案“使用 SELF 构建一个新矩形”。但这不会是很多工作。

替换这个:

  MEMBER FUNCTION setWidth(w NUMBER) RETURN rectangle IS
    BEGIN
        stWidth(w);
        RETURN SELF;
  END;

有了这个:

  MEMBER FUNCTION setWidth(w NUMBER) RETURN rectangle IS
      v_rectangle rectangle := self;
    BEGIN
        v_rectangle.width := w;
        RETURN v_rectangle;
  END;

您实际上遇到了编译错误。默认情况下,SELF是一个IN参数。调用stWidth失败,因为它正在IN使用 修改参数self.width := w;

请参阅:http ://docs.oracle.com/cd/B28359_01/appdev.111/b28371/adobjbas.htm#CHDCFEEE

SELF 始终是传递给方法的第一个参数。

  • 在成员函数中,如果没有声明SELF,则其参数模式默认为IN。

  • 在成员过程中,如果不声明 SELF,则其参数方式默认为 IN OUT。默认行为不包括 NOCOPY 编译器提示。

于 2013-08-06T19:36:28.327 回答
2

抱歉,我来晚了,但其他答案不正确,至少使用 Oracle 11gR2,您想要实现的目标确实是完全可能的。

我最近刚刚偶然发现了这个问题,我看到一个人可以明确地返回一个参考,SELF就像你试图做的那样,而不需要任何权衡或应用变通方法。

唯一需要的是通过(显式)将(隐式)SELF参数设置为来重新定义方法SELF IN OUT rectangle。是静默传递给每个对象方法的主要参数,并且对于函数被定义为
(不可变的;这可能是编译器抱怨的原因)。这是在编译时建立的,但好的部分是在调用该方法时可以省略它。 在文章末尾的示例中(根据您的稍作改写),我们定义了一个SELFIN

MEMBER FUNCTION incrementWidth(SELF IN OUT rectangle, w NUMBER) RETURN rectangle

我们执行它忽略SELF引用:

declare
  r rectangle := rectangle(1,2);
begin

  dbms_output.put_line('width is: ' || r.width);
  dbms_output.put_line('new width is: ' || r.incrementWidth(3).width);

end;
/

请注意,有两个警告需要注意。

警告 1

每个方法调用都会临时创建对象的新副本。
但只是暂时的,新实例是短暂的,就在方法的开始和结束之间。这是在所有函数或过程上使用IN OUT参数所固有的,并不特定于对象类型。如果您想防止这种行为,您可能需要使用NOCOPY提示重新定义函数的签名:

MEMBER FUNCTION incrementWidth(SELF IN OUT NOCOPY rectangle, w NUMBER) RETURN rectangle

有关详细信息,请参阅ORACLE-BASE - NOCOPY。请注意,这是一个提示,但不能保证您最终使用的是相同的对象引用而不是新创建的对象,因此请谨慎使用。

警告 2

鉴于您提出了这个问题,您可能有 OOP 背景,并且在尝试调用该方法而不使用返回的引用时,您可能会感到惊讶

r.incrementWidth(10);

编译器将返回错误:

PLS-00221: 'INCREMENTWIDTH' is not a procedure or is undefined

那么这里发生了什么?好吧,pl/sql 中所谓的“静态多态”(即编译过程中方法重载的选择)与其他 OOP 语言略有不同,因为它甚至考虑了 RETURNed 类型的使用。为了解决这个问题,添加一个带有签名的伴随过程,其区别在于缺少返回类型:

MEMBER FUNCTION incrementWidth(SELF IN OUT rectangle, w NUMBER) RETURN rectangle,
MEMBER PROCEDURE incrementWidth(SELF IN OUT rectangle, w NUMBER)

合理地,如果您不想在函数和过程中重复相同的代码,过程将在内部委托函数;并且根据您使用的 Oracle 版本,您可能希望使用代码内联(请参阅OCP:更多新的 PL/SQL 功能)以达到与复制粘贴实现相同的速度(您几乎不会注意到真正的区别) . 显式“内联”按名称指向方法,但它也适用于方法名称重载的情况。

在下面的示例中,您将看到根据返回/未返回参数的使用情况交替调用函数或过程。

所以最后...

可能想要编写的代码如下(我没有使用NOCOPY不污染相关的东西,但它很简单)

CREATE OR REPLACE TYPE rectangle AS OBJECT
(
  length NUMBER,
  width NUMBER,
  CONSTRUCTOR FUNCTION rectangle(length NUMBER, width NUMBER)
    RETURN SELF AS RESULT,
  MEMBER FUNCTION incrementWidth(SELF IN OUT rectangle, w NUMBER) RETURN rectangle,
  MEMBER PROCEDURE incrementWidth(SELF IN OUT rectangle, w NUMBER)
);
/

CREATE OR REPLACE TYPE BODY rectangle AS
  CONSTRUCTOR FUNCTION rectangle(length NUMBER, width NUMBER)
    RETURN SELF AS RESULT
  AS
  BEGIN
    SELF.length := length;
    SELF.width := width;
    RETURN;
  END;
  MEMBER FUNCTION incrementWidth(SELF IN OUT rectangle, w NUMBER) RETURN rectangle IS
    BEGIN
        dbms_output.put_line('...invoking the function with input ' || w);
        width := width + w;
        RETURN SELF;
  END;
  MEMBER PROCEDURE incrementWidth(SELF IN OUT rectangle, w NUMBER) IS
    BEGIN
        PRAGMA INLINE (incrementWidth, 'YES');
        dbms_output.put_line('...invoking the procedure with input ' || w || ', that in turn is...');
        self := incrementWidth(w);
  END;
END;
/

在执行...

set serveroutput on
select * from v$version where rownum = 1;

declare
  r rectangle := rectangle(1,2);
begin

  dbms_output.put_line('width is: ' || r.width);

  --this is invoking the "function" version, because we are making use of
  --the returned rectangle object
  dbms_output.put_line('new width is: ' || r.incrementWidth(3).width);

  --the original reference has been updated even without using the NO COPY hint
  dbms_output.put_line('original object has width updated: ' || r.width);

  --this is invoking the "procedure" version, because we are not using the returned object
  r.incrementWidth(3);

  --of course this has finally worked as well
  dbms_output.put_line('again what is the new width like now?: ' || r.width);

end;

/

你得到

BANNER                                                                     
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production  

width is: 2
...invoking the function with input 3
new width is: 5
original object has width updated: 5
...invoking the procedure with input 3, that in turn is...
...invoking the function with input 3
again what is the new width like now?: 8
于 2016-10-27T11:44:57.627 回答
0

您不能从成员函数返回 SELF。您可以创建一个副本,但我不确定为什么 setwidth 函数会返回一个矩形对象。我知道这个基本示例可能来自一些旧的 Oracle 文档,但我不会将计算字段(区域)作为属性。如果它被计算,它应该是一个成员函数(或过程)。原因是除非您想始终记住在影响区域的函数中更新区域,否则您将自取其辱。您的示例已损坏(除了 setWidth 返回 self),因为只有构造函数计算 area

set serveroutput on
declare
  r rectangle := rectangle(3,2);
begin
  dbms_output.put_line('Area is: ' || r.area);
  -- change the width
  r.stWidth(4);
  dbms_output.put_line('Area is: ' || r.area);
end;

输出:

Area is: 6
Area is: 6

显然这是不正确的。我会做类似的事情:

CREATE OR REPLACE TYPE rectangle AS OBJECT
(
-- The type has 2 attributes.
  len NUMBER,
  width NUMBER,
-- Define a constructor that has only 2 parameters.
  CONSTRUCTOR FUNCTION rectangle(len NUMBER, width NUMBER)
    RETURN SELF AS RESULT,
  MEMBER PROCEDURE setLength(l NUMBER),
  MEMBER PROCEDURE setWidth(w NUMBER),
  MEMBER FUNCTION getArea return NUMBER,
  MEMBER PROCEDURE showArea
);


CREATE OR REPLACE TYPE BODY rectangle AS
  CONSTRUCTOR FUNCTION rectangle(len NUMBER, width NUMBER)
    RETURN SELF AS RESULT
  AS
  BEGIN
    SELF.len := len;
    SELF.width := width;
    RETURN;
  END;

  MEMBER PROCEDURE setLength(l NUMBER) IS
  BEGIN
    self.len := l;

  END;
  MEMBER PROCEDURE setWidth(w NUMBER) IS
  BEGIN
    self.width := w;

  END;

  MEMBER FUNCTION getArea return NUMBER IS
  BEGIN
    return self.len * self.width;
  END;

  MEMBER PROCEDURE showArea IS
  BEGIN
    -- Just shows how we calculated area and spits to console
    dbms_output.put_line('Area is: ' || self.getArea || ' (' || self.len || ' * ' || self.width || ')');
  END;
END;

这样你就有了:

set serveroutput on
declare
  r rectangle := rectangle(3,2);
begin
  dbms_output.put_line('Area is: ' || r.getArea);
  -- change the width
  r.setWidth(4);
  dbms_output.put_line('Area is: ' || r.getArea);
end;

输出:

Area is: 6
Area is: 12
于 2013-08-06T20:29:39.417 回答
0

我遇到了同样的问题,由于最重要的答案不同意,我不得不自己试验。

尽管安东尼奥的回答说,我有以下代码不起作用:

create or replace type tst as object (
  inner_value varchar2(100),

  constructor function tst return self as result,

  member function update_value(
    self in out nocopy tst, 
    p_value varchar2)
  return tst,

  member procedure print_value(self in out nocopy tst)

) final;
/

create or replace type body tst as
  constructor function tst return self as result
  is
  begin
    self.inner_value := 'DEFAULT';
    return;
  end;

  member function update_value(
    self in out nocopy tst, 
    p_value varchar2)
  return tst
  is
  begin
    self.inner_value := p_value;
    return self;
  end;

  member procedure print_value(self in out nocopy tst)
  is
  begin
    dbms_output.put_line(self.inner_value);
  end;

end;
/

无论您是否self定义了参数,以下代码都不会运行nocopy

set serveroutput on;
begin
  tst().update_value('TEST').print_value;
end;
/

如果print_value重新定义为member procedure print_value(self in tst),则tst().print_value;成为有效语句。

in out如果 self 参数是or ,则构造函数不能被链接in out nocopy,无论您返回一个副本 or self,您总是会得到一个PLS-00363. 在构造函数中运行嵌套调用的唯一方法是在self in任何地方使用参数并进行复制,这令人失望:

create or replace type tst as object (
  inner_value varchar2(100),

  constructor function tst return self as result,

  member function update_value(
    self in tst, 
    p_value varchar2)
  return tst,

  member procedure print_value(self in tst)

) final;
/

create or replace type body tst as
  constructor function tst return self as result
  is
  begin
    self.inner_value := 'DEFAULT';
    return;
  end;

  member function update_value(
    self in tst, 
    p_value varchar2)
  return tst
  is
    a_copy tst;
  begin
    a_copy := tst;
    a_copy.inner_value := p_value;
    return a_copy;
  end;

  member procedure print_value(self in tst)
  is
  begin
    dbms_output.put_line(self.inner_value);
  end;

end;
/

然后TEST按预期打印:

set serveroutput on;
begin
  tst().update_value('TEST').print_value;
end;
/

如果您可以将语句拆分为t := tst(); t.update_value('TEST').print_value;,那么它甚至可以使用in out nocopy self参数。

于 2018-02-21T09:51:45.663 回答