- 浏览: 147922 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
hk_581:
这个有java实现吗
SSDP协议 -
堕落星辰:
太谢谢了! 找了好久 就你这个例子能显示
Extjs TreeGrid加载数据报未组织好 -
lizhuang:
毫秒数算起来应该比这个好
Java 返回两个时间段的小时数和分钟数 -
love_nana:
这个方法是有问题的吧,2012-05-16 12:30 ~~~ ...
Java 返回两个时间段的小时数和分钟数 -
猫小小质:
找了好几天 就你这个例子能运行起来,爱死你了
Extjs TreeGrid加载数据报未组织好
一、抽象的角色
抽象是对一个真实世界实体的高级描述或建模。它能排除掉无关的细节内容,使我们的日常生活更有条理。例如,驾驶一辆汽车时,我们是不需要知道它的发动机是如何工作的。由变速排档、方向盘、加速器和刹车组成的接口就能让我们有效地使用它。而其中每一项的详细信息对于日常驾驶来说并不重要。
抽象是编程的核心内容。例如,我们在隐藏一个复杂的算法时只要编写一个过程,然后为它传递参数就可以做到过程化抽象。如果需要改变具体的实现,换一个过程体即可。有了抽象后,那些调用过程的程序就不需要再修改了。
我们在指定变量的数据类型时,可以使用数据抽象。数据类型代表了对于想操作的数值的值集合和操作符集合。比如说一个POSITIVE类型的变量,只能存放正整数,也只能用于加减乘等运算。使用这个变量时,我们不必知道PL/SQL是如何存储整数或是实现算术运算的。
对象类型是大多数编程语言内置类型的一个概括。PL/SQL提供了大量的标量类型和复合类型,每种类型都与一组预定义操作符相关联。标量类型(如 CHAR)是没有内部组成成分的。但复合类型(如RECORD)是有内部组成成分的,并且其中每一个部分都可以被独立操作。同RECORD类型一样,对象类型也是复合类型。但是,它的操作是用户自定义的,而不是预定义的。
目前,我们还不能用PL/SQL定义对象类型。它们必须用CREATE语句创建并存放在Oracle数据库中,这样才能被许多程序所共享。使用对象类型的程序称为客户端程序,它可以声明并操作对象,但并不要求知道对象类型是如何表现数据或实现操作。这就能够让我们分别编写程序和对象类型,即便是在改变了对象实现时也不会影响到程序。因此,对象类型既支持过程化和又支持数据抽象。
二、什么是对象类型
对象类型是一个用户自定义复合类型,它封装了数据结构和操作这个数据结构的函数和过程。数据结构中的变量称为属性,函数和过程称为方法。通常,我们认为对象(如人、车、银行账户)都是有着属性和行为的。例如一个婴儿有性别、年龄和体重这些属性,行为有吃、喝、睡等。对象类型能够让我们把这些内容抽象出来并在应用程序中使用。
使用CREATE TYPE语句创建对象类型的时候,我们实际上是创建了真实世界中某个对象的抽象模板。模板只指定了我们在程序中能用到的属性和行为。比如一个雇员有很多属性,但通常只有他的一部分信息是我们的应用程序所需要的,见下图:
假设我们现在需要编写一个为雇员分发奖金的程序。因为并不是雇员的所有属性都能用于解决这个问题,所以,我们只需设计一个抽象的雇员,拥有与解决问题相关的属性即可:姓名、ID号、部门、职称、工资和级别。然后,设计一些具体的操作方法,例如更改雇员的级别。
下一步就是定义用于数据表现的变量(属性)和用于执行操作的子程序集(方法)。最后,我们把属性和方法封装到对象类型中去。
对象的属性是公有的(对客户端程序可见)。但是,设计良好的程序是不应该直接操作这些属性的,我们应该为这些操作编写相应的方法。这样,雇员数据就能保存在一个合适的状态。
在运行时,我们可以建立抽象雇员的实例(通常称为对象),然后为它的属性赋值。我们可以按照我们的需求创建任意多个实例。每个对象都有姓名、编号、职别等,如下图所示:
三、为什么使用对象类型
对象类型能把大的系统划分为多个逻辑实体,简化系统复杂度。这就使我们可以创建模块化、可维护、可重用的组件。也能让不同小组的程序员并行开发软件组件。
对象类型靠封装数据的操作来把数据维护代码从SQL脚本中分离出来,并把PL/SQL块封装到方法里。使用对象方法可以避免很多在数据访问时带来的负面影响,同时,对象类型隐藏实现细节,更新细节内容时不必修改客户端程序。
对象类型可以为现实数据建模。现实世界中的复杂实体和关系都可以直接映射到对象类型中。并且,对象类型还可以直接映射到面向对象语言(如Java和C++)的类中。
四、对象类型的结构
与包相同,对象类型也有两部分:说明和体,如下图所示。说明部分是应用程序接口;它声明了数据结构(属性集合)和所需的操作(方法)。方法体部分是对已声明方法的实现。
客户端程序要使用到的所有方法都在说明中声明。我们可以把对象说明想象成一个可选的接口,把对象体想象成一个黒盒。我们可以在不改变说明部分的前提下调试,增强或替换对象体,并且不会对客户端程序造成影响。
在一个对象说明中,所有的属性都必须声明在方法之前。只有子程序的声明才需要实现。所以,如果一个对象类型的说明只声明了属性,那么对象类型的体就没有必要了。我们不能在对象体中声明属性。对象说明中的声明都是公有的。
为了能更好的了解结构,请看下面的例子。这是一个复数的对象类型,有实数部分和虚数部分,并有几个与复数操作相关的方法。
CREATE TYPE complex AS OBJECT(
rpart REAL , -- attribute
ipart REAL ,
MEMBER FUNCTION plus(x complex)
RETURN complex, -- method
MEMBER FUNCTION LESS(x complex)
RETURN complex,
MEMBER FUNCTION times(x complex)
RETURN complex,
MEMBER FUNCTION divby(x complex)
RETURN complex
);
CREATE TYPE BODY complex AS
MEMBER FUNCTION plus(x complex)
RETURN complex IS
BEGIN
RETURN complex(rpart + x.rpart, ipart + x.ipart);
END plus;
MEMBER FUNCTION LESS(x complex)
RETURN complex IS
BEGIN
RETURN complex(rpart - x.rpart, ipart - x.ipart);
END LESS;
MEMBER FUNCTION times(x complex)
RETURN complex IS
BEGIN
RETURN complex(rpart * x.rpart - ipart * x.ipart,
rpart * x.ipart + ipart * x.rpart);
END times;
MEMBER FUNCTION divby(x complex)
RETURN complex IS
z REAL := x.rpart ** 2 + x.ipart ** 2;
BEGIN
RETURN complex((rpart * x.rpart + ipart * x.ipart) / z,
(ipart * x.rpart - rpart * x.ipart) / z);
END divby;
END ;
五、对象类型组件
对象类型封装了数据和操作。我们可以在对象类型说明中声明属性和方法,但不能声明常量、异常、游标或类型。我们至少要声明一个属性(最多1000个),方法是可选的。
1、属性
同变量一样,属性也有名称和数据类型。对象类型中的名称必须是唯一的(但在其他的对象类型中可以重用)。除了下面几种类型之外,其他任何Oralce类型都可以使用:
- LONG和LONG RAW
- ROWID和UROWID
- PL/SQL特定类型BINARY_INTEGER及它的子类型、BOOLEAN、PLS_INTEGER、RECORD、REF CURSOR、%TYPE和%ROWTYPE
- PL/SQL包内定义的数据类型
我们不能在声明属性的时候用赋值语句或DEFAULT子句为它初始化。同样,也不能对属性应用NOT NULL约束。但是,对象是可以存放到添加了约束的数据表中。
数据结构中的属性集合依赖于真实世界中的对象。例如,为了表现一个分数,我们只需要两个INTEGER类型的变量。另一方面,要是表现一个学生,我们需要几个VARCHAR2来存放姓名、住址、电话号码和状态等,再添加一个VARRAY类型变量用来存储课程和分数。
数据结构可能是复杂的。例如,一个属性的数据类型可能是另外一个对象类型(称为嵌套对象类型)。有些对象类型,像队列、链表和树,都是动态的,它们是随着使用的需要而动态改变存储长度的。递归对象类型能够直接或间接的包含自身类型,这样就能创建出更诡异的数据类型。
2、方法
一般的,方法就是用关键字MEMBER或STATIC声明在对象说明部分的子程序。方法名不能和对象类型名、属性名重复。MEMBER方法只能通过对象实例调用,如:
instance_expression.method()
但是,STATIC方法直接通过对象类型调用,而不是实例,如:
object_type_name.method()
方法的定义规则与打包子程序的相同,也分为说明和体两个部分。说明部分由一个方法名和一个可选的参数列表组成,如果是函数,还需要包含一个返回类型。包体就是一段能执行一个特殊任务的代码。
对于对象类型说明中的每个方法说明,在对象类型体中都必须有与之对应的方法体实现,除非这个方法是用关键字NOT INSTANTIABLE加以限定,它的意思就是方法体的实现只在子类中出现。为了使方法说明和方法体相匹配,PL/SQL编译器采用token-by- token的方式把它们的头部进行比较。头部必须精确匹配。
与属性相同,一个形式参数的声明也是由名称和数据类型组成。但是,参数的类型不能受到大小约束。数据的类型可以是任何Oracle类型,除了那些不适用于属性的类型。这些约束也适用于返回值的类型。
- 方法实现所允许使用的语言
Oracle允许我们在PL/SQL、Java或C语言中实现对象方法。我们可以在Java或C语言中实现类型方法,只需提供一个调用说明即可。调用说明在Oracle的数据词典中公布了Java方法或外部C函数。它把程序的名称、参数类型和返回值信息映射到对应的SQL中去。
- SELF参数
MEMBER方法接受一个内置的SELF参数,它代表了对象类型的实例。不论显式或隐式声明,它总是第一个传入MEMBER方法的参数。但是,STATIC方法就不能接受或引用SELF。
在方法体中,SELF指定了被调用方法所属的对象实例。例如,方法transform把SELF声明为IN OUT参数:
CREATE TYPE Complex AS OBJECT (
MEMBER FUNCTION transform (SELF IN OUT Complex) ...
我们不能把SELF指定成其他数据类型。在MEMBER函数中,如果SELF没有声明的话,它的参数默认为IN。但是,在MEMBER过程中,如果SELF没有什么,那么它的参数模式默认为IN OUT。并且,我们不能把SELF的模式指定为OUT。
如下例所示,方法可以直接引用SELF的属性,并不需要限定修饰词:
CREATE FUNCTION gcd(x INTEGER , y INTEGER )
RETURN INTEGER AS
-- find greatest common divisor of x and y
ans INTEGER ;
BEGIN
IF (y <= x) AND (x MOD y = 0) THEN
ans := y;
ELSIF x < y THEN
ans := gcd(y, x);
ELSE
ans := gcd(y, x MOD y);
END IF ;
RETURN ans;
END ;
CREATE TYPE rational AS OBJECT(
num INTEGER ,
den INTEGER ,
MEMBER PROCEDURE normalize,
...
);
CREATE TYPE BODY rational AS
MEMBER PROCEDURE normalize IS
g INTEGER ;
BEGIN
g := gcd(SELF.num, SELF.den);
g := gcd(num, den); -- equivalent to previous statement
num := num / g;
den := den / g;
END normalize;
...
END ;
如果我们从SQL语句中调用了一个空实例(即SELF为空)的MEMBER方法,方法不会被调用,并且会返回一个空值。如果从过程语句调用的话,PL/SQL就会抛出预定义异常SELEF_IS_NULL。
- 重载
与打包子程序一样,同种类型的方法(函数或过程)都能被重载。也就是说,我们可以为不同的方法起相同的名字,只要它们的形式参数在数量、顺序或数据类型上有所不同。当我们调用其中一个方法的时候,PL/SQL会把实参列表和形参列表作比较,然后找出合适的方法。
子类型也可以重载它的基类方法。这种情况下,方法必须有完全相同的形式参数。
如果两个方法只是在参数模式上不同的话,我们是不能进行重载操作的。并且,我们不能因两个函数的返回值类型不同而对它们进行重载。
- MAP和ORDER方法
一个标量类型,如CHAR或REAL的值都有一个预定义的顺序,这样它们之间就能进行比较。但是对象类型的实例没有预定义的顺序。要想对它们进行比较或排序就要调用我们自己实现的MAP函数。在下面的例子中,关键字MAP指明了方法convert()通过把Relational对象影射到REAL型上,来对它们进行排序操作:
CREATE TYPE rational AS OBJECT(
num INTEGER ,
den INTEGER ,
MAP MEMBER FUNCTION CONVERT
RETURN REAL ,
...
);
CREATE TYPE BODY rational AS
MAP MEMBER FUNCTION CONVERT
RETURN REAL IS
BEGIN
RETURN num / den;
END CONVERT;
...
END ;
PL/SQL使用顺序来计算布尔表达式的值,如x < y,并且可以在DISTINCT,GROUP BY和ORDER BY子句中作比较。MAP方法convert()可以返回一个对象在所有Relation对象中的相对位置。
一个对象类型只能包含一个MAP方法,它接受一个内置参数SELF并返回一个标量类型:DATE、NUMBER、VARCHAR2,或是一个ANSI SQL类型,如CHARACTER或REAL。
另外,我们还可以用ORDER方法。一个对象类型只能有一个ORDER方法,它必须是一个能返回数字结果的函数。在下面的例子中,关键字ORDER表明了方法match()可以对两个对象进行比较操作:
CREATE TYPE customer AS OBJECT(
ID NUMBER ,
NAME VARCHAR2 (20),
addr VARCHAR2 (30),
ORDER MEMBER FUNCTION match(c customer)
RETURN INTEGER
);
CREATE TYPE BODY customer AS
ORDER MEMBER FUNCTION match(c customer)
RETURN INTEGER IS
BEGIN
IF ID < c.ID THEN
RETURN -1; -- any negative number will do
ELSIF ID > c.ID THEN
RETURN 1; -- any positive number will do
ELSE
RETURN 0;
END IF ;
END ;
END ;
每个ORDER方法都只能接受两个参数:内置参数SELF和另外一个同类型的对象。如果c1和c2是Customer对象,一个c1 > c2这样的比较就会自动调用方法match。该方法能够返回负数、零或正数,分别代表SELF比另外一个对象小、等或大。如果传到ORDER方法的参数任意一个为空,方法就会返回空值。
知道方针:一个MAP方法就好比一个哈希函数,能把对象值影射到标量值,然后用操作符,如<、=等来进行比较。一个ORDER方法只是简单地将两个对象进行比较。
我们可以声明一个MAP方法或是一个ORDER方法,但不同时声明这两个方法。如果我们声明了其中一个,我们就可以在SQL或过程语句中进行对象比较。但是,如果我们没有声明方法,我们就只能在SQL语句中进行等或不等的比较。(两个同类型的对象只有它们对应的属性值相同的时候才相等。)
在对大量的对象进行排序或合并的时候,可以使用一个MAP方法。一次调用会把所有的对象影射到标量中,然后对标量值进行排序。ORDER方法的效率相对比较低,因为它必须反复地调用(它一次只能比较两个对象)。
- 构造方法
每个对象类型都有一个构造方法,它是一个名称与对象类型名称相同的函数,用于初始化,并能返回一个对象类型的新的实例。
Oracle会为每个对象类型生成一个构造函数,其中形参与对象类型的属性相匹配。也就是说,参数和属性是一一对应的关系,并且顺序、名称和数据类型都完全相同。
我们也可以定义自己的构造方法,要么覆盖掉系统定义的构造函数,要么定义一个有着不同方法签名的新构造函数。
PL/SQL从来不会隐式地调用构造函数,所以我们必须显式地调用它。
3、更改已存在对象类型的属性和方法
我们可以使用ALTER TYPE语句来添加、修改或删除属性,并可以为已存在的对象类型添加或删除方法:
CREATE TYPE person_typ AS OBJECT(
NAME CHAR (20),
ssn CHAR (12),
address VARCHAR2 (100)
);
CREATE TYPE person_nt IS TABLE OF person_typ;
CREATE TYPE dept_typ AS OBJECT(
mgr person_typ,
emps person_nt
);
CREATE TABLE dept OF dept_typ;
-- Add new attributes to Person_typ and propagate the change
-- to Person_nt and dept_typ
ALTER TYPE person_typ ADD ATTRIBUTE (picture BLOB, dob DATE )
CASCADE NOT INCLUDING TABLE DATA;
CREATE TYPE mytype AS OBJECT(
attr1 NUMBER ,
attr2 NUMBER
);
ALTER TYPE mytype ADD ATTRIBUTE (attr3 NUMBER ),
DROP ATTRIBUTE attr2,
ADD ATTRIBUTE attr4 NUMBER CASCADE;
在过程编译时,它总是使用当前引用的对象类型版本。在对象类型发生改变时,服务器端引用那个对象类型的过程就变得无效了,在下次过程被调用时它会被自动重新编译。而对于客户端引用被更改过的类型的过程,我们就必须手动编译。
如果从基类删除一个方法,那么也必须修改覆盖被删除方法的子类。我们可以用ALTER TYPE的CASADE选择来判断是否有子类被影响到;如果有子类覆盖了方法,那么语句就会被回滚。为了能成功地从基类删除一个方法,我们可以:
- 先从子类删除方法
- 从基类删除方法,然后用不带OVERRIDING关键字的ALTER TYPE把它重新添加进去
六、定义对象类型
对象类型可以表现现实世界中的任何实体。例如,一个对象类型能表现学生,银行账户,计算机显示器,有理数或者是像队列,栈,链表这样的数据结构。这一节给出了几个完整的例子,让我们了解如何设计对象类型并编写我们自己的对象类型。
目前我们还不能在PL/SQL块、子程序或包中定义对象类型。但是,我们可以在SQL*Plus中用下面的语法来定义它:
CREATE [OR REPLACE] TYPE type_name
[AUTHID {CURRENT_USER | DEFINER}]
{ {IS | AS } OBJECT | UNDER supertype_name }
(
attribute_name datatype[, attribute_name datatype]...
[{MAP | ORDER } MEMBER function_spec,]
[{FINAL| NOT FINAL} MEMBER function_spec,]
[{INSTANTIABLE| NOT INSTANTIABLE} MEMBER function_spec,]
[{MEMBER | STATIC} {subprogram_spec | call_spec}
[, {MEMBER | STATIC} {subprogram_spec | call_spec}]...]
) [{FINAL| NOT FINAL}] [ {INSTANTIABLE| NOT INSTANTIABLE}];
[CREATE [OR REPLACE] TYPE BODY type_name {IS | AS }
{ {MAP | ORDER } MEMBER function_body;
| {MEMBER | STATIC} {subprogram_body | call_spec};}
[{MEMBER | STATIC} {subprogram_body | call_spec};]...
END ;]
1、PL/SQL类型继承一览
PL/SQL支持单继承模式。我们可以定义对象类型的子类型。这些子类型包括父类型(或超类)所有的属性和方法。子类型还可以包括额外的属性和方法,并可以覆盖超类的方法。
我们还可以定义子类是否能继承于某个特定的类型。我们也可以定义不能直接初始化的类型和方法,只有声明它们的子类才可以进行初始化操作。
有些类型属性可以用ALTER TYPE语句动态的改变。当基类发生变化时,无论是用ALTER TYPE语句还是重新定义基类,子类会自动的应用这些改变的内容。我们可以用TREAT操作符只返回某一个指定的子类的对象。
从REF和DEREF函数中产生的值可以代表声明过的表或视图类型,或是一个或多个它的子类型。
- PL/SQL类继承举例
-- Create a supertype from which several subtypes will be derived.
CREATE TYPE person_typ AS OBJECT(
ssn NUMBER ,
NAME VARCHAR2 (30),
address VARCHAR2 (100)
)
NOT FINAL;
-- Derive a subtype that has all the attributes of the supertype,
-- plus some additional attributes.
CREATE TYPE student_typ UNDER person_typ(
deptid NUMBER ,
major VARCHAR2 (30)
)
NOT FINAL;
-- Because Student_typ is declared NOT FINAL, you can derive
-- further subtypes from it.
CREATE TYPE parttimestudent_typ UNDER student_typ(
numhours NUMBER
)
;
-- Derive another subtype. Because it has the default attribute
-- FINAL, you cannot use Employee_typ as a supertype and derive
-- subtypes from it.
CREATE TYPE employee_typ UNDER person_typ(
empid NUMBER ,
mgr VARCHAR2 (30)
)
;
-- Define an object type that can be a supertype. Because the
-- member function is FINAL, it cannot be overridden in any
-- subtypes.
CREATE TYPE T AS OBJECT (..., MEMBER PROCEDURE Print(), FINAL MEMBER
FUNCTION foo(x NUMBER )...) NOT FINAL;
-- We never want to create an object of this supertype. By using
-- NOT INSTANTIABLE, we force all objects to use one of the subtypes
-- instead, with specific implementations for the member functions.
CREATE TYPE Address_typ AS OBJECT(...) NOT INSTANTIABLE NOT FINAL;
-- These subtypes can provide their own implementations of
-- member functions, such as for validating phone numbers and
-- postal codes. Because there is no "generic" way of doing these
-- things, only objects of these subtypes can be instantiated.
CREATE TYPE USAddress_typ UNDER Address_typ(...);
CREATE TYPE IntlAddress_typ UNDER Address_typ(...);
-- Return REFs for those Person_typ objects that are instances of
-- the Student_typ subtype, and NULL REFs otherwise.
SELECT TREAT(REF (p) AS REF student_typ)
FROM person_v p;
-- Example of using TREAT for assignment...
-- Return REFs for those Person_type objects that are instances of
-- Employee_type or Student_typ, or any of their subtypes.
SELECT REF (p)
FROM person_v p
WHERE VALUE(p) IS OF (employee_typ, student_typ);
-- Similar to above, but do not allow any subtypes of Student_typ.
SELECT REF (p)
FROM person_v p
WHERE VALUE(p) IS OF (ONLY student_typ);
-- The results of REF and DEREF can include objects of Person_typ
-- and its subtypes such as Employee_typ and Student_typ.
SELECT REF (p)
FROM person_v p;
SELECT DEREF(REF (p))
FROM person_v p;
2、对象类型实例:栈
栈是一个有序集合。栈有一个栈顶和一个栈底。栈中的每一项都只能在栈顶添加或删除。所以,最后一个被加入栈的项会被最先删除。(可以把栈想象成自助餐厅中的盘子。)压栈和退栈操作能够对栈进行后进先出(LIFO)更新。
栈能应用在很多地方。例如,它们可以用在系统编程中控制中断优先级并对递归进行管理。最简单的栈实现就是使用整数数组,数组的一端代表了栈顶。
PL/SQL提供了VARRAY数据类型,它能让我们声明变长数组。要声明变长数组属性,必须先定义变长数组类型。但是,我们不能再对象说明中定义类型,所以,我们只能单独的定义变长数组类型,并指定它的最大长度,具体实现如下:
CREATE TYPE IntArray AS VARRAY(25) OF INTEGER ;
现在我们可以编写对象类型说明了:
CREATE TYPE stack AS OBJECT(
max_size INTEGER ,
top INTEGER ,
POSITION intarray,
MEMBER PROCEDURE initialize,
MEMBER FUNCTION FULL
RETURN BOOLEAN ,
MEMBER FUNCTION empty
RETURN BOOLEAN ,
MEMBER PROCEDURE push(n IN INTEGER ),
MEMBER PROCEDURE pop(n OUT INTEGER )
);
最后,我们可以编写对象类型体:
CREATE TYPE BODY stack AS
MEMBER PROCEDURE initialize IS
BEGIN
top := 0;
/* Call constructor for varray and set element 1 to NULL. */
POSITION := intarray(NULL );
max_size := POSITION.LIMIT; -- get varray size constraint
POSITION.EXTEND(max_size - 1, 1); -- copy element 1 into 2..25
END initialize;
MEMBER FUNCTION FULL
RETURN BOOLEAN IS
BEGIN
RETURN (top = max_size); -- return TRUE if stack is full
END FULL;
MEMBER FUNCTION empty
RETURN BOOLEAN IS
BEGIN
RETURN (top = 0); -- return TRUE if stack is empty
END empty;
MEMBER PROCEDURE push(n IN INTEGER ) IS
BEGIN
IF NOT FULL THEN
top := top + 1; -- push integer onto stack
POSITION(top) := n;
ELSE -- stack is full
raise_application_error(-20101, 'stack overflow' );
END IF ;
END push;
MEMBER PROCEDURE pop(n OUT INTEGER ) IS
BEGIN
IF NOT empty THEN
n := POSITION(top);
top := top - 1; -- pop integer off stack
ELSE
-- stack is empty
raise_application_error(-20102, 'stack underflow' );
END IF ;
END pop;
END ;
在成员过程push和pop中,我们使用内置过程raise_application_error来关联用户定义的错误消息。这样,我们就能把错误报告给客户端程序而避免把未控制异常传给主环境。客户端程序捕获PL/SQL异常后,可以在OTHERS异常控制句柄中用SQLCODE和SQLERRM 来确定具体的错误信息。下例中,当异常被抛出时,我们就把对应的错误消息输出:
DECLARE
...
BEGIN
...
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line(SQLERRM );
END ;
另外,程序还可以使用编译指示EXCEPTION_INIT把raise_application_error返回的错误编号影射到命名异常中,如下例所示:
DECLARE
stack_overflow EXCEPTION ;
stack_underflow EXCEPTION ;
PRAGMA EXCEPTION_INIT(stack_overflow, -20101);
PRAGMA EXCEPTION_INIT(stack_underflow, -20102);
BEGIN
...
EXCEPTION
WHEN stack_overflow THEN
...
END ;
3、对象类型实例:售票处
假如有一个连锁电影院,每个影院有三个银幕。每个影院有一个售票处,销售三种不同电影的影票。所有影票的价格都为三美元。定期检查影票的销售情况,然后及时补充影票。
在定义代表销售处的对象类型之前,我们必须考虑到必要的数据和操作。对于一个简单的售票处来说,对象类型需要票价、当前影票存量和已售影票的收据这些属性。它还需要一些方法:购票、盘存、补充存量和收集收据。
对于收据,我们可以使用含有三个元素的数组。元素1、2和3各自记录电影1、2和3。要声明一个变长数组属性,我们就必须先像下面这样定义它的类型:
CREATE TYPE RealArray AS VARRAY(3) OF REAL ;
现在,我们可以编写对象类型说明:
CREATE TYPE ticket_booth AS OBJECT(
price REAL ,
qty_on_hand INTEGER ,
receipts realarray,
MEMBER PROCEDURE initialize,
MEMBER PROCEDURE purchase(movie INTEGER , amount REAL , CHANGE OUT REAL ),
MEMBER FUNCTION inventory
RETURN INTEGER ,
MEMBER PROCEDURE replenish(quantity INTEGER ),
MEMBER PROCEDURE COLLECT (movie INTEGER , amount OUT REAL )
);
最后,我们可以编写对象类型体:
CREATE TYPE BODY ticket_booth AS
MEMBER PROCEDURE initialize IS
BEGIN
price := 3.00;
qty_on_hand := 5000; -- provide initial stock of tickets
-- call constructor for varray and set elements 1..3 to zero
receipts := realarray(0, 0, 0);
END initialize;
MEMBER PROCEDURE purchase(movie INTEGER , amount REAL , CHANGE OUT REAL ) IS
BEGIN
IF qty_on_hand = 0 THEN
raise_application_error(-20103, 'out of stock' );
END IF ;
IF amount >= price THEN
qty_on_hand := qty_on_hand - 1;
receipts(movie) := receipts(movie) + price;
CHANGE := amount - price;
ELSE -- amount is not enough
CHANGE := amount; -- so return full amount
END IF ;
END purchase;
MEMBER FUNCTION inventory
RETURN INTEGER IS
BEGIN
RETURN qty_on_hand;
END inventory;
MEMBER PROCEDURE replenish(quantity INTEGER ) IS
BEGIN
qty_on_hand := qty_on_hand + quantity;
END replenish;
MEMBER PROCEDURE COLLECT (movie INTEGER , amount OUT REAL ) IS
BEGIN
amount := receipts(movie); -- get receipts for a given movie
receipts(movie) := 0; -- reset receipts to zero
END COLLECT ;
END ;
4、对象类型实例:银行账户
在定义银行账户对象类型之前,我们必须考虑一下要使用的数据和操作。对于一个简单的银行账户来说,对象类型需要一个账号、余额和状态这三个属性。所需的操作有:打开帐户,验证账号,关闭账户,存款,取款和余额结算。
首先,我们要像下面这样编写对象类型说明:
CREATE TYPE bank_account AS OBJECT(
acct_number INTEGER (5),
balance REAL ,
status VARCHAR2 (10),
MEMBER PROCEDURE OPEN (amount IN REAL ),
MEMBER PROCEDURE verify_acct(num IN INTEGER ),
MEMBER PROCEDURE CLOSE (num IN INTEGER , amount OUT REAL ),
MEMBER PROCEDURE deposit(num IN INTEGER , amount IN REAL ),
MEMBER PROCEDURE withdraw(num IN INTEGER , amount IN REAL ),
MEMBER FUNCTION curr_bal(num IN INTEGER )
RETURN REAL
);
然后编写对象体:
CREATE TYPE BODY bank_account AS
MEMBER PROCEDURE OPEN (amount IN REAL ) IS
-- open account with initial deposit
BEGIN
IF NOT amount > 0 THEN
raise_application_error(-20104, 'bad amount' );
END IF ;
SELECT acct_sequence.NEXTVAL
INTO acct_number
FROM DUAL;
status := 'open' ;
balance := amount;
END OPEN ;
MEMBER PROCEDURE verify_acct(num IN INTEGER ) IS
-- check for wrong account number or closed account
BEGIN
IF (num <> acct_number) THEN
raise_application_error(-20105, 'wrong number' );
ELSIF (status = 'closed' ) THEN
raise_application_error(-20106, 'account closed' );
END IF ;
END verify_acct;
MEMBER PROCEDURE CLOSE (num IN INTEGER , amount OUT REAL ) IS
-- close account and return balance
BEGIN
verify_acct(num);
status := 'closed' ;
amount := balance;
END CLOSE ;
MEMBER PROCEDURE deposit(num IN INTEGER , amount IN REAL ) IS
BEGIN
verify_acct(num);
IF NOT amount > 0 THEN
raise_application_error(-20104, 'bad amount' );
END IF ;
balance := balance + amount;
END deposit;
MEMBER PROCEDURE withdraw(num IN INTEGER , amount IN REAL ) IS
-- if account has enough funds, withdraw
-- given amount; else, raise an exception
BEGIN
verify_acct(num);
IF amount <= balance THEN
balance := balance - amount;
ELSE
raise_application_error(-20107, 'insufficient funds' );
END IF ;
END withdraw;
MEMBER FUNCTION curr_bal(num IN INTEGER )
RETURN REAL IS
BEGIN
verify_acct(num);
RETURN balance;
END curr_bal;
END ;
发表评论
-
PL\SQL用户指南与参考12结束篇 转载
2011-04-22 17:02 955第十二章 PL/SQL应用程序性能调优 一、P ... -
PL\SQL用户指南与参考11 转载
2011-04-22 17:01 1025第十一章 本地动态SQL 一、什么是动态SQL 大多数PL ... -
PL\SQL用户指南与参考10.2 转载
2011-04-22 17:00 9485、对象类型实例:实数 有理数能够表现成两个整数相除的形式, ... -
PL\SQL用户指南与参考9.2 转载
2011-04-22 16:58 942九、系统包一览 Oracle和各种Oracle工具都提供了系 ... -
PL\SQL用户指南与参考9.1 转载
2011-04-22 16:57 912第九章 PL/SQL包 一、什么是PL/SQL包 ... -
PL\SQL用户指南与参考8 转载
2011-04-22 16:56 928第八章 PL/SQL子程序 一、什么是子程序 ... -
PL\SQL用户指南与参考7.2 转载
2011-04-22 16:55 874八、处理PL/SQL异常 异常抛出时,PL/SQL块或子程序 ... -
PL\SQL用户指南与参考7.1 转载
2011-04-22 16:54 869第七章 控制PL/SQL错误 ... -
PL\SQL用户指南与参考6.3 转载
2011-04-22 16:53 1425八、PL/SQL中的事务处理 ... -
PL\SQL用户指南与参考6.2 转载
2011-04-22 16:52 1593其它的OPEN-FOR语句可以使用不同的查询打开同样的游标变量 ... -
PL\SQL用户指南与参考6.1 转载
2011-04-22 16:49 1074第六章 PL/SQL与Oracle间交互 一、PL/SQ ... -
PL\SQL用户指南与参考5.2.2 转载
2011-04-22 09:53 1013十五、什么是记录 记录就是相关的数据项集中存储在一个单元中, ... -
PL\SQL用户指南与参考5.2.1 转载
2011-04-22 09:52 802六、集合的赋值 集合可以用INSERT、UPDATE、FET ... -
PL\SQL用户指南与参考5.1.2 转载
2011-04-22 09:50 901六、集合的赋值 集合可以用INSERT、UPDATE、FET ... -
PL\SQL用户指南与参考5.1.1 转载
2011-04-22 09:50 1024第五章 PL/SQL集合与记录 ... -
PL\SQL用户指南与参考4 转载
2011-04-21 11:26 911第四章 PL/SQL的控制结构 一、PL/SQL控制结构一览 ... -
PL\SQL用户指南与参考3 转载
2011-04-20 17:41 1441第三章 PL/SQL数据类型 ... -
PL\SQL用户指南与参考2 转载
2011-04-19 09:25 1117第二章 PL/SQL基础 一、字符集 ... -
PL\SQL用户指南与参考 转载
2011-04-18 17:58 1004第一章 PL/SQL一览 一、理解PL/SQL的主要特性 ...
相关推荐
### PL/SQL Developer 7 中文用户指南 #### 一、引言 PL/SQL Developer 是一款专为 Oracle 数据库设计的强大开发工具,它能够帮助开发者高效地编写、调试及优化 PL/SQL 代码。本指南旨在为使用 PL/SQL Developer 7 ...
### PL/SQL Developer 6.0 用户指南知识点详解 #### 一、介绍 **PL/SQL Developer** 是一款专门用于开发、调试和管理 Oracle 数据库中的 PL/SQL 对象的强大工具。版本 6.0 作为早期的一个版本,虽然可能在某些方面...
### PL/SQL Developer 7.0 用户指南知识点详解 #### 一、介绍 **PL/SQL Developer** 是一款专门用于开发、调试以及管理 Oracle 数据库中的 PL/SQL 对象的强大工具。版本 7.0 作为该系列的一个重要版本,为用户提供...
《PL/SQL Developer 7.0 用户指南》是一份详尽的指南,旨在帮助用户理解和掌握如何使用PL/SQL Developer工具进行高效地开发、测试和优化PL/SQL程序。 #### 二、系统安装与配置 ##### 2.1 系统需求 - **硬件要求**:...
### PL/SQL Developer 7.0 用户指南关键知识点解析 #### 一、介绍 **PL/SQL Developer** 是一款专为 Oracle 数据库设计的强大集成开发环境(IDE),它旨在简化和提升 PL/SQL 应用程序的开发效率。版本 7.0 作为该...
本手册介绍的是Oracle 10g Release 1 (10.1)版本下的PL/SQL用户指南和参考文档。自1996年首次发布以来,PL/SQL经历了多个版本的迭代和发展,每一次更新都带来了更多的特性和改进性能。10g Release 1是Oracle在2003年...
### PL/SQL Developer 7.0 用户指南 #### 一、引言 PL/SQL Developer 7.0 是一款专为 Oracle 数据库设计的强大集成开发环境(IDE),它旨在简化和加速 PL/SQL 应用程序的开发过程。通过提供一系列高效的功能和工具...
### PL/SQL Developer 7.0 用户指南关键知识点解析 #### 一、介绍 **PL/SQL Developer** 是一款专为 Oracle 数据库设计的强大集成开发环境(IDE),它旨在简化和提高 PL/SQL 应用程序的开发效率。此版本(7.0)提供了...
这篇详尽的手册旨在帮助用户更好地理解和操作 PostgreSQL 10.1,特别是对于开发“高斯”这样的项目来说,这将是一个不可多得的参考资料。 1. **新特性与改进** - **并行查询**:PostgreSQL 10 引入了并行查询机制...
- **与SQL紧密结合**:PL/SQL与SQL无缝集成,可以轻松地在两者之间切换。 - **高性能**:PL/SQL的执行效率非常高,尤其适用于处理大量数据的应用场景。 - **执行过程**:PL/SQL引擎可以将程序分解成SQL和PL/SQL两...
### PL/SQL开发指南知识点概览 #### 一、序言与特点介绍 - **PL/SQL的应用领域**: - 实现商业规则通过存储过程和数据库触发器。 - 在数据库内生成并管理XML文档。 - 实现Web页面与数据库的无缝集成。 - 自动化...
PUSQL基础 3.1 PL/SQL块简介 3.1.1 PL/SQL块结构 3.1.2 PL/SQL块分类 3.2 定义并使用变量 3.2.1 标量变量 3.2.2 复合变量 3.2.3 参照变量 3.2.4 LOB变量 3.2.5 非PL...
### Oracle PL/SQL Programming 开发指南 #### 序言 - 特点介绍 PL/SQL(Procedural Language for SQL)作为Oracle数据库的一种内置编程语言,自Oracle 6版本引入以来,便以其强大的功能和高效的性能深受开发者们...
### PL/SQL Developer 7 用户详细指南 #### 一、介绍 **PL/SQL Developer** 是一款专为 Oracle 数据库设计的集成开发环境(IDE),主要用于开发存储过程、触发器等存储程序单元。该工具提供了丰富的特性来提高开发...
### PL/SQL Developer 9.0 用户指南知识点详解 #### 一、介绍 **PL/SQL Developer** 是一款专门用于开发、调试、管理和优化 Oracle 数据库应用程序的强大工具。该版本为 **9.0**,发布于 **2011年4月**。此文档...
《SQL*Plus 用户指南与参考》是一本关于 Oracle SQL*Plus 的官方文档,该版本为 Release 10.1,出版时间为2003年12月。SQL*Plus 是 Oracle 数据库的一个非常重要的工具,它不仅是一个强大的命令行界面工具,也是一个...
本段内容涉及的是Oracle数据库10g版本的PL/SQL用户指南和参考手册,版本号为10.1,文档部分编号为B10807-01。 文档的发布日期是2003年12月,作者包括John Russell以及其他一些贡献者。文档中明确指出,该文档受版权...
#### 八、创建与修改非PL/SQL对象 ##### 8.1 表定义编辑器 - 用于创建和修改表结构。 - 支持定义主键、索引等。 ##### 8.2 序列定义编辑器 - 用于管理和修改序列对象。 - 序列是用于生成唯一标识符的自动递增数字...