📄 第十章 pl-sql对象类型 - pl-sql用户指南与参考 - whatiswhat.htm
字号:
<TD noWrap>object_type_name.method()
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P>方法的定义规则与打包子程序的相同,也分为说明和体两个部分。说明部分由一个方法名和一个可选的参数列表组成,如果是函数,还需要包含一个返回类型。包体就是一段能执行一个特殊任务的代码。</P>
<P>对于对象类型说明中的每个方法说明,在对象类型体中都必须有与之对应的方法体实现,除非这个方法是用关键字NOT
INSTANTIABLE加以限定,它的意思就是方法体的实现只在子类中出现。为了使方法说明和方法体相匹配,PL/SQL编译器采用token-by-
token的方式把它们的头部进行比较。头部必须精确匹配。</P>
<P>与属性相同,一个形式参数的声明也是由名称和数据类型组成。但是,参数的类型不能受到大小约束。数据的类型可以是任何Oracle类型,除了那些不适用于属性的类型。这些约束也适用于返回值的类型。</P>
<UL>
<LI>方法实现所允许使用的语言 </LI></UL>
<P>Oracle允许我们在PL/SQL、Java或C语言中实现对象方法。我们可以在Java或C语言中实现类型方法,只需提供一个调用说明即可。调用说明在Oracle的数据词典中公布了Java方法或外部C函数。它把程序的名称、参数类型和返回值信息映射到对应的SQL中去。
</P>
<UL>
<LI>SELF参数 </LI></UL>
<P>MEMBER方法接受一个内置的SELF参数,它代表了对象类型的实例。不论显式或隐式声明,它总是第一个传入MEMBER方法的参数。但是,STATIC方法就不能接受或引用SELF。
</P>
<P>在方法体中,SELF指定了被调用方法所属的对象实例。例如,方法transform把SELF声明为IN
OUT参数: </P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD
noWrap><STRONG>CREATE</STRONG> <STRONG>TYPE</STRONG> Complex <STRONG>AS</STRONG> OBJECT (<BR> MEMBER <STRONG>FUNCTION</STRONG> transform (SELF <STRONG>IN</STRONG> <STRONG>OUT</STRONG> Complex) ...
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P>我们不能把SELF指定成其他数据类型。在MEMBER函数中,如果SELF没有声明的话,它的参数默认为IN。但是,在MEMBER过程中,如果SELF没有什么,那么它的参数模式默认为IN
OUT。并且,我们不能把SELF的模式指定为OUT。 </P>
<P>如下例所示,方法可以直接引用SELF的属性,并不需要限定修饰词: </P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD
noWrap><STRONG>CREATE</STRONG> <STRONG>FUNCTION</STRONG> gcd(x <STRONG>INTEGER</STRONG>, y <STRONG>INTEGER</STRONG>)<BR> <STRONG>RETURN</STRONG> <STRONG>INTEGER</STRONG> <STRONG>AS</STRONG><BR> <EM>-- find greatest common divisor of x and y</EM><BR> ans <STRONG>INTEGER</STRONG>;<BR><STRONG>BEGIN</STRONG><BR> <STRONG>IF</STRONG> (y <= x) <STRONG>AND</STRONG>(x <STRONG>MOD</STRONG> y = 0) <STRONG>THEN</STRONG><BR> ans := y;<BR> <STRONG>ELSIF</STRONG> x < y <STRONG>THEN</STRONG><BR> ans := gcd(y, x);<BR> <STRONG>ELSE</STRONG><BR> ans := gcd(y, x <STRONG>MOD</STRONG> y);<BR> <STRONG>END</STRONG> <STRONG>IF</STRONG>;<BR><BR> <STRONG>RETURN</STRONG> ans;<BR><STRONG>END</STRONG>;<BR><BR><STRONG>CREATE</STRONG> <STRONG>TYPE</STRONG> rational <STRONG>AS</STRONG> OBJECT(<BR> num <STRONG>INTEGER</STRONG>,<BR> den <STRONG>INTEGER</STRONG>,<BR> MEMBER <STRONG>PROCEDURE</STRONG> normalize,<BR> ...<BR>);<BR><BR><STRONG>CREATE</STRONG> <STRONG>TYPE</STRONG> <STRONG>BODY</STRONG> rational <STRONG>AS</STRONG><BR> MEMBER <STRONG>PROCEDURE</STRONG> normalize <STRONG>IS</STRONG><BR> g <STRONG>INTEGER</STRONG>;<BR> <STRONG>BEGIN</STRONG><BR> g := gcd(SELF.num, SELF.den);<BR> g := gcd(num, den); <EM>-- equivalent to previous statement</EM><BR> num := num / g;<BR> den := den / g;<BR> <STRONG>END</STRONG> normalize;<BR> ...<BR><STRONG>END</STRONG>;
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P>如果我们从SQL语句中调用了一个空实例(即SELF为空)的MEMBER方法,方法不会被调用,并且会返回一个空值。如果从过程语句调用的话,PL/SQL就会抛出预定义异常SELEF_IS_NULL。
</P>
<UL>
<LI>重载 </LI></UL>
<P>与打包子程序一样,同种类型的方法(函数或过程)都能被重载。也就是说,我们可以为不同的方法起相同的名字,只要它们的形式参数在数量、顺序或数据类型上有所不同。当我们调用其中一个方法的时候,PL/SQL会把实参列表和形参列表作比较,然后找出合适的方法。</P>
<P>子类型也可以重载它的基类方法。这种情况下,方法必须有完全相同的形式参数。</P>
<P>如果两个方法只是在参数模式上不同的话,我们是不能进行重载操作的。并且,我们不能因两个函数的返回值类型不同而对它们进行重载。</P>
<UL>
<LI>MAP和ORDER方法 </LI></UL>
<P>一个标量类型,如CHAR或REAL的值都有一个预定义的顺序,这样它们之间就能进行比较。但是对象类型的实例没有预定义的顺序。要想对它们进行比较或排序就要调用我们自己实现的MAP函数。在下面的例子中,关键字MAP指明了方法convert()通过把Relational对象影射到REAL型上,来对它们进行排序操作:</P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD
noWrap><STRONG>CREATE</STRONG> <STRONG>TYPE</STRONG> rational <STRONG>AS</STRONG> OBJECT(<BR> num <STRONG>INTEGER</STRONG>,<BR> den <STRONG>INTEGER</STRONG>,<BR> MAP MEMBER <STRONG>FUNCTION</STRONG> CONVERT<BR> <STRONG>RETURN</STRONG> <STRONG>REAL</STRONG>,<BR> ...<BR>);<BR><BR><STRONG>CREATE</STRONG> <STRONG>TYPE</STRONG> <STRONG>BODY</STRONG> rational <STRONG>AS</STRONG><BR> MAP MEMBER <STRONG>FUNCTION</STRONG> CONVERT<BR> <STRONG>RETURN</STRONG> <STRONG>REAL</STRONG> <STRONG>IS</STRONG><BR> <STRONG>BEGIN</STRONG><BR> <STRONG>RETURN</STRONG> num / den;<BR> <STRONG>END</STRONG> CONVERT;<BR> ...<BR><STRONG>END</STRONG>;
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P>PL/SQL使用顺序来计算布尔表达式的值,如x < y,并且可以在DISTINCT,GROUP
BY和ORDER
BY子句中作比较。MAP方法convert()可以返回一个对象在所有Relation对象中的相对位置。</P>
<P>一个对象类型只能包含一个MAP方法,它接受一个内置参数SELF并返回一个标量类型:DATE、NUMBER、VARCHAR2,或是一个ANSI
SQL类型,如CHARACTER或REAL。</P>
<P>另外,我们还可以用ORDER方法。一个对象类型只能有一个ORDER方法,它必须是一个能返回数字结果的函数。在下面的例子中,关键字ORDER表明了方法match()可以对两个对象进行比较操作:</P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD
noWrap><STRONG>CREATE</STRONG> <STRONG>TYPE</STRONG> customer <STRONG>AS</STRONG> OBJECT(<BR> ID <STRONG>NUMBER</STRONG>,<BR> NAME <STRONG>VARCHAR2</STRONG>(20),<BR> addr <STRONG>VARCHAR2</STRONG>(30),<BR> <STRONG>ORDER</STRONG> MEMBER <STRONG>FUNCTION</STRONG> match(c customer)<BR> <STRONG>RETURN</STRONG> <STRONG>INTEGER</STRONG><BR>);<BR><BR><STRONG>CREATE</STRONG> <STRONG>TYPE</STRONG> <STRONG>BODY</STRONG> customer <STRONG>AS</STRONG><BR> <STRONG>ORDER</STRONG> MEMBER <STRONG>FUNCTION</STRONG> match(c customer)<BR> <STRONG>RETURN</STRONG> <STRONG>INTEGER</STRONG> <STRONG>IS</STRONG><BR> <STRONG>BEGIN</STRONG><BR> <STRONG>IF</STRONG> ID < c.ID <STRONG>THEN</STRONG><BR> <STRONG>RETURN</STRONG> -1; <EM>-- any negative number will do</EM><BR> <STRONG>ELSIF</STRONG> ID > c.ID <STRONG>THEN</STRONG><BR> <STRONG>RETURN</STRONG> 1; <EM>-- any positive number will do</EM><BR> <STRONG>ELSE</STRONG><BR> <STRONG>RETURN</STRONG> 0;<BR> <STRONG>END</STRONG> <STRONG>IF</STRONG>;<BR> <STRONG>END</STRONG>;<BR><STRONG>END</STRONG>;
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P>每个ORDER方法都只能接受两个参数:内置参数SELF和另外一个同类型的对象。如果c1和c2是Customer对象,一个c1
>
c2这样的比较就会自动调用方法match。该方法能够返回负数、零或正数,分别代表SELF比另外一个对象小、等或大。如果传到ORDER方法的参数任意一个为空,方法就会返回空值。</P>
<P>知道方针:一个MAP方法就好比一个哈希函数,能把对象值影射到标量值,然后用操作符,如<、=等来进行比较。一个ORDER方法只是简单地将两个对象进行比较。</P>
<P>我们可以声明一个MAP方法或是一个ORDER方法,但不同时声明这两个方法。如果我们声明了其中一个,我们就可以在SQL或过程语句中进行对象比较。但是,如果我们没有声明方法,我们就只能在SQL语句中进行等或不等的比较。(两个同类型的对象只有它们对应的属性值相同的时候才相等。)</P>
<P>在对大量的对象进行排序或合并的时候,可以使用一个MAP方法。一次调用会把所有的对象影射到标量中,然后对标量值进行排序。ORDER方法的效率相对比较低,因为它必须反复地调用(它一次只能比较两个对象)。</P>
<UL>
<LI>构造方法 </LI></UL>
<P>每个对象类型都有一个构造方法,它是一个名称与对象类型名称相同的函数,用于初始化,并能返回一个对象类型的新的实例。</P>
<P>Oracle会为每个对象类型生成一个构造函数,其中形参与对象类型的属性相匹配。也就是说,参数和属性是一一对应的关系,并且顺序、名称和数据类型都完全相同。</P>
<P>我们也可以定义自己的构造方法,要么覆盖掉系统定义的构造函数,要么定义一个有着不同方法签名的新构造函数。</P>
<P>PL/SQL从来不会隐式地调用构造函数,所以我们必须显式地调用它。</P>
<P class=title2>3、更改已存在对象类型的属性和方法</P>
<P>我们可以使用ALTER TYPE语句来添加、修改或删除属性,并可以为已存在的对象类型添加或删除方法:
</P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD
noWrap><STRONG>CREATE</STRONG> <STRONG>TYPE</STRONG> person_typ <STRONG>AS</STRONG> OBJECT(<BR> NAME <STRONG>CHAR</STRONG>(20),<BR> ssn <STRONG>CHAR</STRONG>(12),<BR> address <STRONG>VARCHAR2</STRONG>(100)<BR>);<BR><BR><STRONG>CREATE</STRONG> <STRONG>TYPE</STRONG> person_nt <STRONG>IS</STRONG> <STRONG>TABLE</STRONG> <STRONG>OF</STRONG> person_typ;<BR><BR><STRONG>CREATE</STRONG> <STRONG>TYPE</STRONG> dept_typ <STRONG>AS</STRONG> OBJECT(<BR> mgr person_typ,<BR> emps person_nt<BR>);<BR><BR><STRONG>CREATE</STRONG> <STRONG>TABLE</STRONG> dept <STRONG>OF</STRONG> dept_typ;<BR><EM>-- Add new attributes to Person_typ and propagate the change</EM><BR><EM>-- to Person_nt and dept_typ</EM><BR><STRONG>ALTER</STRONG> <STRONG>TYPE</STRONG> person_typ ADD ATTRIBUTE (picture BLOB, dob <STRONG>DATE</STRONG>)<BR> CASCADE <STRONG>NOT</STRONG> INCLUDING <STRONG>TABLE</STRONG> DATA;<BR><BR><STRONG>CREATE</STRONG> <STRONG>TYPE</STRONG> mytype <STRONG>AS</STRONG> OBJECT(<BR> attr1 <STRONG>NUMBER</STRONG>,<BR> attr2 <STRONG>NUMBER</STRONG><BR>);<BR><BR><STRONG>ALTER</STRONG> <STRONG>TYPE</STRONG> mytype ADD ATTRIBUTE (attr3 <STRONG>NUMBER</STRONG>),<BR> <STRONG>DROP</STRONG> ATTRIBUTE attr2,<BR> ADD ATTRIBUTE attr4 <STRONG>NUMBER</STRONG> CASCADE;
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P>在过程编译时,它总是使用当前引用的对象类型版本。在对象类型发生改变时,服务器端引用那个对象类型的过程就变得无效了,在下次过程被调用时它会被自动重新编译。而对于客户端引用被更改过的类型的过程,我们就必须手动编译。
</P>
<P>如果从基类删除一个方法,那么也必须修改覆盖被删除方法的子类。我们可以用ALTER
TYPE的CASADE选择来判断是否有子类被影响到;如果有子类覆盖了方法,那么语句就会被回滚。为了能成功地从基类删除一个方法,我们可以:
</P>
<OL>
<LI>先从子类删除方法
<LI>从基类删除方法,然后用不带OVERRIDING关键字的ALTER TYPE把它重新添加进去
</LI></OL>
<P class=title1>六、定义对象类型</P>
<P>对象类型可以表现现实世界中的任何实体。例如,一个对象类型能表现学生,银行账户,计算机显示器,有理数或者是像队列,栈,链表这样的数据结构。这一节给出了几个完整的例子,让我们了解如何设计对象类型并编写我们自己的对象类型。</P>
<P>目前我们还不能在PL/SQL块、子程序或包中定义对象类型。但是,我们可以在SQL*Plus中用下面的语法来定义它:
</P>
<BLOCKQUOTE>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -