您正在查看: siddim 发布的文章

MySQL中tinytext、text、mediumtext和longtext详解

一、数字类型

类型 范围 说明
Char(N) [ binary] N=1~255 个字元 binary :分辨大小写 固定长度 std_name cahr(32) not null
VarChar(N) [binary] N=1~255 个字元 binary :分辨大小写 可变长度 std_address varchar(256)
TinyBlob 最大长度255个字元(2^8-1)

Blob (Binary large objects)储存二进位资料,且有分大小写

memo text not null
TinyText 最大长度255个字元(2^8-1)
Blob 最大长度65535个字元(2^16-1)
Text 最大长度65535个字元(2^16-1)
MediumBlob 最大长度 16777215 个字元(2^24-1)
MediumText 最大长度 16777215 个字元(2^24-1
LongBlob 最大长度4294967295个字元 (2^32-1)
LongText 最大长度4294967295个字元 (2^32-1)
Enum 集合最大数目为65535 列举(Enumeration),Enum单选、Set复选 sex enum(1,0) habby set(‘玩电玩’,'睡觉’,'看电影’,'听音乐’)
Set 集合最大数目为64

辨別Null与Not Null :Null为允许储存空值(Null)

二、数值

类型 范围 说明 例如
TinyInt[M] [UNSIGNED] -128~127 UNSIGNED : 0~255 num tinyint unsigned
SmallInt[M] [UNSIGNED] -32768~32767 UNSIGNED :0~ 65535
MediumInt[M] [UNSIGNED] -8388608~8388607 UNSIGNED :0~16777215
Int[M] [UNSIGNED] -2^31~2^31-1 UNSIGNED : 0~2^32
BigInt[M] [UNSIGNED] -2^63~2^63-1 UNSIGNED : 0~2^64
Float [(M,D)]

-3.4E+38~3.4E+38( 约 )

注: M 为长度, D 为小数,Float 4 bytes,Double 8 bytes
Double [(M,D)] -1.79E+308~1.79E+308( 约 )
Decimal [(M,D)]

辨別ZeroFill:当宣告关键字ZeroFill为自动填满0,如 000021

三、日期时间

类型 范围 说明
Date 日期(yyyy-mm-dd)
Time 时间(hh:mm:ss)
DateTime 日期与时间組合(yyyy-mm-dd hh:mm:ss)
TimeStamp yyyymmddhhmmss
Year

年份yyyy

XML 简介

第5 章 XML 简介

<ul>

<li>Producer: Jacques Morali

<li>Publisher: PolyGram Records

<li>Length: 6:20

<li>Written: 978

<li>Artist: Village People

</ul>

而在XML 中同样的数据可能标记为

程序清单5.2(song.xml)

<SONG>

<TITLE>Hot Cop</TITLE>

<COMPOSER>Jacques Morali</COMPOSER>

<COMPOSER>Henri Belolo</COMPOSER>

<COMPOSER>Victor Willis</COMPOSER>

<PRODUCER>Jacques Morali</PRODUCER>

<PUBLISHER>PolyGram Records</PUBLISHER>

<LENGTH>6:20</LENGTH>

<YEAR> 978</YEAR>

<ARTIST>Village People</ARTIST>

</SONG>

在程序清单5.2 中没有使用通用的标记如<dt>和<li> 而是使用了具有意义的标记如

<SONG> <TITLE> <COMPOSER>和<YEAR>等这种用法具有许多优点包括源码易

于被人阅读使人能够看出作者的含义可以选择XML 的元素名称以便使其在附加的

上下文中具有额外的意义例如元素名称可以是数据库的域名XML 比html 更为灵

活而且适用于各种应用因为有限数目的标记不必用于许多不同的目的

5.1.2 XML 的语法规则及其良构性

html 不同XML 对于语法有着严格的规定只有当一个XML 文档符合格式

良好的基本要求时处理程序才能对它加以分析和处理格式良好的这一标准通过对

XML 文档的各个逻辑成分和物理成分进行语法规定保证了XML 文档严密的条理性逻

辑性和良好的结构性从而大大提高了XML应用处理程序处理XML数据的准确性和效率

实际上格式良好的要求就是XML 规范的语法要求一个简单的检验方法就是用Internet

Explorer 5.01 以上版本的浏览器打开正在编辑的XML 文档如果报错这个文档就不是格

式良好的XML 文档的结构包括逻辑结构和物理结构

逻辑结构

一个XML 文档通常以一个XML 声明开始通过XML 元素来组织XML 数据XML

元素包括标记和字符数据为了组织数据更加方便清晰还可以在字符数据中引入CDATA

数据块并可以在文档中引入注释此外由于有时需要给XML 处理程序提供一些指示

信息XML 文档中可以还包含处理指令

具体说来各个逻辑元素的作用和形式如下

________

第二部分 JSP 技术和XML 技术

1. XML 声明

XML 声明是处理指令的一种一个XML 文档最好以一个XML 声明作为开始下面

是一个完整的XML 声明

<?xml version = "1.0" encoding = "GB2312" standalone = "no"?>

在一个XML 的处理指令中必须包括version 属性指明所采用的XML 的版本号而

且它必须在属性列表中排在第一位standalone 属性表明该XML 文档是否和一个外部文档

类型定义DTD 配套使用encoding 属性则指明了数据所采用的编码标准如果需要显示中

文那么编码应该是GB2312 或者GBK

2. 元素

元素是XML 文档内容的基本单元从语法上讲一个元素包含一个起始标记一个

结束标记以及标记之间的数据内容其形式是

<元素标记>数据内容</元素标记>

对于标记有以下语法规定

(1) 标记必不可少任何一个格式良好的XML 文档中至少要有一个元素

(2) 大小写有别

(3) 要有正确的结束标记结束标记除了要和起始标记在拼写和大小写上完全相同

还必须在前面加上一个斜杠/ 当一对标记之间没有任何文本内容时可以不写结束标

记而在起始标记的最后冠以斜杠/ 来确认这样的标记称为空标记

(4) 标记要正确嵌套例如<学生><姓名></学生><姓名>就是一个错误的嵌套

(5) 标记命名要合法标记名应该以字母下划线_ 或冒号开头后面跟字

母数字句号. 冒号下划线或连字符- 但是中间不能有空格而且任何标记名

不能以xml 或者xml 大小写的任何组合如XML xML xmL 等等起

(6) 有效使用属性标记中可以包含任意多个属性属性以名称/取值对出现属性名

不能重复名称与取值之间用等号= 分隔且取值用引号引起来

3. CDATA 节

在标记CDATA 下所有的标记实体引用都被忽略而被XML 处理程序当作字符数

据看待CDATA 的形式如下

< [CDATA[ 文本内容 ]>

CDATA 的文本内容中不能出现字符串]]> CDATA 不能嵌套

4. 注释

在XML 中注释的方法与HTML 完全相同用< -- 和--> 将注释文本引起来

对于注释还有以下规定

(1) 在注释文本中不能出现字符- 或字符串—

(2) 不要把注释文本放在标记之中类似地不要把注释文本放在实体声明之中或之

(3) 注释不能被嵌套例如<学生><!--学习成绩优秀--></学生>就是错误的

5. 处理指示

处理指示是用来给处理XML 文件的应用程序提供信息的所有的处理指示应该遵循

________

第5 章 XML 简介

下面的格式

< 处理指示名 处理指示信息>

也就是说XML 分析器可能对它并不感兴趣而把这些信息原封不动地传给XML 应

用程序然后这个应用程序来解释这个指示遵照它所提供的信息进行处理或者再把

它原封不动地传给下一个应用程序

实际上前面例子中的XML 声明就是一个处理指示

<?xml version = "1.0" encoding = "GB2312" standalone = "no"?>

6 样式表声明

从本质上讲样式表声明和XML 声明一样这两者都是处理指示的一种但只有需

要对XML 文档进行解析和显示的时候才需要给出这两个处理指示而且XML 的语法规

定XML 文档的第一行必须是XML 声明两者都出现时文档类型声明和样式表声明必须

分别位于第三行和第二行例如下面的XML 文档就是格式良好的具有良构性

程序清单5.3(test.xml)

<?xml version="1.0" encoding="GB2312" standalone="no"?>

<?xml-stylesheet type="text/xsl" href="mystyle.xsl"?>

<学生>

<姓名>许国华</姓名>

</学生>

7. 文档类型声明

为了对XML 文档进行文档类型定义在XML 文档中必须进行文档类型的说明一般

是在XML 风格样式表声明之后如下的XML 文档就是有效的

<?xml version="1.0" encoding="GB2312" standalone="no"?>

<?xml-stylesheet type="text/xsl" href="mystyle.xsl"?>

<!DOCTYPE 根元素名 SYSTEM "mydtd.dtd" >

<学生>

<姓名>方中星</姓名>

</学生>

物理结构

从物理结构上讲XML 文档是由一个或多个存储单元构成的这些存储单元就是所谓

的实体所有的XML 文档都包含了一个根实体又称作文档实体这个实体是由

XML 本身给出的无需显式定义就可以使用它指的其实就是整个文档的内容是XML

语法分析器处理的起点除此之外可能还需要用到其他一些实体这些实体都用名字来

标识在文档类型定义DTD 中给出定义

简单地说实体充当着和别名类似的角色即一个简单的实体名称可以用来代表一

大段文本内容像任何计算机别名系统一样实体引用简化了录入工作因为每当要使用

同样一大段文本时只需使用它的别名就可以了处理器会自动把这个别名替换为相应的

文本

实体引用有以下几点规则

1 除了XML 标准规定的预定义实体以外在XML 文档引用一个实体之前必须

________

第二部分 JSP 技术和XML 技术

已经对此实体进行过声明

2 实体引用中不能出现空格

3 尽管在一个实体中可以再引用其他实体但是不能出现循环引用

4 实体引用的文档必须符合XML 语法的种种要求任何一个独立的逻辑要素比

如元素标记注释处理指令实体引用等等都不能开始于一个实体而结束于另一

个实体

至于这一部分的详细内容我们在后面的DTD 的学习会深入讨论在这里就不再赘

述了

5.2 DTD 的书写及实例

5.2.1 什么是DTD

XML 的精髓就是基于信息描述的能够体现数据信息之间逻辑关系的可以确保文件

的易读性和易搜索性的自定义标记为支持词汇表XML 提供了一种定义文档结构与元素

的机制这就是文档类型定义对XML 的元素属性及实体等读者都已经似曾相识了

而DTD 正是把元素元素属性实体等包容进来给整个文档提供声明保证了文档的有效

性DTD 是Document Type Definition 的简写中文意思是文档类型定义DTD 是“元标记”

这个概念的产物它描述了标记语言的语法和词汇表也就是定义了文件的整体结构以及

文件的语法简而言之DTD 规定了一个语法分析器为了解释一个“有效的”XML 文件所

需要知道的所有规则的细节

DTD 可以是一个完全独立的文件也可以在XML 文件中直接设定所以DTD 分为

外部DTD 在XML 文件中调用另外已经编辑好的DTD 和内部DTD 在XML 文件中直

接设定DTD 两种比如有几十家相互联系的合作伙伴关系的公司厂商他们相互

之间的交换电子文档都是用XML 文档那么我们可以将这些XML 文档的DTD 放在某个

地方让所有交换的XML 文档都使用此DTD 这是最方便的做法同时也适用于公司内

部的XML 文件使用

5.2.2 一个DTD 的实例的简单分析

在XML 所描述的指标语言中DTD 便提供了语法规定以便给各个语言要素赋予一

定的顺序为了说明特定的语法规则DTD 采用了一系列正则式语法分析器将这些正则

式与XML 文件内部的数据模式相匹配从而判别一个文件是否是有效的匹配被严格执

行因此如果XML 文件中有任何信息不符合DTD 的规定都不会通过读者可以先看

下面一个实例从后面的简单分析可以了解DTD 的基本设计格式(此文件只是DTD 定义

在浏览器中会显示一个树状列表)

程序清单5.4 (book.dtd)

<!DOCTYPE BOOKS[

<!ELEMENT BOOKS(BOOK+)>

<!ELEMENT BOOK(AUTHOR TITLE DESCRIPTION PRICE)>

<!ATTLIST BOOK language CDATA #REQUIRED>

________

第5 章 XML 简介

<!ELEMENT AUTHOR (#PCDATA)+>

<!ELEMENT DESCRIPTION (#PCDATA)>

<!ELEMENT PRICE(QUANTITY CURRENCY)>

<!ELEMENT QUANTITY (#PCDATA)>

<!ELEMENT CURRENCY (#PCDATA)>

]>

从上面的例子读者不难看出DTD 的基本格式是

<!DOCTYPE 根元素名 [ ]>

其中!DOCTYPE 是关键字表示文档类型定义开始而[ ]中则包括了文档类型定义的

实际内容在上面这个例子中我们还看到了!ELEMENT !ALLIST 这两个关键字它们

分别承担了元素组成类型的定义和元素属性类型的定义

5.2.3 DTD 语法

DTD 有4 种类型的声明即元素类型声明属性列表声明实体声明符号声明

元素类型声明ETD

下面是两个典型的元素类型声明

<!ELEMENT STUDENT (NAME ADDRESS)>

<!ELEMENT STUDENT (#PCDATA)>

第一个元素类型声明定义了一个<STUDENT>元素并规定该元素必须有NAME 和

ADDRESS 两个子元素而且NAME 的位置必须在ADDRESS 的前面第二个元素类型声

明也定义了一个<STUDENT> 并规定该元素由纯文本的一般字符组成元素类型声明不

但说明了每个文件中可能存在的元素给出了元素的名字而且给出了元素的具体类型

元素类型声明应该采用如下的结构

<!ELEMENT 元素名 元素内容描述>

上面的例子中读者可以发现一个XML 元素即可以是子元素的集合也可以是一段纯文

本字符事实上根据XML 的标准可以将元素按内容划分为4 类空元素类型ANY 元素

类型父元素类型混合元素类型

1 空元素类型

空元素类型定义方式为

<!ELEMENT 元素名 EMPTY>

这类元素在XML 文档中使用空元素标记元素中没有内容例如程序清单5.5 的XML

文档就是有效的该代码可运行

程序清单5.5(student1.xml)

<?xml version = "1.0" encoding="GB2312" standalone = "yes"?>

<!DOCUMENT [

<!ELEMENT 学生 EMPTY>

]>

<学生></学生>

________

第二部分 JSP 技术和XML 技术

2. ANY 元素类型

ANY 元素类型定义方式为

<!ELEMENT 元素名 ANY>

XML 文档里该元素中可以包含任何内容建议一般只把文档的根元素规定为ANY 类

型例如程序清单5.6 的XML 文档是有效的

程序清单5.6(student2.xml)

<?xml version = "1.0" encoding="GB2312" standalone = "yes"?>

<!DOCTYPE 学生[

<!ELEMENT 学生ANY >

]>

<学生>

</学生>

这个DTD 定义了一个XML 文件它只有一个根元素名为<学生> 这个元素可以有

任何类型的子元素也可以包含纯文本字符还可以为空元素上面的XML 文档中的<学

生>元素就为空元素这容易造成读者的错觉由于ANY 元素类型的元素被定义为可以包

含其它元素是不是就可以对要加入这个根元素的子元素不加定义就进行使用回答是否

定的因为尽管元素<学生>被定义为可以包含其它元素但实际上这个DTD 除了<学生>

元素本身外没有定义任何其它元素所以也就没有其它元素可以用作<学生>元素的子元素

“有效的”XML 文件规定文件中所使用的任何元素都必须在DTD 中给出定义

不过并不是所有这种根元素的组成部分都需要另加说明纯文本字符就是一个例外

ANY 定义下的根元素使用纯文本都是不需要说明的在相同的DTD 定义下程序清单5.7

所示的XML 文件则是合法的

程序清单5.7(student3.xml)

<?xml version = "1.0" encoding="GB2312" standalone = "yes"?>

<!DOCTYPE 学生[

<!ELEMENT 学生 ANY >

]>

<学生>

姓名李超群 性别男 电话62764586

</学生>

3 父元素类型

这类元素中可以包含子元素在DTD 中通过正则表达式规定子元素出现的顺序和次

数语法分析器将这些正则式与XML 文档内部的数据模式相匹配判别出一个文档是否

是有效的DTD 尽管要求严格但也有它的灵活性使用正则表达式我们就可以解

决上述问题描述父元素与子元素之间非常复杂的关系例如你可以对一个元素作如下

任何一种类型的定义它有一个子元素有一个或多个子元素有零个或多个子元素至

少有一个子元素你还可以定义复合关系比如元素X 是有效的如果它含有一个或多

个子元素Y 或一个子元素Z 你还可以定义复合关系举个例子就明白了例如<个

人资料>中必须包括<姓名><性别> 可以包括一个或多个<联系方式> 可以有<个人奖励>

也可以没有

________

第5 章 XML 简介

由于以上的特点父元素类型的元素的声明就可以有各种灵活而多样的形式各种形

式之间主要是在子元素出现的顺序出现的次数以及各个子元素之间的复合关系这几方面

存在着差异要搞清楚它们的区别熟悉下面的表格是必不可少的

表5.1 正则表达式中的元字符的含义

正则表达式中的元字符 元字符所对应的含义

Space(空格) 不要求严格遵从顺序要求

AND 要求严格遵从顺序要求

+ 出现一次或多次

* 出现零次或多次

可选不出现或出现一次

一组要共同匹配的表达式

| OR 或

元素A 元素B 元素C 元素列表无需遵从顺序要求

程序清单5.8 有助于读者更好地把握这种元素类型下的声明形式例如程序清单5.8

中XML 文档就是有效的该DTD 并没有对< 学生>的子元素的顺序作出要求

程序清单5.8(student5.xml)

<?xml version = "1.0" encoding="GB2312" standalone = "yes"?>

<!DOCUMENT 学生>

[<!ELEMENT 学生(姓名 EMAIL)>

<!ELEMENT 姓名(#PCDATA)>

<!ELEMENT EMAIL(#PCDATA)>]>

< 学生>

<EMAIL>zhang@etang.com</EMAIL>

<姓名>张三</姓名>

</ 学生>

程序清单5.9 中的XML 文档是无效的因为元字符要求在DTD 中定义的两个

子元素都必须出现而文档中只出现了子元素<姓名> 而把<EMAIL>抛之脑后显然是偷

工减料的做法是语法分析器所不能容忍的

程序清单5.9(studenterror.xml) 元字符的错误用法示例不能编译执行将报错

<?xml version = "1.0" encoding="GB2312" standalone = "yes"?>

<!DOCUMENT 学生

[<!ELEMENT 学生(姓名EMAIL)>

<!ELEMENT 姓名(#PCDATA)>

<!ELEMENT EMAIL(#PCDATA)>]>

< 学生>

<姓名>张三</姓名>

</ 学生>

还可以用括号把各个子元素并成一组然后用元字符对其进行操作例如

<!ELEMENT 联系人(姓名EMAIL)+>

________

第二部分 JSP 技术和XML 技术

<!ELEMENT 姓名(#PCDATA)>

<!ELEMENT EMAIL(#PCDATA)>

程序清单5.10 所示的XML 文件是符合该DTD 文件的要求的

程序清单5.10(temp.xml)

<学生>

<姓名>张三</姓名>

<EMAIL>zhang@aaa.com</EMAIL>

<姓名>李四</姓名>

<EMAIL>li@bbb.org</EMAIL>

<姓名>王五</姓名>

<EMAIL>wang@ccc.org</EMAIL>

</学生>

仅仅因为元字符+ 的位置的改变DTD 文件的含义就发生了很大的变化

元字符| 的作用是符号| 描述了一个OR 操作因此下面的DTD 片段所规定

的XML 元素是所有的<学生>元素应该有一个<姓名>子元素同时在此之后还应该有一

个<电话>或一个<EMAIL>元素但不能同时有<电话>和<EMAIL>两个元素

<!DODUMENT 学生[

<!ELEMENT 学生(姓名(电话|EMAIL))>

<!ELEMENT 姓名(#PCDATA)>

<!ELEMENT 电话(#PCDATA)>

<!ELEMENT EMAIL(#PCDATA)>

]>

XML 正则表达式的匹配原则不允许循环逻辑所以OR 的意思是或者选这个或者选

那个但不能两个都选也不能两个都不选

< 学生>

<姓名>张三</姓名>

</学生>

这个例子是一个无效的XML 片段因为DTD 中规定或者有一个<电话>元素或

者有一个<EMAIL>元素但上面这个例子中两种元素都没有

字符? 说明一个子元素是可选的它可以出现也可以不出现因此在下面的

DTD 片断中我们规定每一个<学生>都必须有一个<姓名>子元素同时或者有一个<电

话>子元素或者有一个<EMAIL>子元素此外它还可以包含一个<地址>子元素也可

以不包含这种元素

<!ELEMENT 学生(姓名(电话|EMAIL) 地址?)>

<!ELEMENT 姓名(#PCDATA)>

<!ELEMENT 电话(#PCDATA)>

<!ELEMENT EMAIL(#PCDATA)>

<!ELEMENT 地址(学校系宿舍)>

________

第5 章 XML 简介

<!ELEMENT 学校(#PCDATA)>

<!ELEMENT 系 (#PCDATA)>

<!ELEMENT 宿舍(#PCDATA)>

根据这个DTD 的描述下面的XML 片段是有效的

<学生>

<姓名>张风林</姓名>

<EMAIL>zhang@etang .com</EMAIL>

<地址>

<学校>北京科技大学</学校>

<系>机械工程系</系>

<宿舍>34楼345号</宿舍>

</地址>

</学生>

4 混合元素类型

这类元素中即可以包含子元素也可以包含纯文本字符同样也可以在DTD 中通过

正则表达式规定子元素纯文本字符出现的顺序和次数语法分析器通过同样的方式验证

文档的有效性程序清单5.11 中<学生>就是一个混合元素

程序清单5.11(student5.xml)

<?xml version = "1.0" encoding="GB2312" standalone = "yes"?>

<!DOCTYPE CONTACTS [

<!ELEMENT 学生列表 ANY>

<!ELEMENT 学生(姓名|电话|EMAIL|#PCDATA)*>

<!ELEMENT 姓名(#PCDATA)>

<!ELEMENT 电话(#PCDATA)>

<!ELEMENT EMAIL(#PCDATA)>

]>

<学生列表>

<学生>

<姓名>雷雨风</姓名>

<电话>(010)62345678</电话>

<EMAIL>zhang@etang.com</EMAIL>

最喜爱的运动游泳

最喜欢的歌曲一天到晚游泳的鱼

最喜欢的菜酸菜鱼

</学生>

</学生列表>

在定义元素时在下还要提醒读者注意以下问题

1 ETD(元素类型声明)的顺序是无关紧要的如下例

<!ELEMENT 姓名(#PCDATA)>

<!ELEMENT 联系人列表 ANY>

<!ELEMENT 联系人(姓名)>

________

第二部分 JSP 技术和XML 技术

<!ELEMENT 联系人列表 ANY>

<!ELEMENT 联系人(姓名)>

<!ELEMENT 姓名(#PCDATA)>

所定义的文件结构是完全相同的

2 不能对不同的元素使用相同的元素名即便这些元素的内容包含的子元素不

同也不行因为它只会引起文件各个元素的混淆使文件的可读性大打折扣

<!ELEMENT 联系人列表 ANY>

<!ELEMENT 联系人(姓名)>

<!ELEMENT 联系人(EMAIL)>

<!ELEMENT 姓名(#PCDATA)>

<!ELEMENT EMAIL(#PCDATA)>

在这个例子中对<联系人>的重复定义会引起错误

属性列表声明

<!ELEMENT BOOK(AUTHOR TITLE DESCRIPTION PRICE)>

< !ATTLIST BOOK language CDATA #REQUIRED >

上面例子的第二行实际就是一个属性列表的声明DTD 的属性列表声明采用了如下的

格式

<!ATTLIST 元素名 属性名 属性类型 缺省值*>

结合前面的知识我们可以知道对于某一个元素它的属性可以有零个或者多个每个

属性的声明都应当包括属性缺省值属性类型及属性名称等三项内容缺省值说明在XML

文件中如果没有特别说明属性的取值语法分析器默认它具有的取值属性类型则用来

指定该属性是属于10 个有效属性类型中的哪种类型

1 元素属性的缺省值

我们先从元素属性的缺省值说起要了解这一点读者需要浏览一下表5.2 这会对读

者有所帮助

表5.2 属性缺省值的意义

属性缺省值 对于缺省值的描述

#required 表示在标记中必须出现此属性

#implied 标记中可以不出现此属性

#fix 属性的值是固定的某个值

字符串 标记中如没有指定属性的值那么此字符串就是此属性的值

1 必须赋值的属性关键字REQUIRED 说明XML 文件中必须为这个属性给出一

个属性值例如假设你想定义一个页面作者元素并把这个元素加入所有网站中的

每一个页面之所以定义这个元素是为了页面编辑者能够提供他的联系信息以便当发

现页面错误或无效链接时可以及时地通知他在这种情况下每个页面作者都有不同的

________

第5 章 XML 简介

个人信息所以你无法事先知道应该用什么作为缺省值但你又的确需要提供每个人的信

息这时候你就可以把与联系信息相关的属性定义为必需的REQUIRED 而且不用提

供缺省值

2 属性值可有可无的属性当使用IMPLIED 关键字时文法解释器不再强行要求

你在XML 文件中给该属性赋值而且也无需在DTD 中为该属性提供缺省值可以说这

是对属性值有无的最低要求现实中经常用到

3 固定取值的属性还有一种特殊情况你需要为一个特定的属性提供一个缺省

值并且不希望XML 文件的编写者把你的缺省值替代掉这时候就应该使用FIXED 关

键字同时为该属性提供一个缺省值

4 定义缺省值的属性如果不使用上面任何一种关键字的话该种属性就是属于

这种类型对于这种属性你需要在DTD 中为它提供一个缺省值而在XML 文件中可以

为该属性给出新的属性值来覆盖事先定义的缺省值也可以不另外给出属性值后一种情

况下它就默认为采用DTD 中给出的缺省值

下面举一个简单的XML 代码片断

<!ATTLIST 大四学生

身高 #CDATA #IMPLIED

体重 #CDATA #IMPLIED

联系信息 #CDATA #REQUIRED

政治面貌#CDATA #FIXED "群众"

家庭出身 #CDATA "独生子女">

2 属性类型

对于属性类型读者在前面的章节可能已经接触了一些在这里我想结合DTD 的

实例再和读者一起熟悉一遍读者都知道属性类型主要有以下10 种

CDATA ENYMERATED ID IDREF IDREFS ENTITY ENTITIES NMTOKEN

NMTOKENS NOTATION 我们将一一分析它们的实例

CDATA 指的是纯文本即由字符符号& 小于号< 和引号" 组成的字符

串请看程序清单5.12 这个节目单的例子

程序清单5.12(program.xml)

<?xml version = "1.0" encoding="GB2312" standalone = "yes"?>

<!DOCTYPE 节目单[

<!ELEMENT 节目单 ANY>

<!ELEMENT 节目 (#PCDATA)>

<!ATTLIST 节目 演员 CDATA>

]>

<节目单>

<节目 演员="赵本山">卖拐</节目>

<节目 演员="冯巩 郭东林">的寸进尺</节目>

<节目 演员="郭达 蔡明">西厢记<节目>

</节目单>

属性也可以被描述为一组可接受的取值的列表XML 文件中对属性的赋值将从这个列

________

第二部分 JSP 技术和XML 技术

表中选取一个值这类属性属于枚举类型ENUMERATED 不过关键字ENUMERATED

是不出现在DTD 定义中的这种属性类型在处理现实中本身受到很大限制的属性时优越

性尤为突出例如性别政治面貌最高学历诸如此类的属性的处理就可以采用这种方

程序清单5.13(shop.xml)

<?xml version = "1.0" encoding="GB2312" standalone = "yes"?>

<!DOCTYPE 购物篮 [

<!ELEMENT 购物篮 ANY>

<!ELEMENT 肉 EMPTY>

<!ATTLIST 肉 类型( 鸡肉 | 牛肉 | 猪肉 | 鱼肉 ) "鸡肉">

]>

<购物篮>

<肉 类型 = "鱼肉"/>

<肉 类型 = "牛肉"/>

<肉/>

</购物篮>

程序清单5.13 就是一个枚举类型的属性其中第3 个<肉>的类型属性的缺省值为鸡

肉不知读者理解得如何

ID 是用属性值的方式为文件中的某个元素定义唯一标识的方法它的作用类似于

HTML 文件中的内部链接在大多数情况下ID 由处理文件的程序或脚本语言使用一般

而言不要给ID 类型的属性事先指定缺省值这很容易引起不同的元素具有相同的标识的

情况更不能使用FIXED 型的缺省值此类属性经常使用REQUIRED 缺省类型当然

这也不是必需的有的应用并不要求每个元素都有自己的标识所以也可以使用IMPLIED

缺省类型

程序清单5.14(student6.xml)

<?xml version = "1.0" encoding="GB2312" standalone = "yes"?>

<!DOCTYPE 学生列表[

<!ELEMENT 学生列表 ANY>

<!ELEMENT 学生姓名EMAIL]>

<!ELEMENT 姓名(#PCDATA)>

<!ELEMENT EMAIL(#PCDATA)>

<!ATTLIST 学生 编号 ID #REQUIRED>

]>

<学生列表>

<学生 编号="1">

<姓名>雷雨风</姓名>

<EMAIL>lei@etang.com</EMAIL>

</学生>

<学生 编号="2">

________

第5 章 XML 简介

<姓名>路朝天</姓名>

<EMAIL>lu@sina.com</EMAIL>

</学生>

</学生>

IDREF 类型允许一个元素的属性使用文件中的另一个元素方法就是把那个元素的ID

标识值作为该属性的取值例如程序清单5.15

程序清单5.15(student7.xml)

<?xml version = "1.0" encoding="GB2312" standalone = "yes"?>

<!DOCTYPE 学生列表[

<!ELEMENT 学生列表 ANY>

<!ELEMENT 学生(姓名EMAIL)>

<!ELEMENT 姓名(#PCDATA)>

<!ELEMENT EMAIL(#PCDATA)>

<!ATTLIST 学生 编号 ID #REQUIRED>

<!ATTLIST 学生 导师 IDREF #IMPLIED>

]>

<学生列表>

<学生 编号="2">

<姓名>路朝天</姓名>

<EMAIL>lu@sina.com</EMAIL>

</学生>

<学生 编号="1" 导师="58">

<姓名>雷雨风</姓名>

<EMAIL>lei@etang.com</EMAIL>

</学生>

</学生列表>

NMTOKEN 是 Name token 即命名符号的缩写该类属性值是一种受限制的字符

串表示属性值只能由字母数字下划线. - 这些符号组成一般

地一个NMTOKEN 属性必须由单个单词构成但该单词不必符合其他约束比如不必匹

配其他的属性或实体 NMTOKENS 类型的属性值可以包含多个由空格或制表符换行

符分隔的 NMTOKEN 值请看程序清单5.16

程序清单5.16(data.xml)

<?xml version = "1.0" encoding="GB2312" standalone = "yes"?>

<!DOCUMENT 数据

[<!ELEMENT 数据(#PCDATA)>

<!ATTLIST 数据

安全性( ON | OFF ) "OFF">

授权用户 NMTOKENS #IMPLIED

>]>

<数据 安全性="ON" 授权用户 = "Toms Cattuso">

________

第二部分 JSP 技术和XML 技术

芝麻开门123321

</数据>

实体声明

在DTD 中还可以声明一些称为Entity(实体)的东西让DTD 和XML 文件使用这就

是实体声明实体实际上是属性类型的一种可以把Entity 看作是一个常量它有一定的

值在DTD 中Entity 的声明语法为

<!ENTITY entity-name entity-definition>

例如我们在DTD 中声明<!ENTITY PC "(#PCDATA)"> 那么在后面的元素设定中

就可以使用这个Entity 来代替“(#PCDATA)” 这个字符串如<!ELEMENT 作者

(#PCDATA)>可以写成<!ELEMENT 作者 &PC;> 引用Entity 的时候必须要在Entity 名

称前面加上“&”符号后面加上“ ”符号实体可以分为内部实体外部实体和参数实体

3 种下面是3 个典型的实体

< !ENTITY entity1 " 文本 ">

< !ENTITY entity2 SYSTEM "/standard/legalnotice.xml">

< !ENTITY entity3 SYSTEM "/standard/logo.gif" NDATA gif87A>

内部实体是对文本字符串的命名如上例3 个实体中的第一个此后使用&entity1 就相

当于插入了字符串文本内部实体经常用于定义重复出现的文本或经常要求改动的文

本如文档的修订情况记录

外部实体是对其他外部文件的命名它使得 XML 文件可以引用外部文件的内容外

部文件可以包含文本也可以包含二进制数据与内部实体一样外部文件将被插入实体

引用出现的位置而且将随同文档的其余部分一起被解析器处理对二进制数据的引用只

能出现于元素属性因为二进制数据不可被解析对二进制数据的引用通常用来提供图片

等非 XML ”的内容前面示例中的 2 3 两个实体声明即为外部实体声明

与前面两种实体不同参数实体只能出现于文档类型定义中包括定义和引用声明

参数实体时名字前面要加一个 % ”和空格在引用参数实体时引用其他实体时使用

的 & ”符号也要改为 % ” 参数实体引用将直接展开为它所代表的文本而其他实体引

用并不立即展开 例如我们声明了如下参数实体

< !ENTITY % personcontent "#PCDATA | quote">

则用下面这种形式引用参数实体

< !ELEMENT allen (%personcontent;)*>

符号声明

和实体声明一样符号声明也是属性声明的一种实际上就是NOTATION 类型符号

声明允许属性值为一个DTD 中声明的符号这个类型对于使用非XML 格式的数据非常有

用现实世界中存在着很多无法或不易用XML 格式组织的数据例如图像声音影像

等等对于这些数据XML 应用程序常常并不提供直接的应用支持通过为它们设定

NOTATION 类型的属性可以向应用程序指定一个外部的处理程序例如当你想要为一

个给定的文件类型指定一个演示设备时可以用NOTATION 类型的属性作为触发要使用

NOTATION 类型作为属性的类型首先要在DTD 中为可选用的记号作出定义定义的方

________

第5 章 XML 简介

式有两种一种是使用MIME 类型形式是

< NOTATION 记号名 SYSTEM "MIME 类型">

另一种是使用一个URL 路径指定一个处理程序的路径

< NOTATION 记号名 SYSTEM "URL 路径名">

在程序清单5.17 中为<视频文件>元素指定了两种可选设备一种是RealPlayer.exe

用来播映.mov 文件另一种则用来显示.gif 图像

程序清单5.17(fuhao.dtd)

<?xml version = "1.0" encoding="GB2312" tandalone = "yes"?>

<!DOCTYPE 文件[

<!ELEMENT 文件 ANY>

<!ELEMENT 视频文件 EMPTY>

<!ATTLIST 视频文件 演示设备 NOTATION ( mp | gif ) #REQUIRED>

<!NOTATION mp SYSTEM "RealPlayer.exe">

<!NOTATION gif SYSTEM "Image/gif">

]>

<文件>

<视频文件 演示设备 = "mp"/>

</文件>

条件段

条件段是文档类型声明外部子集的一部分取决于相应的关键字它们或被包含在

DTD 逻辑结构之内或被排除在DTD 逻辑结构之外同内部或外部DTD 子集一样条件

段可以包含一个或多个完整的声明注释处理指令或嵌套的条件段其间可以夹杂空白

如果条件段的关键字是INCLUDE 那么条件段的内容是DTD 的一部分如果条件段

的关键字是IGNORE 那么条件段的内容逻辑上不是DTD 的一部分如果一个关键字为

INCLUDE 的条件段出现在更大的关键字为IGNORE 的条件段中内外两个条件段都被忽

略如果条件段的关键字是一个参数实体引用处理器在决定是否包含或忽略此条件段前

必须先将该参数实体置换成其内容

<ENTITY % draft 'INCLUDE' >

<!ENTITY % final 'IGNORE' >

<![%draft;[

<!ELEMENT book (comments* title body supplements?)>

]]>

<![%final;[

<!ELEMENT book (title body supplements?)>

]]>

在上面的例子中考虑条件段的作用最终该DTD 片段应当理解为定义了一个元素

<book> 其中可以包括零个或多个子元素<comments> 还必须包含<title><dody >子元素各

________

第二部分 JSP 技术和XML 技术

一个有一个可选子元素<supplements> 而且各子元素必须按照前面的顺序出现

5.2.4 如何使用DTD 文件

将DTD 与XML 联系起来就要用到文档类型声明文档类型声明必须紧跟 XML 声明

类型定义信息可以来自外部 DTD 称为外部子集也可直接嵌入 XML 文档内称为内

部子集还可以同时提供外部子集和内部子集使用外部DTD 的XML 文档的结构为

<?xml version = "1.0" encoding="GB2312" standalone = "no"?>

<!DOCTYPE 根元素名SYSTEM "外部DTD文件的URL">

文档体.......

读者可以看出要使用外部DTD 只需将XML 声明中的standalone 属性声明改为no

然后在文档的第二行加入文档类型声明就OK 了如程序清单5.18 5.19 所示的XML 文

档在相应的DTD 文件的作用下就是有效的

程序清单5.18(Reference.dtd)

<?xml version="1.0" encoding="GB2312" ?>

<!ELEMENT Reference (书籍*)>

<!ELEMENT 书籍 (名称作者价格)>

<!ELEMENT 名称 (#PCDATA)>

<!ELEMENT 作者 (#PCDATA)>

<!ELEMENT 价格 (#PCDATA)>

<!ATTLIST 价格 货币单位 CDATA #REQUIRED>

程序清单5.19(Reference.xml)

<?xml version="1.0" encoding="GB2312" standalone="no"?>

<!DOCTYPE Reference SYSTEM "Recipe.dtd">

<Refenence>

<书籍>

<名称>二二年研究生入学考试指南英语分册< 名称>

<作者>朱泰琪< 作者>

<价格 货币单位"元" >35.00<价格>

< 书籍>

<书籍>

<名称>一九九九Java词汇 < 名称>

<作者>黄敏红< 作者>

<价格 货币单位"元" >50.00<价格>

< 书籍>

</Reference>

5.2.5 XML 的数据模式问题

XML 是一种完全面向数据语义的标识语言取消了HTML 的显示样式与布局描述能

力突出了数据的语义与元素结构描述能力简单地说数据模式就是指元素的嵌套形式

它并没有像元素那样在DTD 中单独指出它是以元素之间的嵌套关系来表现的是一种逻

辑而已数据模式强调元素之间的逻辑嵌套关系而不是元素标记或者元素属性的定义

________

第5 章 XML 简介

和描述

XML 的数据模式数据逻辑结构的特点

XML 提供了一个将大量信息组织成为具有确定意义的整体结构的功能这种具有确定

意义的整体称为文档XML文档是数据的容器它并不关心数据的显示样式与布局效果 构

造XML 文档的基本成分是元素Element

XML 文档的元素形成一种层次结构处于顶端的元素称为根元素根元素包含了所有

其他元素XML 支持元素的嵌套但不像HTML 那样允许元素交迭即XML 中元素结束

标记出现次序应该和开始标记出现次序严格相反XML 通过元素的这种层次关系支持丰富

数据结构Rich Data Structure 换句话说XML 文档中的元素之间不是简单的前后次序

关系而是具有明确的从属依赖等关系如图5.1 所示根元素Student 学生包含了

Contaction 联系方式元素Contaction 又包含了Email(电子邮件)元素最后形成一个树

状结构元素的名字描述了元素的内容而分层结构描述的是元素之间的关系例如元

素Contaction 包含元素Email 表明Email 是Contaction 的子元素

图5.1 XML 文档中的元素关系

L 数据的输出与存储

XML 描述的是数据的内容或语义而不像HTML 那样描述显示样式和布局那么

如何将XML 描述的内容“展现”给用户呢

XML 文档除了可以用文本编辑器浏览外由于它具有天然的层次结构许多工具还可

以将XML 文档显示为一个可扩展的树形结构更为复杂的输出样式需要用到过滤器例

如对于一部XML 格式的小说如果要将它以传统的纸张方式Web 页面格式和适合掌

上设备阅读的格式发布就需要分别为这3 种不同媒体提供输出样式说明但描述内容的

XML 文档无需任何改动这就实现了内容与显示样式的分离

为了向用户提供基于XML 的内容查询与存储技术非常重要假设某个用户需要在

一个包含大量医疗设备信息的XML 文档中访问有关助听器的数据如何才能以足够的精

确度查找到目标数据这就需要有一种合适的存储系统和查询语言虽然目前还没有专门

用于XML 的标准查询语言但有关查询语言规范的工作正在进行当中一些厂商已经向

W3C 提交了建议W3C 在1998 年12 月成立了一个工作组以讨论查询语言需求期间

Student(学生)

Name(姓名) Address 住址 Contaction(联系方式)

Telephone(电话) Email 电子邮件

________

第二部分 JSP 技术和XML 技术

已经有对象数据库ODBMS 供应商为填补这个空白开发相关工具包括访问XML 资源

子集的查询接口及用来管理大容量XML 资源的一些工具ODBMS 可谓XML 数据的天然

存储工具XML 本质上是元素与对象的一个分层结构而ODBMS 又特别适合于存储层次

型数据此外ODBMS 能够在元素这一层次上管理和操纵数据并在这一层次上提供了

极为完善的加锁模式

除了ODBMS 之外将信息存储在传统的关系数据库然后将查询结果转换为XML

也是一种可行的方法例如在Web 开发中使用ADO 查询ODBC 数据源然后将结果用

XML 来描述使用这种方法的好处在于可以用标准SQL 来获得结果集的XML 表达也许

在不远的将来许多关系数据库会提供将查询结果直接转换为XML 的机制

面向eb 的数据挖掘技术XML 的数据模式优势在网络应用中的体现

由于XML 能够使不同来源的结构化的数据很容易地结合在一起因而使搜索多样的

不兼容的数据库能够成为可能从而为解决Web 数据挖掘难题带来了希望XML 的扩展

性和灵活性允许XML 描述不同种类应用软件中的数据从而能描述搜集的Web 页中的数

据记录同时由于基于XML 的数据是自我描述的数据不需要有内部描述就能被交换

和处理

Web 上的数据与传统的数据库中的数据不同传统的数据库都有一定的数据模型

可以根据模型来具体描述特定的数据而Web 上的数据非常复杂没有特定的模型描述

每一站点的数据都各自独立设计并且数据本身具有自述性和动态可变性因而Web 上

的数据具有一定的结构性但因自述层次的存在从而是一种非完全结构化的数据这也

被称之为半结构化数据半结构化是Web 上数据的最大特点

Web 数据挖掘技术首要解决半结构化数据源模型和半结构化数据模型的查询与集成问

题解决Web 上的异构数据的集成与查询问题就必须要有一个模型来清晰地描述Web

上的数据针对Web 上的数据半结构化的特点寻找一个半结构化的数据模型是解决问题

的关键所在除了要定义一个半结构化数据模型外还需要一种半结构化模型抽取技术

即自动地从现有数据中抽取半结构化模型的技术面向Web 的数据挖掘必须以半结构化模

型和半结构化数据模型抽取技术为前提

以XML 为基础的新一代WWW 环境是直接面对Web 数据的不仅可以很好地兼容原

有的Web 应用而且可以更好地实现Web 中的信息共享与交换XML 可看作一种半结构

化的数据模型可以很容易地将XML 的文档描述与关系数据库中的属性一一对应起来

实施精确地查询与模型抽取

1 XML 对异源结构化数据的处理

XML 给基于Web 的应用软件赋予了强大的功能和灵活性因此它给开发者和用户带

来了许多好处比如进行更有意义的搜索并且Web 数据可被XML 唯一地标识没有XML

搜索软件必须了解每个数据库是如何构建的但这实际上是不可能的因为每个数据库描

述数据的格式几乎都是不同的由于不同来源数据的集成问题的存在现在搜索多样的不

兼容的数据库实际上是不可能的XML 能够使不同来源的结构化的数据很容易地结合在一

起软件代理商可以在中间层的服务器上对从后端数据库和其它应用处来的数据进行集成

然后数据就能被发送到客户或其他服务器做进一步的集合处理和分发XML 的扩展性

________

第5 章 XML 简介

和灵活性允许它描述不同种类应用软件中的数据从描述搜集的Web 页到数据记录从而

通过多种应用得到数据同时由于基于XML 的数据是自我描述的数据不需要有内部

描述就能被交换和处理利用XML 用户可以方便地进行本地计算和处理XML 格式的

数据发送给客户后客户可以用应用软件解析数据并对数据进行编辑和处理使用者可以

用不同的方法处理数据而不仅仅是显示它XML 文档对象模式(DOM)允许用脚本或其他

编程语言处理数据数据计算不需要回到服务器就能进行XML 可以被利用来分离使用者

观看数据的界面使用简单灵活开放的格式可以给Web 创建功能强大的应用软件而原

来这些软件只能建立在高端数据库上另外数据发到桌面后能够用多种方式显示

2 XML 对数据简单开放式的描述

XML 还可以通过以简单开放扩展的方式描述结构化的数据XML 补充了HTML 被

广泛地用来描述使用者界面通过XML 数据可以粒状地更新每当一部分数据变化后

不需要重发整个结构化的数据变化的元素必须从服务器发送给客户变化的数据不需要

刷新整个使用者的界面就能够显示出来XML 也允许加进其他数据比如预测的温度加

入的信息能够进入存在的页面不需要浏览器重新发一个新的页面XML 应用于客户需要

与不同的数据源进行交互时数据可能来自不同的数据库它们都有各自不同的复杂格式

但客户与这些数据库间只通过一种标准语言进行交互那就是XML 由于XML 的自定义

性及可扩展性它足以表达各种类型的数据客户收到数据后可以进行处理也可以在不

同数据库间进行传递总之在这类应用中XML 解决了数据的统一接口问题但是与

其他的数据传递标准不同的是XML 并没有定义数据文件中数据出现的具体规范而是在

数据中附加TAG 来表达数据的逻辑结构和含义这使XML 成为一种程序能自动理解的规

3 XML 对运算负荷的分布式处理

XML 应用于将大量运算负荷分布在客户端即客户可根据自己的需求选择和制作不同

的应用程序以处理数据而服务器只需发出同一个XML 文件如按传统的Client/Server

工作方式客户向服务器发出不同的请求服务器分别予以响应这不仅加重服务器本身

的负荷而且网络管理者还需事先调查各种不同的用户需求以做出相应不同的程序但假

如用户的需求繁杂而多变则仍然将所有业务逻辑集中在服务器端是不合适的因为服务

器端的编程人员可能来不及满足众多的应用需求也来不及跟上需求的变化双方都很被

动应用XML 则将处理数据的主动权交给了客户服务器所作的只是尽可能完善准确

地将数据封装进XML 文件中正是各取所需各司其职XML 的自解释性使客户端在收

到数据的同时也理解数据的逻辑结构与含义从而使广泛通用的分布式计算成为可能

5.3 CSS 与XSL 及其实例

在XML 文件中使用的基本上是自定义的标记显然一个浏览器是无法理解这些标

记的现在浏览器仅仅是作为一个XML 文件的解析器——只要你的XML 文件是

Well-Formed 的那么它就将文件原封不动地给你显示出来在XML 中内容与表现形式是

分开的这就使得不同的用户可以根据他们自己的需要来定义数据的表现形式在一个

XML 的源文件中并没有关于它表现形式的信息

________

第二部分 JSP 技术和XML 技术

XML 文件的所有表现信息多发放在了stylesheet 样式表文件当中stylesheet 文件

全权负责XML 源文件的表现形式所以说如果一个XML 源文件对应不同的stylesheet 文

件它就会有不同的表现形式. 有了stylesheet 文件我们可以对文件表现型始终的大小颜色

空白作特定的规定样式单(Style Sheet)是一种描述结构文档表现方式的文档它既可以描

述这些文档如何在屏幕上显示也可以描述它们的打印效果甚至声音效果与传统使用

的<font>等标记相比样式单有许多突出的优点表达效果丰富文档体积小便于信息

检索可读性好

迄今为止W3C 已经给出了两种样式单语言的推荐标准一种是层叠样式单CSS

Cascading Style Sheets 另一种是可扩展样式单语言XSL eXtensible Stylesheet

Language CSS 可以展现HTML 和XML 文件而XSL 可以展现XML 和Transformation

转型语言

5.3.1 CSS 简介

CSS 就是一种叫做样式表stylesheet 的技术也有的人称之为层叠样式表Cascading

Stylesheet 在主页制作时采用CSS 技术可以有效地对页面的布局字体颜色背景

和其它效果实现更加精确的控制只要对相应的代码做一些简单的修改就可以改变同一

页面的不同部分或者页数不同的网页的外观和格式CSS 是通过对页面结构的风格控制

的思想控制整个页面的风格的如果站点上所有的网页风格都使用一个CSS 文件进行控

制只要修改这个CSS 文件中相应的行那么整个站点的所有页面都会随之发生变动

CSS 的主要特点和对XML 的贡献如下

实现了所有浏览器和平台之间的兼容性

使页面的字体变得更漂亮而且可以选择各种不同的字体颜色背景等使页

面不再像单纯的XML 包括DTD 文档那样枯燥单调

使得网页中的布局变得轻松而简单并且更合理更清晰

只要这些网页套用的都是一个风格样式表可以将许多网页的风格格式同时更新

不用再一页一页地更新了

更少的编码更少的页数和更快的下载速度

样式表只是简单的文本就像XML 那样它不需要图像不需要执行程序不需

要插件不需要流式它就像XML 指令那样快

5.3.2 CSS 的基本格式问题

定义CSS 的基本格式如下

part{property : value ; property : value ; ...}

part 选择符被施加样式的元素可以是标记tag 类(class) 标识(id)等

property 样式属性可以是颜色字体背景等等

value 样式属性取值决定样式结果

对于每个被施加样式的元素都采用了命令括号 {……} 的格式封闭成为一个独立的

单元样式单一般不包含在XML 文档内部以独立的文档方式存在可以通过如下格式

在XML 文档内部引用CSS 风格样式表

________

第5 章 XML 简介

< ? xml-stylesheet type = " text / css " href="example1.css"?>

其中xml-stylesheet 为关键字type="text /css "表示风格样式表为CSS 类型的

href="example1.css"表示所引用的风格样式表文件为example1.css 而且该文件与引用它的

XML 文档位于同一目录下如果二者不在同一目录下则必须给出该文件的绝对路径

CSS 风格样式表对文本图片超链接等各种网页元素都可以进行控制下面我们主

要介绍一下对文字和超链接的控制其他元素的控制与此类似读者可以自己类比学习

掌握这部分内容

文字控制

请看下面的简单例子

P art1{FONT FAMILY: "宋体"; FONT SIZE: 9pt; LINE HEIGHT: 12pt; color: 000000}

说明FONT FAMILY: "宋体"; 用来指定网页文字的字体FONT SIZE:9pt; 用

来指定网页文字的字号大小pt 是表示大小的单位LINE HEIGHT:12pt; 用来指定行

与行的垂直距离即行高color: 000000 指定网页文字的颜色000000 代表黑色

为十六进制数

链接色彩变化及下划线的控制

A:hover {BACKGROUND COLOR: ffccff; COLOR: 0080ff}

说明hover 表示鼠标指示时链接文字背景色为ffccff 前景色为0080ff

A:link {color: 000000;TEXT DECORATION: none}

说明link 表示未被访问时链接颜色为黑色链接无下划线

A:visited {color:gray;TEXT DECORATION: none}

说明visited 表示被访问后链接颜色为灰色链接无下划线

A:active {color:green;text decoration: none}

说明active 表示鼠标点击时链接颜色为绿色链接无下划线

A:hover {TEXT DECORATION: underline}

说明hover 表示鼠标指示时链接显示下划线

注释

none——没有下划线

underline——下划线

overline——上划线

line-through——中划线

________

第二部分 JSP 技术和XML 技术

5.3.3 一个应用CSS 的XML 文档的实例说明

程序清单 5.20所示的是一个简单的 XML 文档和它的样式单例子

程序清单5.20(information.xml)

<?xml version="1.0" encoding="gb2312" ? Standalone="no" >

<?xml-stylesheet type="text/css" href="example1.css"?>

<!DOCTYPE information SYSTEM "customer.dtd">

<information>

<customer>

<name>李超群</name>

<name>雷雨风</name>

<id>1</id>

<address>当代商城</ address >

<email>lichaoqun@sina.com</email>

<email>leiyufeng@sohu.com</email>

<goods>钻石主板2代</goods>

</customer>

<customer>

<name>朱峰</name>

<id>3</id>

<address>上海市淮海路35号</address>

<email>zhufeng@hotmail.com</email>

<email>zhufeng1@hotmail.com</email>

<telephone>021-76483218</telephone>

<goods>轻骑兵组合音响</goods>

</customer >

</information>

上面是一个完整的XML 文档描述的是一个客户信息表其中有两个客户的资料

有关客户信息表的DTD 定义如程序清单5.21

程序清单5.21(customer.dtd)

<!ELEMENT information(customer*) >

<!ELEMENT customer(name+ id address telephone? email* goods) >

<!ELEMENT name(#PCDATA) >

<!ELEMENT id(#PCDATA) >

<!ELEMENT address(#PCDATA) >

<!ELEMENT telephone(#PCDATA) >

<!ELEMENT email(#PCDATA)>

<!ELEMENT goods(#PCDATA)>

下面看一下如何利用CSS 将上述资料显示在浏览器中见程序清单5.22

程序清单5.22(example1.css)

information customer

{

font-family: "宋体";

________

第5 章 XML 简介

font-size:10.5pt;

font-weight:bold;

color:black;

display:block;

margin-bottom:5pt;

}

id address telephone

{

font-family: "隶书";

font-weight:normal;

font-size:10.5pt;

display:block;

color:blue;

margin-left:20pt;

}

name email

{

font-family: "行楷"

font-weight:bold;

font-size:10.5pt;

display:block;

color:red;

margin-top:5pt;

margin-left:8pt;

}

利用上面的CSS 风格样式文件我们可以得到像普通网页一般的浏览效果这种效果

和单纯的XML 包括DTD 显然不可同日而语读者结合前面一节的例子就可以发现这

一点

5.3.4 XSL 简介

XSL(eXtensible Stylesheet Language 可扩展样式语言)是为XML 文件定义的一种标识

语言它将提供远远超过CSS 的强大功能如将元素再排序等实际上简单的XML 已可

被CSS 所解释然而复杂的高度结构化的XML 数据或XML 文档则只能依赖于XSL 极强

的格式化的能力呈现给用户正如XML 介于HTML 和SGML 之间一样XSL 标准介于

CSS 和SGML 的DSSSL(Document Style Semanticsand Specification Language 文档样式

语义和规范语言)之间DSSSL 定义格式化对象的全特征模式由于DSSSL 使用框架语法

而且是很复杂的所以DSSSL 未能得到推广应用XSL 支持DSSSL 流对象和CSS 对象

并对复杂的任务提供进入脚本语言的通道而且允许扩展

XSL 可扩展样式语言也是一种显示XML 文件的规范和CSS 不同的是XSL 是

________

第二部分 JSP 技术和XML 技术

遵循XML 的规范来制定的也就是说XSL 文件本身符合XML 的语法规定XSL 在排

版样式的功能上要比CSS 强大比如CSS 适用于那些元素顺序不变的文件它不能改变

XML 文件中元素的顺序——元素在XML 文件中是什么顺序排列的那么通过CSS 表现出

来顺序不能改变对于那些需要经常按不同元素排序的文件我们就要用XSL XSL 是怎

样工作的呢XML 文件在展开后是一种树状结构称为原始树XSL 处理器现在只

有IE 5 支持XSL 在IE 5 中的处理器叫XSL Stylesheet Processor 从这个树状结构读取信

息根据XSL 样式的指示对这个原始树进行排序复制过滤删除选择运算等

操作后产生另外一个结果树然后在结果树中加入一些新的显示控制信息如表格

其他文字图形以及一些有关显示格式的信息XSL 处理器根据XSL 样式表的指示读取

XML 文件中的信息然后再重新组合后转换产生一个Well-Formed 的HTML 文件浏览

器显示HTML 文件肯定是没问题的这样XML 文件中的信息就会以一定的形式显示在

我们面前了

下面提供一个XSL 的简单实例读者可以通过浏览体会XSL 的实际效果DTD 文件

读者可以参照前面的讲述来书写并把该DTD 文件和下面的XML 文件存放在一个目录下

即可见程序清单5.23

程序清单5.23(employee.xml)

<?xml version="1.0" encoding="GB2312"? standalone="no">

<?xml:stylesheet type="text/xsl" href="employee.xsl"?>

<!DOCTYPE 打工人员名单 SYSTEM "employee.dtd">

<打工人员名单>

<打工人员>

<姓名>梅幽明</姓名>

CORBA 技术

4.1.2 目前流行的分布式计算解决方案

目前市场上流行的分布式计算解决方案除了EJB 技术之外还有CORBA 技术RMI

技术微软的COM/DCOM 技术了在这一小节中我们就对这3 种解决方案做一个简单

的对比首先是CORBA 技术

CORBA 技术

CORBA 构件模型的底层结构为ORB 一个CORBA 构件采用IDL 进行描述CORBA

提供了IDL 到C C Java COBOL 等语言的映射机制IDL 编译器IDL 编译器

可以生成Server 方的Skeleton(框架) 和Client 方的Stub(存根)代码通过分别与客户端和

服务端程序的联编(指的是Stub Skeleton 代码分别与客户端程序服务端程序的联编) 即

可得到相应的Server 和Client 程序

CORBA 提供了一系列的公共对象服务规范COSS(Common Object Service

Specification) 其中包括名字服务永久对象服务生命周期服务事务处理服务对象事

件服务和安全服务等它们相当于一类用于企业级计算的公共构件此外CORBA 还针

对电信石油等典型的应用行业提供了一系列的公共设施

CORBA 是一种语言中性的软件构件模型可以跨越不同的网络不同的机器和不同

的操作系统实现分布对象之间的互操作

CORBA 有几个基本的优点与开发语言无关的独立性与开发者无关的独立性和与

操作系统无关的独立性CORBA 的ORB 在当前每一种主流操作系统上均有实现 (仅就

Microsoft 的各种操作系统来说CORBA 获得的支持甚至超越了微软自己推荐的DCOM 模

________

第4 章 JSP 与J2EE 分布式处理技术

型) 除此之外CORBA ORB 可以访问多种语言实现的对象包括C++ COBOL Smalltalk

和Java 借助于CORBA 的IIOP 协议(CORBA’s Internet Interoperability Protocol) 某一开

发者比如说我开发的CORBA ORB 能够获取并操作存在于远程机器上的由其他的开发

者比如说你开发的分布式对象Java ORB 允许客户端在没有安装任何特别软件的情况

下实现Java 客户端应用程序Java ORB 的类可以像Java Applet 一起动态下载也可能与

浏览器捆绑在一起

DCOM 技术

目前Microsoft 的分布式组件对象模型DCOM Distributed Componont Object Model

仅运行于Windows 操作系统之上Microsoft 正在与第三方开发商协作以将DCOM 移植

到其它的操作系统上(包括MVS 和几种UNIX 操作系统) 像CORBA 一样DCOM 是独立

于语言的它用Microsoft 的对象描述语言ODL)通过接口对对象加以描述

与CORBA 相比DCOM 有三个重大缺点首先它由单一开发者(微软)定义并控制

这大大限制了DCOM 使用者的选择范围(比如DCOM 开发工具) 其次DCOM 缺乏众多

的平台支持这极大程度地制约了代码的可重用性和DCOM 应用的可扩展性最后与

CORBA 相比DCOM 是一种相对不十分成熟的技术尽管微软目前正为DCOM 加入消

息和事务支持但这些功能在1994 年的CORBA 2.0 就已经实现了并且正由几家不同的

CORBA 软件开发商所发行

为了使得应用程序能够访问服务端的DCOM对象开发者不得不使用Internet Explorer

浏览器和Windows 平台只有这样才能支持DCOM 的客户端程序这样的限制当然削弱

了 DCOM 客户端应用程序的可用性而另一方面DCOM 的一个重大优势在于对

Microsoft Windows 的用户是免费的DCOM 的发展经历了一个相当曲折的过程DCOM

起源于动态数据交换(DDE)技术通过剪切/粘贴(Cut/Paste)实现两个应用程序之间共享数据

的动态交换对象连接与嵌入(OLE)就是从DDE 模型中引伸而来的随后Microsoft 引入

了构件对象模型COM 形成了COM 对象之间实现互操作的接口标准COM 模型规

定了对象模型和编程要求使COM 对象可以与其他对象相互操作这些对象可以用不同

的语言实现其结构也可以不同基于COM 微软进一步将OLE 技术发展到OLE2 其

中COM 实现了OLE 对象之间的底层通信工作其作用类似于CORBA/ORB 不过此时的

COM 只能作用在单机Wintel 平台上在OLE2 中也出现了我们今天熟知的拖放技术以

及OLE 自动化与此同时微软在VB 中引入了可以嵌入任何可视构件的通用模型

VBX(OCX 的前身) VBX 的主要局限在于它并不是一个开放的结构也没有为第三方软件

开发商提供VBX 集成的标准最后微软将上述思想集中在一起以COM 作为构件之间

通信框架VBX 也发展为OLE 控件OCX 的形式DCOM 是COM 在分布计算方面的自然

延续它为分布在网络不同节点的两个COM 构件提供了互操作的基础结构而所有以OLE

为标志的技术如今也已挂上了ActiveX 标志

从CORBA 的观点来看我们可以粗略地说ActiveX 控件与DCOM 的关系相当于

CORBA 构件与ORB 的关系当然按照微软一贯的产品开发逻辑微妙的思想都退到了

幕后而提供给开发者的是一个以Wizard 方式生成各种应用的可视化开发环境在公共服

务方面微软提出了自己的事务服务器MTS(Microsoft Transaction Server)和消息队列服务

________

第一部分 JSP 技术与J2EE 技术

器MSMQ(Microsoft Message Queue Server) 前者与CORBA 对象事务服务目标类似后

者则是为了保证应用系统之间进行可靠的消息通讯和管理 此外微软在网络安全方面也

有一整套实用的解决方案

RMI 技术

按照Javasoft 对Java 的界定Java 是一个应用程序开发平台它按照高性能可移植

可解释的原则提供面向对象的编程语言和运行环境Java 计算的本质就是利用分布在网

络中的各类对象协同完成相应的任务EJB 组件模型就是一个最有说服力的例子还有

Java Applet 可按用户的需求从服务器上动态地下载到客户机的浏览器上完成html 页 面

的动态变化

远程方法调用机制RMI 是构成Java 分布对象模型的基础结构RMI 系统包括存

根/框架层远程引用层和传输层目前RMI 的传输层是基于TCP 协议实现的将来的

RMI 体系结构将建立在IIOP 协议之上可以实现Java 技术与CORBA 技术的深层融合

应用层建立在RMI 系统之上

RMI(Remote Method Invocation) 是JDK 中的重要特色RMI 使得Java 客户能够访问

远程机器上的服务对象这听起来似乎十分类似于CORBA 但两者并不一样其关键在

于服务器端的应用程序也必须用Java 编写且只能使用JDK 中提供的工具或者与JDK 兼

容的工具你根本无法把过去编制的代码加到新程序中去除此之外RMI 还有许多其它

缺陷与CORBA 不同RMI 没有服务这一概念另外根据RMI 写出的Java 服务端对

象往往性能低劣这个缺点源于Java 虚拟机(有趣的是Java CORBA 服务器比RMI 服务

器表现出更好的性能) RMI 也不包括像CORBA ORB 那样的对象激活功能

实际上RMI 及Java 技术更可能向OMG 的标准靠拢而不是背道而驰Sun 已经宣

布Java 事务服务Java Transaction Services 将建立在OMG 的对象事务服务Object

Transaction Services 该公司还曾发布其长远计划使RMI 对象可以通过IIOP 协议相互

通讯(现在已经实现了通过RMI_IIOP 桥)

总而言之RMI 对于用纯Java 书写的小规模的应用程序来说是一种可行方案但

CORBA 提供了集成的基础这种集成是指新开发的代码和已有对象的集成同时允许将

来加以扩展在做出取此舍彼的选择之前你必须权衡上面的各种因素并仔细审视每种

技术的现状

4.1.3 JSP/Java 分布式计算模型

长期以来Client/Server 结构一直是企业计算中使用最普遍的计算架构之一但广大

用户在享受C/S 结构带来的利益时也忍受着越来越大的成本投入和使用与管理上的麻烦

在C/S 结构当中几乎所有的应用逻辑都在客户端实现导致客户端的应用程序越来

越复杂也给开发人员带来了大量的移植工作同时要维护如此肥且节点众多的客户

机更是一件庞杂的工作

Java 给上述问题的解决带来了曙光跨平台和从服务器下载执行等特性使Java 技术在

企业计算中得到越来越多的应用C/S 正逐渐退出舞台代之而起的是一种新的分布式计

算架构三层次3 tier 企业计算架构

________

第4 章 JSP 与J2EE 分布式处理技术

三层架构中处于第一层的是客户端表示层与C/S 结构中的肥客户端不同客户

层仅仅是整个应用系统中的图形用户界面表示不表示任何应用逻辑其某些运行代码可

以从位于第二层的Web 服务器下载到本地的浏览器中执行(如Java Applet 小程序) 几乎不

需要任何管理工作这就是我们平时说的瘦客户机JSP/servlet 程序就是客户端表示

层的代表处于第二层的是应用服务层由一或多台服务器Web 服务器也位于这一层

组成处理企业应用中的所有业务逻辑和对数据库的访问等工作该层具有良好的可扩充

性可以随着应用的需要任意增加服务器的数目构成宽的服务器层EJB 组件CORBA

应用RMI 应用就是第二层的代表处于第三层的是数据中心层由数据库系统和企业中

的已有系统组成

比三层计算更新的概念是多层计算主要是将三层计算中的应用服务层继续细分为

Web 服务器层和应用服务层Web 服务器不再处理任何业务逻辑而只是将处理请求转发

到相应的应用服务单元由应用服务器调用中间件具体实现商业逻辑并把处理结果通过

Web 服务器发送到客户端表示层

在多层计算中用户的开发工作主要集中在应用服务层进行许多中间件(Middleware)

厂商推出了各自的应用服务器中间件产品负责提供较底层的服务如负载均衡状态监测

等以便用户能够将大部分精力集中在业务逻辑的开发

Java 为多层计算中的每个层次都提供了强大的技术Java 2 平台的JFC 所提供的客户

端技术成为开发客户端图形用户界面的首选JSP/servlet 程序功能强大运行效率高

能够快速生成动态页面反馈回客户端在应用服务层和数据中心层包括EJB RMI

JDBC Java IDL 在内的四种技术能够让企业快速地开发他们的分布式应用对于市场上各

式各样的中间件产品Java 也提供了独特而有效的解决方案Java 技术正逐渐成为企业计

算的潮流

当前有许多企业采用CORBA 作为它们的分布式计算对象模型它们需要Java 提供开

发CORBA 应用的能力Java IDL 正是为了满足这一需求而推出的Java 成为又一种能够

开发CORBA 对象的语言

Java IDL 与CORBA/IIOP2.0 规范兼容包括一个ORB Object Request Broker 和一

个IDL 语言编译器用于将用IDL 语言定义的界面编译成Java 源代码然后用Java 来实现

该CORBA 对象Java IDL 还提供了一个名服务器用于通过名称寻找CORBA 对象开发

人员通过Java IDL API 可以快速开发CORBA 应用

上面所介绍的就是Java 的分布式计算模型实际上JSP 的分布式计算模型和Java 的

分布式计算模型没有什么两样它们的第二第三层都是一样的只是在第一层客户端表

示层有些小的区别在JSP 的分布式计算模型中客户端表示层是JSP/Servlet 程序也许

还有XML XLST 程序而在Java 的分布式计算模型中客户端表示层是Java Applet 或

者是基于JFC 组件的Java Application JSP 分布式计算模型中的客户端表示层一般是在服

务端运行的而Java 分布式计算模型中的客户端表示层一般是在客户端运行的其运行环

境是浏览器或这JRE

下面我们就来详细描述一下JSP/Java 分布式计算模型的各个细节部分

________

第一部分 JSP 技术与J2EE 技术

客户层Client Tier

J2EE 应用可以是基于Web 的也可以是不基于Web 的在一个基于Web 的J2EE 应

用中用户的浏览器在客户层中运行并从一个Web 服务器上下载Web 层中的静态html

页面或由JSP 或Servlets 等程序生成的动态HTML 页面也许还有XML XSL XSLT

等程序一个不基于Web 的J2EE 应用系统中一个独立的客户端程序或者不运行在一

个HTML 页面中而是运行在其它一些基于网络的系统比如手持设备或汽车电话中的

Java 程序在客户层中运行并在不经过Web 层的情况下访问中间件对象(例如EJB)

该不基于Web 的客户端程序可能也包括一个JavaBeans 类来管理用户输入并将该输入发

送到在企业层中运行的EJB 对象来处理根据J2EE 规范JavaBeans 类不被视为组件

为J2EE 平台编写的JavaBeans 类有实例变量和用于访问实例变量中的数据的getXXX()

和set XXX()方法以此种方式使用的JavaBeans 类在设计和实现上通常都是简单的但是

它们必须符合JavaBeans 规范中列出的命名和设计约定

Web 层

Web 层是一个特殊的客户层J2EE Web 层的Web 组件可以由JSP/Servlet 程序基于

Web 的Java Applets 组成调用Servlets 或者JSP 页面的HTML 页面在应用程序组装时

与Web 组件打包在一起就像客户层一样Web 层可能包括一个JavaBeans 类来管理用

户输入并将输入发送到在业务层中运行的EJB 对象来处理 运行在客户层的Web 组件

依赖Web 服务器来支持响应客户的请求

业务层

作为解决或满足某个特定业务领域比如银行零售或金融业需要的逻辑的业务代

码由运行在业务层的EJB 组件或者其他的中间件来执行一个EJB 对象从客户程序处接收

数据对数据进行处理如果需要再将数据发送到企业信息系统层(可能是数据库系统)

存储一个EJB 对象还可以从企业信息系统层中检索数据并将数据送回客户程序运行

在业务层的EJB 对象依赖于EJB 容器来为诸如事务生命期状态管理多线程及资源存

储池提供通常都非常复杂的系统级代码

业务层经常被称作Enterprise JavaBeans EJB 层业务层构成了3 层J2EE 应用的中

间层而其它两层是客户层和企业信息系统层

查询服务lookup services

因为一个J2EE 应用程序的组件是单独运行的并且往往在不同的设备上运行因此

需要一种能让客户层和Web 层代码查询并引用其他代码和资源的方法客户层和Web 层

代码使用Java 命名和目录接口JNDI 来查询用户定义的对象例如EJB 环境条目例

如一个数据库驱动器的位置企业信息系统层中用于查找资源的JDBC DataSource 对象

以及消息连接

安全和事务管理Security and Transaction Management

诸如安全和事务管理这样的应用行为可以在部署时在Web 和EJB 组件上进行配置这

________

第4 章 JSP 与J2EE 分布式处理技术

个特征将应用逻辑从可能随装配而变化的配置设定中分开了

安全

J2EE 安全模型允许配置一个Web 或EJB 组件使系统资源只能由授权的用户访问

例如一个Web 组件可以被配置成提示输入用户名和密码一个EJB 组件可以被配置成

只让特定团体中的成员调用其某些方法或者一个Servlet 程序可以被配置成让某个组织

中的所有人都能访问其某些方法同时只让该组织中的某些享有特权的人访问一些方法

同样是该Servlet 程序可以针对另外一个环境而被配置成让每个人都能访问其所有方法

或者仅让选定的少数人访问其所有方法

事务管理(Transaction Management)

J2EE 事务模型使得能够在部署时定义构成一个单一事务的方法之间的关系以使一

个事务中的所有方法被处理成一个单一的单元这是我们所希望的因为一个事务是一系

列步骤这些步骤要么全部完成要么全部取消例如一个EJB 对象可能有一组方法

使我们可以通过从第一个账户借出并存入第二个账户的方式而将钱从第一个账户转移到第

二个账户我们希望全部的操作被作为一个单元对待这样如果在借出之后存入之前发

生了故障该借出操作被取消事务属性是在装配期间定义在一个组件上的这使得能将

来自多个应用组件的方法归到一个事务中这说明我们可以轻易变更一个J2EE 应用程序

中的应用组件并重新指定事务属性而不必改变代码或重新编译 在设计应用组件时

要记住尽管EJB 对象有一个可使应用组件的容器自动启动多步事务的机制但是Java

Applet 和应用的客户容器可能并不支持这一点然而Java Applet 和应用客户容器总是能

够调用支持这一点的一个EJB 对象还应当注意JSP 和Servlets 程序没有被设计成是支

持事务的它们通常应当将事务工作交给一个EJB 对象来完成然而如果事务工作在一

个JSP 或Servlet 程序中是必需的那么此种工作也应当是非常有限的

可再用应用组件Reusable Application Components

J2EE 组件Java Applets EJB 组件JSP Servlets 都被打包成模块并以Java Archive

JAR 文件的形式交付一个模块由相关的组件相关的文件及描述如何配置组件的配置

描述文件组成例如在组装过程中一个HTML 页面和Servlet 程序被打包进一个模块之

中该模块包含该HTML 文件Servlet 程序及相关的配置描述文件并以一个Web Archive

WAR 文件的形式交付该WAR 文件是一个带.war 扩展名的标准JAR 文件使用模块

使得利用相同的组件中的某些组件来组装不同的J2EE 应用程序成为可能 例如一个J2EE

应用程序的Web 版可能有一个EJB 组件还有一个JSP 程序该EJB 组件可以与一个应用

客户组件结合以生成该应用程序的非Web 版本这不需要进行额外的编码只是一个装

配和部署的问题并且可再用组件使得将应用开发和部署过程划分成由不同的角色来完成

成为可能这样不同的人或者公司就能完成封装和部署过程的不同部分

设计用户界面和引擎Designing the User Interface and Engine

在为J2EE 应用程序设计用户界面和后端引擎时我们需要决定是让该程序基于Web

________

第一部分 JSP 技术与J2EE 技术

还是不基于Web 在做出这个决定时我们可能希望考虑平台配置下载速度安全网

络流量和网络服务例如包含有用户界面并且经常被大量用户访问的一个Java Applet 可

能需要花很长的时间才能被下载下来这让用户沮丧然而如果知道该Java Applet 要运

行在公司的内部网内的受控环境中那么在这种情况下该Applet 将拥有一个完全可接

受的下载速度

注意所谓的三层模型有多种看法上面的叙述只是代表我们的看法有人认为

JSP/Servlet 程序应该算是业务层也就是第二层我们认为应该把商业逻辑

从JSP/Servlet 程序中最大限度的剥离让JSP/Servlet 程序只负责数据的显示与

接受用户请求这样它们就是属于客户端表示层的了

4.2 远程方法调用RMI 技术

在这一节中我们将介绍RMI 技术的基础知识结构开发和应用

4.2.1 RMI 概述

在大型的企业计算中需要将整个应用系统分解成若干子系统并运行于相应的部门当

中以提高整个系统的应用效率和可维护性及安全性由于不同的工作由不同的系统来完

成在它们之间需要一种安全便利的通讯机制从面向对象的角度来说企业计算需要一

种分布式的对象模型使得运行于不同主机之间的对象能够互相进行方法调用RMI 正是

基于Java 的能够满足上述计算的分布式计算模式实现了在运行于不同虚拟机的对象之间

的方法调用

RMI 的研究工作其实在1994 年Java 诞生之前就已在Sun 公司的实验室里开始了

研究人员在充分评估了分布计算的趋势并比较了多种分布式系统以后开发了RMI 对象模

式在1997 年JDK1.1 发布时正式推出成为Java 分布式计算的重要技术之一JDK1.1

为RMI 的开发提供了一系列的API 简化了RMI 的开发工作

从调用方式来看RMI 和RPC 远过程调用有很大的相似之处但二者之间最大的

不同在于RMI 是面向对象而RPC 是基于过程调用的由于RMI 面向对象的特性RMI

调用可以直接将对象在调用的两端之间进行传递不但可以传送数据而且还可以传递方

法(mobile behavior) 扩展了RMI 的使用另外RMI 还支持两个RMI 对象之间的方法回调

(callback)

RMI 赋予每个RMI 对象一个唯一的名字并将之与实际的对象捆绑在一起binding

这种对象关系登记在RMI 的注册登记表中调用者通过对象的名字找到相应的对象后调用

它的方法而不需要考虑该对象的实际物理存储位置这不但更符合人们的使用习惯而

且提高了系统的可扩充性和鲁棒性RMI 将多个RMI 对象的名字登记在同一张登记表中

监听于某端口一个对象有一或多个方法以供远程调用从而使一个端口可以提供多种

服务节约了系统的端口资源

目前RMI 不限于用Java 语言编写的RMI 对象之间的调用它们之间通过JRMP Java

Remote Method Protocol 译为Java 远程方法协议进行通信对于另一种对象模式

CORBA RMI 已经实现同CORBA 对象之间的互相调用由Sun 公司开发的进行二者之间

________

第4 章 JSP 与J2EE 分布式处理技术

相互通讯的产品RMI over IIOP 已经正式发布

4.2.2 开发RMI 应用

在这一小节中我们将介绍如何开发RMI 应用要开发RMI 必须构造4 个主要的

类它们分别是远程对象的本地接口RMI 客户远程对象实现和RMI 服务程序RMI

服务程序生成远程对象实现的一个实例并用一个特殊的URL 注册它RMI 客户在远程服

务器上查找对象若找到就把它转换成本地接口类型然后像一个本地对象一样使用它

下面是一个简单的RMI 例子远程对象只返回一个消息字符串要使这个例子更有价值

我们需要做的就是完善远程对象实现类

远程对象的本地接口

该类仅仅是一个接口而不是实现它声明了RMI 客户端程序可以调用的方法RMI

客户机可以直接使用它RMI 服务程序必须产生一个远程对象来实现它并用某个URL

注册它的一个实例请看程序清单4.1 这是一个简单的远程对象的本地接口

程序清单4.1

//File Name:Rem.java

//Author:fancy

//Date:2001.5

//Note:Remote interface

import java.rmi.*;

public interface Rem extends Remote

{

public String getMessage() throws RemoteException;

public String getAuthor() throws RemoteException;

}

在编写远程对象的本地接口的时候读者应该注意本地接口Rem 必须是公共的

否则客户机在加载一个实现该接口的远程对象时就会出错此外它还必须从

java.rmi.Remote 接口继承而来远程对象的本地接口中的每一个方法都必须抛出远程异常

java.rmi.RemoteException

远程对象实现类

这个类真正实现RMI 客户所调用的远程对象本地接口它必须从UnicastRemoteObject

继承其构造函数应抛出RemoteException 异常而且每一个实现方法都必须抛出

RemoteException 异常请看程序清单4.2

程序清单4.2

//File Name:RemImpl.java

//Author:fancy

//Date:2001.5

//Note:implemention remote interface

import java.rmi.*;

________

第一部分 JSP 技术与J2EE 技术

import java.rmi.server.UnicastRemoteObject;

public class RemImpl extends UnicastRemoteObject implements Rem

{

public RemImpl() throws RemoteException

{

}

public String getMessage() throws RemoteException

{

return("Here is a remote message.");

}

public String getAuthor() throws RemoteException

{

return("fancy.");

}

}

RMI 服务器类

该类将创建远程对象实现RemImpl 的一个实例然后用一个特定的URL 来注册它

所谓注册就是通过调用Naming.bind()方法或Naming.rebind()方法来将RemImpl 的实例对象

绑定到特定的URL 上 请看程序清单4.3

程序清单4.3

//File Name:RemServer.java

//Author:fancy

//Date:2001.5

//Note: remote server

import java.rmi.*;

import java.net.*;

public class RemServer

{

public static void main(String[] args)

{

try

{

RemImpl localObject = new RemImpl();

Naming.rebind("rmi://localhost/Rem" localObject);

}

catch(RemoteException re)

{

System.out.println("RemoteException:"+re);

}

catch(MalformedURLException mfe)

________

第4 章 JSP 与J2EE 分布式处理技术

{

System.out.println("MalformedURLException: "+mfe);

}

}

}

RMI 客户类

RMI 客户使用Naming.lookup()方法在指定的远程主机上查找对象若找到就把它转换

成本地接口类型(在本例中是Rem 类型) 然后像一个本地对象一样使用它与CORBA 不

同之处在于RMI 客户必须知道提供远程服务主机的URL 这个URL可以通过rmi://host/path

或rmi://host:port/path 来指定如果省略端口号就使用1099 Naming.lookup()方法可能产

生3 个异常RemoteException NotBoundException MalformedURLException 这3 个异

常都需要捕获RemoteException Naming 和NotBoundException 在java.rmi 包中定义

MalformedURLException 在java.net 包中定义所以我们要在应用程序中导入这两个包另

外客户机将向远程对象传递串行化对象Serializable 所以还应在程序中导入java.io 包

请看程序清单4.4

程序清单4.4

//File Name: RemClient.java

//Author:fancy

//Note:rmi client

//Date:2001.5

import java.rmi.*;

import java.net.*;

import java.io.*;

public class RemClient

{

public static void main(String[] args)

{

try

{

String host = (args.length > 0) ? args[0] : "localhost";

//通过URL在远程主机上查找对象并把它转化为本地接口Rem类型

Rem remObject=(Rem)Naming.lookup("rmi://" + host + "/Rem");

System.out.println(remObject.getMessage()); //调用远程对象的方法

System.out.println(remObject.getMessage()); //调用远程对象的方法

}

catch(RemoteException re)

{

System.out.println("RemoteException: " + re);

}

________

第一部分 JSP 技术与J2EE 技术

catch(NotBoundException nbe)

{

System.out.println("NotBoundException: " + nbe);

}

catch(MalformedURLException mfe)

{

System.out.println("MalformedURLException:"+ mfe);

}

}

}

如上所述RMI 应用已经编写好了下面我们应该编译运行这个RMI 应用步骤如下

1 编译RMI 客户和服务器这将自动编译远程对象的本地接口和远程对象实现

命令行代码如下

javac RemClient.java

javac RemServer.java

2 生成客户存根模块(stub)和___________服务器框架(skeleton)

rmic RemImpl

这将构造RemImpl_Stub.class 和RemImpl_Skeleton.class

3 请将Rem.class RemClient.class 和RemImpl_Stub.class 拷贝到RMI 客户机将

Rem.class RemImpl.class RemServer.class 和RemImpl_Skeleton.class 拷贝到RMI 服务器

4 启动RMI 注册程序在命令行状态下运行rmiregistry 程序rmiregistry 程序

在JDK 的bin 文件夹内这个程序在服务器上执行不论有多少个RMI 远程对象需要运行

本操作只需做一次

5 运行RMI 服务程序在命令行下执行下面的命令

java RemServer.class

这一步的作用是启动RMI 服务器(在服务器上执行)

在命令行下执行下面的命令

java RemClient.class

这一步的作用是启动RMI 客户程序(在客户端计算机上执行) 输出结果为

Here is a remote message.

fancy

4.2.3 使用JSP 编写RMI 应用客户端

我们可以编写基于JSP 的RMI 应用客户端使得在JSP 程序中也能够调用远程RMI

对象的商业方法这种技术无疑大大地扩展了JSP 程序的能力也是利用JSP+J2EE 技术

构建企业应用系统中关键的一环

实际上基于JSP 的RMI 应用客户端可以从程序清单4.4 改写而来请看程序清单4.5

程序清单4.5

<%--

File Name:rem.jsp

Author:fancy

________

第4 章 JSP 与J2EE 分布式处理技术

Date:2001.5

Note:the client for rmi object

--%>

<%@ page import=” java.rmi.*” %>

<%@ page import=”java.net.*” %>

<%@ page import=”java.io.*” %>

<%@ page import=”Rem.class” %>

<%

try

{

String host = "localhost";

//通过URL在远程主机上查找对象并把它转化为本地接口Rem类型

Rem remObject=(Rem)Naming.lookup("rmi://" + host + "/Rem");

out.println(remObject.getMessage()+”<br>”); //调用远程对象的方法

out.println(remObject.getMessage()); //调用远程对象的方法

}

catch(RemoteException re)

{

out.println("RemoteException: " + re);

}

catch(NotBoundException nbe)

{

out.println("NotBoundException: " + nbe);

}

catch(MalformedURLException mfe)

{

out.println("MalformedURLException:"+ mfe);

}

%>

程序清单4.5 和程序清单4.4 的输出结果一致

4.3 CORBA 技术

4.3.1 CORBA 技术简介

公用对象请求代理程序体系结构Common Object Request Broker Architecture 缩写

为 CORBA 是对象管理组织Object Management Group 简称为OMG 对应当今快速增

长的软硬件的协同工作能力的要求而提出的方案简而言之CORBA 允许应用程序和其

他的应用程序通讯而不论它们在什么地方或者由谁来设计CORBA 1.1 标准由对象管理

组织(OMG)在 1991 年发布它定义了接口定义语言IDL 和应用编程接口API 从

而通过实现对象请求代理ORB Object Request Broker 来激活客户/服务器的交互CORBA

2.0 标准于1994 年的12 月发布它定义了如何跨越不同的ORB 提供者而进行通讯

________

第一部分 JSP 技术与J2EE 技术

ORB (Object Request Broker)是一个中间件它在对象间建立客户-服务器的关系通过

ORB 一个客户可以很简单地使用服务器对象的方法而不论服务器是在同一机器上还是通

过一个网络访问ORB 截获调用然后负责找到一个对象实现这个请求传递参数和方法

最后返回结果客户不用知道对象在哪里是什么语言实现的远程对象所在的操作系统

以及其它和远程对象接口无关的东西

在传统的客户/服务器程序中开发者使用他们自己设计的或者公认的标准定义设备/

对象之间通信的协议协议的定义依赖于实现的语言网络的传输和其他许许多多因素

ORB 将这个过程简单化使用 ORB 协议定义是通过应用接口而该接口是接口定义语

言IDL 的一个实现它和使用的编程语言无关的并且ORB 提供了很大的灵活性它

让程序员选择最适当的操作系统运行环境和设计语言来建设系统中每个组件更重要的

是它允许集成已经存在的组件

CORBA 是在面向对象标准化和互操作性道路上的一个信号通过CORBA 用户不必

要知道软硬件的平台和他们处在企业网的什么地方就可以操作

Java 提供了一个概念清晰结构紧凑的分布计算模型和构件互操作的方法为构件应

用开发提供了相当的灵活性但由于它还处于发展时期因此其形态很难界定CORBA

是一种集成技术而不是编程技术它提供了对各种功能模块进行构件化处理并将它们捆

绑在一起的粘合剂Java 技术和CORBA 技术在很大的程度上可以看作是互补的为了适

应Web 应用的发展要求许多软件厂商都急于促成CORBA 技术和Java 技术的结合RMI

可以建立在IIOP 协议之上CORBA 不只是对象请求代理ORB 也是一个非常完整的分布

式对象平台CORBA 可以扩展Java 在网络语言组件边界操作系统中的各种应用

其次Java 也不仅仅是与CORBA 捆绑的语言它还是一个易变的对象系统也是一个运

行对象的便携式OS 而且允许CORBA 对象在主机网络计算机和蜂窝电话等应用上运行

Java 还简化了大型CORBA 系统中的代码分配其中内置的多线程和垃圾收集使编写可靠

的网络对象更为方便此外Java 能创建可移动对象并将它们分发出去而采用CORBA

可以将它们连接在一起并与计算环境中的数据库遗留系统其他语言编写的对象和应

用相互集成

CORAB 与Java 的基础结构也可以很好地互补CORBA 处理网络透明性Java 处理

实现透明性CORBA 提供了Java 便携应用环境与对象间的连接由此看来CORAB/Java

技术紧密结合的趋势是势不可挡的而且二者的结合将成为Object Web 技术的主要形态之

4.3.2 CORBA 模型

OMG 是一个开放组织成立于1989 年到今天已有600 多名成员其主要目标是

使用对象技术使基于对象的软件成员在分布的异构环境中可重用可移植可互操作

OMG 成立不久便制定了对象管理体系结构Object Management Architecture OMA 它

由以下4 个部分组成

对象请示代理ORB Object Request Broker 使对象在分布式环境中透明地收发

请求和响应它是构造分布式对象应用使应用在不同的层次的异构环境下相互

操作的基础

________

第4 章 JSP 与J2EE 分布式处理技术

对象服务OS(Object Services) 是为使用和实现对象而提供的基本服务集合对于

分布式应用来说是基本服务同时这些服务独立于应用领域如生命循环服务定

义了对象创建删除拷贝移动的方式它不考虑对象在应用中如何实现的

通用设施CE(Common Facilites) 是许多应用提供的共享服务集合如系统管理

电子邮件等

应用对象AO(Application Objects) 它相当于传统的应用应用对象可由独立的厂

商生产是参考模型中的最高层OMG 对此不作规定

由此可见ORB 在对象管理系统中处于最关键的位置是对象间的通信总线为

ORB 制定的规范称为CORBA 规范CORBA 是一种规范它定义了分布式对象如何实现

相互操作在World Wide Web 盛行之前特别是Java 编程语言风靡之前C 开发者基

本将CORBA 作为其高端分布式对象的解决方案最初OMG 在1990 年制订了对象管理

体系Object Management Architecture 即OMA 来描述应用程序如何实现互操作的时

候作为其中(OMA)的一部分需要有一个标准规范应用程序片段即对象的互操作这导

致了CORBA 的诞生OMA 定义了组成CORBA 的4 个主要部分分别与上面提到过的

OMA 的4 个部分相对应它们是

Object Request Broker ORB 作为对象互通讯的软总线

CORBA Services CORBA 服务定义加入ORB 的系统级服务如安全性命名

和事务处理是开发分布式应用所必需的模块这些服务提供异步事件管理事

务持续并发名字关系和生存周期的管理

CORBA Facilities CORBA 工具定义应用程序级服务如复合文档等对于开

发分布式应用不是必需的但是在某些情况下是有用的这些工具提供信息管理

系统管理任务管理和用户界面

Business Objects 或者是Application Objects 商业对象或者应用对象主要用于

定义现实世界的对象和应用如飞机或银行帐户

上述4 部分的关系如图4.1 所示

图4.1 CORBA 的组成

CORBA 组件包括ORB 核心ORB 接口IDL 存根(stub) DII(Dynamic Invocation

Interface 动态调用接口) 对象适配器IDL 框架(skeleton) DSI(Dynamic Skeleton Interface

动态框架界面) CORBA 运行结构ORB 核心是由特定开发商决定的不是由CORBA

定义的不管怎样ORB 接口是一个标准的功能接口是由所有的CORBA 兼容ORB 提

________

第一部分 JSP 技术与J2EE 技术

供的IDL 编译器为每个界面产生存根(stub) 这就屏蔽了低层次的通信提供了较高层次

的对象类型的特定API DII 是相对于IDL 存根的另一种方法它为运行时构建请求提供

了一个通用的方法对象适配器为把可选的对象技术集成进OMA(Object Management

Architecture)提供支持IDL 框架类似于IDL 存根但是它是工作于服务器端的对象实

现对象适配器发送请求给IDL 框架然后IDL 框架就为之调用合适的对象实现中的方

CORBA 接口和数据类型是用OMG 接口定义语言(OMG IDL)定义的每个接口方法也

是用OMG IDL 声明的IDL 是CORBA 体系结构的关键部分它为CORBA 和特定程序设

计语言的数据类型之间提供映射(例如从CORBA 到Java 之间的映射) IDL 也允许对原有

代码实行封装IDL 是一个面向对象的接口定义语言具有和C++相类似的语法由于所

有的接口都是通过IDL 定义的CORBA 规范允许客户端程序和服务端对象用不同的程序

设计语言书写彼此的细节都是透明的CORBA 在不同对象请求代理(ORB)之间使用IIOP

协议进行通信使用TCP 协议作为网络通信协议

使用接口描述语言(IDL Interface Description Language)编写的对象接口使得与语言

无关的独立性成为可能IDL 使得所有CORBA 对象以一种方式被描述仅仅需要一个由

本地语言C/C++ CORBA Java 到IDL 的“桥梁”

CORBA 被设计和架构为服务端对象使用不同程序语言书写运行于不同平台上的对

象系统CORBA 依赖于ORB 中间件在服务器和客户之间进行通信ORB 扮演一个透明地

连接远程对象的对象每个CORBA 对象提供一个调用接口并且有一系列的方法与之相

联ORB 负责为请求发现相应的实现并且把请求传递给CORBA 服务器ORB 为客户提

供透明服务客户永远都不需要知道远程对象的位置以及用何种语言实现的OMA 最重要

的部分就是ORB 为了创建一个遵从CORBA 规范的应用程序ORB 是CORBA 4 大部分

中唯一必须提供的许多ORB 产品根本不带CORBA Services 或是CORBA Facilities 你

可以自己编写商用对象但是没有ORB CORBA 应用程序绝对无法工作

既然ORB 如此重要那么我们就来讨论一些ORB 的细节问题CORBA ORB 最显而

易见的功能就是对你的应用程序或是其它ORB 的请求予以响应在CORBA 应用程序运

行期间你的ORB 可能被请求做许多不同的事情包括

查找并调用远程计算机上的对象

负责不同编程语言之间的参数转换如C 到Java

可超越本机界限的安全管理

为其它的ORB 收集并发布本地对象的元数据(metadata)

用下载的客户端存根代码stub 中描述的静态方法调用去激活远程对象中的方法

用动态方法调用激活远程对象

自动激活一个当前没有装入内存运行的对象

将回调方法导引向其(当前ORB 对象)管理之下的本地对象

实现细节对软件开发者的透明性是ORB 的一个杰出的特性用户只须在代码中提

供相应的hooks(钩子) 用于初始化ORB 并向ORB 登记该应用程序就可以将该应用程序

和大量分布式对象建立联系

目前对于较为流行的编程语言包括C Smalltalk Java 和Ada 95 已经有了

________

第4 章 JSP 与J2EE 分布式处理技术

许多第三方的ORB 随着其他语言的逐渐流行CORBA 开发商毫无疑问地要做出相应的

ORB 来支持它们

下面我们简单描述CORBA 应用的工作流程如图4.2 所示

图4.2 CORBA 的工作模型

图4.2 说明了从客户端到服务器发送一个消息请求的过程其中客户端也可以是一个

CORBA 对象客户机不需要了解CORBA 对象的位置与实现细节也不需要了解哪个ORB

用于存取对象在客户端应用系统包括远程对象的引用对象引用使用stub 类(存根)方

法作为远程方法的代理这个方法事实上是在ORB 中的所以调用stub 类方法会自动调

用ORB 对象的连接功能ORB 对象会把对stub 类方法的调用传递到服务器端

在服务器端ORB 对象利用框架类(skeleton)代码把远程调用转换成本地对象的方法调

用框架类需要对调用和参数的格式进行转换同时当方法返回时框架类对结果进行

变换然后通过ORB 对象把结果返回给客户机

4.3.3 接口定义语言IDL 以及Java 语言映射

Java IDL Interface Definition Language 可实现网络上不同平台上的对象相互之间的

交互该技术基于通用对象请求代理体系结构CORBA 规范说明IDL 是不依赖于语言的

接口定义语言所有支持CORBA 的语言都有IDL 到该语言的映射就像其名字所表示的

那样Java IDL 支持IDL 到Java 语言的映射CORBA 规范说明和IDL 映射是由OMG 组

织Object Management Group 定义的Sun 公司是其成员之一它在定义IDL 到Java 映

射的工作中起了主要作用

为了保持CORBA 的商业中立性和语言中立性必须有一个中介存在于像C++

CORBA 服务器代码和Java CORBA 客户机这样的实体之间这就是IDL 一个底层对象的

若干相关方法和属性被IDL 封装为一个单一的接口一旦IDL 接口定义完成它可以以stub

类(存根类)或skeleton(框架类)的形式编译成你所使用的开发语言在所有的ORB 中都有

IDL 编译器例如JDK 中提供了idlj.exe 编译器

有一点值得注意的是IDL 不同于其它的面向对象程序设计语言我们不能用它具体实

________

第一部分 JSP 技术与J2EE 技术

现它所声明的类或者是方法因此将它仅仅作为一种定义底层对象接口的语言要好得多

就像在Java 中将属性和方法封装到相关的类中一样上述各项均包含在IDL 的模块之中

在一个模块之中可以定义一个或多个接口

接口定义语言IDL 用于定义CORBA 对象的接口所有的CORBA 对象都必须支持

IDL IDL 的语法和C++的语法非常类似利用Java IDL 可以在Java 中定义实现存

取CORBA 对象对于每个IDL idlj.exe 程序生成一个Java 接口和其他一些必要的.java

文件包括一个客户端stub Stub 和服务器端skeleton Skeleton

为了使Java 支持CORBA 规范说明需要一个把IDL 中的元素映射为Java 中元素的

标准途径Sun 已经制订了两者之间的映射规则并提供了编译器idlj.exe 以便从IDL 得

到相应的stub 类和skeleton 类

表4.1 列出了一些IDL 中元素和Java 中元素的映射关系

表4.1 IDL 和Java 的映射关系

IDL 元素 Java元素

Module package

Interface Interface help class hold class

Constant public static final

Boolean boolean

Char wchar Char

String wstring java.lang.String

Short unsigned short Short

Long unsigned long Long

Float float

Double double

Enum struct union class

Sequence array array

Read only attribute method for accessing value of attribute

Read write attribute Methods for accessing and setting value of attribute

Operation Method

下面我们就来定义一个简单的IDL 程序请看程序清单4.6(hello.idl)

程序清单4.6(hello.idl)

module HelloApp

{

interface Hello

{

string sayHello();

};

};

hello.idl 中声明了sayHello()方法sayHello()方法的返回值是一个字符串我们使用IDL

________

第4 章 JSP 与J2EE 分布式处理技术

编译器idlj.exe 程序(idlj.exe 程序是JDK 自带的程序在bin 文件夹中)把这个hello.idl 文件

映射为Java 源代码文件用以下命令编译Hello.idl 文件

idlj hello.idl

根据命令行中的不同选项idlj.exe 程序将产生不同的文件上面这条命令将创建子目

录HelloApp 并在其中产生5 个文件HelloOperations.java _HelloStub.java Hello.java

HelloHelper.java HelloHolder.java

这5 个文件的程序清单如程序清单4.7 4.11

程序清单4.7(HelloOperations.java)

package HelloApp;

/**

* HelloApp/HelloOperations.java

* Generated by the IDL-to-Java compiler (portable) version "3.0"

* from hello.idl

* 1999年3月27日 18时22分21秒 CST

*/

public interface HelloOperations

{

String sayHello ();

} // interface HelloOperations

程序清单4.8(_HelloStub.java)

package HelloApp;

/**

* HelloApp/_HelloStub.java

* Generated by the IDL-to-Java compiler (portable) version "3.0"

* from hello.idl

* 1999年3月27日 18时22分21秒 CST

*/

public class _HelloStub extends org.omg.CORBA.portable.ObjectImpl

implements HelloApp.Hello

{

// Constructors

// NOTE: If the default constructor is used the

// object is useless until _set_delegate (...)

// is called.

public _HelloStub ()

{

super ();

}

public _HelloStub (org.omg.CORBA.portable.Delegate delegate)

________

第一部分 JSP 技术与J2EE 技术

{

super ();

_set_delegate (delegate);

}

public String sayHello ()

{

org.omg.CORBA.portable.InputStream _in = null;

try

{

org.omg.CORBA.portable.OutputStream _out = _request ("sayHello" true);

_in = _invoke (_out);

String __result = _in.read_string ();

return __result;

}

catch (org.omg.CORBA.portable.ApplicationException _ex)

{

_in = _ex.getInputStream ();

String _id = _ex.getId ();

throw new org.omg.CORBA.MARSHAL (_id);

}

catch (org.omg.CORBA.portable.RemarshalException _rm)

{

return sayHello ();

}

finally

{

_releaseReply (_in);

}

} // sayHello

// Type-specific CORBA::Object operations

private static String[] __ids = {"IDL:HelloApp/Hello:1.0"};

public String[] _ids ()

{

return (String[])__ids.clone ();

}

private void readObject (java.io.ObjectInputStream s)

{

try

{

String str = s.readUTF ();

________

第4 章 JSP 与J2EE 分布式处理技术

org.omg.CORBA.Object obj =

org.omg.CORBA.ORB.init ().string_to_object (str);

org.omg.CORBA.portable.Delegate delegate =

((org.omg.CORBA.portable.ObjectImpl) obj)._get_delegate ();

_set_delegate (delegate);

}

catch (java.io.IOException e)

{

}

}

private void writeObject (java.io.ObjectOutputStream s)

{

try

{

String str = org.omg.CORBA.ORB.init ().object_to_string (this);

s.writeUTF (str);

}

catch (java.io.IOException e)

{

}

}

} // class _HelloStub

程序清单4.9(Hello.java)

package HelloApp;

/**

* HelloApp/Hello.java

* Generated by the IDL-to-Java compiler (portable) version "3.0"

* from hello.idl

* 1999年3月27日 18时22分21秒 CST

*/

public interface Hello extends HelloOperations org.omg.CORBA.Object

org.omg.CORBA.portable.IDLEntity

{

} // interface Hello

程序清单4.10(HelloHelper.java)

package HelloApp;

/**

* HelloApp/HelloHelper.java

* Generated by the IDL-to-Java compiler (portable) version "3.0"

________

第一部分 JSP 技术与J2EE 技术

* from hello.idl

* 1999年3月27日 18时22分21秒 CST

*/

abstract public class HelloHelper

{

private static String _id = "IDL:HelloApp/Hello:1.0";

public static void insert (org.omg.CORBA.Any a HelloApp.Hello that)

{

org.omg.CORBA.portable.OutputStream out = a.create_output_stream ();

a.type (type ());

write (out that);

a.read_value (out.create_input_stream () type ());

}

public static HelloApp.Hello extract (org.omg.CORBA.Any a)

{

return read (a.create_input_stream ());

}

private static org.omg.CORBA.TypeCode __typeCode = null;

synchronized public static org.omg.CORBA.TypeCode type ()

{

if (__typeCode == null)

{

__typeCode = org.omg.CORBA.ORB.init ().create_interface_tc

(HelloApp.HelloHelper.id () "Hello");

}

return __typeCode;

}

public static String id ()

{

return _id;

}

public static HelloApp.Hello read (org.omg.CORBA.portable.InputStream istream)

{

return narrow (istream.read_Object (_HelloStub.class));

}

public static void write (org.omg.CORBA.portable.OutputStream ostream

________

第4 章 JSP 与J2EE 分布式处理技术

HelloApp.Hello value)

{

ostream.write_Object ((org.omg.CORBA.Object) value);

}

public static HelloApp.Hello narrow (org.omg.CORBA.Object obj)

{

if (obj == null)

return null;

else if (obj instanceof HelloApp.Hello)

return (HelloApp.Hello)obj;

else if (!obj._is_a (id ()))

throw new org.omg.CORBA.BAD_PARAM ();

else

{

org.omg.CORBA.portable.Delegate delegate =

((org.omg.CORBA.portable.ObjectImpl)obj)._get_delegate ();

return new HelloApp._HelloStub (delegate);

}

}

}

程序清单4.11(HelloHolder.java)

package HelloApp;

/**

* HelloApp/HelloHolder.java

* Generated by the IDL-to-Java compiler (portable) version "3.0"

* from hello.idl

* 1999年3月27日 18时22分21秒 CST

*/

public final class HelloHolder implements org.omg.CORBA.portable.Streamable

{

public HelloApp.Hello value = null;

public HelloHolder ()

{

}

public HelloHolder (HelloApp.Hello initialValue)

{

value = initialValue;

}

________

第一部分 JSP 技术与J2EE 技术

public void _read (org.omg.CORBA.portable.InputStream i)

{

value = HelloApp.HelloHelper.read (i);

}

public void _write (org.omg.CORBA.portable.OutputStream o)

{

HelloApp.HelloHelper.write (o value);

}

public org.omg.CORBA.TypeCode _type ()

{

return HelloApp.HelloHelper.type ();

}

}

idlj.exe 程序自动产生的代码只是一个基本的框架我们若要完成该应用程序还要分

别在这5 个文件的基础上提供服务器端和客户机端的实现

至于如何开发一个完整的CORBA 应用我们将在下一小节中进行介绍

4.3.4 编写CORBA 应用

在这一小节中我们将描述如何开发一个完整的CORBA 应用系统利用Java IDL 开

发CORBA 应用系统一般可分为下面的5 个步骤

定义远程接口

用IDL 定义远程对象的接口使用IDL 而不是Java 语言是因为idlj.exe 编译器可以自

动地从IDL 文件中产生Java 语言的stub 和skeleton 源文件以及和ORB 对___________象连接时所需

要的一些代码使用Java IDL 开发人员可以用Java 语言来实现客户机和服务器如果要

为一个已经存在的CORBA 服务实现客户机或为一个已经存在的客户机实现服务则首

先要给出IDL 接口然后运行idlj.exe 编译器产生stub 和skeleton 在此基础上再编码实现

编译远程接口

对IDL 文件运行idlj.exe 编译器产生Java 版本的接口以及stub 和skeleton 代码文

件这些代码文件使得应用程序可以和ORB 相连接

实现服务器

把idlj.exe 编译器产生的skeleton 和服务器应用程序集成在一起除了要实现远程接口

中的方法之外服务器代码还要包括启动ORB 以及等待远程客户机的调用等部分

CORBA 服务器程序的结构和大部分Java 应用程序的结构一样即导入所需要的包

声明服务器类定义main()方法处理一些例外等另外CORBA 服务器要有一个ORB

对象每个服务器实例化一个ORB 对象并向其注册服务对象因此当ORB 对象接收到

________

第4 章 JSP 与J2EE 分布式处理技术

调用请求时可以寻找到CORBA 服务器最后是CORBA 服务对象的管理服务器是一个

进程实例化了一个或多个服务对象CORBA 服务对象具体实现接口中说明的操作

CORBA 服务对象和命名服务一起工作使得CORBA 服务对象对于客户机来说是可用的

CORBA 服务器需要对名字服务的对象引用可以向名字服务注册并保证向CORBA 接口

的调用被路由到其服务对象上最后是等待客户机的调用

实现客户机

类似地以stub 作为客户端应用程序的基础客户机建立在stub 之上通过Java IDL

提供的名字服务查询服务器获得远程对象的引用然后调用远程对象中的方法

CORBA 客户机的结构和大部分Java 应用程序的结构基本相似即导入所需要的包

声明应用类定义main()方法处理一些例外等另外和服务器程序一样一个CORBA

客户机也需要本地的ORB 对象来执行所有的配置工作每个客户机实例化一个

org.omg.CORBA.ORB 对象然后向该对象传递一些必要的信息以进行初始化最后是利用

ORB 对象取得所需要的服务一旦一个应用程序有了ORB 对象即可通过ORB 对象来确

定应用所需要的服务的位置为了调用CORBA对象中的方法客户端应用要有对该对象(指

的是CORBA 对象)的引用有很多种方法可以得到这种引用如命名服务等方法

启动应用程序

一旦实现了服务器和客户机就可以启动名字服务接着启动服务器然后运行客户

下面我们简要介绍一下开发CORBA 应用的步骤

1 使用IDL 创建CORBA 接口程序清单4.12 使用OMG IDL 描述一个CORBA

对象

程序清单4.12(Show.idl)

module About

{

interface Show

{

string ShowName();

};

};

将其存为Show.idl 文件

2 编___________译接口并生成CORBA 支持文件在MS-DOS 状态下我们用以下命令编译

这个IDL 文件

idlj.exe Show.idl

idlj.exe 是Sun 公司的IDL 编译器包含在JDK 中你可以在bin 文件夹中找到它

编译后将在当前目录下生成About 子目录其中会包括一些支持文件这些文件和程

序清单4.7 程序清单4.11 十分相似读者如有兴趣可以看一下但一定不要修改

3 实现服务器(ShowServer.java) 请看程序清单4.13(showServer.java)

________

第一部分 JSP 技术与J2EE 技术

程序清单4.13(showServer.java)

import About.*;

import org.omg.CosNaming.*;

import org.omg.CosNaming.NamingContextPackage.*;

import org.omg.CORBA.*;

public class ShowObject extends ShowOperations

{

public String ShowName()

{

return "\nMy name isfancy!!\n";

}

}

public class ShowServer

{

public static void main(String args[])

{

try

{

// 创建和初始化 ORB

ORB orb = ORB.init(args null);

// 创建服务对象并将其向 ORB 注册

ShowObject ShowRef = new ShowObject();

orb.connect(ShowRef);

// 获取根命名上下文

org.omg.CORBA.Object objRef =

orb.resolve_initial_references("NameService");

NamingContext ncRef = NamingContextHelper.narrow(objRef);

// 绑定命名中的对象引用

NameComponent nc = new NameComponent("About" "");

NameComponent path[] = {nc};

ncRef.rebind(path ShowRef);

// 等待来自客户机的调用

java.lang.Object sync = new java.lang.Object();

synchronized (sync)

{

sync.wait();

}

}

________

第4 章 JSP 与J2EE 分布式处理技术

catch (Exception e)

{

System.err.println("ERROR: " + e);

e.printStackTrace(System.out);

}

}

}

ShowServer 类的main()方法可完成以下任务

1 创建一个ORB 实例(orb) 如下面的代码

ORB orb = ORB.init(args null);

2 创建一个服务对象实例CORBA About 对象的实现并通知ORB 对象如下面

的代码

ShowObject ShowRef = new ShowObject();

orb.connect(ShowRef);

3 获取一个命名上下文的CORBA 对象引用在该命名上下文中注册新的CORBA 对

4 在命名上下文中将新对象注册在“About”名下如下面的代码

NameComponent nc = new NameComponent("About" "");

NameComponent path[] = {nc};

ncRef.rebind(path ShowRef);

5 等待客户端程序对新对象的调用

4 实现客户机 (ShowClient.java)

请看程序清单4.14(ShowClient.java)

程序清单4.14(ShowClient.java)

import About.*;

import org.omg.CosNaming.*;

import org.omg.CORBA.*;

public class ShowClient

{

public static void main(String args[])

{

try

{

// 创建和初始化 ORB

ORB orb = ORB.init(args null);

// 获取根命名上下文

org.omg.CORBA.Object objRef =

orb.resolve_initial_references("NameService");

NamingContext ncRef = NamingContextHelper.narrow(objRef);

//解析命名中的对象引用

NameComponent nc = new NameComponent("About" "");

________

第一部分 JSP 技术与J2EE 技术

NameComponent path[] = {nc};

About.Show ShowRef = ShowHelper.narrow(ncRef.resolve(path));

// 调用 Show 服务对象并打印结果

String show = ShowRef.ShowName();

System.out.println(show);

}

catch (Exception e)

{

System.out.println("ERROR : " + e) ;

e.printStackTrace(System.out);

}

}

}

在ShowClient 类中完成了以下任务

1 创建一个ORB 如下面的代码

ORB orb = ORB.init(args null);

2 获取一个指向命名上下文的引用如下面的代码

org.omg.CORBA.Object objRef =

orb.resolve_initial_references("NameService");

NamingContext ncRef = NamingContextHelper.narrow(objRef);

3 在命名上下文中查找“Show”并获得指向该 CORBA 对象的引用如下面的代码

NameComponent nc

EJB 技术进阶

第3 章 EJB 技术进阶

try

{

if (statement != null)

{

statement.close();

}

}

catch(SQLException e)

{

}

try

{

if (connection != null)

{

connection.close();

}

}

catch(SQLException e)

{

}

}

}

public int ejbCreate(int id) throws CreateException

{

return ejbCreate(null null null null null id);

}

public void ejbRemove() throws RemoveException

{

super.ejbRemove();

Connection connection = null;

PreparedStatement statement = null;

try

{

connection = dataSource.getConnection();

statement = connection.prepareStatement("DELETE FROM goods WHERE

id = ?");

statement.setInt(1 id);

if (statement.executeUpdate() < 1)

{

throw new RemoveException("Error deleting row");

}

statement.close();

statement = null;

________

第一部分 JSP 技术与J2EE 技术

connection.close();

connection = null;

}

catch(SQLException e)

{

throw new EJBException("Error executing SQL DELETE FROM goods

WHERE id = ?: " + e.toString());

}

finally

{

try

{

if (statement != null)

{

statement.close();

}

}

catch(SQLException e)

{

}

try

{

if (connection != null)

{

connection.close();

}

}

catch(SQLException e)

{

}

}

}

public void ejbLoad()

{

id = ((Integer) entityContext.getPrimaryKey()).intValue();

Connection connection = null;

PreparedStatement statement = null;

try

{

connection = dataSource.getConnection();

statement = connection.prepareStatement("SELECT goodsname goodstype

comment price priceoff FROM goods WHERE id = ?");

statement.setInt(1 id);

________

第3 章 EJB 技术进阶

ResultSet resultSet = statement.executeQuery();

if (!resultSet.next())

{

throw new NoSuchEntityException("Row does not exist");

}

goodsname = resultSet.getString(1);

goodstype = resultSet.getString(2);

comment = resultSet.getString(3);

price = new Double(resultSet.getDouble(4));

priceoff = new Double(resultSet.getDouble(5));

statement.close();

statement = null;

connection.close();

connection = null;

}

catch(SQLException e)

{

throw new EJBException("Error executing SQL SELECT goodsname

goodstype comment price priceoff FROM goods WHERE id = ?: "

+e.toString());

}

finally

{

try

{

if (statement != null)

{

statement.close();

}

}

catch(SQLException e)

{

}

try

{

if (connection != null)

{

connection.close();

}

}

catch(SQLException e)

{

}

________

第一部分 JSP 技术与J2EE 技术

}

super.ejbLoad();

}

public void ejbStore()

{

super.ejbStore();

Connection connection = null;

PreparedStatement statement = null;

try

{

connection = dataSource.getConnection();

statement = connection.prepareStatement("UPDATE goods SET goodsname

= ? goodstype = ? comment = ? price = ? priceoff = ?

WHERE id = ?");

statement.setString(1 goodsname);

statement.setString(2 goodstype);

statement.setString(3 comment);

statement.setDouble(4 price.doubleValue());

statement.setDouble(5 priceoff.doubleValue());

statement.setInt(6 id);

if (statement.executeUpdate() < 1)

{

throw new NoSuchEntityException("Row does not exist");

}

statement.close();

statement = null;

connection.close();

connection = null;

}

catch(SQLException e)

{

throw new EJBException("Error executing SQL UPDATE goods SET

goodsname = ? goodstype = ? comment = ? price = ? priceoff = ?

WHERE id = ?: " + e.toString());

}

finally

{

try

{

if (statement != null)

{

statement.close();

}

________

第3 章 EJB 技术进阶

}

catch(SQLException e)

{

}

try

{

if (connection != null)

{

connection.close();

}

}

catch(SQLException e)

{

}

}

}

public int ejbFindByPrimaryKey(int key) throws ObjectNotFoundException

{

Connection connection = null;

PreparedStatement statement = null;

try

{

connection = dataSource.getConnection();

statement = connection.prepareStatement("SELECT id FROM goods WHERE

id = ?");

statement.setInt(1 key);

ResultSet resultSet = statement.executeQuery();

if (!resultSet.next())

{

throw new ObjectNotFoundException("Primary key does not exist");

}

statement.close();

statement = null;

connection.close();

connection = null;

return key;

}

catch(SQLException e)

{

throw new EJBException("Error executing SQL SELECT id FROM goods

WHERE id = ?: " + e.toString());

}

finally

________

第一部分 JSP 技术与J2EE 技术

{

try

{

if (statement != null)

{

statement.close();

}

}

catch(SQLException e)

{

}

try

{

if (connection != null)

{

connection.close();

}

}

catch(SQLException e)

{

}

}

}

public Collection ejbFindAll()

{

Connection connection = null;

PreparedStatement statement = null;

try

{

connection = dataSource.getConnection();

statement = connection.prepareStatement("SELECT id FROM goods");

ResultSet resultSet = statement.executeQuery();

Vector keys = new Vector();

while (resultSet.next())

{

int id = resultSet.getInt(1);

keys.addElement(new Integer(id));

}

statement.close();

statement = null;

connection.close();

connection = null;

return keys;

________

第3 章 EJB 技术进阶

}

catch(SQLException e)

{

throw new EJBException("Error executing SQL SELECT id FROM goods: "

+ e.toString());

}

finally

{

try

{

if (statement != null)

{

statement.close();

}

}

catch(SQLException e)

{

}

try

{

if (connection != null)

{

connection.close();

}

}

catch(SQLException e)

{

}

}

}

public void setEntityContext(EntityContext entityContext)

{

super.setEntityContext(entityContext);

try

{

javax.naming.Context context = new javax.naming.InitialContext();

dataSource = (DataSource)

context.lookup("java:comp/env/jdbc/DataSource");

}

catch(Exception e)

{

throw new EJBException("Error looking up dataSource:" + e.toString());

________

第一部分 JSP 技术与J2EE 技术

}

}

}

看完程序清单3.10 了吗?程序清单3.10 虽然很长但是很简单主要是错误处理的部

分占的篇幅比较多实际上真正的实现代码并不是很多程序清单3.10 主要是实现在程序

清单3.9 中定义的那些ejbLoad() ejbStore() ejbRemove() ejbActivate() ejbPassivate()

ejbCreate()方法其实这些方法的实现代码十分类似都是建立数据库连接创建SQL

句柄对象然后根据方法的输入参数设定SQL 语句的输入参数执行SQL 语句把结

果返回

我们就以ejbCreate()方法为例说明如何做到BMP 其他方法的实现都和ejbCreate()

方法的实现差不多我们就不再介绍了读者可以模拟得知ejbCreate()方法的实现代码如

下例所示

public int ejbCreate(String goodsname String goodstype String comment Double price Double priceoff

int id) throws CreateException

{

super.ejbCreate(goodsname goodstype comment price priceoff id);

try

{

//First see if the object already exists

ejbFindByPrimaryKey(id);

//If so then we have to throw an exception

throw new DuplicateKeyException("Primary key already exists");

}

catch(ObjectNotFoundException e)

{

//Otherwise we can go ahead and create it...

}

Connection connection = null;

PreparedStatement statement = null;

try

{

connection = dataSource.getConnection();

statement = connection.prepareStatement("INSERT INTO goods (goodsname

goodstype comment price priceoff id) VALUES (? ? ? ? ? ?)");

statement.setString(1 goodsname);

statement.setString(2 goodstype);

statement.setString(3 comment);

statement.setDouble(4 price.doubleValue());

statement.setDouble(5 priceoff.doubleValue());

statement.setInt(6 id);

if (statement.executeUpdate() != 1)

________

第3 章 EJB 技术进阶

{

throw new CreateException("Error adding row");

}

statement.close();

statement = null;

connection.close();

connection = null;

return id;

}

catch(SQLException e)

{

throw new EJBException("Error executing SQL INSERT INTO goods

(goodsname goodstype comment price priceoff id) VALUES

(? ? ? ? ? ?): " + e.toString());

}

finally

{

try

{

if (statement != null)

{

statement.close();

}

}

catch(SQLException e)

{

}

try

{

if (connection != null)

{

connection.close();

}

}

catch(SQLException e)

{

}

}

}

这个ejbCreate()方法的流程如下

首先调用父类的ejbCreate()方法如

super.ejbCreate(goodsname goodstype comment price priceoff id);

判断有没有同样主键存在如果有那么会抛出DuplicateKeyException 异常

________

第一部分 JSP 技术与J2EE 技术

获取数据库连接对象如

connection = dataSource.getConnection();

建立SQL 句柄对象如

statement = connection.prepareStatement("INSERT INTO goods (goodsname

goodstype comment price priceoff id) VALUES (? ? ? ? ? ?)");

利用PreparedStatement 对象的setXXX()方法和输入参数组装SQL 语句如

statement.setString(1 goodsname);

statement.setString(2 goodstype);

调用PreparedStatement 对象的executeUpdate()方法执行SQL 语句如

if (statement.executeUpdate() != 1)

{

throw new CreateException("Error adding row");

}

关闭statement connection 等对象如

statement.close();

statement = null;

connection.close();

connection = null;

返回新的EJB 对象的主键(标识符)

return id;

这就是ejbCreate()方法的执行流程剩余的代码都是错误处理代码读者不难发现

ejbCreate()方法就是往数据库里插入新的纪录Entity EJB 就代表一个数据库行纪录Goods

EJB 对象其实就代表了goods 表的一个行纪录

3.2.5 部署描述符

程序清单3.11 和3.12 就是BMP 模式的Goods EJB 的部署描述符BMP 模式的Entity

EJB 的部署描述符与CMP 模式的Entity EJB 的部署描述符几乎一模一样不过还是有一点

点小区别读者不妨比较程序清单3.11 和程序清单3.4 看看这两个部署描述符都有哪些

差别

程序清单3.11(ejb-jar.xml)

<?xml version="1.0" encoding="GBK"?>

<!DOCTYPE ejb-jar PUBLIC '-//Sun Microsystems Inc.//DTD Enterprise JavaBeans

1.1//EN' 'http://java.sun.com/j2ee/dtds/ejb-jar_1_1.dtd'>

<ejb-jar>

<enterprise-beans>

<entity>

<ejb-name>Goods</ejb-name>

________

第3 章 EJB 技术进阶

<home>jdbcbean.GoodsHome</home>

<remote>jdbcbean.GoodsRemote</remote>

<ejb-class>jdbcbean.GoodsBeanBMP</ejb-class>

<persistence-type>Bean</persistence-type>

<prim-key-class>int</prim-key-class>

<reentrant>False</reentrant>

<resource-ref>

<res-ref-name>jdbc/DataSource</res-ref-name>

<res-type>javax.sql.DataSource</res-type>

<res-auth>Container</res-auth>

</resource-ref>

</entity>

</enterprise-beans>

<assembly-descriptor>

<container-transaction>

<method>

<ejb-name>Goods</ejb-name>

<method-name>*</method-name>

</method>

<trans-attribute>Required</trans-attribute>

</container-transaction>

</assembly-descriptor>

</ejb-jar>

怎么样找到了这两个程序之间的不同了吗?让我们来告诉你吧程序清单3.4 定义了

很多Entity EJB 所映射的goods Table 的字段它们都是Goods EJB 对象的一个属性而在

程序清单3.11 中就没有这些定义更重要的是在程序清单3.4 中有这样的一行代码

<persistence-type>Container</persistence-type>

这说明这个Entity EJB 的管理模式为CMP 模式

而在程序清单3.11 中对应的代码为

<persistence-type>Bean</persistence-type>

这表明对应Entity EJB 的管理模式一定是BMP 模___________式

下面是程序清单3.12(ejb-inpries.xml) 它和程序清单3.5 类似读者不妨也比较一下

这两者的差别我们就不再多说了

程序清单3.12(ejb-inprise.xml)

<?xml version="1.0" encoding="GBK"?>

<!DOCTYPE inprise-specific PUBLIC '-//Inprise Corporation//DTD Enterprise JavaBeans

1.1//EN' 'http://www.borland.com/devsupport/appserver/dtds/ejb-inprise.dtd'>

<inprise-specific>

<enterprise-beans>

<entity>

<ejb-name>Goods</ejb-name>

________

第一部分 JSP 技术与J2EE 技术

<bean-home-name>Goods</bean-home-name>

<resource-ref>

<res-ref-name>jdbc/DataSource</res-ref-name>

<jndi-name>DataSource</jndi-name>

</resource-ref>

</entity>

</enterprise-beans>

<datasource-definitions>

<datasource>

<jndi-name>DataSource</jndi-name>

<url>jdbc:odbc:test</url>

<username>sa</username>

<password></password>

<driver-class-name>sun.jdbc.odbc.JdbcOdbcDriver</driver-class-name>

</datasource>

</datasource-definitions>

</inprise-specific>

3.2.6 创建EJB Container

这一节中我们需要创建EJB Container 步骤和3.1.6 节所介绍的步骤一模一样但

是结果却很不一样3.1.6 节中创建的EJB Container 必须实现ejbLoad() ejbRemove()等方

法必须管理EJB 和JDBC 数剧源之间的交互操作但是在这一节中创建的EJB Container

却不需要实现这些功能因为我们已经在程序清单3.10 中实现了BMP 模式的EJB Class

这些任务都在其中完成了

3.2.7 创建EJB 客户端

本小节我们将编写BMP 模式的Goods EJB 的客户端程序与CMP 模式的Goods EJB

的客户端程序一样我们只介绍如何编写基于Java Application 的EJB 客户端程序至于基

于JSP 的EJB 客户端程序读者可以自己尝试

下面请看程序清单3.13(GoodsTestClient.java) 其中黑体的部分是我们自己加上去的代

程序清单3.13

//File Name:GoodsTestClient.java

//Author:fancy

//Date:2001.5.10

//Note:to test the ejb

package jdbcbean;

import javax.naming.*;

import javax.rmi.PortableRemoteObject;

________

第3 章 EJB 技术进阶

import java.util.Collection;

import java.util.Iterator;

public class GoodsTestClient

{

private static final int MAX_OUTPUT_LINE_LENGTH = 50;

private GoodsHome goodsHome = null;

/**Construct the EJB test client*/

public GoodsTestClient()

{

try

{

//get naming context

Context ctx = new InitialContext();

//look up jndi name

Object ref = ctx.lookup("Goods");

//cast to Home interface

goodsHome = (GoodsHome)

PortableRemoteObject.narrow(ref GoodsHome.class);

GoodsRemote gr=goodsHome.findByPrimaryKey(22);

System.out.println("name:"+gr.getGoodsname());

System.out.println("type:"+gr.getGoodstype());

System.out.println("id:"+gr.getId());

System.out.println("price:"+gr.getPrice());

System.out.println("off:"+gr.getPriceoff());

System.out.println("comment:"+gr.getComment());

System.out.println("is session ejb:"

+goodsHome.getEJBMetaData().isSession());

}

catch(Exception e)

{

e.printStackTrace();

}

}

//----------------------------------------------------------------------------

// Utility Methods

//----------------------------------------------------------------------------

public GoodsHome getHome()

{

return goodsHome;

________

第一部分 JSP 技术与J2EE 技术

}

public void executeFindAll()

{

try

{

Collection collection = goodsHome.findAll();

Iterator iterator = collection.iterator();

while (iterator.hasNext())

{

Object object = iterator.next();

GoodsRemote goodsRemote = (GoodsRemote)

PortableRemoteObject.narrow(object GoodsRemote.class);

log(goodsRemote.toString());

}

}

catch(Exception e)

{

e.printStackTrace();

}

}

private void log(String message)

{

if (message.length() > MAX_OUTPUT_LINE_LENGTH)

{

System.out.println("-- "

+ message.substring(0 MAX_OUTPUT_LINE_LENGTH) + " ...");

}

else

{

System.out.println("-- " + message);

}

}

/**Main method*/

public static void main(String[] args)

{

GoodsTestClient client = new GoodsTestClient();

//client. executeFindAll();

// Use the getHome() method of the client object to call Home interface

// methods that will return a Remote interface reference. Then

// use that Remote interface reference to access the EJB.

// For quick access to all available records you can use the

________

第3 章 EJB 技术进阶

// executeFindAll() method in this class.

}

}

不知道读者发现没有程序清单3.13 和程序清单3.6 其实是一模一样的这说明了一

个问题那就是CMP EJB 的客户端与BMP EJB 的客户端程序并没有实质上的区别尽管

服务端的实现方法可能不一样但是所对应的客户端程序却是一样的这也是分布式处理

的好处之一服务端可以改变服务实现的方法但是只要提供服务的接口没有变化客户

端的程序就不需要变化

3.2.8 运行和测试

下面我们按照3.1.8 小节的方法运行Goods EJB(BMP 模式下)的服务端与客户端程序

客户端程序(GoodsTestClient.java)的输出结果如下所示

name:小楼一夜听春雨

type:兵器

id:22

price:3000.8

off:0.8

comment:小楼一夜听春雨深巷明朝卖杏花好优美的诗意啊可惜它是一把弯刀

又名圆月弯刀曾经是青青所用之物后归丁鹏所有和万水青山踏遍齐名

is session ejb:false

至于服务端的输出结果读者可以参照3.1.8 小节的结果

3.3 EJB 开发实例封装数据源

在这一节中我们将在3.2 节的基础上对Goods EJB 进行改写实现动态查询的功

能实际上这就相当于完成了封装数据源的功能因为在3.2 中我们已经可以利用Goods

EJB 完成插入新纪录更新行纪录删除行纪录返回特定行纪录的功能现在再加上动

态查询的功能那么一个数据源的功能也就全了我们为什么不在3.1 小节的基础上进行

再开发呢这是因为3.1 小节开发的是CMP 模式的Goods EJB 要扩充起来很困难3.2

小节开发的是BMP 模式的Goods EJB 扩展起来容易得多了

那么如何扩展BMP 模式的Goods EJB 呢?

首先需要修改GoodsBeanBMP.java 程序定义一个执行动态查询的方法这个方法的

名字是ejbFindAll(String name) 它的主要作用是查询goods Table 的comment 字段返回

所有符合条件的Goods EJB 对象的集合ejbFindAll(String name)方法其实是从ejbFindAll()

方法改写过来的请看程序清单3.14(GoodsBeanBMP.java) 其中黑体部分就是我们在程序

清单3.10 的基础上加进去的代码

程序清单3.14

//File Name:GoodsBeanBMP.java

//Author:Fancy

//Date:2001.5

________

第一部分 JSP 技术与J2EE 技术

//Note:the EJB Class

package jdbcbean;

import java.sql.*;

import javax.ejb.*;

import javax.sql.DataSource;

import java.util.*;

public class GoodsBeanBMP extends GoodsBean

{

DataSource dataSource;

public int ejbCreate(String goodsname String goodstype String comment Double price Double

priceoff int id) throws CreateException

{

super.ejbCreate(goodsname goodstype comment price priceoff id);

try

{

//First see if the object already exists

ejbFindByPrimaryKey(id);

//If so then we have to throw an exception

throw new DuplicateKeyException("Primary key already exists");

}

catch(ObjectNotFoundException e)

{

//Otherwise we can go ahead and create it...

}

Connection connection = null;

PreparedStatement statement = null;

try

{

connection = dataSource.getConnection();

statement = connection.prepareStatement("INSERT INTO goods (goodsname

goodstype comment price priceoff id) VALUES (? ? ? ? ? ?)");

statement.setString(1 goodsname);

statement.setString(2 goodstype);

statement.setString(3 comment);

statement.setDouble(4 price.doubleValue());

statement.setDouble(5 priceoff.doubleValue());

statement.setInt(6 id);

if (statement.executeUpdate() != 1)

{

throw new CreateException("Error adding row");

________

第3 章 EJB 技术进阶

}

statement.close();

statement = null;

connection.close();

connection = null;

return id;

}

catch(SQLException e)

{

throw new EJBException("Error executing SQL INSERT INTO goods

(goodsname goodstype comment price priceoff id) VALUES

(? ? ? ? ? ?): " + e.toString());

}

finally

{

try

{

if (statement != null)

{

statement.close();

}

}

catch(SQLException e)

{

}

try

{

if (connection != null)

{

connection.close();

}

}

catch(SQLException e)

{

}

}

}

public int ejbCreate(int id) throws CreateException

{

return ejbCreate(null null null null null id);

}

public void ejbRemove() throws RemoveException

{

________

第一部分 JSP 技术与J2EE 技术

super.ejbRemove();

Connection connection = null;

PreparedStatement statement = null;

try

{

connection = dataSource.getConnection();

statement = connection.prepareStatement("DELETE FROM goods WHERE

id = ?");

statement.setInt(1 id);

if (statement.executeUpdate() < 1)

{

throw new RemoveException("Error deleting row");

}

statement.close();

statement = null;

connection.close();

connection = null;

}

catch(SQLException e)

{

throw new EJBException("Error executing SQL DELETE FROM goods

WHERE id = ?: " + e.toString());

}

finally

{

try

{

if (statement != null)

{

statement.close();

}

}

catch(SQLException e)

{

}

try

{

if (connection != null)

{

connection.close();

}

}

catch(SQLException e)

________

第3 章 EJB 技术进阶

{

}

}

}

public void ejbLoad()

{

id = ((Integer) entityContext.getPrimaryKey()).intValue();

Connection connection = null;

PreparedStatement statement = null;

try

{

connection = dataSource.getConnection();

statement = connection.prepareStatement("SELECT goodsname goodstype

comment price priceoff FROM goods WHERE id = ?");

statement.setInt(1 id);

ResultSet resultSet = statement.executeQuery();

if (!resultSet.next())

{

throw new NoSuchEntityException("Row does not exist");

}

goodsname = resultSet.getString(1);

goodstype = resultSet.getString(2);

comment = resultSet.getString(3);

price = new Double(resultSet.getDouble(4));

priceoff = new Double(resultSet.getDouble(5));

statement.close();

statement = null;

connection.close();

connection = null;

}

catch(SQLException e)

{

throw new EJBException("Error executing SQL SELECT goodsname

goodstype comment price priceoff FROM goods WHERE id = ?: "

+e.toString());

}

finally

{

try

{

if (statement != null)

{

________

第一部分 JSP 技术与J2EE 技术

statement.close();

}

}

catch(SQLException e)

{

}

try

{

if (connection != null)

{

connection.close();

}

}

catch(SQLException e)

{

}

}

super.ejbLoad();

}

public void ejbStore()

{

super.ejbStore();

Connection connection = null;

PreparedStatement statement = null;

try

{

connection = dataSource.getConnection();

statement = connection.prepareStatement("UPDATE goods SET goodsname

= ? goodstype = ? comment = ? price = ? priceoff = ?

WHERE id = ?");

statement.setString(1 goodsname);

statement.setString(2 goodstype);

statement.setString(3 comment);

statement.setDouble(4 price.doubleValue());

statement.setDouble(5 priceoff.doubleValue());

statement.setInt(6 id);

if (statement.executeUpdate() < 1)

{

throw new NoSuchEntityException("Row does not exist");

}

statement.close();

statement = null;

________

第3 章 EJB 技术进阶

connection.close();

connection = null;

}

catch(SQLException e)

{

throw new EJBException("Error executing SQL UPDATE goods SET

goodsname = ? goodstype = ? comment = ? price = ? priceoff = ?

WHERE id = ?: " + e.toString());

}

finally

{

try

{

if (statement != null)

{

statement.close();

}

}

catch(SQLException e)

{

}

try

{

if (connection != null)

{

connection.close();

}

}

catch(SQLException e)

{

}

}

}

public int ejbFindByPrimaryKey(int key) throws ObjectNotFoundException

{

Connection connection = null;

PreparedStatement statement = null;

try

{

connection = dataSource.getConnection();

statement = connection.prepareStatement("SELECT id FROM goods WHERE

id = ?");

________

第一部分 JSP 技术与J2EE 技术

statement.setInt(1 key);

ResultSet resultSet = statement.executeQuery();

if (!resultSet.next())

{

throw new ObjectNotFoundException("Primary key does not exist");

}

statement.close();

statement = null;

connection.close();

connection = null;

return key;

}

catch(SQLException e)

{

throw new EJBException("Error executing SQL SELECT id FROM goods

WHERE id = ?: " + e.toString());

}

finally

{

try

{

if (statement != null)

{

statement.close();

}

}

catch(SQLException e)

{

}

try

{

if (connection != null)

{

connection.close();

}

}

catch(SQLException e)

{

}

}

}

public Collection ejbFindAll()

{

________

第3 章 EJB 技术进阶

Connection connection = null;

PreparedStatement statement = null;

try

{

connection = dataSource.getConnection();

statement = connection.prepareStatement("SELECT id FROM goods");

ResultSet resultSet = statement.executeQuery();

Vector keys = new Vector();

while (resultSet.next())

{

int id = resultSet.getInt(1);

keys.addElement(new Integer(id));

}

statement.close();

statement = null;

connection.close();

connection = null;

return keys;

}

catch(SQLException e)

{

throw new EJBException("Error executing SQL SELECT id FROM goods: "

+ e.toString());

}

finally

{

try

{

if (statement != null)

{

statement.close();

}

}

catch(SQLException e)

{

}

try

{

if (connection != null)

{

connection.close();

}

}

________

第一部分 JSP 技术与J2EE 技术

catch(SQLException e)

{

}

}

}

public void setEntityContext(EntityContext entityContext)

{

super.setEntityContext(entityContext);

try

{

javax.naming.Context context = new javax.naming.InitialContext();

dataSource = (DataSource)

context.lookup("java:comp/env/jdbc/DataSource");

}

catch(Exception e)

{

throw new EJBException("Error looking up dataSource:" + e.toString());

}

}

public Collection ejbFindAll(String name)

{

Connection connection = null;

PreparedStatement statement = null;

try

{

connection = dataSource.getConnection();

statement = connection.prepareStatement("SELECT id FROM goods

where comment like ?");

statement.setBytes(1 name.getBytes("GBK"));

ResultSet resultSet = statement.executeQuery();

Vector keys = new Vector();

while (resultSet.next())

{

int id = resultSet.getInt(1);

keys.addElement(new Integer(id));

}

statement.close();

statement = null;

connection.close();

connection = null;

return keys;

________

第3 章 EJB 技术进阶

}

catch(Exception e)

{

throw new EJBException("Error executing SQL SELECT id FROM

goods: " + e.toString());

}

finally

{

try

{

if (statement != null)

{

statement.close();

}

}

catch(SQLException e)

{

}

try

{

if (connection != null)

{

connection.close();

}

}

catch(SQLException e)

{

}

}

}

}

ejbFindAll(String name)方法的实现逻辑很简单关键之处在于构造SQL 语句以及根

据方法参数(name)设定SQL 语句的输入参数还需要注意数据库的中文问题此外就没有

什么特别的了

下面我们需要修改Goods EJB 的Home Interface 在Home Interface 中声明

ejbFindAll(String name)方法修改过的Home Interface 的源代码如程序清单3.15 所示其

中黑体部分是我们添加上的代码

程序清单3.15

package jdbcbean;

import java.rmi.*;

import javax.ejb.*;

________

第一部分 JSP 技术与J2EE 技术

import java.util.*;

public interface GoodsHome extends EJBHome

{

public GoodsRemote create(String goodsname String goodstype String comment

Double price Double priceoff int id) throws RemoteException CreateException;

public GoodsRemote create(int id) throws RemoteException CreateException;

public GoodsRemote findByPrimaryKey(int primaryKey) throws RemoteException

FinderException;

public Collection findAll() throws RemoteException FinderException;

public Collection findAll(String name) throws RemoteException FinderException;

}

我们把整个Project 再编译一遍就可以运行Goods EJB 的服务端了下面我们应该编

写新的Goods EJB 的客户端了这是基于Java Application 的客户端基于JSP 的EJB 客户

端我们就不介绍了读者可以把前者改写一下就行了请看程序清单3.16 其中黑体部分

是我们在JBuilder4 自动产生的代码的基础上添加的代码

程序清单3.16

package jdbcbean;

import javax.naming.*;

import javax.rmi.PortableRemoteObject;

import java.util.Collection;

import java.util.Iterator;

public class GoodsTestClient

{

private static final int MAX_OUTPUT_LINE_LENGTH = 50;

private GoodsHome goodsHome = null;

/**Construct the EJB test client*/

public GoodsTestClient()

{

try

{

//get naming context

Context ctx = new InitialContext();

//look up jndi name

Object ref = ctx.lookup("Goods");

//cast to Home interface

goodsHome = (GoodsHome)

PortableRemoteObject.narrow(ref GoodsHome.class);

________

第3 章 EJB 技术进阶

GoodsRemote gr=goodsHome.findByPrimaryKey(22);

System.out.println("name:"+gr.getGoodsname());

System.out.println("type:"+gr.getGoodstype());

System.out.println("id:"+gr.getId());

System.out.println("price:"+gr.getPrice());

System.out.println("off:"+gr.getPriceoff());

System.out.println("comment:"+gr.getComment());

System.out.println("is session ejb:"

+goodsHome.getEJBMetaData().isSession());

}

catch(Exception e)

{

e.printStackTrace();

}

}

//----------------------------------------------------------------------------

// Utility Methods

//----------------------------------------------------------------------------

public GoodsHome getHome()

{

return goodsHome;

}

public void find()

{

try

{

Collection collection = goodsHome.findAll("%小楼%");

Iterator iterator = collection.iterator();

System.out.println(collection.size());

while (iterator.hasNext())

{

Object object = iterator.next();

GoodsRemote goodsRemote = (GoodsRemote)

PortableRemoteObject.narrow(object GoodsRemote.class);

System.out.println("name:"+goodsRemote.getGoodsname());

System.out.println("type:"+goodsRemote.getGoodstype());

System.out.println("id:"+goodsRemote.getId());

System.out.println("price:"+goodsRemote.getPrice());

System.out.println("off:"+goodsRemote.getPriceoff());

System.out.println("comment:"+goodsRemote.getComment());

}

}

________

第一部分 JSP 技术与J2EE 技术

catch (Exception fe)

{

fe.printStackTrace();

}

}

public void executeFindAll()

{

try

{

Collection collection = goodsHome.findAll();

Iterator iterator = collection.iterator();

while (iterator.hasNext())

{

Object object = iterator.next();

GoodsRemote goodsRemote = (GoodsRemote)

PortableRemoteObject.narrow(object GoodsRemote.class);

log(goodsRemote.toString());

}

}

catch(Exception e)

{

e.printStackTrace();

}

}

private void log(String message)

{

if (message.length() > MAX_OUTPUT_LINE_LENGTH)

{

System.out.println("-- "

+ message.substring(0 MAX_OUTPUT_LINE_LENGTH) + " ...");

}

else

{

System.out.println("-- " + message);

}

}

/**Main method*/

public static void main(String[] args)

{

GoodsTestClient client = new GoodsTestClient();

client.find();

// Use the getHome() method of the client object to call Home interface

________

第3 章 EJB 技术进阶

// methods that will return a Remote interface reference. Then

// use that Remote interface reference to access the EJB.

// For quick access to all available records you can use the

// executeFindAll() method in this class.

}

}

在程序清单3.16 中我们仿照executeFindAll()方法定义了一个find()方法在find()

方法体中利用了Home Interface 调用findAll()方法然后再对返回的结果集进行处理

程序清单3.16 十分简单我们就不多做解释了程序清单3.16 的运行输出如下所示

name:小楼一夜听春雨

type:兵器

id:22

price:3000.8

off:0.8

comment:小楼一夜听春雨深巷明朝卖杏花好优美的诗意啊可惜它是一把弯刀

又名圆月弯刀曾经是青青所用之物后归丁鹏所有和万水青山踏遍齐名

is session ejb:false

1

name:小楼一夜听春雨

type:兵器

id:22

price:3000.8

off:0.8

comment:小楼一夜听春雨深巷明朝卖杏花好优美的诗意啊可惜它是一把弯刀

又名圆月弯刀曾经是青青所用之物后归丁鹏所有和万水青山踏遍齐名

好了关于Goods EJB 的开发我们就介绍到这里至于如何分发Goods EJB 到应用程

服务器上如何编写Entity EJB 的JSP 客户端请读者参考第2 章的相关内容限于篇

幅我们就不多说了说到这里想必读者对于EJB 的强大功能及其开发方法已经有了一

定程度的了解但是EJB 的技术体系博大精深不是我们这两章书就能够概括的了的读

者如果希望对EJB 技术有进一步的了解关键之处在于多加练习还需要多参考相关的英

文文档

3.4 本 章 小 结

本章主要介绍了两种Entity EJB 的开发方法并比较了它们的异同这两种Entity EJB

分别是CMP 模式的Entity EJB与BMP 模式的Entity EJB 本章的第三节改写了Goods EJB

使得它封装了JDBC 数据源的功能

在第4 章中我们将介绍RMI CORBA JNDI 等技术在JSP 程序中的应用

________

第4 章 JSP 与J2EE 分布式处理技术

在Java 平台上有三大分布式处理技术其一就是我们前两章所介绍的EJB 组件技术

除此之外就是CORBA 技术(for java)和RMI 技术在本章中我们将简要介绍CORBA

和RMI 应用的基础知识及开发方法并试图把它们和JSP 技术结合起来编写功能强大的

应用程序

本章需要重点掌握的内容有

J2EE 平台的结构

RMI 模型的开发方法和应用

CORBA 的结构模型开发方法和应用

JNDI 技术简介

4.1 J2EE 和分布式处理技术

4.1.1 J2EE 体系结构

近来的一段时间J2EE 这个词似乎十分热门相信有很多读者都对J2EE 技术不甚了

了那么什么是J2EE J2EE 是Java 2 Enterprise Edition 的缩写意为Java 2 平台企业版

J2EE 是一种利用Java 2 平台来简化诸多与多级企业解决方案的开发部署和管理相关的复

杂问题的体系结构J2EE 技术的基础就是核心Java 平台或Java 2 平台的标准版(简称为

J2SE) J2EE 不仅具有了J2SE 中的许多优点例如“编写一次到处运行”的特性访问数

据库存取数据的JDBC API CORBA 技术等等同时还提供了对 EJB Enterprise

JavaBeans JMS JTS Java servlet API JSP 以及XML 技术的全面支持J2EE 平台的

servlet RMI JDBC 和Java IDL 等强大的分布式计算技术使Java 具备了在企业计算中一

展身手的能力一般认为现代的企业计算解决方案除了企业的业务逻辑business logic

外还需要提供对8 种基本服务的支持这些服务分别是

1 命名/目录服务Naming and Directory Service 在企业范围内的信息共享包

括计算机用户打印机应用程序等所有资源过程中名/目录服务扮演着重要的角色

它维护着名字和实际资源之间的连接关系以便使用者能通过名字实现对实际资源的透明

访问在一个大型的企业中可能有多种命名/目录服务并存如何将所有的命名/目录服务在

现有的计算架构完整地统一起来以便让应用程序透明地访问所有的命名/目录服务就成

了企业计算解决方案必须解决的问题之一

2 数据访问服务(Data Access Service) 大部分的应用都需要访问数据库企业计算

解决方案必须提供一种统一的界面对所有的数据库进行访问

3 分布式对象服务(Distributed Object Service) 在一个分布式的环境中构成整个

应用的所有组件可能位于不同的服务器上这些组件通常通过CORBA 进行互联在企业

计算环境里必须提供一种统一的方法访问这些组件以保护企业的投资

________

第4 章 JSP 与J2EE 分布式处理技术

4 企业管理服务(Enterprise Management Service) 在任何分布式环境中管理都是

一个关键的需求需要应用程序提供远程管理的能力以防止出现非法操作和访问以及对

设备应用程序状态的管理

5 事务处理服务(Transaction Processing Service) 一些关键部门在日常工作中有大

量的事务处理事务处理的开发是一件极其复杂的工作

6 消息服务(Messaging Service) 在一些企业特别是一些对同步传输要求不高的

企业通常采用消息机制在应用程序之间进行通讯

7 安全服务(Security Service) 应用程序的安全性是任何企业都关心的焦点之一

任何企业计算解决方案都不能回避

8 Web 服务(Web Service) 大部分企业级应用都以Web 服务器为中心Web 服务

成为企业计算解决方案的重要组成部分之一

为了实现上述服务在J2EE 平台中包含了若干个新的API 与这些服务一一对应

J2EE 的重要组成部分如下所示

JDBC(Java Database Connectivity)提供了连接各种数据库的统一接口

EJB(Enterprise JavaBeans)使得开发者方便地创建部署和管理跨平台的基于组件的

企业应用

Java RMI(Java Remote Method Invocation)用来开发分布式Java 应用程序一个Java

对象的方法能被远程Java 虚拟机调用这样远程方法激活可以发生在对等的两

端也可以发生在客户端和服务器之间只要双方的应用程序都是用Java 写的

Java IDL(Java Interface Definition Language) 提供与CORBA(Common Object

Request Broker Architecture)的无缝集成这使得Java 能够集成异构的商务信息资

JNDI(Java Naming and Directory Interface)提供从Java 平台到系统的无缝连接这

个接口屏蔽了企业网络所使用的各种命名和目录服务

在一个企业内部往往多种命名/目录服务并存如何将它们统一起来以使企业在

开发应用程序时能忽略各命名目录服务之间的区别而透明地访问这些命名目

录服务成为企业需要考虑的问题JNDI 技术解决了这一难题JNDI 技术命名

目录服务本身的特性出发提出了一些统一的访问名目录服务的界面由各命

名目录服务开发商对这些界面加以实现使用者就可以通过这些统一的界面去

访问底层的各种服务因此在JNDI 技术中其实存在两种角色一个是JNDI API

另一个是JNDI SPI( Service Provider Interface) SPI 是各命名目录服务开发商对

JNDI API 的实现以便用户通过JNDI API 访问他们所提供的服务

通过JNDI API 开发人员只要了解JNDI 就可以开发适用于所有实现了JNDI 的命

名/目录服务的应用保证了应用程序的可移植性且免去了大量的学习精力

JMAPI(Java Management API)为异构网络上的系统网络和服务管理的开发提供一

整套丰富的对象和方法JMAPI 是一组与协议无关的API 开发的应用不但可以

在不同的硬件平台上运行而且适用于多种网管协议

JMS(Java Message Service)提供企业消息服务如可靠的消息队列发布和订阅通

讯以及有关推拉(Push/Pull)技术的各个方面

________

第一部分 JSP 技术与J2EE 技术

消息机制在一些异步传输系统中被普遍使用同JNDI 服务相似市场上有许多命

名目录服务产品实现这种应用如Tibco MessageQ 等JMS 将这些所有的产

品统一于一个界面且获得了大量的中间件厂商的支持包括BEA System Etsee

Soft IBM MQSeries Novell 和Sybase 等在内的中间件厂商都已宣称要实现JMS

有的如Etsee Soft BEA WebLogic 已经实现

JTS(Java Transaction Service)提供存取事务处理资源的开发标准这些事务处理资

源包括事务处理应用程序事务处理管理程序

短短的几年多时间Java 已经从当初只能开发applet 等小小的应用发展到今天

在企业计算中的大量运用相信随着Java 技术的进一步发展一定会有越来越多

的企业受益于Java 技术革命带来的好处

JSA(Java Security API) 企业计算的安全性主要体现在4 个方面认证

Authentication 授权(Authorization) 信息在传输过程中的完整性(Integrity)和私

有性(Privacy) Java 2 平台提供了包括公钥public key 加密数字签名DSA

等功能在内的加密框架实现各加密功能以保证Java 计算的安全性

综合JTS JMS JNDI 等API 的特点可以发现J2EE 平台所包含的Java Enterprise API

不是自己去重新开发实现这些应用系统而只是在这些产品之上又增加了一个抽象层以

此来提升Java 和各类应用软件在企业中的应用保证了用Java 开发的企业计算应用能够在

多种平台上运行

创建EJB Container

3.1.6 创建EJB Container

在上文中我们已经介绍了Home Interface Remote Interface EJB Class 现在我们

应该利用JBuilder4 来生成Goods EJB 的EJB Container 方法如第2 章所述

在JBuilder 的主窗口中左侧右键单击GoodsHome.java 选择Properties…. 选择

Build Tab VisiBroker 在Generate IIOP 前面的小方框前打勾

然后编译Project JBuilder4 会自动产生十几个java 文件这些java 文件就构成了EJB

Container 和Home Object 这一节中开发的是CMP 类型的Entity EJB 所以在EJB Container

中应该具体实现ejbLoad() ejbStore() ejbRemove() ejbActivate() ejbPassivate()等方法

________

第一部分 JSP 技术与J2EE 技术

至于如何实现这些方法我们就不用关心了JBuilder4 会自动处理这些细节问题的编译

完Project 以后千万不要忘了保存所有的项目文件

3.1.7 创建EJB 客户端

在上面的几个小节中我们已经开发了一个CMP 类型的实体EJB 现在应该编写一

个EJB 客户端程序对它进行测试为了节省篇幅和方便起见我们仅仅介绍如何编写基于

Java Application 的EJB 客户端至于基于JSP 的EJB 客户端读者可以自行编写实际上

编写EJB 比使用JSP 技术编写EJB 的客户端要简单很多倍而且我们只要会编写基于Java

Application 的EJB 客户端那么在此基础上稍微改写一下就是基于JSP 的EJB 客户端程

序了这一项技术想必读者早已经掌握了即使读者还没有掌握这门技术那么读者可以

参考第2 章最后的EJB 客户端程序那就是基于JSP 的EJB 客户端程序

如何开发基于Java Application 的 EJB 的客户端程序呢Entity EJB 的客户端程序与

Session EJB 的客户端程序有何不同呢?我们的回答是这两者没有任何不同的地方都是查

找Home Interface 创建Remote Interface 调用商业逻辑这三部曲对于第一个问题的回

答如下请读者在JBuilder 中选择File New… Enterprise Tab EJB Test Client OK

将出现如图3.14 所示的窗口

图3.14 JBuilder4 的EJB Client Test Wizard

在图3.14 中我们把Class 框的缺省值GoodsTestClient1 改为GoodsTestClient 其他

的一切都不要改动单击OK 回到JBuilder 的主界面JBuilder 为我们自动创建了

GoodsTestClient.java 程序编辑GoodsTestClient.java 使得它如下面的程序清单3.6 所示

其中黑体的部分是我们自己加上去的其余的部分是JBuilder 自动产生的

________

第3 章 EJB 技术进阶

程序清单3.6

//File Name:GoodsTestClient.java

//Author:fancy

//Date:2001.5.10

//Note:test the client

package jdbctest;

import javax.naming.*;

import javax.rmi.PortableRemoteObject;

import java.util.Collection;

import java.util.Iterator;

public class GoodsTestClient

{

private static final int MAX_OUTPUT_LINE_LENGTH = 50;

private GoodsHome goodsHome = null;

/**Construct the EJB test client*/

public GoodsTestClient()

{

try

{

//get naming context

Context ctx = new InitialContext();

//look up jndi name

Object ref = ctx.lookup("Goods");

//cast to Home interface

goodsHome = (GoodsHome)

PortableRemoteObject.narrow(ref GoodsHome.class);

GoodsRemote gr=goodsHome.findByPrimaryKey(22);

System.out.println("name:"+gr.getGoodsname());

System.out.println("type:"+gr.getGoodstype());

System.out.println("id:"+gr.getId());

System.out.println("price:"+gr.getPrice());

System.out.println("off:"+gr.getPriceoff());

System.out.println("comment:"+gr.getComment());

System.out.println("is session ejb:"+

goodsHome.getEJBMetaData().isSession());

}

catch(Exception e)

{

e.printStackTrace();

}

}

________

第一部分 JSP 技术与J2EE 技术

//----------------------------------------------------------------------------

// Utility Methods

//----------------------------------------------------------------------------

public GoodsHome getHome()

{

return goodsHome;

}

public void executeFindAll()

{

try

{

Collection collection = goodsHome.findAll();

Iterator iterator = collection.iterator();

while (iterator.hasNext())

{

Object object = iterator.next();

GoodsRemote goodsRemote = (GoodsRemote)

PortableRemoteObject.narrow(object GoodsRemote.class);

log(" id="+goodsRemote.getId()+" "+goodsRemote.toString());

}

}

catch(Exception e)

{

e.printStackTrace();

}

}

private void log(String message)

{

if (message.length() > MAX_OUTPUT_LINE_LENGTH)

{

System.out.println("-- " +

message.substring(0 MAX_OUTPUT_LINE_LENGTH) + " ...");

}

else

{

System.out.println("-- " + message);

}

}

/**Main method*/

public static void main(String[] args)

{

GoodsTestClient client = new GoodsTestClient();

________

第3 章 EJB 技术进阶

client.executeFindAll();

// Use the getHome() method of the client object to call Home interface

// methods that will return a Remote interface reference. Then

// use that Remote interface reference to access the EJB.

// For quick access to all available records you can use the

// executeFindAll() method in this class.

}

}

程序清单3.6 很简单但是我们有两点要说明

1 首先找到下面的代码行这几行代码首先利用Home Interface 的findPrimaryKey()

方法查找主键等于22 的Remote Interface 亦即GoodsRemote 对象一旦获取了

GoodsRemote 对象就可以调用在Remote Interface 中声明的各种getXXX()方法与setXXX()

方法获取GoodsRemote 对象的属性值或者设定它的属性值这些getXXX()方法与setXXX()

方法实质上是在存取数据库某个Table 的行纪录这个Table 被Entity EJB 所映射在本例

中这个Table 是goods 这个Entity EJB 是Goods EJB

GoodsRemote gr=goodsHome.findByPrimaryKey(22);

System.out.println("name:"+gr.getGoodsname());

System.out.println("type:"+gr.getGoodstype());

System.out.println("id:"+gr.getId());

System.out.println("price:"+gr.getPrice());

System.out.println("off:"+gr.getPriceoff());

System.out.println("comment:"+gr.getComment());

System.out.println("is session ejb:"+goodsHome.getEJBMetaData().isSession());

2 读者请留意executeFindAll()方法这个自定义的方法调用Home Interface 声明的

findAll()方法遍历EJB Server/Container 中所有可用的Goods EJB 对象(实际上是

GoodsRemote 对象) 并且输出它们的主键信息

3.1.8 运行和测试

下面我们该运行和测试Goods EJB 了在运行Goods EJB 之前首先要对Project 做一

些设定选择Run Configuration… 选中<Default> 然后单击OK 即可别忘了要启动

Smart Agent 如果读者对这一部分的内容不熟悉不妨参考第2 章的内容

首先应该运行Goods EJB 运行Goods EJB 的输出结果如下所示(前半段输出已经省略

了)

Initializing EJBs........ done

Container [ejbcontainer] is ready

EJB Container Statistics

========================

Time Thu Mar 25 23:52:59 CST 1999

Memory (used) 1704 Kb (max 1704 Kb)

Memory (total) 2736 Kb (max 2736 Kb)

Memory (free) 37.0%

________

第一部分 JSP技术与J2EE技术

------------------------

Home Goods

Total in memory 0

Total in use 0

========================

接着应该运行Goods EJB 的客户端程序GoodsTestClient.java 在JBuilder 的主界

面中左侧右键单击GoodsTestClient.java Run 运行输出如下所示

name:小楼一夜听春雨

type:兵器

id:22

price:3000.8

off:0.8

comment:小楼一夜听春雨深巷明朝卖杏花好优美的诗意啊可惜它是一把弯刀

又名圆月弯刀曾经是青青所用之物后归丁鹏所有和万水青山踏遍齐名

is session ejb:false

-- id=1 Stub[repository_id=RMI:jdbctest.GoodsRemote: ...

-- id=2 Stub[repository_id=RMI:jdbctest.GoodsRemote: ...

-- id=3 Stub[repository_id=RMI:jdbctest.GoodsRemote: ...

-- id=4 Stub[repository_id=RMI:jdbctest.GoodsRemote: ...

-- id=22 Stub[repository_id=RMI:jdbctest.GoodsRemote ...

-- id=6 Stub[repository_id=RMI:jdbctest.GoodsRemote: ...

-- id=7 Stub[repository_id=RMI:jdbctest.GoodsRemote: ...

-- id=8 Stub[repository_id=RMI:jdbctest.GoodsRemote: ...

-- id=9 Stub[repository_id=RMI:jdbctest.GoodsRemote: ...

-- id=10 Stub[repository_id=RMI:jdbctest.GoodsRemote ...

-- id=11 Stub[repository_id=RMI:jdbctest.GoodsRemote ...

-- id=12 Stub[repository_id=RMI:jdbctest.GoodsRemote ...

-- id=13 Stub[repository_id=RMI:jdbctest.GoodsRemote ...

-- id=14 Stub[repository_id=RMI:jdbctest.GoodsRemote ...

-- id=15 Stub[repository_id=RMI:jdbctest.GoodsRemote ...

-- id=16 Stub[repository_id=RMI:jdbctest.GoodsRemote ...

-- id=17 Stub[repository_id=RMI:jdbctest.GoodsRemote ...

-- id=18 Stub[repository_id=RMI:jdbctest.GoodsRemote ...

-- id=19 Stub[repository_id=RMI:jdbctest.GoodsRemote ...

-- id=20 Stub[repository_id=RMI:jdbctest.GoodsRemote ...

这时如果再查看Goods EJB 服务端的输出将会有下面的结果

EJB Container Statistics

========================

Time Fri Mar 26 00:04:36 CST 1999

Memory (used) 1522 Kb (max 1736 Kb)

Memory (total) 2664 Kb (max 2664 Kb)

Memory (free) 42.0%

------------------------

Home Goods

POOLED 1

________

第3 章 EJB 技术进阶

Total in memory 1

Total in use 1

========================

EJB Container Statistics

========================

Time Fri Mar 26 00:04:51 CST 1999

Memory (used) 1670 Kb (max 1736 Kb)

Memory (total) 2664 Kb (max 2664 Kb)

Memory (free) 37.0%

------------------------

Home Goods

READY 1

POOLED 1

Total in memory 2

Total in use 2

========================

EJB Container Statistics

========================

Time Fri Mar 26 00:04:56 CST 1999

Memory (used) 1692 Kb (max 1736 Kb)

Memory (total) 2664 Kb (max 2664 Kb)

Memory (free) 36.0%

------------------------

Home Goods

READY 20

Total in memory 20

Total in use 20

========================

上面的输出结果说明了在EJB Server/Container 中可用的Goods EJB 对象一共有20

个刚好等于goods Table 纪录的行数这是巧合吗?读者不妨考虑一下

3.2 实体EJB 的开发技术之二——BMP EJB

上一节我们已经介绍了CMP 模式的Entity EJB 的开发技术在这一节中我们将介

绍BMP 模式的Entity EJB 的开发技术

3.2.1 BMP EJB 简介

BMP EJB 全___________名是Beans Managed Persistence EJB 中文译名为Bean 自管理模式的EJB

BMP 模式的EJB 与CMP 模式的EJB 最大的不同之处在于ejbLoad() ejbStore()

ejbRemove() ejbActivate() ejbPassivate() ejbCreate()等方法都必须由开发者编码实现

EJB Container 不会再为我们代劳也就是说我们必须直接和数据源打交道必须自己编写

存取数据库纪录集的代码如果没有开发工具的帮助这真是一项十分繁琐复杂的任务

使用BMP EJB 的好处在于它的灵活性如果我们要实现一个条件查询查询条件会

经常变化这时候使用CMP EJB 不是一个最好的选择这种情况下BMP EJB 就比CMP

________

第一部分 JSP 技术与J2EE 技术

EJB 要优越得多我们可以在编码实现上述方法的同时添加自己的商业逻辑这就是BMP

模式的一个突出之处扩展性如果你对于EJB 编程十分熟练的话你就会发现BMP

模式带给你的便利远远超过了带给你的不便

我们建议读者在必要的时候采用BMP 模式的EJB 完成你的开发项目

3.2.2 创建EJB 工程

开发CMP 模式的EJB 与开发BMP 模式的EJB 有何不同?应该说在创建EJB 的框架

时这两种模式没有什么不同但是在开发的后期CMP 模式的EJB 与BMP 模式的EJB

的EJB Class 大不相同同时它们的部署描述符也有所不同

在本小节中我们将利用JBuilder4 创建一个Entity EJB Modeler Project 这个Project

的名字为JDBCBean 前面的步骤和开发Goods EJB(JDBCTest Project)一模一样读者可以

参照3.2 节的说明

在EJB Entity Bean Modeler Wizard 的第六步我们需要修改此EJB 的Home Interface

Remote Interface 的名字如图3.15 所示这个Entity EJB 的名字仍然是Goods 映射的Table

仍然是goods d 代表主键的字段仍然是id 字段主键的数据类型为int

图3.15 JBuilder4 的EJB Entity Bean Modeler Wizard

在图3.15 的窗口中都设定好以后单击Next 按钮将出现如图3.16 所示的窗口

在这个窗口中我们将要设定Entity EJB的开发模式本节开发的是BMP 模式的Entity EJB

所以我们应该选中Bean managed peresistence 然后单击Finish JBuilder4 会根据我们的设

定自动产生Home Interface Remote Interface EJB Class 部署描述符等文件这样就算

完成EJB 框架的开发工作下面就来看看JBuilder4 为我们自动产生的代码

________

第3 章 EJB 技术进阶

图3.16 JBuilder4 的EJB Entity Bean Modeler Wizard

3.2.3 Home Interface 和Remote Interface

读者请看程序清单3.7 和程序清单3.8 这就是BMP 模式的Goods EJB 的Home

Interface Remote Interface 的源代码读者不妨把它们和程序清单3.1 3.2 比较一下看

看有没有差别实际上是没有任何差别的BMP 模式的EJB 与CMP 模式的EJB 的差别不

在于Home Interface 或者是Remote Interface 而在于EJB Class 与EJB Container 的不同

程序清单3.7

//File Name: GoodsHome.java

//Author:fancy

//Date:2001.5

//Note:the Home Interface

package jdbcbean;

import java.rmi.*;

import javax.ejb.*;

import java.util.*;

public interface GoodsHome extends EJBHome

{

public GoodsRemote create(String goodsname String goodstype String comment

Double price Double priceoff int id) throws RemoteException CreateException;

public GoodsRemote create(int id) throws RemoteException CreateException;

public GoodsRemote findByPrimaryKey(int primaryKey) throws RemoteException

FinderException;

________

第一部分 JSP 技术与J2EE 技术

public Collection findAll() throws RemoteException FinderException;

}

程序清单3.8

//File Name: GoodsRemote.java

//Author:fancy

//Date:2001.5

//Note:the Remote Interface

package jdbcbean;

import java.rmi.*;

import javax.ejb.*;

public interface GoodsRemote extends EJBObject

{

String getGoodsname() throws RemoteException;

void setGoodsname(String goodsname) throws RemoteException;

String getGoodstype() throws RemoteException;

void setGoodstype(String goodstype) throws RemoteException;

String getComment() throws RemoteException;

void setComment(String comment) throws RemoteException;

Double getPrice() throws RemoteException;

void setPrice(Double price) throws RemoteException;

Double getPriceoff() throws RemoteException;

void setPriceoff(Double priceoff) throws RemoteException;

int getId() throws RemoteException;

}

正因为程序清单3.7 和3.1 没有差别程序清单3.8 和程序清单3.2 也没有差别所以

我们就不再重复解释这两个程序了读者可以参考上文的叙述

3.2.4 EJB 类

在上文我们已经不止一次提到过BMP 模式的EJB 与CMP 模式的EJB 的结构最大的

不同之处在于EJB Class 与EJB Container BMP 模式的EJB 的EJB Class 分为两个部分

一个是普通的EJB Class(GoodsBean.java) 继承自EntityBean 接口这部分只是简单地声明

了ejbLoad() ejbStore() ejbRemove() ejbActivate() ejbPassivate() ejbCreate()等方法

并没有真正地去实现它们请看程序清单3.9(GoodsBean.java) 这一部分和CMP 模式的

EJB 的EJB Class 一模一样读者如果不信可以比较程序清单3.9 和程序清单3.3 的异同

怎么样找到了吗?实际上这两个程序没有任何不同只不过CMP 模式的EJB 把这些方

法的实现放到了EJB Container 而在BMP 模式的EJB 中我们必须新定义一个EJB Class

继承GoodsBeans 类并且编码实现ejbLoad() ejbStore() ejbRemove() ejbActivate()

ejbPassivate() ejbCreate()等方法读者请看程序清单3.10 (GoodsBeanBMP.java)

________

第3 章 EJB 技术进阶

程序清单3.9

//File Name:GoodsBean.java

//Author:Fancy

//Date:2001.5

//Note:the EJB Class

package jdbcbean;

import java.rmi.*;

import javax.ejb.*;

public class GoodsBean implements EntityBean

{

EntityContext entityContext;

public String goodsname;

public String goodstype;

public String comment;

public Double price;

public Double priceoff;

public int id;

public int ejbCreate(String goodsname String goodstype String comment Double

price Double priceoff int id) throws CreateException

{

this.goodsname = goodsname;

this.goodstype = goodstype;

this.comment = comment;

this.price = price;

this.priceoff = priceoff;

this.id = id;

return 0;

}

public int ejbCreate(int id) throws CreateException

{

return ejbCreate(null null null null null id);

}

public void ejbPostCreate(String goodsname String goodstype String comment

Double price Double priceoff int id) throws CreateException

{

}

public void ejbPostCreate(int id) throws CreateException

{

ejbPostCreate(null null null null null id);

}

public void ejbRemove() throws RemoveException

________

第一部分 JSP 技术与J2EE 技术

{

}

public void ejbActivate()

{

}

public void ejbPassivate()

{

}

public void ejbLoad()

{

}

public void ejbStore()

{

}

public void setEntityContext(EntityContext entityContext)

{

this.entityContext = entityContext;

}

public void unsetEntityContext()

{

entityContext = null;

}

public String getGoodsname()

{

return goodsname;

}

public void setGoodsname(String goodsname)

{

this.goodsname = goodsname;

}

public String getGoodstype()

{

return goodstype;

}

public void setGoodstype(String goodstype)

{

this.goodstype = goodstype;

}

public String getComment()

{

return comment;

}

public void setComment(String comment)

________

第3 章 EJB 技术进阶

{

this.comment = comment;

}

public Double getPrice()

{

return price;

}

public void setPrice(Double price)

{

this.price = price;

}

public Double getPriceoff()

{

return priceoff;

}

public void setPriceoff(Double priceoff)

{

this.priceoff = priceoff;

}

public int getId()

{

return id;

}

}

程序清单3.10

//File Name:GoodsBeanBMP.java

//Author:Fancy

//Date:2001.5

//Note:the EJB Class

package jdbcbean;

import java.sql.*;

import javax.ejb.*;

import javax.sql.DataSource;

import java.util.*;

public class GoodsBeanBMP extends GoodsBean

{

DataSource dataSource;

public int ejbCreate(String goodsname String goodstype String comment Double price

Double priceoff int id) throws CreateException

{

________

第一部分 JSP 技术与J2EE 技术

super.ejbCreate(goodsname goodstype comment price priceoff id);

try

{

//First see if the object already exists

ejbFindByPrimaryKey(id);

//If so then we have to throw an exception

throw new DuplicateKeyException("Primary key already exists");

}

catch(ObjectNotFoundException e)

{

//Otherwise we can go ahead and create it...

}

Connection connection = null;

PreparedStatement statement = null;

try

{

connection = dataSource.getConnection();

statement = connection.prepareStatement("INSERT INTO goods (goodsname

goodstype comment price priceoff id) VALUES (? ? ? ? ? ?)");

statement.setString(1 goodsname);

statement.setString(2 goodstype);

statement.setString(3 comment);

statement.setDouble(4 price.doubleValue());

statement.setDouble(5 priceoff.doubleValue());

statement.setInt(6 id);

if (statement.executeUpdate() != 1)

{

throw new CreateException("Error adding row");

}

statement.close();

statement = null;

connection.close();

connection = null;

return id;

}

catch(SQLException e)

{

throw new EJBException("Error executing SQL INSERT INTO goods

(goodsname goodstype comment price priceoff id) VALUES

(? ? ? ? ? ?): " + e.toString());

}

finally

{

________