注册 登录
八达网 返回首页

的个人空间 https://www.8-da.com/?0 [收藏] [复制] [RSS]

日志

database

已有 281 次阅读2007-5-17 11:30

Risenet Database组件初级使用教程

 


 

我们的产品将运行在各种环境中,不同的操作系统(w2k、Linux、HP UNIX、Sun Solaris等),不同的应用服务器(BEA WebLogic、IBM WebSphere、Novell WebApps、Sybase EAServer等),不同的数据库(Oracle、Sql Server、Sybase等),不同的目录服务(Novell eDirectory、Netscape LDAP Server、Open LDAP等)。

Java虚拟机使我们不用关心操作系统的细节,J2EE规范让我们的程序可以运行在各种应用服务器上,我们的平台接口使我们可以调用各种目录服务,而Database组件将协助我们“跨越”不同的数据库。

下面是Database组件中定义的基本接口和功能描述

Insert            向数据库表中插入记录

Update         更新记录

Execute        可以执行INSERT、UPDATE、DELETE语句或DDL语句

Lob              读写LOB数据

PrimaryKey  获取主键信息

 

对于不同的数据库,工厂(DatabaseFactory)将创建不同的对象,这些对象实现了上面的这些接口,如:对于Oracle数据库,创建Insert对象,实际返回的是OracleInsert,只不过OracleInsert实现了Insert接口,你完全可以认为,那就是Insert对象。

       我们看看如何用这些“接口”操作真正的数据库,先创建一个测试表,使用各种字段类型,包含BLOB和CLOB字段,由id和name作为复合主键(注1):

CREATE TABLE DATABASE_TEST_TABLE

    id number(10,0) NOT NULL,

    name char(38) NOT NULL,

    postTime DATE,

    fileName varchar2(255),

    photo blob,

    photoDesc clob,

    PRIMARY KEY (id, name),

    )

(注2)

首先看看最简单的,Execute定义了用于“执行sql”的对象,此处的“执行”是与查询相对应的,可以执行INSERT、UPDATE、DELETE语句,也可以执行SQL DDL (Data Definition Language)语句,如:CREATE TABLE、 DROP TABLE 或者 ALTER TABLE。使用方法:先创建一个对象,然后执行execute()方法,此对象的execute()方法可以反复使用,执行多个sql语句,如:(注2)

Execute e = DatabaseFactory.createExecute();

e.execute(insert into DATABASE_TEST_TABLE(id,name) values(88,'name1'));

e.execute(delete from DATABASE_TEST_TABLE where id=88 and name='name1');

 

