这个题目完全可以按一本书来写,一篇文章远远不能描述清楚Oracle面向对象技术,不过就笔者所知,很少Oracle项目会
大规模使用其面向对象技术做开发,笔者在这里谈Oracle面向对象,翼此能够起到抛砖引玉的作用,让大家有想法进一步去探索
数据库面向对象编程。当然,笔者在这里和大家探讨的内容主要来自项目中的积累,而非笔者兴起之作,大家可以放心的在项目中
使用。
我们可以把应用按技术简单的分两层:数据库层和应用层,数据层一般来说是面向关系模型,应用层是面向对象模型,很多时候我们需要写
代码把关系数据转化为实体对象,这是一个繁重又需要开发者细心的一个工作。为了把开发者从中解脱出来,诞生了很多技术,O/R
Mapping的有Hibernate,一些JDO产品,EJB CMP等,对象数据库的有DB4O、MyOODB等。但是,到目前为止,这些技术并不能帮我们解决开发中的
主要矛盾。O/R Mapping工具只能映射简单的关系模型,处理普通的DML(Insert,Update,Delete),在批处理和过程处理上就捉襟见肘了,
大数据量处理的时候又存在效率低下、内存消耗巨大等诸多问题。对象数据库是九十年代兴起的技术,目前在嵌入式开发以及一些小应用中有一定的市场,
当然还不能用于大型企业应用,主要的原因是功能太简单了,大数据量存储和处理性能都无法得到保证,开发者的需求不能充分地得到满足。
大型应用还是要借助传统的关系数据库,这样性能和可靠性都能够得到保证。关系数据库也在不断地发展,许多数据库也引入了面向对象的
思想,这其中以Oracle为代表,Oracle9i就号称面向对象数据库,当然,这有打广告的嫌疑,不过如果使用好了Oracle的面向对象技术,
还是可以大大简化我们的许多开发工作。
下面我们切入主题,自Oracle9i以来,Oracle就不再是单纯的关系数据库管理系统,它在关系数据库模型的基础上,添加了一系列面向对象的特性。
Oracle的对象体系遵从面向对象思想的基本特征,许多概念同C++,JAVA中的类似,具有继承,重载,多态等特征,但又有自己的特点。Oracle
面向对象的最基本元素是它的对象类型,也就是Type,在Oracle8i中,Type只用作数据类型,而且成员只能是基本数据类型,这里顺便提一下,创建者之外
的其他用户需要得到授权才能使用对象类型。前面也提到,Oracle面向对象的内容很多,以Type为基础的对象存储,对象查询内容很丰富,限于篇幅,
本文就不探讨这些内容,重心放在面向对象编程的几大特性(继承、重载、多态)上。
我们先写一个简单的Type:
CREATE OR REPLACE TYPE SIMPLE AS OBJECT
(
id VARCHAR2(10),
name VARCHAR2(30)
)
现在有了这个对象,如何使用呢?
declare
l_o_1 SIMPLE;
l_o_2 SIMPLE;
begin
l_o_1 := SIMPLE('1','Jean');
DBMS_OUTPUT.PUT_LINE(l_o_1.name);
l_o_2 := SIMPLE(id => '2',name => 'Henry');
DBMS_OUTPUT.PUT_LINE(l_o_2.name);
l_o_2 := SIMPLE(3,NULL);
DBMS_OUTPUT.PUT_LINE(l_o_2.name);
end;
有了上面的测试代码,读者应该很容易找到其与面向对象语言(比如Java)的差异,没有了new,也没有了默认无参数构造器,默认使用定义的属性作为
构造器参数,不过足以描述各种实体。另外要说明的是,Type的属性没有private,protected,public的说法,所有的属性调用者都可以访问,即都是public。
刚才那个那个例子只有属性,没有行为,即没有方法,我们举第二个例子:
CREATE OR REPLACE TYPE AO AS OBJECT
(
id VARCHAR2(10),
name VARCHAR2(30),
CONSTRUCTOR FUNCTION AO
(
SELF IN OUT NOCOPY AO ,
m_id IN VARCHAR2
) RETURN SELF AS RESULT,
STATIC PROCEDURE MAIN,
MEMBER PROCEDURE test) NOT FINAL
/
CREATE OR REPLACE TYPE BODY AO AS
CONSTRUCTOR FUNCTION AO
(
SELF IN OUT NOCOPY AO ,
m_id IN VARCHAR2
) RETURN SELF AS RESULT IS
BEGIN
SELF.id := m_id;
RETURN;
END;
STATIC PROCEDURE MAIN IS
l_ao AO;
BEGIN
DBMS_OUTPUT.PUT_LINE('AO:MAIN');
l_ao := AO(id => '1',name => 'Jean');
l_ao.test;
l_ao := AO(m_id => '2');
l_ao.test;
l_ao := AO('3');
l_ao.test;
l_ao := AO('4','Henry');
l_ao.test;
END MAIN;
MEMBER PROCEDURE test IS
BEGIN
DBMS_OUTPUT.PUT_LINE('AO:test action,id is '|| SELF.id);
END test;
END;
/
测试代码:
declare
begin
AO.MAIN;
end;
从这个例子,相信我们可以看到很多东西,对象可以有对象方法,静态方法,我们可以用静态方法做测试,对象可以自定义构造器,但是不能覆盖默认的属性构造器。
定义类型的时候可以指定NOT FINAL(可被继承)以及NOT INSTANTIABLE(可实例化)。到了这里,相信大家对Oracle面向对象的东西应该有了一个初步的了解,下一步
我们需要关注的是继承。
下面的BO继承了AO,并有了自己的行为,
CREATE OR REPLACE TYPE BO UNDER AO
(address VARCHAR2(30),
OVERRIDING MEMBER PROCEDURE test,
MEMBER PROCEDURE test(str VARCHAR2),
MEMBER PROCEDURE write
)
/
CREATE OR REPLACE TYPE BODY BO AS
OVERRIDING MEMBER PROCEDURE test IS
BEGIN
DBMS_OUTPUT.PUT_LINE('BO:test action,id is '|| SELF.id);
END test;
MEMBER PROCEDURE test(str VARCHAR2) IS
BEGIN
DBMS_OUTPUT.PUT_LINE('BO:test action,id is '|| SELF.id || ',parameter is ' || str);
END test;
MEMBER PROCEDURE write IS
BEGIN
DBMS_OUTPUT.PUT_LINE('BO:write action');
END write;
END;
/
测试代码:
declare
l_bo BO;
begin
l_bo := BO('1','Jean','china');
DBMS_OUTPUT.PUT_LINE(l_bo.name);
l_bo := BO(ID => '2',name => 'Henry',address => 'US');
DBMS_OUTPUT.PUT_LINE(l_bo.name);
l_bo.test;
l_bo.test('123');
l_bo.write;
end;
请注意,如果是覆盖父类方法,必须写OVERRIDING关键字,否则机会编译出错。子类如果添加了属性,那默认的构造器参数就会发生变化。
不过,正因为Type的面向对象特性,也给开发者带了一些不便利的地方,比如要调试Type的某个方法,就必须构造对象;另外,由于有了继承关系,
如果一个Type存在子Type,那么不能随便replace以及drop,但是我们又需要添加新的方法或者属性,那怎么办?解决的办法是强制drop,然后再创建,
例如:drop type AO force;不过创建以后,又需要重新编译所有无效对象。
说到这里,我相信大家对基于Type的编程有了一个全面的了解,面向对象的好处我想我就不用多说了,对于我们程序员来说,最大的益处就在于我们
可以利用继承关系很好地封装我们的代码。
要使用Oracle面向对象的特性,除了在PL/SQL中调用以外,我们还可以在外部语言中去调用,我们就以Java为例来说明,Java访问DB我们知道是通过
JDBC调用,有两种方式可以采用:
1,使用Oracle提供的Jpublisher将ORACLE中的一个对象类型映射成为一个JDBC的类,这里就不具体说明了,读者可以到Oracle网站上去看。
2,直接调用PL/SQL包装,在这里写个简单的例子
import java.sql.*;
public class TestType {
public static void main(String[] args)
throws Exception
{
Class.forName("oracle.jdbc.OracleDriver");
Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@192.168.3.1:1521:sic","user","password");
StringBuffer sb = new StringBuffer("{Call \n");
sb.append("DECLARE \n");
sb.append("l_bo BO; \n");
sb.append("BEGIN \n");
sb.append("l_bo := BO('1','Jean','china');\n");
sb.append("l_bo.write; \n");
sb.append("END} ");
String sql = sb.toString();
CallableStatement cs = conn.prepareCall(sql);
cs.execute();
System.out.println("test finished");
}
}
有些读者可能本文感觉有点虎头蛇尾,没办法,最近笔者太忙了,有时间再完善。欢迎提意见发送至lxjchengcu@gmail.com