接下来看看如何插入一条记录,很简单,先创建一个Map对象,将各个字段的值按照字段名放入Map对象中,然后从DatabaseFactory中创建一个Insert对象,执行insert.execute()就可以了,代码如下:

    HashMap map = new HashMap();

    map.put("id", new Integer(100);

    map.put("nAMe", 'HERE is Name Field');

    map.put("postTime", new java.util.Date());

    map.put("FileName", "hhh.txt");

    String s = "这是一个测试字符串。";

    map.put("photo", s.getBytes());

    map.put("photoDesc", s);

    Insert insert = DatabaseFactory.createInsert(DATABASE_TEST_TABLE, map);

    int return = insert.execute();

Update的使用方法和Insert几乎完全一样,我们就不再赘述了。

 

Lob是用来读写大对象字段的对象,对Oracle是blob和clob,对Sql Server是image和text。不过用它来写数据实在有点麻烦(注3),还是用Insert和Update来写比较方便。用它来读倒是很方便,只要定位到要读取数据的记录,代码如下:

Char[] charBuf;

Lob lob = DatabaseFactory.createLob();

String sql=“SELECT * FROM DATABASE_TEST_TABLE where id=88 and name='name1'”

ResultSet rs = stmt.executeQuery(sql);

if (rs.next()) {

  charBuf = lob.readBLOB(rs, "photo");  //读BLOB数据

}

rs.close();

 

有时,并不能确定记录是否存在,如果存在就更新,不存在就插入,可以使用Operate类来完成,代码如下:

Operate p = DatabaseFactory.createOperate(TEST_TABLENAME, map);

op.execute();

 

 

说了这么的多,我们没有涉及最重要的数据库操作--查询,我们将把它放在RiseTable组件中介绍。

 

 

 

 

 

 

 

 

 

 

补充说明:

1、我们使用log4j来处理日志,如果不希望Database组件输出debug信息,请参考log4j的文档,修改net.risesoft.commons.log.LogFactory或相关配置文件,将Database组件的输出调至info或更高级别。

 

注1:表必须有主键,否则不能处理。

注2:对于创建表或执行某些sql语句,这些语句可能是数据库相关的,即对于不同的数据库,语句是不同的,如:创建表、左关联查询、不同数据库函数等情况,下面示例如何用Execute处理:

Execute e = DatabaseFactory.createExecute();

e.setSQL("oracle", oraclesqlString);

e.setSQL("mssql", mssqlString);    //指定MS SQL Server使用mssqlString语句

e.setSQL(defaultsqlString);    //设置缺省的

e.exeucte();

数据库组件将根据当前的数据库产品,自动调用相应的sql语句,如果没有对应的,就使用缺省的sql语句。

注3:如果真的要用Lob对象写数据,请仔细阅读实现Lob接口的各个子类的文档,并且很可能因此丧失跨数据库特性。所以,强烈建议,也使用Insert、Update来操纵Lob数据


??如何让我们的应用程序可以在不修改源代码、不用重新编译的情况下,运行于不同的数据库?如果能够跨越数据库,我们的产品将具有更强的适应性,更高的灵活性,具有更强的市场竞争力。

??JDBC作为Java操纵数据库的标准工具,在很大程度上消除了不同数据库的差异,如:getString(String fieldName)方法,对于各种数据库,都是获取字段的字符串值,对于Sql Server或Sybase,字段类型可能是varchar,对于Oracle,字段类型可能是Varchar2。但是,仅仅依靠jdbc还很不够,各个数据库对于我们还是有很大的差异,比如插入日期,Oracle的语法是:TO_Date('2003-5-1 15:24:59','YYYY-MM-DD HH24:MI:SS'),这肯定和Sql Server或Sybase不同,对于大对象字段的处理,各个数据库产品更是各显神通,没有统一的标准。

??长期以来,没有一个完美的方法解决这一难题,以前没有,以后也不会有,因为这个世界的变化太快了,我们只有“暂时”的解决方案。

??数据库产品非常复杂,基于数据库的应用更是百花齐放,如果我们的目光被这些奇花异草所吸引,可能永远都无法处理这个问题。我们要把目光放在树根上,所有这一切的最根本、最基础的就是我们所熟知的对数据库的操作:增删改查,只要我们很好的处理了这四种操作,也就可以实现跨数据库了。

??查询数据库可能是最复杂、使用频率最高的操作,我们将在RiseTable组件中专门介绍,今天只处理“增删改”操作。

??我们先来讨论新增,一定要用java的面向对象的设计思想来看待新增操作,我们所需要的是一个数据库的链接,数据库表名和要插入的数据,数据中要包含字段名和值,这正好可以用java.util.Map表示,所以,我们设计了Insert接口,能够接受表名和数据,当然也可以指定数据库链接,如果不指定的话,我们就自己创建一个,当执行Insert接口execute()方法时,数据就插入了,如果发生错误,抛出SQLException。接口设计完成后,我们需要针对不同的实据库进行实现,于是,就有了OracleInsert、SqlInsert、SybaseInsert。而对于setConnection()、setTableName()等无聊的方法,我们将他放在AbstractInsert中,这样各个子类只需要实现execute()方法就可以了。


(Insert接口及其实现的类图)

??修改和新增非常相似,我们采用几乎完全相同的方法进行设计,我们就不再赘述了,Update类图在这里。

??对于删除操作,我们并没有设计Delete接口,因为那没有什么意义,相反我们设计了Execute接口,用于执行各种数据库操作,并且使用DefaultExecute作为缺省实现。此处的“执行”是与查询相对应的,可以执行INSERT、UPDATE、DELETE语句,也可以执行SQL DDL (Data Definition Language)语句,如:CREATE TABLE、 DROP TABLE 或者 ALTER TABLE。当然,为了我们的目标--跨越数据库,要执行的sql必须是各个数据库通用的,或者是针对每个数据库进行“本地化”的。对于删除记录这样的操作,如果只使用字符串或数字作主键,那么delete from table1 where id=9对于各个数据库是通用的,如果使用了日期等比较特殊的字段作为主键,那么我们可以用TableUtil的convert2DataStr(),转换成该数据库中的日期的字符串表示。如果要执行的sql语句没有简单的换算关系,即对于不同的数据库,语句可能是完全不同的,这可以用资源文件、或针对不同的数据库子类化进行处理。


(Execute接口及RSMetaData类图)

??操作大对象字段的Lob接口,提供实用方法的TableUtil接口,也是通过针对不同的数据库进行子类化来实现的,类图在这里。

??现在,我们有了统一的接口,和一大堆针对不同数据库的子类,对于Oracle数据库,我们将使用OracleInsert、OracleUpdate、OracleLob、OracleTableUtil等等,如何正确的创建这些对象成了最大的问题。我们使用Abstract Factory和Singleton设计模式来解决这个问题,在DatabaseFactory中决定使用哪一个工厂,对于Oracle使用OracleFactory,对于Sql使用SqlFactory,并且保持了工厂的单一实例,由那个工厂去创建自己的对象。


(DatabaseFactory及各个数据库的Factory的类图)

??现在,Database组件的整体结构已经有了,但如果仅仅支持Oracle数据库的话,就失去存在的必要了。我们需要加入对其它数据库的支持,如某种数据库XXX,我们需要实现XXXFactory、XXXInsert、XXXLob等,并且把他们放在net.risesoft.commons.database.xxx包下,在DatabaseConstant中加入对此数据库的标识Xxx,然后,完成了。

??真的完成了吗?至少我们需要对它进行测试,可以使用net.risesoft.commons.database包下的AllDatabaseTests进行unit test,然后,再实际测试我们的产品。


路过

鸡蛋

鲜花

握手

雷人

评论 (0 个评论)

手机版|Archiver|八达网    

GMT+8, 2025-11-23 05:53

Powered by Discuz! X2.5

© 2001-2012 Comsenz Inc.

回顶部