﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-小江西-文章分类-网络资料</title><link>http://www.blogjava.net/raozhh/category/5989.html</link><description /><language>zh-cn</language><lastBuildDate>Thu, 01 Mar 2007 02:37:41 GMT</lastBuildDate><pubDate>Thu, 01 Mar 2007 02:37:41 GMT</pubDate><ttl>60</ttl><item><title>Linux 基本管理知识</title><link>http://www.blogjava.net/raozhh/articles/45760.html</link><dc:creator>饶志华</dc:creator><author>饶志华</author><pubDate>Fri, 12 May 2006 00:58:00 GMT</pubDate><guid>http://www.blogjava.net/raozhh/articles/45760.html</guid><wfw:comment>http://www.blogjava.net/raozhh/comments/45760.html</wfw:comment><comments>http://www.blogjava.net/raozhh/articles/45760.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/raozhh/comments/commentRss/45760.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/raozhh/services/trackbacks/45760.html</trackback:ping><description><![CDATA[
		<table class="content" cellspacing="0" cellpadding="2" width="95%" border="0">
				<tbody>
						<tr>
								<td align="middle">
								</td>
						</tr>
						<tr>
								<td>
										<div class="content" style="TABLE-LAYOUT: fixed; WORD-WRAP: break-word">
												<span class="content">作者：田 逸（<a href="mailto:sery@163.com">sery@163.com</a>）<br />&lt;网管员世界&gt; 2005年11期<br />通过第一招的实践，我们已经可以顺利的安装好linux操作系统，这仅仅是工作的一小部分，现在最迫不及待的事情就是使用这个系统。打开安装好linux操作系统计算机的电源，让机器正常引导，待系统引导完毕，我们的第一个操作---登录开始了；在这一步，能执行的任务就是输入用户名root和初始安装时设定的密码，一旦输入无误，便可取得操作整个计算机的所有权限，开始了挑战Linux的艰难之旅。<br /><br />超级用户账号密码<br />为安全起见，需要定期更改超级用户root的密码。这个操作十分简单，在shell提示符#后输入 passwd回车，然后两次输入新密码即可。退出root登录，然后再次登录，检验密码修改是否正确。然而不幸的事情还是偶尔会发生，刚才更改的密码记不清楚了，root用户登录不了，急人啦，难道还要重装linux操作系统？如果你还想尝试一遍安装操作，本是无可厚非的，但这并不是一个好主意，特别是在硬盘上存储很多重要数据的情形下。请参照下面的操作来解决这个问题：<br />1、     手动重启系统：按计算机复位键或先关闭计算机在开启。<br />2、     当引系统到达“引导加载程序（选择欲启动的操作系统）”GRUB时（redhat 9在安装的时候，我们默认安装的引导程序是 GRUB），按键盘字母“e”键，如果硬盘上安装多个操作系统的话，还需要用箭头选中linux 启动条目。将看到如下几行文本（为节省篇幅，多余的文本省略了）<br />root (hd0,4)<br />kernel /boot/vmlinuz-2.4.20-8 ro root=LABLE/ hdc=ide-scsi   <br />initrd /boot/initrd-2.4.20-8.img<br />3、     把 “kernel /boot/vmlinuz-2.4.20-8 ro root=LABLE/ hdc=ide-scsi” 改成 “kernel /boot/vmlinuz-2.4.28 single root=LABLE/ hdc=ide-scsi”后按回车键，返回编辑屏幕。<br />4、     按“b”键使用上面更改后的选项引导计算机，这样计算机就进入单用户模式（运行级别1）而不用输入用户名和密码登录。<br />5、     输入命令passwd回车，两次输入新口令，然后小心保存。关于口令设置的建议有很多，可以根据自己的情况设置复杂口令。<br />6、     再次重启计算机，这时便可顺利使用新密码进入系统了。<br />这个操作对于管理员来讲，是十分有用的，但是如果这个服务器运行的是关键应用，那么应当把它锁在机柜里，因为不是公司聘用的非管理员也能够替你执行这个操作，很危险的。<br /><br />用户账号<br />有句名言叫“linux就是网络”，这就意味着linux系统不是系统管理员一个人把玩的，有太多的理由让更多的人来使用这个资源。刚安装完的linux系统，只有一个用户root；没有任何责任让其他仅需使用一部分功能的人来共享root账号和密码。既然如此，给要用计算机资源的人开设账号吧。<br />1、     开设账号：在命令提示符输入 #useradd sery 就可以成功的添加账号sery，用命令#passwd sery 来给账号sery 设置密码。通过这种方式添加的账号的主目录将是/home/sery, sery用户的环境变量.bash_profile在目录 /home/sery中。也可以指定用户的主目录，例如我们要安装数据库软件sybase ASE，需要指定它的主目录为/opt/sybase，通过输入命令#useradd –d /opt/sybase sybse 就可以了，这样sybase用户的环境变量路径也变成 /opt/sybase/.bash_profile。可以把一些用户添加到一个组了，以满足特定的功能。上面开设的两个账号，默认产生两个组sery和sybase。<br />2、     更改账号：某天，公司的sery离职了，由新来的tieny接替他的工作，可以用命令#usermod –l tieny –m –d /home/tieny sery来实现。当然还应当把口令也改一下。<br />3、     删除账号：sery由于工作不力，被老板开除了，那么作为系统管理员应当把他的账号删除，先删除账号#userdel sery,然后还要删除sery用户的主目录/home/sery。<br />需要注意的是，上述的账号操作，只有具备管理员权限的用户（比如root）才可以进行的，普通用户仅仅能更改自己的密码而已。<br /><br />文本编辑器vi<br />要使用Unix/linux来完成工作，你没有办法来回避使用文本编辑器这个工具。在unix/linux的世界里，vi是赫赫有名的，ibm的AIX、SUN的Solaris、SCO UNIX、RedHat Linux等等，没有一个不用vi 这个编辑工具的。因此，作为unix/linux系统管理员，必须熟练掌握这个基本技能。<br />vi文本编辑器打开以后，有两种模式：命令模式和输入（或者编辑）模式。在使用过程中，这两种模式是需要来回相互切换的，初学者对此往往感到迷惑，下面举一个例子来说明一下：<br />我们在此要把计算机的主机名改成sery,那么就用vi编辑器修改配置文件/etc/hosts文件。输入命令 #vi /etc/hosts 回车，立刻进入vi的命令模式。在这种模式下，可以进行查找、保存文件等操作，按字母“i”键切换到编辑模式---屏幕底部显示“Insert”，然后光标定位，输入相关的字符；输入完毕并检查无误后，按“Esc”键切换到命令模式，接着输入“：”加wq回车就把刚才的输入保存在文件/etc/hosts里。整个过程总结一下流程就是：vi filename 打开文件---切换到编辑模式（“i”）----输入文本----切换到命令模式（“Esc” ，“：”）----保存文件（wq）。<br />[root@sybase root]# vi /etc/hosts<br /><br /># Do not remove the following line, or various programs<br /># that require network functionality will fail.<br />127.0.0.1           localhost.localdomain localhost //按i切换编辑模式<br /><br />202.108.93.200       sery //这行是加上去的<br />//按Esc键回到命令模式，再输入“：”<br />：wq                 //保存文件退出<br /><br />有时，文件修改后不想保存它，则在命令模式下输入“:q!”,如果只是打开文件而没有做任何修改，在命令模式下输入“:q”就可以了。有一个技巧还是需要掌握的，那就是在文件中查找字符。编辑一个小的文件（字符数不多）用光标定位就可以完成修改，但是如果要编辑的一个文件较大，例如修改Apache的配置文件，根据实际应用，我们仅需对/etc/httpd/conf/httpd.conf做少许修改就可以了，假如用光标定位的方法来做这个事情，恐怕是一件很费时的事，这时，查找定位这个功能就派上用场了。查找是在命令模式下进行的，这一点要记牢。在apache的配置文件里，我们要修改根文档的位置，首先使用#vi /etc/httpd/conf/httpd.conf打开文件进入命令模式，然后输入“？DocumentRoot”很快就可定位输入点，接着按“i”切换到编辑模式，按要求修改文本，而后切换到命令模式保存退出。查找方式也是很灵活的，根据当前光标所在的位置，既可以向后查找，有可以向前查找，不过向前查找开始字符是“/”而不是“?”。vi的功能特别多，在此无法一一列举，不过笔者介绍的方法已经足以对付日常的工作，随着时间的推移，你同样会逐步掌握更多的 vi 使用技能。随着linux版本不断的升级，vi也越来越便于使用，例如在linux桌面环境下，可以用鼠标选定文本，然后使用复制功能来简化操作。<br /><br />配置网络参数<br />如果linux服务器不连入网络，除了供管理员测试而外，再没有别的实际意义，因此把linux服务器连入网络就是必须的了。在第一招安装linux时，我们已经把网络的一些参数手动输入了，但是有时还得在实际应用中更改某些参数。网络参数主要包括：IP地址、子网掩码、默认网关和指定dns服务器。IP地址、子网掩码、默认网关由文件/etc/sysconfig/network-scripts/ifcfg-eth0来指定。用编辑器 vi 打开/etc/sysconfig/network-scripts/ifcfg-eth0，修改对应的值，我本人习惯把默认网关加在这个文件里，即在这个文件里插入一行“GATEWAY=X.X.X.X”。默认网关是个非常重要的设置，如果设置错误，linux服务器就只能访问同一网段的主机而不能与其他网段的计算机互访尽管其他的网络参数设置完全无误。我曾有意无意的犯过这个错误，结果每次都是一样让人沮丧。在讲授网络的课程里，默认网关被定义成一个三层设备，讲得通俗一点就是路由器，它的作用是连接不同网段/子网的通信，它记录与它某一端口直连的同一网段的机器mac与ip地址对应值（称为ARP）。路由器/网关不会自动获得同一网段主机的mac-ip值，只有通过手动指定默认网关把主机的mac-ip值通告给网关。指定dns服务器地址可以让linux服务器以域名的方式访问互联网上的其他机器。指定域名服务器地址过程很简单，只需用vi编辑文件/etc/resolve.conf,输入 nameserver 202.106.0.20 保存退出。需要特别注意的是，不要把指定dns服务器与设定本linux服务器为dns服务器搞混了，这是两个完全不同的东西：前者为客户端，后者为服务器端。<br />还有一种特殊情况，在只有一个网络设备的条件下，我们需要设置几个IP地址，比如基于IP地址的虚拟主机。这种操作称为设备别名。eth0的第一个别名是/etc/sysconfig/network-scripts/ifcfg-eth0:1,用编辑器vi参照/etc/sysconfig/network-scripts/ifcfg-eth0的格式输入网络参数即可。<br />在系统提示符下，用命令#ifconfig eth0 200.200.200.200 255.255.255.248 up也可指定或修改网络设备的网络参数。但是这种修改方式是动态进行的，系统重启以后就无效了。以命令#route add default gw X.X.X.X 添加的默认网关也是同样的效果。<br />修改网络参数后，要使修改立即生效，不需要重启计算机，重新启动网络服务就可以了。在RedHat linux 9以后的版本，只要输入命令#service network restart即可，然后用命令#ifconfig –a来检验修改的结果。<br />修改网络参数需要管理员权限才能进行。<br /><br />文件系统和原始设备<br />在这里介绍文件系统和原始设备（ raw device）的目的主要是为将来安装大型数据库oracle或sybase奠定一些基础，因为这些数据库可以使用文件系统或原始设备作为逻辑存储区域。<br />文件系统是一种存储数据的方法，采用分层目录结构来存储文件，由一个根目录和许多子目录、文件组成（IBM AIX 5L系统管理技术定义）。Redhat linux 9主要包括以下一些目录：<br />1、/bin:linux通用命令。<br />2、/dev:系统设备文件。<br />3、/etc:管理配置文件。<br />4、/home:各用户的目录。<br />5、/mnt:挂接设备的位置。<br />6、/root:根用户的主目录。<br />7、/sbin:系统管理命令和守护进程。<br />8、/tmp:临时文件目录。<br />9、/var:应用程序使用的数据目录，如系统日志文件目录、匿名ftp目录等。<br />10、/usr:手动安装程序的目录。<br />对于文件系统的目录及其目录下的文件，我们可以直接进行访问，例如在/tmp下创建目录/tmp/test。<br />原始设备指不是由基础操作系统可以管理的磁盘，这句话不太好理解。给linux服务器添加一个硬盘，创建分区，但是不能直接用基本命令对这个分区进行操作，比如直接在上面创建目录，只有通过挂接才可以进行磁盘I/O操作。例子的命令：#mount /dev/sdb1 /mnt/dsk2, cd /mnt/dsk2，在这个目录下就可以创建文件等基本操作了。Sybase等数据库可以直接指定原始设备为存储空间，其目的是使数据的修改立即写入磁盘。<br /><br />运行级别<br />RedHat linux 有0-6等几种运行级别。与solaris和AIX的运行级别的定义差异较大。按照前面安装linux的设定，系统启动后进入的运行级别是3。要切换到别的运行环境只需输入#init &lt;runlevel&gt; 。下面列出各个运行级别的功能：<br />1、     运行级别0：关机操作。<br />2、     运行级别1：单用户维护模式。这种模式只能是用户root,上文我们更改遗忘的 root 密码就是在这种模式下进行的。另外，有时文件系统损坏时进行修复，也要在这种模式下进行。运行级别1相当与MS windows 2000的安全模式。<br />3、     运行级别2：多用户模式。所有配置文件系统被挂装，但NFS资源不可用。运行级别2与MS windows 2000的带网络的安全模式有些类似。<br />4、     运行级别3：这是linux服务器的正常模式，系统资源完全可用。<br />5、     运行级别4：用户自定义。<br />6、     运行级别5：多用户带网络服务加图形界面（init 3加图形）。这相当于MS windows的正常模式。如果没有安装xwindow就不能启用这个级别，用命令#startx也可从运行级别3切换到运行级别5。<br />7、     运行级别6：重新启动计算机。<br />在一般情况下，我们需要linux服务器工作在运行级别3下，偶尔为了操作方便会使用5这个级别。Linux还有另外一些运行级别，但不多用。熟练掌握上述几个运行级别对于系统维护是很有帮助的。<br /><br />远程操作linux服务器<br />linux服务器基本配置完成后，需要把它撤离测试台，也许要把它放在隔自己办公室很远的idc机房，或者由于同事无法忍受机器巨大的噪音而必须把它放在某个隔离的小房间的机柜里。我们更愿意坐在自己的办公桌前，用桌面系统或笔记本来远程控制和操作放在在某个地方的linux服务器，然后惬意的喝一杯咖啡。<br />telnet曾作为最主要的远程管理工具，由于其存在安全隐患而逐渐失宠，从RedHat linux 7.1开始，默认情况下 telnet 服务是不启用的。安全shell包（ssh）由于它提供对数据的加密传输，具备较高的安全特性，因此越来越多的系统管理员正在在使用ssh来管理远程的 linux服务器。<br />要使用ssh服务，应当保证openssh-server软件包被安装，一般情况下，ssh服务器被配置成自动启动，在 linux服务器端，不必对ssh做任何设置，只需保证它被安装和启动就可以了。RedHat linux 允许root用户远程登录，而RedFlag linux则在默认情况下不允许root 用户远程登录。可通过修改文件 /etc/sshd/sshd_config，“PermitRootLogin no”表示不允许root用户远程登录，“PermitRootLogin yes”表示允许root用户远程登陆。我本人习惯使用root远程登录。<br />1、     从linux连接：输入命令#ssh &lt;远程linux服务器的ip地址或域名&gt;，回车后输入 root密码就可以得到root的shell环境，与直接操作linux服务器一样。<br />2、     从windows连接：有多款用来连接linux服务器的远程管理工具，比较有名的有SecureCRT、NetTerm、XManager、Putty等。SecureCRT是个不错的工具，它不仅支持ssh,还支持文件上传功能。Windows下配置SecureCRT是比较容易的，在此不做介绍。<br />下图是SecureCRT使用ssh登录linux服务器的事例： <br />  <br />当作好上述的准备后，真正的远程操作/控制可以开始了。<br /><br /><br />                                    </span>
										</div>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/raozhh/aggbug/45760.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/raozhh/" target="_blank">饶志华</a> 2006-05-12 08:58 <a href="http://www.blogjava.net/raozhh/articles/45760.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> Ajax原理详细说明(转自ibm开发者网站) </title><link>http://www.blogjava.net/raozhh/articles/42361.html</link><dc:creator>饶志华</dc:creator><author>饶志华</author><pubDate>Fri, 21 Apr 2006 08:53:00 GMT</pubDate><guid>http://www.blogjava.net/raozhh/articles/42361.html</guid><wfw:comment>http://www.blogjava.net/raozhh/comments/42361.html</wfw:comment><comments>http://www.blogjava.net/raozhh/articles/42361.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/raozhh/comments/commentRss/42361.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/raozhh/services/trackbacks/42361.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Ajax 						由						 HTML						、						JavaScript™ 						技术、						DHTML 						和						 DOM 						组成，这一杰出的方法可以将笨拙的						 Web 						界面转化成交互性的						 Ajax 						应用程序。本文的作者是一位						...&nbsp;&nbsp;<a href='http://www.blogjava.net/raozhh/articles/42361.html'>阅读全文</a><img src ="http://www.blogjava.net/raozhh/aggbug/42361.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/raozhh/" target="_blank">饶志华</a> 2006-04-21 16:53 <a href="http://www.blogjava.net/raozhh/articles/42361.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Portlet开发基知识 </title><link>http://www.blogjava.net/raozhh/articles/40636.html</link><dc:creator>饶志华</dc:creator><author>饶志华</author><pubDate>Wed, 12 Apr 2006 06:07:00 GMT</pubDate><guid>http://www.blogjava.net/raozhh/articles/40636.html</guid><wfw:comment>http://www.blogjava.net/raozhh/comments/40636.html</wfw:comment><comments>http://www.blogjava.net/raozhh/articles/40636.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/raozhh/comments/commentRss/40636.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/raozhh/services/trackbacks/40636.html</trackback:ping><description><![CDATA[
		<div class="postTitle">
				<a href="http://blog.csdn.net/zl198183/archive/2005/11/23/535354.aspx">
						<img height="13" src="http://blog.csdn.net/images/authorship.gif" width="15" border="0" /> Portlet开发基知识</a>
				<script language="javascript"><![CDATA[ocument.title="Portlet开发基知识 - "+document.title]]&gt;</script>
		</div>
		<div class="postText">
				<p>1.portlet.xml文件，放置在WEB_INF/portlet.xml下面。典型的例子：</p>
				<p>
						<font color="#666666">&lt;portlet&gt;<br />&lt;description&gt;Description goes here&lt;/description&gt;<br />&lt;portlet-name&gt;first&lt;/portlet-name&gt;<br />&lt;portlet-class&gt;com.malani.examples.portlets.jsr168.FirstPortlet<br />&lt;/portlet-class&gt;<br />&lt;portlet-info&gt;<br />&lt;title&gt;First&lt;/title&gt;<br />&lt;/portlet-info&gt;<br />&lt;/portlet&gt;</font>
				</p>
				<p>
						<font color="#666666">portlet-class就是实现的类。可以和web.xml对照比较一下。</font>
				</p>
				<p>
						<font color="#666666">2.实现的Portlet类。和传统的servlet一样，portal容器来管理protlet的生命周期。它必须实现javax.portlet.Portlet接口。但是jsr168标准提供了一个方便类：GenericPortlet你可以直接<br />继承它就ok了。</font>
				</p>
				<p>
						<font color="#666666">1)生命周期，同servlet一样Portlet必须有自己生命周期。这定义在javax.portlet.Portlet接口中。它的方法是：init(),render(),processAction(),destroy().但创建portlet实例的时候调用init()方法。它完成一些需要花费昂贵的资源的动作。但实例被销毁的时候，容器调用destroy方法来释放这些资源。</font>
				</p>
				<p>
						<font color="#666666">Portlet规范清晰的区分了render请求和action请求的区别。renderRequest()会调用该portlet上的render()方法，actionRequst()会调用processAction()方法.要注意的是一次用户完整的请求将是：portal页面上的所有的protlet的 <font color="#000000">render()</font>调用，该请求protlet的<font color="#000000">processAction()调用。具体看图：</font></font>
				</p>
				<p>
						<img alt="" src="http://dev2dev.bea.com.cn/techdoc/wlintegra/images/image2004102208.gif" border="0" />
				</p>
				<p>3.Portlet模式</p>
				<p>VIEW模式是你必须实现的，EDIT，HELP模式是可选的。你可以通过修改doEdit()和doHelp()<br />方法来实现这些模式。另外还需要在portlet.xml配置模式：</p>
				<p>
						<font color="#666666">&lt;supports&gt;<br />&lt;mime-type&gt;text/html&lt;/mime-type&gt;<br />&lt;portlet-mode&gt;edit&lt;/portlet-mode&gt;<br />&lt;portlet-mode&gt;help&lt;/portlet-mode&gt;<br />&lt;/supports&gt; </font>
				</p>
				<p>
						<font color="#666666">修改本文件但没有实现该模式容器会抛出</font>
						<font color="#000000">javax.portlet.PortletException异常.</font>
				</p>
				<p>实现window状态，注意JSR 168并没有对这个进行规定，但webLogic 完成了：</p>
				<p>
						<font color="#666666">&lt;portlet&gt;<br />&lt;portlet-name&gt;state&lt;/portlet-name&gt;<br />&lt;supports&gt;<br />&lt;mime-type&gt;text/html&lt;/mime-type&gt;<br />&lt;excluded-window-state&gt;minimized&lt;/excluded-window-state&gt;<br />&lt;excluded-window-state&gt;maximized&lt;/excluded-window-state&gt;<br />&lt;/supports&gt;<br />&lt;/portlet&gt; </font>
				</p>
				<p>
						<font color="#666666">包含JSP文件：</font>
				</p>
				<p>
						<font color="#666666">上面的例子中我们在doView方法中通过方法得到Writer实例，这样可以输出HTML片断。但是，<br />我们并不推荐您这样做。我们推荐您使用JSP来达到视图－java的分离。为了包含一个特定的jsp<br />首先你必须得到PortletContext.在PortletContext中，通过调用getRequestDispatcher()方法<br />得到PorletRequestDispatcher，通过它的includer()方法来包含JSp例如；<br />// execute the necessary logic here...<br />PortletRequestDispatcher aDispatcher =<br />getPortletContext().getRequestDispatcher(<br />"/IncludePortlet/includeView.jsp"<br />);<br />aDispatcher.include(aRequest, aResponse); </font>
				</p>
				<p>
						<font color="#666666">处理动作</font>
				</p>
				<p>
						<font color="#666666">在标准的web应用程序中,提交表单，或者一个动作url常见的。JSR168规范中定义了portlet taglib <br />来帮助我们完成这些工作：</font>
				</p>
				<p>
						<font color="#666666">&lt;form action="&lt;portlet:actionURL/&gt;" method="post"&gt;<br />...<br />&lt;/form&gt; </font>
				</p>
				<p>
						<font color="#666666">提交的话会自动调用该portlet的<font color="#000000">processAction(ActionRequest aRequest, ActionResponse aResponse)方法，这就跟你传统方式一样了reqeust的getParameter()或者是getAttribute()来<br />得到传来的参数。</font></font>
				</p>
				<p>processAction()方法设置response对象中的值。不要使用ActionRequest或ActionResponse对象的setAttribute()方法。值不会从processAction()传递到render()方法，而且在JSP中是不可用的。相反要使用ActionResponse对象的setRenderParameter()方法。这些render参数将对所有后续render请求可用，这一点与典型的Web应用程序请求属性很不相同。典型的Web application请求属性只对于一个请求可用。另一方面，render请求参数对于许多后续render请求可用。render参数保持可用直到值被动作的重新执行显式地修改或删除。<br /></p>
				<p>所呈现的参数是怎样显示在JSP上的呢？应使用来自portlet标签库的defineObjects标签来定义portlet对象。该标签使renderRequest、renderResponse和portletConfig portlet对象在页面中可用。参数通过调用renderRequest对象的getParameter()方法来显示。请参考与所包含的源代码示例中的favoriteColorView.jsp。<br /><br />　　portlet FavoriteColor也展示了其他概念。第一个是如何在processAction()方法中用编程的方法改变portlet模式。调用ActionResponse对象的setPortletMode()方法来修改portlet模式。第二个概念是如何使用一个HTML链接来修改portlet模式。该链接使用来自portlet标签库的renderURL标签生成。根据希望的portlet模式指定portletMode属性的值。请参考源代码示例中的FavoriteColorPortlet类和favoriteColorView.jsp页面。</p>
		</div>
<img src ="http://www.blogjava.net/raozhh/aggbug/40636.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/raozhh/" target="_blank">饶志华</a> 2006-04-12 14:07 <a href="http://www.blogjava.net/raozhh/articles/40636.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>接触 solaris : 安装 oracle</title><link>http://www.blogjava.net/raozhh/articles/39168.html</link><dc:creator>饶志华</dc:creator><author>饶志华</author><pubDate>Tue, 04 Apr 2006 06:53:00 GMT</pubDate><guid>http://www.blogjava.net/raozhh/articles/39168.html</guid><wfw:comment>http://www.blogjava.net/raozhh/comments/39168.html</wfw:comment><comments>http://www.blogjava.net/raozhh/articles/39168.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/raozhh/comments/commentRss/39168.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/raozhh/services/trackbacks/39168.html</trackback:ping><description><![CDATA[
		<table style="TABLE-LAYOUT: fixed; WORD-BREAK: break-all" cellspacing="0" cellpadding="0" width="575" border="0">
				<tbody>
						<tr>
								<td style="FONT-SIZE: 12pt; COLOR: #ff6600" align="middle" height="32">接触 solaris : 安装 oracle</td>
						</tr>
						<tr>
								<td align="middle" bgcolor="#f7f7f7" height="25">作者：佚名  来源：本站收集  发布时间：2005-11-14 18:40:47  发布人：admin</td>
						</tr>
						<script language="javascript"><![CDATA[
var newasp_fontsize=9;
var newasp_lineheight=12;
]]&gt;</script>
						<tr>
								<td>
										<p align="right">
												<a title="减小字体" style="CURSOR: hand; POSITION: relative" onclick="if(newasp_fontsize&gt;8){NewaspContentLabel.style.fontSize=(--newasp_fontsize)+&quot;pt&quot;;NewaspContentLabel.style.lineHeight=(--newasp_lineheight)+&quot;pt&quot;;}">
														<img height="15" src="http://www.35pp.com/images/1.gif" width="15" border="0" />
														<font color="#ff6600">减小字体</font>
												</a>
												<a title="增大字体" style="CURSOR: hand; POSITION: relative" onclick="if(newasp_fontsize&lt;64){NewaspContentLabel.style.fontSize=(++newasp_fontsize)+&quot;pt&quot;;NewaspContentLabel.style.lineHeight=(++newasp_lineheight)+&quot;pt&quot;;}">
														<img height="15" src="http://www.35pp.com/images/2.gif" width="15" border="0" />
														<font color="#ff6600">增大字体</font>
												</a>
										</p>
										<div class="ContentFont" id="NewaspContentLabel" style="PADDING-RIGHT: 10px; DISPLAY: block; PADDING-LEFT: 10px; PADDING-BOTTOM: 0px; PADDING-TOP: 0px">
												<font id="font_word" style="FONT-SIZE: 14px; FONT-FAMILY: 宋体, Verdana, Arial, Helvetica, sans-serif">
														<font id="zoom">接触 solaris : 安装 oracle 
<p></p><p><br />Install oracle815 in Solaris 7 </p><p>1.建立 ORACLE 用户和 DBA 组，初始化安装目录 <br />================================================= <br />groupadd dba <br />useradd -g dba oracle <br />passwd oracle <br />mkdir /oracle <br />chown -R oracle.dba /oracle </p><p>2.为安装 ORACLE 设置系统变量 <br />================================================= <br />vi /etc/system <br />(add in the end)=&gt; <br />set shmsys:shminfo_shmmax=4294967295 <br />set shmsys:shminfo_shmmin=1 <br />set shmsys:shminfo_shmmni=100 <br />set shmsys:shminfo_shmseg=10 <br />set semsys:seminfo_semmni=100 <br />set semsys:seminfo_semmsl=100 <br />set semsys:seminfo_semmns=2000 <br />set semsys:seminfo_semopm=100 <br />set semsys:seminfo_semvmx=32767 <br />（请根据实际情况做调整） </p><p>vi /etc/services <br />(add in the end)=&gt; <br />orasrv 1525/tcp oracle <br />listener 1521/tcp <br />（请根据实际情况做调整） </p><p>vi /etc/oracle/.profile(.cshrc) <br />(add or modify)=&gt; <br />set path=( /oracle/bin /usr/sbin /usr/bin /usr/openwin/bin /oracle /etc /usr/ccs/bin . ) <br />setenv ORACLE_HOME /oracle <br />setenv ORACLE_SID oracle8 <br />#setenv DISPLAY 10.1.1.110:0.0 <br />setenv NLS_LANG AMERICAN_AMERICA.ZHS16CGB231280 <br />#setenv NLS_LANG=SIMPLIFIED CHINESE_CHINA.ZHS16GBK <br />（请根据实际情况做调整） </p><p><br />说明：加#为可选的参数， 10.1.1.110是用PC机运行EXTRAX登录服务器所配置的。 </p><p>#sync;sync;reboot Sun OS重启，使系统参数生效 </p><p>3.Install <br />================================================= <br />1 、插入ORACLE 安装光盘 <br />#/etc/init.d/volmgt stop <br />#/etc/init.d/volmgt start <br />%volcheck <br />%df -k 检查有无cdrom的设备 </p><p>2、 %cd /cdrom/cdrom0 %./runInstaller 出现 ORACLE Universal Installer的安装窗口,按Next键继续 </p><p>(远程安装则在 KEA!X Server 等远程模拟桌面环境下在命令窗口中运行 ./runInstaller 出现 ORACLE Universal Installer的安装窗口,按Next键继续) </p><p>3、Destination框输入将要安装oracle的目录这里我输入/oracle，接着出现弹出窗口要求用root运行/tmp/orainstRoot.sh (它创建oraInventory的安装目录) 接着选择要安装的产品ORACLE8I 8.1.5 </p><p>4、选typical或custom安装，注意屏幕上端Language按钮，选Chinese语言。 </p><p>5、接着出现将要安装ORACLE产品的全部信息，如果有不对的地方可以按Perious按钮返回修改。如果确认无误，按Next，ORACLE开始安装。 </p><p>(如果选择了要建数据库，会要求你输入数据库的存放地址，这里我输入/oracle/data,那么数据库的真正存放地址是 /oracle/data/oradata/dbdir) </p><p>6、安装成功100%后又回到开始的选项菜单。/oracle/orainst/install.log 安装日志文件最后提示OK </p><p>7、弹出Setup Privileges窗口,要求用root运行 #/oracle/root.sh <br />Are these setting correct (y/n):敲 y Enter the full pathname of the local bin directory (y/n): 敲/bin (此shell 程序在/var/oracle路径下生成一个文件oratab ,并做系统检测) </p><p><br />4.创建数据库时应该注意的问题 <br />================================================= <br />小心库建好后不能修改的参数： </p><p>Character sets : ZHS16GDK <br />db_block_size : 2048 </p><p>建库失败错误 01034 : </p><p>先取消建库过程。 <br />检查oracle安装目录下的 .profile(.cshrc) 设置及其他系统设置。 <br />（必要的时候 reboot 服务器）用/oracle/dbs/dbassist 重新建库 </p><p>建库失败后重建时说库已经存在： </p><p>rm -r /oracle/admin/ <br />rm -r /oracle// </p><p><br />5.建库后的微调 <br />================================================= <br />#vi /var/opt/oracle/oratab 最后一行改 app1:/oracle:Y <br />#vi /var/opt/oracle/listener.ora 改 host= (host_name) oraclehome= (/oracle) <br />例子: listener=(address_list= <br />(address=(protocol=tcp) <br />(port=1521) <br />(host=joe))) <br />sid_list_listener=(sid_list= <br />(sid_desc=(sid_name=oracle8) <br />(oracle_home=/oracle))) <br />trace_level+listener=admin <br />startup_wait_time_listener=0 <br />connect_timeout_listener=5 </p><p>#vi /var/opt/oracle/tnsnames.ora 一些连接字符串，分布式通讯时有用 </p><p><br />6.安装测试 <br />================================================= <br />#su - oracle %env 看环境变量改变了没有 </p><p>%svrmgrl 唤醒SVRMGR状态 <br />SVRMGR&gt;connect internal <br />连接打开数据库 SVRMGR&gt;shutdown immediate 先关闭 instances, 数据库 <br />SVRMGR&gt;startup 再打开数据库，当看到 instances startup, database mount, database open的时候，oracle安装成功 <br />SVRMGR&gt;exit </p><p>%lsnrctl start 启动listener 如果出错检查环境变量是否设好，及lsnrctl文件的内容和执行属性。 </p><p><br />7。配置自启动/关闭 <br />================================================= <br />编写 SH 文件 <br />vi /etc/oracle </p><p>#!/bin/sh <br />OPT_=$1 </p><p>case "$OPT_" in <br />start) <br />/bin/echo "$0 : (start)" <br /># <br /># Your service startup command goes here. <br /># <br />su - oracle -c "/oracle/bin/dbstart" <br />su - oracle -c "/oracle/bin/lsnrctl start" <br /># su - root -c "/www/tomcat/bin/startup.sh" <br /># su - root -c "/www/apache/bin/apachectl start" </p><p># NOTE: Must exit with zero unless error is severe. <br />exit 0 <br />;; <br />stop) <br />/bin/echo "$0 : (stop)" <br /># <br /># Your service shutdown command goes here. <br /># <br />su - oracle -c "/oracle/bin/lsnrctl stop" <br />su - oracle -c "/oracle/bin/dbshut" <br /># su - root -c "/www/tomcat/bin/shutdown.sh" <br /># su - root -c "/www/apache/bin/apachectl stop" </p><p># NOTE: Must exit with zero unless error is severe. <br />exit 0 <br />;; <br />*) /bin/echo '' <br />/bin/echo "Usage: $0 [start|stop]" <br />/bin/echo " Invalid argument ==&gt; \"${OPT_}\"" <br />/bin/echo '' <br />exit 0 <br />;; <br />esac </p><p>设置执行次序 <br />ln -s /etc/init.d/oracle /etc/rc2.d/S99oracle <br />ln -s /etc/init.d/oracle /etc/rc0.d/K01oracle </p><p><br />8.自启动/关闭 测试 <br />================================================= <br />reboot 后 ps -ef | grep oracle</p></font>
												</font>
										</div>
										<div>
										</div>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/raozhh/aggbug/39168.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/raozhh/" target="_blank">饶志华</a> 2006-04-04 14:53 <a href="http://www.blogjava.net/raozhh/articles/39168.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>常用正则表达式</title><link>http://www.blogjava.net/raozhh/articles/38505.html</link><dc:creator>饶志华</dc:creator><author>饶志华</author><pubDate>Fri, 31 Mar 2006 08:37:00 GMT</pubDate><guid>http://www.blogjava.net/raozhh/articles/38505.html</guid><wfw:comment>http://www.blogjava.net/raozhh/comments/38505.html</wfw:comment><comments>http://www.blogjava.net/raozhh/articles/38505.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/raozhh/comments/commentRss/38505.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/raozhh/services/trackbacks/38505.html</trackback:ping><description><![CDATA[
		<h2 class="post-title">常用正则表达式</h2>
		<div class="post-body">　　正则表达式用于字符串处理、表单验证等场合，实用高效。现将一些常用的表达式收集于此，以备不时之需。<br /><br />匹配中文字符的正则表达式： [\u4e00-\u9fa5]<br />评注：匹配中文还真是个头疼的事，有了这个表达式就好办了<br /><br />匹配双字节字符(包括汉字在内)：[^\x00-\xff]<br />评注：可以用来计算字符串的长度（一个双字节字符长度计2，ASCII字符计1）<br /><br />匹配空白行的正则表达式：\n\s*\r<br />评注：可以用来删除空白行<br /><br />匹配HTML标记的正则表达式：&lt;(\S*?)[^&gt;]*&gt;.*?|&lt;.*? /&gt;<br />评注：网上流传的版本太糟糕，上面这个也仅仅能匹配部分，对于复杂的嵌套标记依旧无能为力<br /><br />匹配首尾空白字符的正则表达式：^\s*|\s*$<br />评注：可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等)，非常有用的表达式<br /><br />匹配Email地址的正则表达式：\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*<br />评注：表单验证时很实用<br /><br />匹配网址URL的正则表达式：[a-zA-z]+://[^\s]*<br />评注：网上流传的版本功能很有限，上面这个基本可以满足需求<br /><br />匹配帐号是否合法(字母开头，允许5-16字节，允许字母数字下划线)：^[a-zA-Z][a-zA-Z0-9_]{4,15}$<br />评注：表单验证时很实用<br /><br />匹配GUID：由数字，字母，下划线，中划线组成<br />/^[a-zA-Z0-9_\-]*$/<br /><br />匹配国内电话号码：\d{3}-\d{8}|\d{4}-\d{7}<br />评注：匹配形式如 0511-4405222 或 021-87888822<br /><br />匹配腾讯QQ号：[1-9][0-9]{4,}<br />评注：腾讯QQ号从10000开始<br /><br />匹配中国邮政编码：[1-9]\d{5}(?!\d)<br />评注：中国邮政编码为6位数字<br /><br />匹配身份证：\d{15}|\d{18}<br />评注：中国的身份证为15位或18位<br /><br />匹配ip地址：\d+\.\d+\.\d+\.\d+<br />评注：提取ip地址时有用<br /><br />匹配特定数字：<br />^[1-9]\d*$　 　 //匹配正整数<br />^-[1-9]\d*$ 　 //匹配负整数<br />^-?[1-9]\d*$　　 //匹配整数<br />^[1-9]\d*|0$　 //匹配非负整数（正整数 + 0）<br />^-[1-9]\d*|0$　　 //匹配非正整数（负整数 + 0）<br />^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$　　 //匹配正浮点数<br />^-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$　 //匹配负浮点数<br />^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)$　 //匹配浮点数<br />^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0$　　 //匹配非负浮点数（正浮点数 + 0）<br />^(-([1-9]\d*\.\d*|0\.\d*[1-9]\d*))|0?\.0+|0$　　//匹配非正浮点数（负浮点数 + 0）<br />评注：处理大量数据时有用，具体应用时注意修正<br /><br />匹配特定字符串：<br />^[A-Za-z]+$　　//匹配由26个英文字母组成的字符串<br />^[A-Z]+$　　//匹配由26个英文字母的大写组成的字符串<br />^[a-z]+$　　//匹配由26个英文字母的小写组成的字符串<br />^[A-Za-z0-9]+$　　//匹配由数字和26个英文字母组成的字符串<br />^\w+$　　//匹配由数字、26个英文字母或者下划线组成的字符串<br />评注：最基本也是最常用的一些表达式<br /><br />原载地址：http://lifesinger.3322.org/myblog/?p=185</div>
<img src ="http://www.blogjava.net/raozhh/aggbug/38505.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/raozhh/" target="_blank">饶志华</a> 2006-03-31 16:37 <a href="http://www.blogjava.net/raozhh/articles/38505.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>正则表达式之道</title><link>http://www.blogjava.net/raozhh/articles/38501.html</link><dc:creator>饶志华</dc:creator><author>饶志华</author><pubDate>Fri, 31 Mar 2006 08:27:00 GMT</pubDate><guid>http://www.blogjava.net/raozhh/articles/38501.html</guid><wfw:comment>http://www.blogjava.net/raozhh/comments/38501.html</wfw:comment><comments>http://www.blogjava.net/raozhh/articles/38501.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/raozhh/comments/commentRss/38501.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/raozhh/services/trackbacks/38501.html</trackback:ping><description><![CDATA[
		<center>
				<h1>正则表达式之道</h1>
		</center>
		<center>
				<p>原著：Steve Mansour <br />sman@scruznet.com <br /><font size="-1">Revised: June 5, 1999<br />(copied by jm /at/ jmason.org from http://www.scruz.net/%7esman/regexp.htm, after the original disappeared! ) </font></p>
				<p>翻译：Neo Lee<br />neo.lee@gmail.com<br /><font size="-1">2004年10月16日</font></p>
		</center>
		<hr />
		<p>
				<a href="http://sitescooper.org/tao_regexps.html">英文版原文</a>
		</p>
		<p>译者按：原文因为年代久远，文中很多链接早已过期（主要是关于vi、sed等工具的介绍和手册），本译文中已将此类链接删除，如需检查这些链接可以查看上面链接的原文。除此之外基本照原文直译，括号中有“译者按”的部分是译者补充的说明。如有内容方面的问题请直接和Steve Mansor联系，当然，如果你只写中文，也可以和我联系。</p>
		<hr />
		<h1>目 录</h1>
		<p>
				<b>
						<a href="http://net.pku.edu.cn/~yhf/tao_regexps_zh.html#WhatAreRegularExpressions">什么是正则表达式</a>
				</b>
				<br />
				<b>
						<a href="http://net.pku.edu.cn/~yhf/tao_regexps_zh.html#SimpleCommands">范例</a>
				</b>
				<br />   <a href="http://net.pku.edu.cn/~yhf/tao_regexps_zh.html#SimpleCommands">简单</a><br />   <a href="http://net.pku.edu.cn/~yhf/tao_regexps_zh.html#MediumDifficultyExamples">中级（神奇的咒语）</a><br />   <a href="http://net.pku.edu.cn/~yhf/tao_regexps_zh.html#HardExamples">困难（不可思议的象形文字）</a><br /><b><a href="http://net.pku.edu.cn/~yhf/tao_regexps_zh.html#Regular_Expressions_In_Various_Tools">不同工具中的正则表达式</a></b></p>
		<p>
		</p>
		<hr width="100%" />
		<h1>
				<a name="WhatAreRegularExpressions">
				</a>什么是正则表达式</h1>一个正则表达式，就是用某种模式去匹配一类字符串的一个公式。很多人因为它们看上去比较古怪而且复杂所以不敢去使用——很不幸，这篇文章也不能够改变这一点，不过，经过一点点练习之后我就开始觉得这些复杂的表达式其实写起来还是相当简单的，而且，一旦你弄懂它们，你就能把数小时辛苦而且易错的文本处理工作压缩在几分钟（甚至几秒钟）内完成。正则表达式被各种文本编辑软件、类库（例如Rogue Wave的tools.h++）、脚本工具（像awk/grep/sed）广泛的支持，而且像Microsoft的Visual C++这种交互式IDE也开始支持它了。 
<p>我们将在如下的章节中利用一些例子来解释正则表达式的用法，绝大部分的例子是基于<b><tt>vi</tt></b>中的文本替换命令和<b><tt>grep</tt></b>文件搜索命令来书写的，不过它们都是比较典型的例子，其中的概念可以在sed、awk、perl和其他支持正则表达式的编程语言中使用。你可以看看<a href="http://net.pku.edu.cn/~yhf/tao_regexps_zh.html#Regular_Expressions_In_Various_Tools">不同工具中的正则表达式</a>这一节，其中有一些在别的工具中使用正则表达式的例子。还有一个关于vi中文本替换命令（s）的<a href="http://net.pku.edu.cn/~yhf/tao_regexps_zh.html#ViSubstitutionCommandSyntax">简单说明</a>附在文后供参考。</p><h2>正则表达式基础</h2>正则表达式由一些普通字符和一些<i>元字符（metacharacters）</i>组成。普通字符包括大小写的字母和数字，而元字符则具有特殊的含义，我们下面会给予解释。 
<p>在最简单的情况下，一个正则表达式看上去就是一个普通的查找串。例如，正则表达式"testing"中没有包含任何元字符，，它可以匹配"testing"和"123testing"等字符串，但是不能匹配"Testing"。</p><p>要想真正的用好正则表达式，正确的理解元字符是最重要的事情。下表列出了所有的元字符和对它们的一个简短的描述。 
</p><p></p><table cellspacing="2" cellpadding="2"><tbody><tr valign="baseline"><th align="left"><b><i>元字符</i></b></th><td> </td><th align="left"><b><i>描述</i></b></th></tr><tr><td><hr width="100%" /></td><td></td><td><hr width="100%" /></td></tr><tr><td valign="top" align="middle"><center><b><tt><font face="Courier New"><font size="+1">.</font></font></tt></b></center></td><td></td><td>匹配任何单个字符。例如正则表达式<b><tt>r.t</tt></b>匹配这些字符串：<i>rat</i>、<i>rut</i>、<i>r t</i>，但是不匹配<i>root</i>。 </td></tr><tr><td valign="top"><center><b><tt><font face="Courier New"><font size="+1">$</font></font></tt></b></center></td><td></td><td>匹配行结束符。例如正则表达式<b><tt>weasel$</tt></b> 能够匹配字符串"<i>He's a weasel</i>"的末尾，但是不能匹配字符串"<i>They are a bunch of weasels.</i>"。 </td></tr><tr><td valign="top"><center><b><font size="+1">^</font></b></center></td><td></td><td>匹配一行的开始。例如正则表达式<b><tt>^When in</tt></b>能够匹配字符串"<i>When in the course of human events</i>"的开始，但是不能匹配"<i>What and When in the"。</i></td></tr><tr><td valign="top"><center><b><tt><font face="Courier New"><font size="+1">*</font></font></tt></b></center></td><td></td><td>匹配0或多个正好在它之前的那个字符。例如正则表达式<b><tt></tt></b><b><tt>.*</tt></b>意味着能够匹配任意数量的任何字符。</td></tr><tr><td valign="top"><center><b><tt><font face="Courier New"><font size="+1">\</font></font></tt></b></center></td><td></td><td>这是引用府，用来将这里列出的这些元字符当作普通的字符来进行匹配。例如正则表达式<b><tt>\$</tt></b>被用来匹配美元符号，而不是行尾，类似的，正则表达式<tt><strong>\.</strong></tt>用来匹配点字符，而不是任何字符的通配符。</td></tr><tr><td valign="top"><center><b><tt><font face="Courier New"><font size="+1">[ ] </font></font></tt></b><br /><b><tt><font face="Courier New"><font size="+1">[c</font><font size="-1">1</font><font size="+1">-c</font><font size="-1">2</font><font size="+1">]</font></font></tt></b><br /><b><tt><font face="Courier New"><font size="+1">[^c</font><font size="-1">1</font><font size="+1">-c</font><font size="-1">2</font><font size="+1">]</font></font></tt></b></center></td><td></td><td>匹配括号中的任何一个字符。例如正则表达式<b><tt>r[aou]t</tt></b>匹配<i>rat</i>、<i>rot</i>和<i>rut</i>，但是不匹配<i>ret</i>。可以在括号中使用连字符-来指定字符的区间，例如正则表达式<b><tt>[0-9]</tt></b>可以匹配任何数字字符；还可以制定多个区间，例如正则表达式<b><tt>[A-Za-z]</tt></b>可以匹配任何大小写字母。另一个重要的用法是“排除”，要想匹配<i>除了</i>指定区间之外的字符——也就是所谓的补集——在左边的括号和第一个字符之间使用^字符，例如正则表达式<b><tt>[^269A-Z]</tt></b> 将匹配除了2、6、9和所有大写字母之外的任何字符。</td></tr><tr><td valign="top"><center><b><tt><font face="Courier New"><font size="+1">\&lt; \&gt;</font></font></tt></b></center></td><td></td><td>匹配词（<em>word</em>）的开始（\&lt;）和结束（\&gt;）。例如正则表达式<b><tt><font face="Courier New">\&lt;the</font></tt></b>能够匹配字符串"<i>for the wise</i>"中的"the"，但是不能匹配字符串"<i>otherwise</i>"中的"the"。<strong>注意</strong>：这个元字符不是所有的软件都支持的。</td></tr><tr><td valign="top"><center><b><tt><font face="Courier New"><font size="+1">\( \)</font></font></tt></b></center></td><td></td><td>将 \( 和 \) 之间的表达式定义为“组”（<em>group</em>），并且将匹配这个表达式的字符保存到一个临时区域（一个正则表达式中最多可以保存9个），它们可以用 <b><tt>\1</tt></b> 到<b><tt>\9</tt></b> 的符号来引用。</td></tr><tr><td valign="baseline"><center><b><tt><font face="Courier New"><font size="+1">|</font></font></tt></b></center></td><td></td><td>将两个匹配条件进行逻辑“或”（<em>Or</em>）运算。例如正则表达式<b><tt><font face="Courier New">(him|her)</font></tt></b> 匹配"<i>it belongs to him</i>"和"<i>it belongs to her</i>"，但是不能匹配"<i>it belongs to them.</i>"。<strong>注意</strong>：这个元字符不是所有的软件都支持的。</td></tr><tr valign="baseline"><td><center><b><tt><font face="Courier New"><font size="+1">+</font></font></tt></b></center></td><td></td><td>匹配1或多个正好在它之前的那个字符。例如正则表达式<b><tt></tt></b><b><tt></tt></b><b><tt>9+</tt></b>匹配9、99、999等。<strong>注意</strong>：这个元字符不是所有的软件都支持的。</td></tr><tr valign="baseline"><td><center><b><tt><font size="+1">?</font></tt></b></center></td><td></td><td>匹配0或1个正好在它之前的那个字符。<strong>注意</strong>：这个元字符不是所有的软件都支持的。</td></tr><tr valign="baseline"><td><center><b><font size="+1"><tt><font face="Courier New">\{</font></tt><i>i</i><tt><font face="Courier New">\}</font></tt></font></b><br /><b><font size="+1"><tt><font face="Courier New">\{</font></tt><i>i</i><tt><font face="Courier New">,</font></tt><i>j</i><tt><font face="Courier New">\}</font></tt></font></b></center></td><td></td><td valign="baseline">匹配指定数目的字符，这些字符是在它之前的表达式定义的。例如正则表达式<b><tt><font face="Courier New">A[0-9]\{3\}</font></tt></b> 能够匹配字符"A"后面跟着正好3个数字字符的串，例如A123、A348等，但是不匹配A1234。而正则表达式<b><tt><font face="Courier New">[0-9]\{4,6\}</font></tt></b> 匹配连续的任意4个、5个或者6个数字字符。<strong>注意</strong>：这个元字符不是所有的软件都支持的。</td></tr></tbody></table><p></p><hr width="100%" /><p>最简单的元字符是点，它能够匹配任何单个字符（注意<strong>不</strong>包括新行符）。假定有个文件test.txt包含以下几行内容：</p><ul><tt>he is a rat</tt><br /><tt>he is in a rut</tt><br /><tt>the food is Rotten</tt><br /><tt>I like root beer</tt></ul>我们可以使用grep命令来测试我们的正则表达式，grep命令使用正则表达式去尝试匹配指定文件的每一行，并将至少有一处匹配表达式的所有行显示出来。命令 
<ul><tt>grep r.t test.txt</tt></ul>在test.txt文件中的每一行中搜索正则表达式<b><tt>r.t</tt></b>，并打印输出匹配的行。正则表达式<b><tt>r.t</tt></b>匹配一个<b><tt>r</tt></b>接着任何一个字符再接着一个<b><tt>t</tt></b>。所以它将匹配文件中的<b><tt>rat</tt></b>和<b><tt>rut</tt></b>，而不能匹配<b><tt>Rotten</tt></b>中的<b><tt>Rot</tt></b>，因为正则表达式是大小写敏感的。要想同时匹配大写和小写字母，应该使用字符区间元字符（方括号）。正则表达式<b><tt>[Rr]</tt></b>能够同时匹配<b><tt>R</tt></b>和<b><tt>r</tt></b>。所以，要想匹配一个大写或者小写的<b><tt>r</tt></b>接着任何一个字符再接着一个<b><tt>t</tt></b>就要使用这个表达式：<b><tt>[Rr].t</tt></b>。 
<p>要想匹配行首的字符要使用抑扬字符（<em>^</em>）——又是也被叫做插入符。例如，想找到text.txt中行首"he"打头的行，你可能会先用简单表达式<b><tt>he</tt></b>，但是这会匹配第三行的<b><tt>the</tt></b>，所以要使用正则表达式<b><tt>^he</tt></b>，它只匹配在行首出现的<b><tt>h</tt></b>。 </p><p>有时候指定“除了×××都匹配”会比较容易达到目的，当抑扬字符（<em>^</em>）出现在方括号中是，它表示“排除”，例如要匹配<b><tt>he</tt></b> ，但是排除前面是<b><tt>t</tt></b> or <b><tt>s</tt></b>的情性（也就是<b><tt>the</tt></b>和<b><tt>s</tt></b><b><tt>he</tt></b>），可以使用：<b><tt>[^st]he</tt></b>。 </p><p>可以使用方括号来指定多个字符区间。例如正则表达式<b><tt>[A-Za-z]</tt></b>匹配任何字母，包括大写和小写的；正则表达式<b><tt>[A-Za-z][A-Za-z]*</tt></b> 匹配一个字母后面接着0或者多个字母（大写或者小写）。当然我们也可以用元字符<b><tt>+</tt></b>做到同样的事情，也就是：<b><tt>[A-Za-z]+</tt></b> ，和<b><tt>[A-Za-z][A-Za-z]*</tt></b>完全等价。但是要注意元字符<b><tt>+</tt></b> 并不是所有支持正则表达式的程序都支持的。关于这一点可以参考后面的<a href="http://net.pku.edu.cn/~yhf/tao_regexps_zh.html#Regular%20Expressions%20Syntax">正则表达式语法支持情况</a>。</p><p>要指定特定数量的匹配，要使用大括号（注意必须使用反斜杠来转义）。想匹配所有<b><tt>100</tt></b>和<b><tt>1000</tt></b>的实例而排除<b><tt>10</tt></b>和<b><tt>10000</tt></b>，可以使用：<b><tt>10\{2,3\}</tt></b>，这个正则表达式匹配数字1后面跟着2或者3个0的模式。在这个元字符的使用中一个有用的变化是忽略第二个数字，例如正则表达式<b><tt>0\{3,\}</tt></b> 将匹配至少3个连续的0。</p><h2><a name="SimpleCommands"></a>简单的例子</h2><p>这里有一些有代表性的、比较简单的例子。 
</p><p></p><table cellspacing="2" cellpadding="2"><tbody><tr><td><b><i>vi 命令</i></b></td><td><b><i>作用</i></b></td></tr><tr><td><hr width="100%" /></td><td><hr width="100%" /></td></tr><tr><td><b><tt><font face="Courier New"><font size="+1">:%s/ */ /g</font></font></tt></b></td><td>把一个或者多个空格替换为一个空格。</td></tr><tr><td><b><tt><font face="Courier New"><font size="+1">:%s/ *$//</font></font></tt></b></td><td>去掉行尾的所有空格。</td></tr><tr><td><b><tt><font face="Courier New"><font size="+1">:%s/^/ /</font></font></tt></b></td><td>在每一行头上加入一个空格。</td></tr><tr><td><b><tt><font face="Courier New"><font size="+1">:%s/^[0-9][0-9]* //</font></font></tt></b></td><td>去掉行首的所有数字字符。</td></tr><tr><td><b><tt><font face="Courier New"><font size="+1">:%s/b[aeio]g/bug/g</font></font></tt></b></td><td>将所有的<i>bag</i>、<i>beg</i>、<i>big</i>和<i>bog</i>改为<i>bug</i>。 </td></tr><tr><td><b><tt><font face="Courier New"><font size="+1">:%s/t\([aou]\)g/h\1t/g</font></font></tt></b></td><td>将所有<i>tag</i>、<i>tog</i>和<i>tug</i>分别改为<i>hat</i>、<i>hot</i>和<i>hug</i>（注意用group的用法和使用\1引用前面被匹配的字符）。</td></tr><tr><td></td><td></td></tr></tbody></table><h2><a name="MediumDifficultyExamples"></a>中级的例子（神奇的咒语）</h2><h3>例1</h3><p>将所有方法foo(<i>a,b,c</i>)的实例改为foo(<i>b,a,c</i>)。这里a、b和c可以是任何提供给方法foo()的参数。也就是说我们要实现这样的转换： 
</p><p></p><table cellspacing="4" cellpadding="0"><tbody><tr><td><b>之前</b></td><td> </td><td><b>之后</b></td></tr><tr><td><tt>foo(10,7,2)</tt></td><td></td><td><tt>foo(7,10,2)</tt></td></tr><tr><td><tt>foo(x+13,y-2,10)</tt></td><td></td><td><tt>foo(y-2,x+13,10)</tt></td></tr><tr><td><tt>foo( bar(8), x+y+z, 5)</tt></td><td></td><td><tt>foo( x+y+z, bar(8), 5)</tt></td></tr></tbody></table><p>下面这条替换命令能够实现这一魔法：</p><ul><b><tt><font face="Courier New">:%s/foo(\([^,]*\),\([^,]*\),\([^)]*\))/foo(\2,\1,\3)/g</font></tt></b></ul><p>现在让我们把它打散来加以分析。写出这个表达式的基本思路是找出foo()和它的括号中的三个参数的位置。第一个参数是用这个表达式来识别的：：<b><tt><font face="Courier New">\([^,]*\)</font></tt></b>，我们可以从里向外来分析它：  
</p><p></p><table><tbody><tr><td><b><tt><font face="Courier New">[^,]</font></tt></b></td><td> </td><td>除了逗号之外的任何字符</td></tr><tr><td><b><tt><font face="Courier New">[^,]*</font></tt></b></td><td></td><td>0或者多个非逗号字符</td></tr><tr><td><b><tt><font face="Courier New">\([^,]*\)</font></tt></b></td><td></td><td>将这些非逗号字符标记为<b><tt>\1</tt></b>，这样可以在之后的替换模式表达式中引用它</td></tr><tr valign="baseline"><td><b><tt><font face="Courier New">\([^,]*\),</font></tt></b></td><td></td><td>我们必须找到0或者多个非逗号字符后面跟着一个逗号，并且非逗号字符那部分要标记出来以备后用。</td></tr></tbody></table><p>现在正是指出一个使用正则表达式常见错误的最佳时机。为什么我们要使用<b><tt><font face="Courier New">[^,]*</font></tt></b>这样的一个表达式，而不是更加简单直接的写法，例如：<b><tt><font face="Courier New">.*</font></tt></b>，来匹配第一个参数呢？设想我们使用模式<b><tt><font face="Courier New">.*</font></tt></b>来匹配字符串"10,7,2"，它应该匹配"10,"还是"10,7,"？为了解决这个两义性（ambiguity），正则表达式规定一律按照最长的串来，在上面的例子中就是"10,7,"，显然这样就找出了两个参数而不是我们期望的一个。所以，我们要使用<b><tt><font face="Courier New">[^,]*</font></tt></b>来强制取出第一个逗号之前的部分。</p><p>这个表达式我们已经分析到了：<b><tt><font face="Courier New">foo(\([^,]*\)</font></tt></b>，这一段可以简单的翻译为“当你找到<b><tt>foo(</tt></b>就把其后直到第一个逗号之前的部分标记为<b><tt><font face="Courier New">\1</font></tt></b>”。然后我们使用同样的办法标记第二个参数为<b><tt><font face="Courier New">\2</font></tt></b>。对第三个参数的标记方法也是一样，只是我们要搜索所有的字符直到右括号。我们并没有必要去搜索第三个参数，因为我们不需要调整它的位置，但是这样的模式能够保证我们只去替换那些有三个参数的foo()方法调用，在foo()是一个重载（overoading）方法时这种明确的模式往往是比较保险的。然后，在替换部分，我们找到foo()的对应实例，然后利用标记好的部分进行替换，是的第一和第二个参数交换位置。</p><h3>例2</h3>假设有一个CSV（comma separated value）文件，里面有一些我们需要的信息，但是格式却有问题，目前数据的列顺序是：姓名，公司名，州名缩写，邮政编码，现在我们希望讲这些数据重新组织，以便在我们的某个软件中使用，需要的格式为：姓名，州名缩写-邮政编码，公司名。也就是说，我们要调整列顺序，还要合并两个列来构成一个新列。另外，我们的软件不能接受逗号前后面有任何空格（包括空格和制表符）所以我们还必须要去掉逗号前后的所有空格。 
<p>这里有几行我们现在的数据：</p><ul><tt>Bill Jones,     HI-TEK Corporation ,  CA, 95011</tt><br /><tt><font face="Courier New">Sharon Lee Smith,  Design Works Incorporated,  CA, 95012</font></tt><br /><tt><font face="Courier New">B. Amos   ,  Hill Street Cafe,  CA, 95013</font></tt><br /><tt><font face="Courier New">Alexander Weatherworth,  The Crafts Store,  CA, 95014</font></tt><br /><tt><font face="Courier New">...</font></tt></ul>我们希望把它变成这个样子： 
<ul><tt>Bill Jones,CA 95011,HI-TEK Corporation</tt><br /><tt><font face="Courier New">Sharon Lee Smith,CA 95012,Design Works Incorporated</font></tt><br /><tt><font face="Courier New">B. Amos,CA 95013,Hill Street Cafe</font></tt><br /><tt><font face="Courier New">Alexander Weatherworth,CA 95014,The Crafts Store</font></tt><br /><tt><font face="Courier New">...</font></tt></ul>我们将用两个正则表达式来解决这个问题。第一个移动列和合并列，第二个用来去掉空格。 
<p>下面就是第一个替换命令：</p><ul><b><tt><font face="Courier New">:%s/\([^,]*\),\([^,]*\),\([^,]*\),\(.*\)/\1,\3 \4,\2/</font></tt></b></ul>这里的方法跟例1基本一样，第一个列（姓名）用这个表达式来匹配：<b><tt><font face="Courier New">\([^,]*\)</font></tt></b>，即第一个逗号之前的所有字符，而姓名内容被用<b><tt><font face="Courier New">\1</font></tt></b>标记下来。公司名和州名缩写字段用同样的方法标记为<b><tt><font face="Courier New">\2</font></tt></b>和<b><tt><font face="Courier New">\3</font></tt></b>，而最后一个字段用<b><tt><font face="Courier New">\(.*\)</font></tt></b>来匹配（"匹配所有字符直到行末"）。替换部分则引用上面标记的那些内容来进行构造。 
<p>下面这个替换命令则用来去除空格：</p><ul><b><tt><font face="Courier New">:%s/[ \t]*,[ \t]*/,/g</font></tt></b></ul>我们还是分解来看：<b><tt><font face="Courier New">[ \t]</font></tt></b>匹配空格/制表符，<b><tt><font face="Courier New">[ \t]*</font></tt></b> 匹配0或多个空格/制表符，<b><tt>[ \t]*</tt></b>,匹配0或多个空格/制表符后面再加一个逗号，最后，<b><tt><font face="Courier New">[ \t]*,[ \t]*</font></tt></b>匹配0或多个空格/制表符接着一个逗号再接着0或多个空格/制表符。在替换部分，我们简单的我们找到的所有东西替换成一个逗号。这里我们使用了结尾的可选的<b><tt>g</tt></b>参数，这表示在每行中对所有匹配的串执行替换（而不是缺省的只替换第一个匹配串）。 
<h3>例3</h3>假设有一个多字符的片断重复出现，例如： 
<blockquote><tt>Billy tried really hard</tt><br /><tt>Sally tried really really hard</tt><br /><tt>Timmy tried really really really hard</tt><br /><tt>Johnny tried really really really really hard</tt></blockquote>而你想把"really"、"really really"，以及任意数量连续出现的"really"字符串换成一个简单的"very"（simple is good!），那么以下命令： 
<blockquote><b><tt>:%s/\(really \)\(really \)*/very /</tt></b></blockquote>就会把上述的文本变成： 
<blockquote><tt>Billy tried very hard</tt><br /><tt>Sally tried very hard</tt><br /><tt>Timmy tried very hard</tt><br /><tt>Johnny tried very hard</tt></blockquote>表达式<b><tt>\(really \)*</tt></b>匹配0或多个连续的"really "（注意结尾有个空格），而<b><tt>\(really \)\(really \)*</tt></b> 匹配1个或多个连续的"really "实例。 
<h2><a name="HardExamples"></a>困难的例子（不可思议的象形文字）</h2><i>Coming soon</i>. 
<p></p><hr /><h1><a name="Regular_Expressions_In_Various_Tools"></a>不同工具中的正则表达式</h1>OK，你已经准备使用RE（regular expressions，正则表达式），但是你并准备使用vi。所以，在这里我们给出一些在其他工具中使用RE的例子。另外，我还会总结一下你在不同程序之间使用RE可能发现的区别。 
<p>当然，你也可以在Visual C++编辑器中使用RE。选择Edit-&gt;Replace，然后选择"Regular expression"选择框，Find What输入框对应上面介绍的vi命令<b><tt>:%s/pat1/pat2/g</tt></b>中的pat1部分，而Replace输入框对应pat2部分。但是，为了得到vi的执行范围和<b><tt>g</tt></b>选项，你要使用Replace All或者适当的手工Find Next and Replace（译者按：知道为啥有人骂微软弱智了吧，虽然VC中可以选中一个范围的文本，然后在其中执行替换，但是总之不够vi那么灵活和典雅）。</p><h2>sed</h2><p>Sed是<b><u>S</u></b>tream <b><u>ED</u></b>itor的缩写，是Unix下常用的基于文件和管道的编辑工具，可以在手册中得到关于sed的详细信息。 </p><p>这里是一些有趣的sed脚本，假定我们正在处理一个叫做price.txt的文件。注意这些编辑并不会改变源文件，sed只是处理源文件的每一行并把结果显示在标准输出中（当然很容易使用重定向来定制）： 
</p><p></p><table><tbody><tr><td><b><i>sed脚本</i></b></td><td> </td><td><b><i>描述</i></b></td></tr><tr><td><hr width="100%" /></td><td></td><td><hr width="100%" /></td></tr><tr valign="baseline"><td><b><tt>sed 's/^$/d' price.txt</tt></b></td><td></td><td>删除所有空行</td></tr><tr><td><b><tt>sed 's/^[ \t]*$/d' price.txt</tt></b></td><td></td><td>删除所有只包含空格或者制表符的行</td></tr><tr><td><b><tt>sed 's/"//g' price.txt</tt></b></td><td></td><td>删除所有引号</td></tr></tbody></table><h2>awk</h2>awk是一种编程语言，可以用来对文本数据进行复杂的分析和处理。可以在手册中得到关于awk的详细信息。这个古怪的名字是它作者们的姓的缩写（Aho，Weinberger和Kernighan）。 
<p>在Aho，Weinberger和Kernighan的书<u>The AWK Programming Language</u>中有很多很好的awk的例子，请不要让下面这些微不足道的脚本例子限制你对awk强大能力的理解。我们同样假定我们针对price.txt文件进行处理，跟sed一样，awk也只是把结果显示在终端上。  
</p><p></p><table><tbody><tr><td><b><i>awk脚本</i></b></td><td> </td><td><b><i>描述</i></b></td></tr><tr><td><hr width="100%" /></td><td></td><td><hr width="100%" /></td></tr><tr valign="baseline"><td><b><tt>awk '$0 !~ /^$/' price.txt</tt></b></td><td></td><td>删除所有空行</td></tr><tr><td><b><tt>awk 'NF &gt; 0' price.txt</tt></b></td><td></td><td>awk中一个更好的删除所有行的办法</td></tr><tr valign="baseline"><td><b><tt>awk '$2 ~ /^[JT]/ {print $3}' price.txt</tt></b></td><td></td><td>打印所有第二个字段是'J'或者'T'打头的行中的第三个字段</td></tr><tr valign="baseline"><td nowrap=""><b><tt>awk '$2 !~ /[Mm]isc/ {print $3 + $4}' price.txt</tt></b></td><td></td><td>针对所有第二个字段不包含'Misc'或者'misc'的行，打印第3和第4列的和（假定为数字）</td></tr><tr valign="baseline"><td><b><tt>awk '$3 !~ /^[0-9]+\.[0-9]*$/ {print $0}' price.txt</tt></b></td><td></td><td>打印所有第三个字段不是数字的行，这里数字是指<tt>d.d</tt>或者<tt>d这样的形式，其中</tt><tt>d</tt>是0到9的任何数字</td></tr><tr valign="baseline"><td><b><tt>awk '$2 ~ /John|Fred/ {print $0}' price.txt</tt></b></td><td></td><td>如果第二个字段包含'John'或者'Fred'则打印整行</td></tr></tbody></table><h2>grep</h2>grep是一个用来在一个或者多个文件或者输入流中使用RE进行查找的程序。它的name编程语言可以用来针对文件和管道进行处理。可以在手册中得到关于grep的完整信息。这个同样古怪的名字来源于vi的一个命令，<b><tt>g/</tt></b><i>re</i><b><tt>/p</tt></b>，意思是<b>g</b>lobal <b>r</b>egular <b>e</b>xpression <b>p</b>rint。 
<p>下面的例子中我们假定在文件phone.txt中包含以下的文本，——其格式是姓加一个逗号，然后是名，然后是一个制表符，然后是电话号码：</p><ul><p><tt>Francis, John           5-3871</tt><br /><tt>Wong, Fred              4-4123</tt><br /><tt>Jones, Thomas           1-4122</tt><br /><tt>Salazar, Richard        5-2522</tt></p></ul><p></p><table><tbody><tr><td><b><i>grep命令</i></b></td><td><b><i> </i></b></td><td><b><i>描述</i></b></td></tr><tr><td><hr width="100%" /></td><td></td><td><hr width="100%" /></td></tr><tr valign="baseline"><td><b><tt>grep '\t5-...1' phone.txt</tt></b></td><td></td><td>把所有电话号码以5开头以1结束的行打印出来，注意制表符是用<b><tt>\t</tt></b>表示的</td></tr><tr valign="baseline"><td nowrap=""><b><tt>grep '^S[^ ]* R' phone.txt</tt></b></td><td></td><td>打印所有姓以S打头和名以R打头的行</td></tr><tr valign="baseline"><td><b><tt>grep '^[JW]' phone.txt</tt></b></td><td></td><td>打印所有姓开头是J或者W的行</td></tr><tr valign="baseline"><td><b><tt>grep ', ....\t' phone.txt</tt></b></td><td></td><td>打印所有姓是4个字符的行，注意制表符是用<b><tt>\t</tt></b>表示的</td></tr><tr valign="baseline"><td><b><tt>grep -v '^[JW]' phone.txt</tt></b></td><td></td><td>打印所有不以J或者W开头的行</td></tr><tr valign="baseline"><td><b><tt>grep '^[M-Z]' phone.txt</tt></b></td><td></td><td>打印所有姓的开头是M到Z之间任一字符的行</td></tr><tr valign="baseline"><td><b><tt>grep '^[M-Z].*[12]' phone.txt</tt></b></td><td></td><td>打印所有姓的开头是M到Z之间任一字符，并且点号号码结尾是1或者2的行</td></tr></tbody></table><h2>egrep</h2>egrep是grep的一个扩展版本，它在它的正则表达式中支持更多的元字符。下面的例子中我们假定在文件phone.txt中包含以下的文本，——其格式是姓加一个逗号，然后是名，然后是一个制表符，然后是电话号码： 
<ul><tt>Francis, John           5-3871</tt><br /><tt>Wong, Fred              4-4123</tt><br /><tt>Jones, Thomas           1-4122</tt><br /><tt>Salazar, Richard        5-2522</tt></ul><p></p><table><tbody><tr><td><b><i>egrep command</i></b></td><td><b><i> </i></b></td><td><b><i>Description</i></b></td></tr><tr><td><hr width="100%" /></td><td></td><td><hr width="100%" /></td></tr><tr valign="baseline"><td><b><tt>egrep '(John|Fred)' phone.txt</tt></b></td><td></td><td>打印所有包含名字<i>John</i>或者<i>Fred</i>的行</td></tr><tr valign="baseline"><td nowrap=""><b><tt>egrep 'John|22$|^W' phone.txt</tt></b></td><td></td><td>打印所有包含<i>John</i> 或者以22结束或者以<i>W</i>的行</td></tr><tr><td><b><tt>egrep 'net(work)?s' report.txt</tt></b></td><td></td><td>从report.txt中找到所有包含<i>networks</i>或者<i>nets</i>的行</td></tr></tbody></table><h2><hr width="100%" /></h2><h1><a name="Regular Expressions Syntax"></a>正则表达式语法支持情况</h1><table cellspacing="0" border="1"><tbody><tr><td><b>命令或环境</b></td><td><b><tt><font face="Courier New">.</font></tt></b></td><td><b><tt><font face="Courier New">[ ]</font></tt></b></td><td><b><tt><font face="Courier New">^</font></tt></b></td><td><b><tt><font face="Courier New">$</font></tt></b></td><td><b><tt><font face="Courier New">\( \)</font></tt></b></td><td><b><tt><font face="Courier New">\{ \}</font></tt></b></td><td><b><tt><font face="Courier New">?</font></tt></b></td><td><b><tt><font face="Courier New">+</font></tt></b></td><td><b><tt><font face="Courier New">|</font></tt></b></td><td><b><tt><font face="Courier New">( )</font></tt></b></td></tr><tr><td>vi</td><td> X </td><td> X </td><td> X </td><td> X </td><td> X </td><td> </td><td> </td><td> </td><td> </td><td> </td></tr><tr><td>Visual C++</td><td> X </td><td> X </td><td> X </td><td> X </td><td> X </td><td> </td><td> </td><td> </td><td> </td><td> </td></tr><tr><td>awk</td><td> X </td><td> X </td><td> X </td><td> X </td><td> </td><td> </td><td> X </td><td> X </td><td> X </td><td> X </td></tr><tr><td>sed</td><td> X </td><td> X </td><td> X </td><td> X </td><td> X </td><td> X </td><td> </td><td> </td><td> </td><td> </td></tr><tr><td>Tcl</td><td> X </td><td> X </td><td> X </td><td> X </td><td> X </td><td> </td><td> X </td><td> X </td><td> X </td><td> X </td></tr><tr><td>ex</td><td> X </td><td> X </td><td> X </td><td> X </td><td> X </td><td> X </td><td> </td><td> </td><td> </td><td> </td></tr><tr><td>grep</td><td> X </td><td> X </td><td> X </td><td> X </td><td> X </td><td> X </td><td> </td><td> </td><td> </td><td> </td></tr><tr><td>egrep</td><td> X </td><td> X</td><td> X </td><td> X </td><td> X </td><td> </td><td> X </td><td> X </td><td> X </td><td> X </td></tr><tr><td>fgrep</td><td> X </td><td> X </td><td> X </td><td> X </td><td> X </td><td> </td><td> </td><td> </td><td> </td><td> </td></tr><tr><td>perl</td><td> X</td><td> X</td><td> X</td><td> X</td><td> X</td><td> </td><td> X</td><td> X</td><td> X</td><td> X</td></tr></tbody></table><p> </p><hr /><h1><a name="ViSubstitutionCommandSyntax"></a>vi替换命令简介</h1>Vi的替换命令： 
<ul><b><tt>:</tt></b><i>range</i><b><tt>s/</tt></b><i>pat1</i><b><tt>/</tt></b><i>pat2</i><b><tt>/g</tt></b></ul>其中 
<ul><b><tt>:</tt></b> 这是Vi的命令执行界面。 </ul><ul><i>range </i>是命令执行范围的指定，可以使用百分号（%）表示所有行，使用点（.）表示当前行，使用美元符号（$）表示最后一行。你还可以使用行号，例如<b><tt>10,20</tt></b>表示第10到20行，<b><tt>.,$</tt></b>表示当前行到最后一行，<b><tt>.+2,$-5</tt></b>表示当前行后两行直到全文的倒数第五行，等等。 
<p><b><tt>s</tt></b> 表示其后是一个替换命令。</p><p><i>pat1 </i>这是要查找的一个正则表达式，这篇文章中有一大堆例子。</p></ul><ul><i>pat2 </i>这是希望把匹配串变成的模式的正则表达式，这篇文章中有一大堆例子。 
<p><b><tt>g</tt></b> 可选标志，带这个标志表示替换将针对行中每个匹配的串进行，否则则只替换行中第一个匹配串。</p></ul>网上有很多vi的在线手册，你可以访问他们以获得更加完整的信息。 <img src ="http://www.blogjava.net/raozhh/aggbug/38501.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/raozhh/" target="_blank">饶志华</a> 2006-03-31 16:27 <a href="http://www.blogjava.net/raozhh/articles/38501.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>正则表达式详细解</title><link>http://www.blogjava.net/raozhh/articles/38500.html</link><dc:creator>饶志华</dc:creator><author>饶志华</author><pubDate>Fri, 31 Mar 2006 08:26:00 GMT</pubDate><guid>http://www.blogjava.net/raozhh/articles/38500.html</guid><wfw:comment>http://www.blogjava.net/raozhh/comments/38500.html</wfw:comment><comments>http://www.blogjava.net/raozhh/articles/38500.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/raozhh/comments/commentRss/38500.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/raozhh/services/trackbacks/38500.html</trackback:ping><description><![CDATA[
		<center>
				<h1>正则表达式详细解</h1>
		</center>
		<table cellspacing="0" cellpadding="1" width="90%" align="center" border="0">
				<tbody>
						<tr>
								<td align="middle">
										<hr size="1" />
								</td>
						</tr>
						<tr>
								<td> </td>
						</tr>
						<tr>
								<td> </td>
						</tr>
						<tr>
								<td align="middle">
										<!-- Ads by Google -->
										<script type="text/javascript">
												<!--
google_ad_client = "pub-1374401628164525";
google_alternate_ad_url = "http://www.phpe.net/google_adsense_script.html";
google_ad_width = 468;
google_ad_height = 60;
google_ad_format = "468x60_as";
google_ad_channel ="";
google_ad_type = "text";
google_color_border = "FFFFFF";
google_color_bg = "FFFFFF";
google_color_link = "000000";
google_color_url = "666666";
google_color_text = "333333";
//-->
										</script>
										<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript">
										</script>
										<iframe name="google_ads_frame" marginwidth="0" marginheight="0" src="http://pagead2.googlesyndication.com/pagead/ads?client=ca-pub-1374401628164525&amp;dt=1143793859312&amp;lmt=1143702879&amp;alternate_ad_url=http%3A%2F%2Fwww.phpe.net%2Fgoogle_adsense_script.html&amp;format=468x60_as&amp;output=html&amp;url=http%3A%2F%2Fwww.phpe.net%2Farticles%2F151.shtml&amp;color_bg=FFFFFF&amp;color_text=333333&amp;color_link=000000&amp;color_url=666666&amp;color_border=FFFFFF&amp;ad_type=text&amp;ref=http%3A%2F%2Fwww.google.com%2Fsearch%3Fhl%3Dzh-CN%26q%3D%25E6%25AD%25A3%25E5%2588%2599%25E8%25A1%25A8%25E8%25BE%25BE%25E5%25BC%258F%2B%25E6%2595%25B0%25E5%25AD%2597%25E5%2592%258C%25E5%25AD%2597%25E6%25AF%258D%26lr%3D&amp;cc=449&amp;u_h=768&amp;u_w=1024&amp;u_ah=738&amp;u_aw=1024&amp;u_cd=32&amp;u_tz=480&amp;u_java=true" frameborder="0" width="468" scrolling="no" height="60" allowtransparency="">
										</iframe>
										<br />
										<br />
								</td>
						</tr>
						<tr>
								<td style="FONT-SIZE: 14px; LINE-HEIGHT: 160%; WORD-WRAP: break-word">字符/<br />意义：对于字符，通常表示按字面意义，指出接着的字符为特殊字符，不作解释。<br />例如：/b/匹配字符’b’,通过在b 前面加一个反斜杠，也就是/b/，则该字符变成特殊字符，表示<br />匹配一个单词的分界线。<br />或者：<br />对于几个字符，通常说明是特殊的，指出紧接着的字符不是特殊的，而应该按字面解释。<br />例如：*是一个特殊字符，匹配任意个字符(包括0个字符)；例如：/a*/意味匹配0个或多个a。为了匹配字面上的*，在a前面加一个反斜杠；例如：/a*/匹配’a*’。<br /><br />字符^<br />意义：表示匹配的字符必须在最前边。<br />例如：/^A/不匹配"an A,"中的’A’，但匹配"An A."中最前面的’A’。<br /><br />字符$<br />意义：与^类似，匹配最末的字符。<br />例如：/t$/不匹配"eater"中的’t’，但匹配"eat"中的’t’。<br /><br />字符*<br />意义：匹配*前面的字符0次或n次。<br />例如:/bo*/匹配"A ghost booooed"中的’boooo’或"A bird warbled"中的’b’，但不匹配"Agoat g<br />runted"中的任何字符。<br /><br />字符+<br />意义：匹配+号前面的字符1次或n次。等价于{1,}。<br />例如：/a+/匹配"candy"中的’a’和"caaaaaaandy."中的所有’a’。<br /><br />字符?<br />意义：匹配?前面的字符0次或1次。<br />例如：/e?le?/匹配"angel"中的’el’和"angle."中的’le’。<br /><br />字符.<br />意义：(小数点)匹配除换行符外的所有单个的字符。<br />例如：/.n/匹配"nay, an apple is on the tree"中的’an’和’on’，但不匹配’nay’。<br /><br /><br />字符(x)<br />意义：匹配’x’并记录匹配的值。<br />例如：/(foo)/匹配和记录"foo bar."中的’foo’。匹配子串能被结果数组中的素[1], ...,[n] 返<br />回，或被RegExp对象的属性, ..., 返回。<br /><br />字符x│y<br />意义：匹配’x’或者’y’。<br />例如：/green│red/匹配"green apple"中的’green’和"red apple."中的’red’。<br /><br />字符{ n }<br />意义：这里的n是一个正整数。匹配前面的n个字符。<br />例如：/a{ 2 }/不匹配"candy,"中的’a’，但匹配"caandy," 中的所有’a’和"caaandy."中前面的两个’a’。<br /><br />字符{ n, }<br />意义：这里的n是一个正整数。匹配至少n个前面的字符。<br />例如：/a{ 2, }不匹配"candy"中的’a’，但匹配"caandy"中的所有’a’和"caaaaaaandy."中的所有’a’<br /><br />字符{ n,m }<br />意义：这里的n和m都是正整数。匹配至少n个最多m个前面的字符。<br />例如：/a{ 1,3 }/不匹配"cndy"中的任何字符，但匹配 "candy,"中的’a’，"caandy," 中的前面两个<br />’a’和"caaaaaaandy"中前面的三个’a’，注意：即使"caaaaaaandy" 中有很多个’a’，但只匹配前面的三 个’a’即"aaa"。<br /><br />字符[xyz]<br />意义：一字符列表，匹配列出中的任一字符。你可以通过连字符-指出一个字符范围。<br />例如：[abcd]跟[a-c]一样。它们匹配"brisket"中的’b’和"ache"中的’c’。<br /><br />字符[^xyz]<br />意义：一字符补集，也就是说，它匹配除了列出的字符外的所有东西。 你可以使用连字符-指出一 字符范围。<br />例如：[^abc]和[^a-c]等价，它们最早匹配"brisket"中的’r’和"chop."中的’h’。<br /><br />字符<br />意义：匹配一个空格(不要与b混淆)<br /><br />字符b<br />意义：匹配一个单词的分界线，比如一个空格(不要与混淆)<br />例如：/bnw/匹配"noonday"中的’no’，/wyb/匹配"possibly yesterday."中的’ly’。<br /><br />字符B<br />意义：匹配一个单词的非分界线<br />例如：/wBn/匹配"noonday"中的’on’，/yBw/匹配"possibly yesterday."中的’ye’。<br /><br />字符cX<br />意义：这里的X是一个控制字符。匹配一个字符串的控制字符。<br />例如：/cM/匹配一个字符串中的control-M。<br /><br />字符d<br />意义：匹配一个数字，等价于[0-9]。<br />例如：/d/或/[0-9]/匹配"B2 is the suite number."中的’2’。<br /><br />字符D<br />意义：匹配任何的非数字，等价于[^0-9]。<br />例如：/D/或/[^0-9]/匹配"B2 is the suite number."中的’B’。<br /><br />字符f<br />意义：匹配一个表单符<br /><br />字符n<br />意义：匹配一个换行符<br /><br />字符r<br />意义：匹配一个回车符<br /><br />字符s<br />意义：匹配一个单个white空格符，包括空格，tab，form feed，换行符，等价于[ fnrtv]。<br />例如：/sw*/匹配"foo bar."中的’ bar’。<br /><br />字符S<br />意义：匹配除white空格符以外的一个单个的字符，等价于[^ fnrtv]。<br />例如：/S/w*匹配"foo bar."中的’foo’。<br /><br />字符t<br />意义：匹配一个制表符<br /><br />字符v<br />意义：匹配一个顶头制表符<br /><br />字符w<br />意义：匹配所有的数字和字母以及下划线，等价于[A-Za-z0-9_]。<br />例如：/w/匹配"apple,"中的’a’，".28,"中的’5’和"3D."中的’3’。<br /><br />字符W<br />意义：匹配除数字、字母外及下划线外的其它字符，等价于[^A-Za-z0-9_]。<br />例如：/W/或者/[^$A-Za-z0-9_]/匹配"50%."中的’%’。<br /><br />字符n<br />意义：这里的n是一个正整数。匹配一个正则表达式的最后一个子串的n的值(计数左圆括号)。<br /><br />例如：/apple(,)sorange1/匹配"apple, orange, cherry, peach."中的’apple, orange’，下面有一个更加完整的例子。<br />注意：如果左圆括号中的数字比n指定的数字还小，则n取下一行的八进制escape作为描述。<br /><br />字符ooctal和xhex<br />意义：这里的ooctal是一个八进制的escape值，而xhex是一个十六进制的escape值，允许在一个正则表达式中嵌入ASCII码<br /><br /><b><span style="COLOR: red">附:</span>下表是元字符及其在正则表达式上下文中的行为的一个完整列表：</b><br /><br /><pre class="php"><b>字符 描述 </b><b>\</b>
将下一个字符标记为一个特殊字符、或一个原义字符、或一个后向引用、或一个八进制转义符。例如，'n' 匹配字符 "n"。'\n' 匹配一个换行符。序列 '\' 匹配 "" 而 "\(" 则匹配 "("。
<b>^</b>
匹配输入字符串的开始位置。如果设置了 RegExp 对象的 Multiline 属性，^ 也匹配 '\n' 或 '\r' 之后的位置。
<b>$</b>
匹配输入字符串的结束位置。如果设置了RegExp 对象的 Multiline 属性，$ 也匹配 '\n' 或 '\r' 之前的位置。
<b>*</b>
匹配前面的子表达式零次或多次。例如，zo* 能匹配 "z" 以及 "zoo"。 * 等价于{0,}。
+ 匹配前面的子表达式一次或多次。例如，'zo+' 能匹配 "zo" 以及 "zoo"，但不能匹配 "z"。+ 等价于 {1,}。
<b>?</b>
匹配前面的子表达式零次或一次。例如，"do(es)?" 可以匹配 "do" 或 "does" 中的"do" 。? 等价于 {0,1}。
<b>{n}</b>
n 是一个非负整数。匹配确定的 n 次。例如，'o{2}' 不能匹配 "Bob" 中的 'o'，但是能匹配 "food" 中的两个 o。
<b>{n,}</b>
n 是一个非负整数。至少匹配n 次。例如，'o{2,}' 不能匹配 "Bob" 中的 'o'，但能匹配 "foooood" 中的所有 o。'o{1,}' 等价于 'o+'。'o{0,}' 则等价于 'o*'。
<b>{n,m}</b>
m 和 n 均为非负整数，其中n &lt;= m。最少匹配 n 次且最多匹配 m 次。刘， "o{1,3}" 将匹配 "fooooood" 中的前三个 o。'o{0,1}' 等价于 'o?'。请注意在逗号和两个数之间不能有空格。
<b>?</b>
当该字符紧跟在任何一个其他限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面时，匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串，而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如，对于字符串 "oooo"，'o+?' 将匹配单个 "o"，而 'o+' 将匹配所有 'o'。
<b>. </b>
匹配除 "\n" 之外的任何单个字符。要匹配包括 '\n' 在内的任何字符，请使用象 '[.\n]' 的模式。
<b>(pattern)</b>
匹配pattern 并获取这一匹配。所获取的匹配可以从产生的 Matches 集合得到，在VBScript 中使用 SubMatches 集合，在JScript 中则使用 {CONTENT}… 属性。要匹配圆括号字符，请使用 '\(' 或 '\)'。
<b>(?:pattern)</b>
匹配 pattern 但不获取匹配结果，也就是说这是一个非获取匹配，不进行存储供以后使用。这在使用 "或" 字符 (|) 来组合一个模式的各个部分是很有用。例如， 'industr(?:y|ies) 就是一个比 'industry|industries' 更简略的表达式。
<b>(?=pattern)</b>
正向预查，在任何匹配 pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配，也就是说，该匹配不需要获取供以后使用。例如， 'Windows (?=95|98|NT|2000)' 能匹配 "Windows 2000" 中的 "Windows" ，但不能匹配 "Windows 3.1" 中的 "Windows"。预查不消耗字符，也就是说，在一个匹配发生后，在最后一次匹配之后立即开始下一次匹配的搜索，而不是从包含预查的字符之后开始。
<b>(?!pattern)</b>
负向预查，在任何不匹配Negative lookahead matches the search string at any point where a string not matching pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配，也就是说，该匹配不需要获取供以后使用。例如'Windows (?!95|98|NT|2000)' 能匹配 "Windows 3.1" 中的 "Windows"，但不能匹配 "Windows 2000" 中的 "Windows"。预查不消耗字符，也就是说，在一个匹配发生后，在最后一次匹配之后立即开始下一次匹配的搜索，而不是从包含预查的字符之后开始
<b>x|y </b>
匹配 x 或 y。例如，'z|food' 能匹配 "z" 或 "food"。'(z|f)ood' 则匹配 "zood" 或 "food"。
<b>[xyz]</b>
字符集合。匹配所包含的任意一个字符。例如， '[abc]' 可以匹配 "plain" 中的 'a'。
<b>[^xyz]</b>
负值字符集合。匹配未包含的任意字符。例如， '[^abc]' 可以匹配 "plain" 中的'p'。
<b>[a-z]</b>
字符范围。匹配指定范围内的任意字符。例如，'[a-z]' 可以匹配 'a' 到 'z' 范围内的任意小写字母字符。
<b>[^a-z]</b>
负值字符范围。匹配任何不在指定范围内的任意字符。例如，'[^a-z]' 可以匹配任何不在 'a' 到 'z' 范围内的任意字符。
<b>\b</b>
匹配一个单词边界，也就是指单词和空格间的位置。例如， 'er\b' 可以匹配"never" 中的 'er'，但不能匹配 "verb" 中的 'er'。
<b>\B</b>
匹配非单词边界。'er\B' 能匹配 "verb" 中的 'er'，但不能匹配 "never" 中的 'er'。
<b>\cx</b>
匹配由x指明的控制字符。例如， \cM 匹配一个 Control-M 或回车符。 x 的值必须为 A-Z 或 a-z 之一。否则，将 c 视为一个原义的 'c' 字符。
<b>\d</b>
匹配一个数字字符。等价于 [0-9]。
<b>\D</b>
匹配一个非数字字符。等价于 [^0-9]。
<b>\f</b>
匹配一个换页符。等价于 \x0c 和 \cL。
<b>\n</b>
匹配一个换行符。等价于 \x0a 和 \cJ。
<b>\r</b>
匹配一个回车符。等价于 \x0d 和 \cM。
<b>\s</b>
匹配任何空白字符，包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。
<b>\S</b>
匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。
<b>\t</b>
匹配一个制表符。等价于 \x09 和 \cI。
<b>\v</b>
匹配一个垂直制表符。等价于 \x0b 和 \cK。
<b>\w</b>
匹配包括下划线的任何单词字符。等价于'[A-Za-z0-9_]'。
<b>\W</b>
匹配任何非单词字符。等价于 '[^A-Za-z0-9_]'。
<b>\xn</b>
匹配 n，其中 n 为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如， '\x41' 匹配 "A"。'\x041' 则等价于 '\x04' &amp; "1"。正则表达式中可以使用 ASCII 编码。.
<b>\num</b>
匹配 num，其中 num 是一个正整数。对所获取的匹配的引用。例如，'(.)' 匹配两个连续的相同字符。
<b>\n</b>
标识一个八进制转义值或一个后向引用。如果 \n 之前至少 n 个获取的子表达式，则 n 为后向引用。否则，如果 n 为八进制数字 (0-7)，则 n 为一个八进制转义值。
<b>\nm</b>
标识一个八进制转义值或一个后向引用。如果 \nm 之前至少有is preceded by at least nm 个获取得子表达式，则 nm 为后向引用。如果 \nm 之前至少有 n 个获取，则 n 为一个后跟文字 m 的后向引用。如果前面的条件都不满足，若  n 和 m 均为八进制数字 (0-7)，则 \nm 将匹配八进制转义值 nm。
<b>\nml</b>
如果 n 为八进制数字 (0-3)，且 m 和 l 均为八进制数字 (0-7)，则匹配八进制转义值 nml。
<b>\un</b>
匹配 n，其中 n 是一个用四个十六进制数字表示的 Unicode 字符。例如，\u00A9 匹配版权符号 (?)。
</pre></td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/raozhh/aggbug/38500.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/raozhh/" target="_blank">饶志华</a> 2006-03-31 16:26 <a href="http://www.blogjava.net/raozhh/articles/38500.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SQLServer,Access,Oracle数据库用户对象信息获取</title><link>http://www.blogjava.net/raozhh/articles/37126.html</link><dc:creator>饶志华</dc:creator><author>饶志华</author><pubDate>Fri, 24 Mar 2006 00:36:00 GMT</pubDate><guid>http://www.blogjava.net/raozhh/articles/37126.html</guid><wfw:comment>http://www.blogjava.net/raozhh/comments/37126.html</wfw:comment><comments>http://www.blogjava.net/raozhh/articles/37126.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/raozhh/comments/commentRss/37126.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/raozhh/services/trackbacks/37126.html</trackback:ping><description><![CDATA[------------------------- MS SQLServer ------------------------------------------------------------ 
<p></p><p>--表说明<br />SELECT dbo.sysobjects.name AS TableName, <br />dbo.sysproperties.[value] AS TableDesc<br />FROM dbo.sysproperties INNER JOIN<br />dbo.sysobjects ON dbo.sysproperties.id = dbo.sysobjects.id<br />WHERE (dbo.sysproperties.smallid = 0)<br />ORDER BY dbo.sysobjects.name</p><p>--字段说明<br />SELECT dbo.sysobjects.name AS TableName, dbo.syscolumns.colid, <br />dbo.syscolumns.name AS ColName, dbo.sysproperties.[value] AS ColDesc<br />FROM dbo.sysproperties INNER JOIN<br />dbo.sysobjects ON dbo.sysproperties.id = dbo.sysobjects.id INNER JOIN<br />dbo.syscolumns ON dbo.sysobjects.id = dbo.syscolumns.id AND <br />dbo.sysproperties.smallid = dbo.syscolumns.colid<br />ORDER BY dbo.sysobjects.name, dbo.syscolumns.colid</p><p></p><p>--主键、外键信息(简化)<br />select<br />c_obj.name as CONSTRAINT_NAME<br />,t_obj.name as TABLE_NAME<br />,col.name as COLUMN_NAME<br />,case col.colid <br />when ref.fkey1 then 1 <br />when ref.fkey2 then 2 <br />when ref.fkey3 then 3 <br />when ref.fkey4 then 4 <br />when ref.fkey5 then 5 <br />when ref.fkey6 then 6 <br />when ref.fkey7 then 7 <br />when ref.fkey8 then 8 <br />when ref.fkey9 then 9 <br />when ref.fkey10 then 10 <br />when ref.fkey11 then 11 <br />when ref.fkey12 then 12 <br />when ref.fkey13 then 13 <br />when ref.fkey14 then 14 <br />when ref.fkey15 then 15 <br />when ref.fkey16 then 16<br />end as ORDINAL_POSITION<br />from<br />sysobjects c_obj<br />,sysobjects t_obj<br />,syscolumns col<br />,sysreferences ref<br />where<br />permissions(t_obj.id) != 0<br />and c_obj.xtype in ('F ')<br />and t_obj.id = c_obj.parent_obj<br />and t_obj.id = col.id<br />and col.colid in <br />(ref.fkey1,ref.fkey2,ref.fkey3,ref.fkey4,ref.fkey5,ref.fkey6,<br />ref.fkey7,ref.fkey8,ref.fkey9,ref.fkey10,ref.fkey11,ref.fkey12,<br />ref.fkey13,ref.fkey14,ref.fkey15,ref.fkey16)<br />and c_obj.id = ref.constid<br />union<br />select<br />i.name as CONSTRAINT_NAME<br />,t_obj.name as TABLE_NAME<br />,col.name as COLUMN_NAME<br />,v.number as ORDINAL_POSITION<br />from<br />sysobjects c_obj<br />,sysobjects t_obj<br />,syscolumns col<br />,master.dbo.spt_values v<br />,sysindexes i<br />where<br />permissions(t_obj.id) != 0<br />and c_obj.xtype in ('UQ' ,'PK')<br />and t_obj.id = c_obj.parent_obj<br />and t_obj.xtype = 'U'<br />and t_obj.id = col.id<br />and col.name = index_col(t_obj.name,i.indid,v.number)<br />and t_obj.id = i.id<br />and c_obj.name = i.name<br />and v.number &gt; 0 <br />and v.number &lt;= i.keycnt <br />and v.type = 'P'</p><p>order by CONSTRAINT_NAME, ORDINAL_POSITION</p><p><br />--主键、外键对照(简化)<br />select<br />fc_obj.name as CONSTRAINT_NAME<br />,i.name as UNIQUE_CONSTRAINT_NAME<br />from <br />sysobjects fc_obj<br />,sysreferences r<br />,sysindexes i<br />,sysobjects pc_obj<br />where <br />permissions(fc_obj.parent_obj) != 0<br />and fc_obj.xtype = 'F'<br />and r.constid = fc_obj.id<br />and r.rkeyid = i.id<br />and r.rkeyindid = i.indid<br />and r.rkeyid = pc_obj.id</p><p><br />------------------------------------------ ORACLE ----------------------------------------------------</p><p>--表信息<br />select * from all_tab_comments t<br />where owner='DBO'</p><p>--列信息<br />select * from all_col_comments t<br />where owner='DBO'</p><p>--主键、外键对照<br />select OWNER, CONSTRAINT_NAME, CONSTRAINT_TYPE, TABLE_NAME, R_OWNER, R_CONSTRAINT_NAME<br />from all_constraints<br />where owner='DBO' and (Constraint_Type='P' or Constraint_Type='R')</p><p><br />--主键、外键信息<br />select * <br />from all_cons_columns <br />where owner='DBO'<br />order by Constraint_Name, Position</p><p><br />-------------------------------------------- Access ----------------------------------------------------<br />//Access中的系统表MSysobjects存储属性的字段是二进制格式，不能直接分析<br />//可以采用ADO自带的OpenSchema方法获得相关信息</p><p>//use ADOInt.pas<br />//po: TableName<br />//DBCon:TADOConnection<br />/ds:TADODataSet<br /><br />--表信息<br />DBCon.OpenSchema(siTables, VarArrayOf([Null, Null, 'Table']), EmptyParam, ds);</p><p>--列信息<br />DBCon.OpenSchema(siColumns, VarArrayOf([Null, Null, 'po']), EmptyParam, ds);<br /><br />--主键<br />DBCon.OpenSchema(siPrimaryKeys, EmptyParam, EmptyParam, ds);<br /><br /><br />--主键、外键对照<br />DBCon.OpenSchema(siForeignKeys, EmptyParam, EmptyParam, ds); </p><img src ="http://www.blogjava.net/raozhh/aggbug/37126.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/raozhh/" target="_blank">饶志华</a> 2006-03-24 08:36 <a href="http://www.blogjava.net/raozhh/articles/37126.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JSF自定义验证标签</title><link>http://www.blogjava.net/raozhh/articles/33890.html</link><dc:creator>饶志华</dc:creator><author>饶志华</author><pubDate>Mon, 06 Mar 2006 09:21:00 GMT</pubDate><guid>http://www.blogjava.net/raozhh/articles/33890.html</guid><wfw:comment>http://www.blogjava.net/raozhh/comments/33890.html</wfw:comment><comments>http://www.blogjava.net/raozhh/articles/33890.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/raozhh/comments/commentRss/33890.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/raozhh/services/trackbacks/33890.html</trackback:ping><description><![CDATA[<DIV id=printhead>
<H3>From PmWiki@caterpillar</H3>
<H1 class=pagename><A href="http://caterpillar.onlyfun.net/Gossip/JSF/JavaServerFaces.htm">JavaServerFaces: CustomConverterValidatorTag</A></H1></DIV><!--PageText-->
<DIV id=wikitext>在 <A class=wikilink href="http://caterpillar.onlyfun.net/Gossip/JSF/CustomValidators.htm">自訂驗證器</A> 中，我們的驗證器只能驗證一種pattern（.+[0-9]+），我們希望可以在JSF頁面上自訂匹配的pattern，然而由於我們使用&lt;f: validator&gt;這個通用的驗證器標籤，為了要能提供pattern屬性，我們可以使用&lt;f:attribute&gt;標籤來設置，例如：<BR><PRE> <BR> ....<BR>  &lt;h:inputSecret value="#{user.password}" required="true"&gt; <BR>    &lt;f:validator validatorId="onlyfun.caterpillar.Password"/&gt;<BR>    &lt;f:attribute name="pattern" value=".+[0-9]+"/&gt;<BR> &lt;/h:inputSecret&gt;&lt;p&gt;<BR> ....<BR> <BR></PRE>
<P></P>使用&lt;f:attribute&gt;標籤來設定屬性，接著我們可以如下取得所設定的屬性：<BR><PRE> <BR> ....<BR> public void validate(FacesContext context, <BR>                         UIComponent component, <BR>                         Object obj)<BR>            throws ValidatorException {<BR>     ....<BR>     String pattern = (String)<BR>                  component.getAttributes().get("pattern");<BR>     ....<BR> }<BR> ....<BR> <BR></PRE>
<P></P>您也可以開發自己的一組驗證標籤，並提供相關屬性設定，這需要瞭解JSP Tag Library的撰寫，所以請您先參考 <A class=wikilink href="http://caterpillar.onlyfun.net/PmWiki/pmwiki.php/LanguageTech/JSPServlet?action=print">JSP/Servlet</A> 中有關於JSP Tag Library的介紹。<BR>
<P></P>要開發驗證器轉用標籤，您可以直接繼承javax.faces.webapp.ValidatorTag，這個類別可以幫您處理大部份的細節，您所需要的，就是重新定義它的createValidator()方法，我們以改寫 <A class=wikilink href="http://caterpillar.onlyfun.net/Gossip/JSF/CustomValidators.htm">自訂驗證器</A> 中的PasswordValidator為例：<BR>
<UL>
<LI>PasswordValidator.java </LI></UL><PRE> <BR> package onlyfun.caterpillar;<BR><BR> import javax.faces.application.FacesMessage;<BR> import javax.faces.component.UIComponent;<BR> import javax.faces.context.FacesContext;<BR> import javax.faces.validator.Validator;<BR> import javax.faces.validator.ValidatorException;<BR><BR> public class PasswordValidator implements Validator {<BR>    private String pattern;<BR><BR>    public void setPattern(String pattern) {<BR>        this.pattern = pattern;<BR>    }<BR>    <BR>    public void validate(FacesContext context, <BR>                         UIComponent component, <BR>                         Object obj)<BR>            throws ValidatorException {<BR>        String password = (String) obj;<BR>        <BR>        if(password.length() &lt; 6) {<BR>            FacesMessage message = new FacesMessage(<BR>              FacesMessage.SEVERITY_ERROR, <BR>              "字元長度小於6", "字元長度不得小於6");<BR>            throw new ValidatorException(message);<BR>        }<BR>        <BR>        if(pattern != null &amp;&amp; !password.matches(pattern)) {<BR>            FacesMessage message = new FacesMessage(<BR>                  FacesMessage.SEVERITY_ERROR, <BR>                  "密碼必須包括字元與數字", <BR>                  "密碼必須是字元加數字所組成");<BR>            throw new ValidatorException(message);<BR>        }<BR>    }<BR> }<BR> <BR></PRE>
<P></P>主要的差別是我們提供了pattern屬性，在validate()方法中進行驗證時，是根據我們所設定的pattern屬性，接著我們繼承 javax.faces.webapp.ValidatorTag來撰寫自己的驗證標籤：<BR><PRE> <BR> package onlyfun.caterpillar;<BR><BR> import javax.faces.application.Application;<BR> import javax.faces.context.FacesContext;<BR> import javax.faces.validator.Validator;<BR> import javax.faces.webapp.ValidatorTag;<BR><BR> public class PasswordValidatorTag extends ValidatorTag {<BR>    private String pattern;<BR>    <BR>    public void setPattern(String pattern) {<BR>        this.pattern = pattern;<BR>    }<BR>    <BR>    protected Validator createValidator() {<BR>        Application application = <BR>            FacesContext.getCurrentInstance().<BR>                         getApplication();<BR>        PasswordValidator validator = <BR>            (PasswordValidator) application.createValidator(<BR>                    "onlyfun.caterpillar.Password");<BR>        validator.setPattern(pattern);<BR>        return validator;<BR>    }<BR> }<BR> <BR></PRE>
<P></P>application.createValidator()方法建立驗證器物件時，是根據在faces-config.xml中註冊驗證器的識別（Validater ID）：<BR>
<UL>
<LI>faces-config.xml </LI></UL><PRE> <BR> &lt;?xml version="1.0"?&gt;<BR> &lt;!DOCTYPE faces-config PUBLIC<BR> "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN"<BR> "http://java.sun.com/dtd/web-facesconfig_1_0.dtd"&gt;<BR><BR> &lt;faces-config&gt;<BR> ....<BR>    &lt;validator&gt;<BR>        &lt;validator-id&gt;<BR>            onlyfun.caterpillar.Password<BR>        &lt;/validator-id&gt;<BR>        &lt;validator-class&gt;<BR>            onlyfun.caterpillar.PasswordValidator<BR>        &lt;/validator-class&gt;<BR>    &lt;/validator&gt;<BR> ....<BR> &lt;/faces-config&gt;<BR> <BR></PRE>
<P></P>剩下來的工作，就是佈署tld描述檔了，我們簡單的定義一下：<BR>
<UL>
<LI>taglib.tld </LI></UL><PRE> <BR> &lt;?xml version="1.0" encoding="UTF-8" ?&gt; <BR> <BR> &lt;taglib xmlns="http://java.sun.com/xml/ns/j2ee" <BR>    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <BR>    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee <BR>                        web-jsptaglibrary_2_0.xsd" <BR>    version="2.0"&gt; <BR>    <BR>    &lt;description&gt;PasswordValidator Tag&lt;/description&gt; <BR>    &lt;tlib-version&gt;1.0&lt;/tlib-version&gt; <BR>    &lt;jsp-version&gt;2.0&lt;/jsp-version&gt; <BR>    &lt;short-name&gt;co&lt;/short-name&gt; <BR>    &lt;uri&gt;http://caterpillar.onlyfun.net&lt;/uri&gt; <BR><BR>    &lt;tag&gt; <BR>        &lt;description&gt;PasswordValidator&lt;/description&gt; <BR>        &lt;name&gt;passwordValidator&lt;/name&gt; <BR>        &lt;tag-class&gt;<BR>            onlyfun.caterpillar.PasswordValidatorTag<BR>        &lt;/tag-class&gt; <BR>        &lt;body-content&gt;empty&lt;/body-content&gt; <BR>        &lt;attribute&gt; <BR>            &lt;name&gt;pattern&lt;/name&gt; <BR>            &lt;required&gt;true&lt;/required&gt; <BR>            &lt;rtexprvalue&gt;false&lt;/rtexprvalue&gt; <BR>        &lt;/attribute&gt; <BR>    &lt;/tag&gt; <BR><BR> &lt;/taglib&gt;<BR>  <BR></PRE>
<P></P>而我們的index.jsp改寫如下：<BR>
<UL>
<LI>index.jsp </LI></UL><PRE> <BR> &lt;%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %&gt;<BR> &lt;%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %&gt;<BR> &lt;%@ taglib uri="/WEB-INF/taglib.tld" prefix="co" %&gt;<BR> &lt;%@page contentType="text/html;charset=Big5"%&gt; <BR> &lt;html&gt;<BR> &lt;head&gt;<BR> &lt;title&gt;驗證器示範&lt;/title&gt;<BR> &lt;/head&gt;<BR> &lt;body&gt;<BR>    &lt;f:view&gt;<BR>        &lt;h:messages layout="table" style="color:red"/&gt;<BR>        &lt;h:form&gt;<BR>            &lt;h3&gt;請輸入您的名稱&lt;/h3&gt;<BR>            &lt;h:outputText value="#{user.errMessage}"/&gt;&lt;p&gt;<BR>           名稱: &lt;h:inputText value="#{user.name}" <BR>                              required="true"/&gt;&lt;p&gt;<BR>           密碼: &lt;h:inputSecret value="#{user.password}" <BR>                                required="true"&gt;<BR>                     &lt;co:passwordValidator pattern=".+[0-9]+"/&gt;<BR>                 &lt;/h:inputSecret&gt; &lt;p&gt;<BR>            &lt;h:commandButton value="送出" <BR>                             action="#{user.verify}"/&gt;<BR>        &lt;/h:form&gt;<BR>    &lt;/f:view&gt;<BR> &lt;/body&gt;<BR> &lt;/html&gt;<BR> <BR></PRE>
<P></P>主要的差別是，我們使用了自己的驗證器標籤：<BR><PRE> <BR> &lt;co:passwordValidator pattern=".+[0-9]+"/&gt;<BR> <BR></PRE>
<P></P>如果要自訂轉換器標籤，方法也是類似，您要作的是繼承javax.faces.webapp.ConverterTag，並重新定義其 createConverter()方法。<BR></DIV>
<DIV id=printfoot><BR>
<DIV class=from>Retrieved from http://caterpillar.onlyfun.net/PmWiki/pmwiki.php/JavaServerFaces/CustomConverterValidatorTag</DIV>
<DIV class=lastmod>頁面最後更新於 2005 年 03 月 09 日，12:28 上午</DIV></DIV><img src ="http://www.blogjava.net/raozhh/aggbug/33890.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/raozhh/" target="_blank">饶志华</a> 2006-03-06 17:21 <a href="http://www.blogjava.net/raozhh/articles/33890.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用JSF组件inputFileUpload将客房端文件保存至服务器</title><link>http://www.blogjava.net/raozhh/articles/32280.html</link><dc:creator>饶志华</dc:creator><author>饶志华</author><pubDate>Fri, 24 Feb 2006 04:10:00 GMT</pubDate><guid>http://www.blogjava.net/raozhh/articles/32280.html</guid><wfw:comment>http://www.blogjava.net/raozhh/comments/32280.html</wfw:comment><comments>http://www.blogjava.net/raozhh/articles/32280.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/raozhh/comments/commentRss/32280.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/raozhh/services/trackbacks/32280.html</trackback:ping><description><![CDATA[public class MainCtl {<BR>&nbsp;&nbsp;&nbsp; UploadedFile upFile;<BR>...<BR><BR>&nbsp;&nbsp;&nbsp; public String upload() throws IOException {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // This is using Hibernate to figure out what folder path<BR>should be used to store the file.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Sysctl sys = Sysctl.getRecord();<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FacesContext fc = FacesContext.getCurrentInstance();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fc.getExternalContext().getApplicationMap().put("fileupload_bytes",<BR>upFile.getBytes());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fc.getExternalContext().getApplicationMap().put("fileupload_type",<BR>upFile.getContentType());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fc.getExternalContext().getApplicationMap().put("fileupload_name",<BR>upFile.getName());<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String guid = (new VMID()).toString().replaceAll(":", "");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writeFile(upFile, sys.getUploadfolder().trim() + guid);<BR>&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return null;<BR>&nbsp;&nbsp;&nbsp; }<BR><BR>&nbsp;&nbsp;&nbsp; private void writeFile(UploadedFile uf, String file) throws IOException {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; InputStream is = uf.getInputStream();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FileOutputStream fos = new FileOutputStream(file);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int c;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while ((c = is.read()) != -1) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fos.write(c);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; }<BR><BR>&nbsp;&nbsp;&nbsp; public UploadedFile getUpFile() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return upFile;<BR>&nbsp;&nbsp;&nbsp; }<BR><BR>&nbsp;&nbsp;&nbsp; public void setUpFile(UploadedFile x) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; upFile = x;<BR>&nbsp;&nbsp;&nbsp; }<BR>}<BR><BR>-------jsp---------<BR>&nbsp; &lt;x:inputFileUpload id="fileupload"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; accept="image/*"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; value="#{MainCtl.upFile}"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; storage="file"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; styleClass="input"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; required="false"/&gt;<BR> &lt;h:commandButton value="load it up" action="#{MainCtl.upload}"<BR>styleClass="button"/&gt;<BR><BR><img src ="http://www.blogjava.net/raozhh/aggbug/32280.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/raozhh/" target="_blank">饶志华</a> 2006-02-24 12:10 <a href="http://www.blogjava.net/raozhh/articles/32280.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>利用JSF、SpringFramework和Hibernate构建Web应用的实例讲述</title><link>http://www.blogjava.net/raozhh/articles/25743.html</link><dc:creator>饶志华</dc:creator><author>饶志华</author><pubDate>Wed, 28 Dec 2005 08:42:00 GMT</pubDate><guid>http://www.blogjava.net/raozhh/articles/25743.html</guid><wfw:comment>http://www.blogjava.net/raozhh/comments/25743.html</wfw:comment><comments>http://www.blogjava.net/raozhh/articles/25743.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/raozhh/comments/commentRss/25743.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/raozhh/services/trackbacks/25743.html</trackback:ping><description><![CDATA[<CENTER>
<H4>利用JSF、SpringFramework和Hibernate构建Web应用的实例讲述</H4></CENTER><BR>(来源：http://blog.csdn.net/ylong/archive/2004/07/24/50810.aspx) <BR><BR><FONT face="verdana, arial, helvetica" size=2>[原作者]</FONT><FONT face="verdana, arial, helvetica" size=2><STRONG> Derek Yang Shen<BR></STRONG>[原文链接] <A href="http://www.javaworld.com/javaworld/jw-07-2004/jw-0719-jsf.html">http://www.javaworld.com/javaworld/jw-07-2004/jw-0719-jsf.html</A><BR>[源码链接] <A href="http://www.javaworld.com/javaworld/jw-07-2004/jsf/jw-0719-jsf.zip">http://www.javaworld.com/javaworld/jw-07-2004/jsf/jw-0719-jsf.zip</A><BR>[翻译] 本人<BR>[点评] 该文是我看的第一篇讲述JSF与Spring整合的文章，是一个很好的范例，比较适合于对Spring有了一定了解人学习。其中大量篇幅讲述的JSF，对JSF感兴趣的也可以来看看。<BR>[声明] 该文是本人第一次翻译大块头的文章，由于本人才疏学浅、英语较烂，但考虑到和我一样不喜欢看洋文的大有人在，遂用近4小时的时间翻译该文，对一些技术名词和不会翻译的地方用原文代替，其中难免有很多错误，欢迎批评指正。<BR><BR>[译文]<BR><BR>JSF是一种新的用于构架j2ee应用用户界面的技术，它尤其适合于基于MVC架构的应用中。虽已有很多文章介绍过了JSF，然而它们大多从理论高度来介绍JSF而不是面向于实际应用。目前对于实际应用，JSF仍有很多问题没有解决，例如：如何使JSF适应于MVC整体构架中？如何将JSF与其他Java 框架整合起来？是否应该将业务逻辑放置在JSF的backing beans中？如何处理JSF中的安全机制？更为重要的是如何利用JSF构架现实世界的Web应用？<BR><BR>本文将涉及到上面的这些问题，它将演示如何将JSF、Spring和Hibernate整合在一起，构架出一个名为JCatalog的</FONT><FONT face="verdana, arial, helvetica" size=2>在线产品价目系统</FONT><FONT face="verdana, arial, helvetica" size=2>。利用该Demo，本文涵盖了Web应用开发的每一个阶段，包括需求收集、分析，技术选择，系统架构和实现。本文讨论了在JCatalog中涉及到的各种技术的优点和缺点并展示了一些关键部分的设计方法。<BR><BR>本文的对象是从事基于J2ee的Web应用架构人员和开发人员，它并不是对JSF、SpringFramework和Hibernate的简单介绍，如果对这些领域不甚了解，请参看相关资源。<BR><BR><FONT size=5><B>该范例的功能需求</B></FONT><BR>JCatalog是一个现实世界的Web应用，我首先描述JCatalog的需求，在通篇的技术决策和架构设计时都将涉及到本部分。<BR><BR>在设计Web应用的第一阶段是收集系统的功能需求，范例应用是一个典型的电子商务应用系统，用户可以浏览产品的catalog并查看产品的详细情况，而管理员可以管理产品的catalog。通过增加一些其他功能，如inventory管理和订单处理等，该应用可成为一个成熟的电子商务系统。<BR><BR><B><FONT size=3>Use cases</FONT></B><BR>Use-case分析被用来展示范例应用的功能需求，图1就是该应用的use-case图。<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
<P align=center><A href="http://www.huihoo.com/java/jsf/jw-0719-jsf1.gif"><IMG style="WIDTH: 324px; HEIGHT: 196px" height=127 alt="" hspace=0 src="http://www.huihoo.com/java/jsf/jw-0719-jsf1.gif" width=228 align=baseline border=0></A></P><BR>use-case图用于表示系统中的actors以及可能进行的operations，在该应用中将有七个use-case，用户能够浏览产品 catalog和查看产品的详细情况，一旦用户登录到系统中，她将成为管理员，从而可以创建新的产品，编辑已存在的产品或者删除老的产品等。<BR><BR><B><FONT size=3>Business rules</FONT></B><BR>JCatalog必须符合以下business rules:</FONT><FONT face="宋体, MS Song"> </FONT>
<UL>
<LI><FONT face="verdana, arial, helvetica" size=2>每个产品必须具有唯一的ID</FONT> 
<LI><FONT size=2>每个产品必须属于至少一个category</FONT><FONT face="verdana, arial, helvetica" size=2> <BR></FONT>
<LI><FONT face="verdana, arial, helvetica" size=2>产品ID一旦创立不得修改</FONT></LI></UL><FONT face="verdana, arial, helvetica" size=2><B><FONT size=3>Assumptions</FONT></B><BR>我们在系统的设计和实现中做以下假定：<BR></FONT>
<UL>
<LI><FONT face="verdana, arial, helvetica" size=2>英语讲是缺省语言，且不需事先国际化</FONT> 
<LI><FONT face="verdana, arial, helvetica" size=2>在Catalog不讲不会超过500个产品</FONT> 
<LI><FONT face="verdana, arial, helvetica" size=2>catalog将不会被频繁的修改</FONT></LI></UL><FONT face="verdana, arial, helvetica" size=2><B><FONT size=3>Page flow</FONT></B><BR>图2显示了所有的JCatalog的pages以及它们之间的transitions关系：<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
<P align=center><A href="http://www.huihoo.com/java/jsf/jw-0719-jsf2.gif"><IMG style="WIDTH: 250px; HEIGHT: 159px" height=102 alt="" hspace=0 src="http://www.huihoo.com/java/jsf/jw-0719-jsf2.gif" width=176 align=baseline border=0></A></P><BR>该应用中存在两组pages：公开的internet和用于管理的intranet，其中intranet只能被那些成功登录到系统的用户访问。 ProductSummary不作为一个单独的page展示给用户，它显示在Catalog page中的frame中。ProductList只对管理员可视，它包含用于创建、编辑和删除产品的链接。<BR><BR>图3是一个Catalog页面的示意图，理想状况下，在需求文档中应该包含每一页的详细示意图。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
<P align=center><A href="http://www.huihoo.com/java/jsf/jw-0719-jsf3.gif"><IMG style="WIDTH: 222px; HEIGHT: 160px" height=114 alt="" hspace=0 src="http://www.huihoo.com/java/jsf/jw-0719-jsf3.gif" width=222 align=baseline border=0></A></P><BR><FONT size=5><B>构架设计</B></FONT><BR>Web应用开发的下一个阶段是构架设计，它包括将应用划分为多个功能组件并将这些组件分割组合成层，高层的构架设计应该中立于所选用的特定技术。<BR><BR><B><FONT size=3>多层架构</FONT></B><BR>多层架构是将整个系统清晰的分为多个功能单元：client、presentation、business-logic、integration和 EIS，这将确保职责得到清晰的划分，使得系统更易于维护和扩展。具有三层或等多层的系统被证明比C/S模型具有更好的伸缩性和灵活性。<BR><BR>client层是使用和表示数据模型的地方，对于一个Web应用，client层通常是浏览器，基于浏览器的瘦客户端不包含任何表示逻辑，它依赖于</FONT><FONT face="verdana, arial, helvetica" size=2>presentation</FONT><FONT face="verdana, arial, helvetica" size=2>层。<BR><BR>presentation层将business-logic层的服务展示给用户，它应知道如何处理用户的请求，如何同business-logic层交互，并且知道如何选择下一个视图显示给用户。<BR><BR>business-logic层包含应用的business objects和business services。它接受来在于presentation层的请求、基于请求处理业务逻辑。业务逻辑层组件将受益于系统级的服务，如安全管理、事务管理和资源管理等。<BR><BR>integration层是介于</FONT><FONT face="verdana, arial, helvetica" size=2>business-logic</FONT><FONT face="verdana, arial, helvetica" size=2>层和EIS层之间的桥梁，它封装了与EIS层交互的逻辑。有时，将integration层和business-logic层合称为中间层。<BR><BR>应用的数据被保存在EIS层中，它包括关系数据库、面向对象数据库和以及遗留系统等。<BR><BR><B><FONT size=3>JCatalog的构架设计</FONT></B><BR>图4显示了JCatalog的构架设计以及如何应用于多层构架系统中。<BR>&nbsp; <A href="http://www.huihoo.com/java/jsf/jw-0719-jsf4.gif">
<P align=center><IMG style="WIDTH: 326px; HEIGHT: 218px" height=120 alt="" hspace=0 src="http://www.huihoo.com/java/jsf/jw-0719-jsf4.gif" width=196 align=baseline border=0></A></P><BR>该应用采用了多层非分布式的构架，图4展示了系统的分层以及每一层中选择的技术，它同时又是该范例的部署图，它的presentation、 business-logic和integration层将存在于同一个web容器中。定义良好的接口将孤立每一层的职责，这一架构使得应用更为简单和更好的伸缩性。<BR><BR>对于presentation层，经验表明，最好的方法是选择已存在的并已得到证明了的Web应用框架，而不是自己去设计和开发新的框架。我们拥有多个可选择的框架，如Struts，WebWork和JSF等，在JCatalog中，我们选择采用JSF。<BR><BR>EJB和POJO都可以用来创建业务逻辑层，如果应用是分布式的，采用具有remote接口的EJB是一个好的选择；由于JCatalog是一个典型的不需要远程访问的Web应用，因此选用POJO，并充分利用Spring Framework的帮助，将是实现业务逻辑层的更好选择。<BR><BR>integration层利用关系型数据库事先数据的持续化，存在多种方法可用来实现：<BR></FONT>
<UL>
<LI><FONT face="verdana, arial, helvetica" size=2>JDBC：这是最为灵活的方法，然而，低级的JDBC难以使用，而且质量差的JDBC代码很难运转良好</FONT> 
<LI><FONT face="verdana, arial, helvetica" size=2>Entity beans：CMP的Entity bean是一种分离数据访问代码和处理ORM的昂贵的方法，它是以应用服务器为中心的方法，即entity bean不是将应用与某种数据库类型而是EJB容器约束在一起。</FONT> 
<LI><FONT face="verdana, arial, helvetica" size=2>O/R mapping framework：一个ORM框架采用以对象为中心的方法实现数据持续化，一个以对象为中心的应用易于开发并具有高度的可移植性。在该领域中存在几个框架可用—JDO、Hibernate、TopLink以及CocoBase等。在我们的范例中将选用Hibernate。</FONT></LI></UL><FONT face="verdana, arial, helvetica" size=2>现在，我们将讨论每一层中的设计问题，由于JSF是一个相对较新的技术，因此将着重于它的使用：<BR><BR></FONT><FONT size=3><B><FONT face="verdana, arial, helvetica">presentation</FONT></B></FONT><FONT face="verdana, arial, helvetica" size=2><FONT size=3><B>层和JSF</B></FONT><BR>表示层的功能是收集用户的输入、展示数据、控制页面导航并将用户的输入传递给业务逻辑层，表示层同时需要验证用户的输入以及维护应用的session状态。在下面几部分中，我将讨论表示层设计时的考虑和模式，并说明选择JSF作为JCatalog表示层的原因。<BR><BR><B>MVC</B><BR>MVC是Java-Blueprints推荐的架构设计模式，MVC将几个方面分离开来，从而减少代码的重复，它以控制为中心并使得应用更具扩展性。MVC同时可帮助具有不同技能的用户更关注于自己的技能，通过定义良好的接口进行相互合作。MVC是表示层的架构设计模式。<BR><BR><B>JSF</B><BR>JSF是Web应用的服务器端用户组件框架，它包含以下API：表示UI组件、管理它们的状态、处理事件、服务器端验证、数据转换、定义页面导航、支持国际化，并为这些特性提供扩展能力。它同时包括两个JSP的tag库以在JSP页面中表示UI组件，以及将组件wire为服务器端对象。<BR><BR><B>JSF和MVC</B><BR>JSF非常适合于基于MVC的表示层架构，它在行为和表示之间提供了清晰的分离，它使得你可以采用熟悉的UI组件和web层概念而无需受限于某种特殊的脚本技术或标记语言。<BR><BR>JSF backing beans是JSF的Model层，此外，它同样包含actions，action是controller层的扩展，用于将用户的请求委派给业务逻辑层。这里请注意，从整体的应用构架看，业务逻辑层也被称为model层。包含JSF标签的JSP页面是表示层，Faces Servlet提供了controller的功能。<BR><BR><B>为什么选用JSF？</B></FONT><FONT face="verdana, arial, helvetica" size=2><BR>JSF不仅仅是另外一个Web框架，下面这些特性是JSF区别于其他Web框架之所在：</FONT> 
<UL>
<LI><FONT face="verdana, arial, helvetica" size=2>类Swing的面向对象的Web应用开发：服务器端有状态的UI组件模型，配合event listeners和handlers，促进了面向对象的Web应用开发。</FONT> 
<LI><FONT face="verdana, arial, helvetica" size=2>backing-bean管理： backing bean是与页面中使用的UI组件相关联的javabean组件，backing-bean管理将UI组件对象的定义同执行应用相关处理和拥有数据的对象分离开来。JSF在合适的范围内保存和管理这些backing-bean实例。</FONT> 
<LI><FONT face="verdana, arial, helvetica" size=2>可扩展的UI模型：JSF的UI模型是可配置的、可重用的，用以构建JSF应用的用户界面。你可以通过扩展标准的UI组件来开发出更为复杂的组件，例如菜单条、树组件等。</FONT> 
<LI><FONT face="verdana, arial, helvetica" size=2>灵活的rendering模型：renderer分离了UI组件的功能和显示，多个renderers可创建和用来为同一客户端或不同的客户端定义不同的显示。</FONT> 
<LI><FONT face="verdana, arial, helvetica" size=2>可扩展的转换和验证模型：基于标准的converter和validator，你可以开发出自己的可提供更好的模型保护的converter和validator。</FONT></LI></UL><FONT face="verdana, arial, helvetica" size=2>尽管如此，JSF目前尚未成熟，随同JSF发布的 components、converters和validators都是最基础的，而且per-component验证模型不能处理components 和validators间的many-to-many验证。此外，JSF标签不能与JSTL间无缝的整合在一起。<BR><BR>在下面的章节中，我将讨论几个在JCatalog实现中的关键部分和设计决策。我首先解释managed bean的定义和使用以及JSF中的backing bean，然后，我将说明如何处理安全、分页、caching、file upload、验证以及错误信息定制。<BR><BR><B>Managed bean,backing bean,view object 和domain object model</B><BR>JSF中引入了两个新的名词：managed bean和backing bean。JSF提供了一个强大的managed-bean工具，由JSF来管理的JavaBean对象称为managed-bean，一个 managed bean表述了一个bean如何被创建和管理，它不包含该bean的任何功能性描述。<BR><BR>backing bean定义了与页面中使用的UI组件相关联的属性和处理逻辑。每一个backing-bean属性邦定于一个组件实例或某实例的value。一个 backing-bean同时定义了一组执行组件功能的方法，例如验证组件的数据、处理组件触发的事件、实施与组件相关的导航等。<BR><BR>一个典型的JSF应用将其中的每个页面和一个backing-bean结合起来，然而在现实应用中，强制的执行这种one-on-one的关系不是一种理想的解决方案，它可能会导致代码重复等问题。在现实的应用中，多个页面可以共享一个backing-bean，例如在JCatalog中， CreateProduct和EditProduct将共享同一个ProductBean定义。<BR><BR>model对象特定于表示层中的一个view对象，它包含必须显示在view层的数据以及验证用户输入、处理事件和与业务逻辑层交互的处理逻辑等。在基于 JSF的应用中backing bean就是view对象，在本文中backing bean和view对象是可互换的名词。<BR><BR>对比于struts中的ActionForm和Action，利用JSF中的backing-bean进行开发将能更好的遵循面向对象方法，一个 backing-bean不仅包含view数据，而且还包含与这些数据相关的行为，而在struts中，Action和ActionForm分别包含数据和逻辑。<BR><BR>我们都应该听说过domain object model，那么，domain object model和view对象之间有什么区别呢？在一个简单的Web应用中，一个domain object model能够横穿所有层中，而在复杂的应用中，需要用到一个单独的view对象模型。domain object model应该属于业务逻辑层，它包含业务数据和与特定业务对象相关的业务逻辑；一个view对象包含presentation-specific的数据和逻辑。将view对象从domain object model中分离出来的缺点是在这两个对象模型之间必将出现数据映射。在JCatalog中，ProductBeanBuilder和 UserBeanBuilder利用reflection-based Commons BeanUtils来实现数据映射。<BR><BR><B>安全</B><BR>目前，JSF没有内建的安全特性，而对于范例应用来说安全需求是非常基础的：用户登录到administration intranet中仅需用户名和密码认证，而无需考虑授权。<BR>针对于JSF的认证，已有几种方法提出：<BR></FONT>
<UL>
<LI><FONT face="verdana, arial, helvetica" size=2>利用一个backing bean：这一个方法非常简单，然而它却将backing bean与特殊的继承关系结合起来了</FONT> 
<LI><FONT face="verdana, arial, helvetica" size=2>利用JSF的ViewHandler decorator：这一方法中，安全逻辑紧密地与一特定Web层技术联系在了一起</FONT> 
<LI><FONT face="verdana, arial, helvetica" size=2>利用servlet filter：一个JSF应用与其他的Web应用没有什么两样，filter仍是处理认证检查的最好地方，这种方法中，认证逻辑与Web应用分离开来</FONT></LI></UL><FONT face="verdana, arial, helvetica" size=2>在我们的范例程序中，SecurityFilter类被用来处理用户的认证，目前，受保护的资源只包含三个页面，出于简单的考虑，将它们的位置被硬编码到Filter类中。<BR><BR><B>分页</B><BR>该应用中的Catalog页面需要分页，表示层可用来处理分页，即它取出所有的数据并保存在这一层；分页同样可在business-logic层、 integration层、甚至EIS层中实现。由于在JCatalog中假设不超过500个产品，因此所有的产品信息能存放在一个user session中，我们将分页逻辑放在了ProductListBean中，与分页相关的参数将通过JSF managed-bean工具配置。<BR><BR><B>Caching</B><BR>Caching是提高Web应用性能的最重要技术之一，在应用构建中的很多层中都可以实现caching。JSF managed-bean工具可以使在表示层实现caching非常容易。通过改变一个managed bean的范围，这个managed bean中包含的数据可以在不同的范围内缓存。<BR><BR>范例应用中采用了两级caching，第一级caching存在于业务逻辑层，CachedCatalogServiceImpl类维护了一个所有产品和目录的读写cache，Spring将该类作为一个singleton service bean来管理，所以，一级cache是一个应用范围的读写cache。<BR><BR>为了简化分页逻辑并进而提高应用的速度，产品同样在session范围内缓存到表示层，每一个用户维护着他自己的ProductListBean，这一方法的缺点是内存的消耗和数据的失效问题，在一个用户session中，如果管理员更改了catalog，用户可到的将是失效的数据，然而，由于我们假设应用的数据不会经常的改变，所以这些缺点将能够忍受。<BR><BR><B>File upload</B><BR>目前的JSF Sun参考实现中不支持file upload。Struts虽已具有非常不错的file upload能力，然而要想使用这一特性需要Struts-Faces整合库。在JCatalog中，一个图像与一个产品相关联，在一个用户创建了新的产品后，她必须将相应的图片上传，图片将保存在应用服务器的文件系统里，产品的ID就是图像名称。<BR><BR>范例应用中采用<INPUT type=file>、Servlet和Jakarta Common的file-upload API来实现简单的文件上传功能，该方法包含两个参数：图像路径和图像上传结果页面。它们将通过ApplicationBean来配置，详细内容请参看 FileUploadServlet类。<BR><BR><B>Validation</B><BR>JSF中发布的标准validator是非常基础的，无法满足现实的需要，但很容易开发出自己的JSF validator，在范例中，我开发了SelectedItemsRange validator，它用来验证UISelectMany组件中选择的数量：<BR><BR>&nbsp; <?xml:namespace prefix = h /><h:selectManyListbox id=selectedCategoryIds value="#{productBean.selectedCategoryIds}"><BR>&nbsp; &nbsp;&nbsp; <?xml:namespace prefix = catalog /><catalog:validateSelectedItemsRange minNum="1"></catalog:validateSelectedItemsRange><BR>&nbsp;&nbsp;&nbsp;&nbsp; <?xml:namespace prefix = f /><f:selectItems id=categories value="#{applicationBean.categorySelectItems}"></f:selectItems><BR>&nbsp; </h:selectManyListbox><BR><BR>详细情况请参看范例。<BR><BR><B>定制错误信息</B><BR>在JSF中，你可以为converters和validators创建resource bundle和定制错误信息，一个resource bundle可在faces-config.xml中创建：<BR><BR>&nbsp; <MESSAGE-BUNDLE>catalog.view.bundle.Messages</MESSAGE-BUNDLE><BR><BR>并将错误信息的key-value对加到Message.properties文件中：<BR><BR>&nbsp; javax.faces.component.UIInput.CONVERSION=Input data is not in the correct type.<BR>&nbsp; javax.faces.component.UIInput.REQUIRED=Required value is missing.<BR><BR><B><FONT size=3>业务逻辑层和Spring Framework</FONT></B><BR>业务对象和业务服务存在于业务逻辑层中，一个业务对象不仅包含数据，而且包含相应的逻辑，在范例应用中包含三个业务对象：Product、Category和User。<BR><BR>业务服务与业务对象交互并提供更高级的业务逻辑，需要首先定义一个正式的业务接口，它是直接与终端用户交互的服务接口。在JCatalog中，通过在 Spring Framework帮助下的POJO实现业务逻辑层，其中共有两个业务服务：CatalogService包含Catalog管理相关的业务逻辑， UserService中包含User管理逻辑。<BR><BR>Spring是基于IoC概念的框架，在范例应用中用到的Spring特性包括：<BR></FONT>
<UL>
<LI><FONT face="verdana, arial, helvetica" size=2>Bean management with application contexts：Spring可以有效地组织我们的中间层对象，它能够消除singleton的proliferation，并易于实现良好的面向对象编程方法，即“编程到接口”。</FONT> 
<LI><FONT face="verdana, arial, helvetica" size=2>Declarative Transaction management: Spring利用AOP实现事务管理，而无需借助于EJB容器，利用这种方法，事务管理可以用于任何POJO中。Spring的事务管理不局限于JTA，而是可以采用不同的事务策略，在范例应用中，我们将使用declarative transaction management with Hibernate transaction。</FONT> 
<LI><FONT face="verdana, arial, helvetica" size=2>Data-access exception hierarchy：Spring提供了非常好的异常来代替SQLException，为利用Spring的异常，必须在Spring的配置文件中定义以下异常转换：</FONT></LI></UL><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BEAN class="" id=jdbcExceptionTranslator><BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <property name="dataSource"><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <REF bean="dataSource"><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </property><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </BEAN><BR><BR>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 在范例应用中，如果一个具有重复ID的新产品被插入，将会抛出DataIntegrityViolationException，这一异常将被 <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch并rethrown一个DuplicateProductIdException。这样，该异常就可以与其它的异常区别处理。<BR></FONT>
<UL>
<LI><FONT face="verdana, arial, helvetica" size=2>Hibernate integration：Spring与Hibernate这样的ORM框架整合的非常好，Spring提供了对Hibernate session的高效和安全的处理，它可通过application context配置Hibernate的SessionFactories和JDBC数据源，并使得应用易于测试。</FONT></LI></UL><FONT face="verdana, arial, helvetica" size=2><BR><B><FONT size=3>Integration层和Hibernate</FONT></B><BR>Hibernate是一个开源的ORM框架，它可以支持所有主流SQL数据库系统，Hibernate的查询语言为对象和关系架起了非常好的桥梁。Hibernate提供了强大的功能以实现：数据读取和更新、事务管理、数据连接池、查询和实体关系管理等。<BR><BR><B><FONT size=3>Data Access Ojbect(DAO)</FONT></B><BR>JCatalog中采用了Dao模式，该模式抽象和封装了所有对数据源的访问，该应用中包括两个DAO接口：CatalogDao和UserDao，它们相应的实现HibernateCatalogDaoImpl和HibernateUserDAoImpl包含了Hibernate特定的逻辑来实现数据的管理和持久化。<BR><BR><B><FONT size=5>实现</FONT></B><BR>现在我们来看看如何将上面讨论的这些东西包装在一起以实现JCatalog，你可以从这个地址下载源码：<A href="http://www.javaworld.com/javaworld/jw-07-2004/jsf/jw-0719-jsf.zip">source code</A><BR><BR><B><FONT size=3>数据库设计</FONT></B><BR>我们为该范例应用创建了包含4个表的数据库，如图5所示：<BR>
<P align=center><A href="http://www.huihoo.com/java/jsf/jw-0719-jsf5.gif"><IMG style="WIDTH: 290px; HEIGHT: 211px" height=150 alt="" hspace=0 src="http://www.huihoo.com/java/jsf/jw-0719-jsf5.gif" width=248 align=baseline border=0></A></P><BR><B><FONT size=3>类设计</FONT></B><BR>图6显示了JCatalog的类图<BR>
<P align=center><A href="http://www.huihoo.com/java/jsf/jw-0719-jsf6.gif"><IMG style="WIDTH: 414px; HEIGHT: 255px" height=232 alt="" hspace=0 src="http://www.huihoo.com/java/jsf/jw-0719-jsf6.gif" width=120 align=baseline border=0></A></P><BR>“编程到接口”的思想贯穿了整个设计实现中，在表示层，共用到四个backing bean：ProductBean、ProductListBean、UserBean和MessageBean;业务逻辑层包含两个业务服务 (CatalogService和UserService)和三个业务对象(Product、Category和User);Integration层有两个Dao接口和它们相应的Hibernate实现，Spring的application context用来管理绝大多数的业务逻辑层和integration层的对象；ServiceLocator将JSF和业务逻辑层整合在了一起。<BR><BR><B><FONT size=3>Wire everything up</FONT></B><BR>由于篇幅所限，我们仅举例说明，范例中use case CreateProduct展示了如何装配和构建应用，在详细讲述细节前，我们利用sequence图(图7)来说明所有层的end-tp-end整合。<BR>
<P align=center><A href="http://www.huihoo.com/java/jsf/jw-0719-jsf7.gif"><IMG style="WIDTH: 470px; HEIGHT: 128px" height=128 alt="" hspace=0 src="http://www.huihoo.com/java/jsf/jw-0719-jsf7.gif" width=120 align=baseline border=0></A></P><BR><BR><B>表示层</B>：<BR>表示层实现包括创建JSP页面、定义页导航、创建和配置backing bean以及将JSF与业务逻辑层整合。<BR></FONT>
<UL>
<LI><FONT face="verdana, arial, helvetica" size=2>JSP page：createProduct.jsp是用来创建新产品的页面，它包含UI组件并将组件打包成ProductBean，ValidateItemsRange标签用来验证用户选择的种类数量，对每一个产品至少要有一个种类被选中。</FONT></LI></UL>
<UL>
<LI><FONT face="verdana, arial, helvetica" size=2>页面导航：应用中的导航被定义在应用的配置文件faces-navigation.xml中，CreateProduct的导航准则如下：</FONT></LI></UL>
<BLOCKQUOTE><FONT face="verdana, arial, helvetica" size=2><NAVIGATION-RULE></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; <FROM-VIEW-ID>*</FROM-VIEW-ID></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; <NAVIGATION-CASE></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <FROM-OUTCOME>createProduct</FROM-OUTCOME></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <TO-VIEW-ID>/createProduct.jsp</TO-VIEW-ID></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; </NAVIGATION-CASE></FONT><BR><FONT face="verdana, arial, helvetica" size=2></NAVIGATION-RULE></FONT><BR></BLOCKQUOTE>
<BLOCKQUOTE><FONT face="verdana, arial, helvetica" size=2><NAVIGATION-RULE></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; <FROM-VIEW-ID>/createProduct.jsp</FROM-VIEW-ID></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; <NAVIGATION-CASE></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <FROM-OUTCOME>success</FROM-OUTCOME></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <TO-VIEW-ID>/uploadImage.jsp</TO-VIEW-ID></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; </NAVIGATION-CASE></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; <NAVIGATION-CASE></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <FROM-OUTCOME>retry</FROM-OUTCOME></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <TO-VIEW-ID>/createProduct.jsp</TO-VIEW-ID></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; </NAVIGATION-CASE></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; <NAVIGATION-CASE></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <FROM-OUTCOME>cancel</FROM-OUTCOME></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <TO-VIEW-ID>/productList.jsp</TO-VIEW-ID></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; </NAVIGATION-CASE></FONT><BR><FONT face="verdana, arial, helvetica" size=2></NAVIGATION-RULE></FONT><BR></BLOCKQUOTE>
<UL>
<LI><FONT face="verdana, arial, helvetica" size=2>Backing bean：ProductBean不仅包含有将数据映射到页面上的UI组件的属性，还包括三个action：createAction、editAction和deleteAction，下面是createAction方法的代码：</FONT></LI></UL>
<BLOCKQUOTE><FONT face="verdana, arial, helvetica" size=2>public String createAction() {</FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; try {</FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Product product = ProductBeanBuilder.createProduct(this);</FONT><BR><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //Save the product.</FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.serviceLocator.getCatalogService().saveProduct(product);</FONT><BR><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //Store the current product id inside the session bean.</FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //For the use of image uploader.</FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FacesUtils.getSessionBean().setCurrentProductId(this.id);</FONT><BR><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //Remove the productList inside the cache.</FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.logger.debug("remove ProductListBean from cache");</FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FacesUtils.resetManagedBean(BeanNames.PRODUCT_LIST_BEAN);</FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; } catch (DuplicateProductIdException de) {</FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String msg = "Product id already exists";</FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.logger.info(msg);</FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FacesUtils.addErrorMessage(msg);</FONT><BR><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return NavigationResults.RETRY;</FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; } catch (Exception e) {</FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String msg = "Could not save product";</FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.logger.error(msg, e);</FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FacesUtils.addErrorMessage(msg + ": Internal Error");</FONT><BR><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return NavigationResults.FAILURE;</FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; }</FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; String msg = "Product with id of " + this.id + " was created successfully.";</FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; this.logger.debug(msg);</FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; FacesUtils.addInfoMessage(msg);</FONT><BR><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; return NavigationResults.SUCCESS;</FONT><BR><FONT face="verdana, arial, helvetica" size=2>}</FONT><BR></BLOCKQUOTE><FONT face="verdana, arial, helvetica" size=2></FONT>
<UL>
<LI><FONT face="verdana, arial, helvetica" size=2>Managed-bean声明：ProductBean必须在JSF配置文件faces-managed-bean.xml中配置：</FONT></LI></UL>
<BLOCKQUOTE><FONT face="verdana, arial, helvetica" size=2><MANAGED-BEAN></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; <DESCRIPTION></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Backing bean that contains product information.</FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; </DESCRIPTION></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; <MANAGED-BEAN-NAME>productBean</MANAGED-BEAN-NAME></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; <MANAGED-BEAN-CLASS>catalog.view.bean.ProductBean</MANAGED-BEAN-CLASS></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; <MANAGED-BEAN-SCOPE>request</MANAGED-BEAN-SCOPE>&nbsp;&nbsp;&nbsp; </FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; <MANAGED-PROPERTY></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <PROPERTY-NAME>id</PROPERTY-NAME></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <VALUE>#{param.productId}</VALUE></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; </MANAGED-PROPERTY></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; <MANAGED-PROPERTY></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <PROPERTY-NAME>serviceLocator</PROPERTY-NAME></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <VALUE>#{serviceLocatorBean}</VALUE></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; </MANAGED-PROPERTY></FONT><BR><FONT face="verdana, arial, helvetica" size=2></MANAGED-BEAN></FONT><BR></BLOCKQUOTE>
<UL>
<LI><FONT face="verdana, arial, helvetica" size=2>&nbsp;表示层和业务逻辑层之间的整合： ServiceLocator抽象了查找服务的逻辑，在范例应用中，ServiceLocator被定义为一个接口，该接口实现为一个JSF的 managed bean，即ServiceLocatorBean，它将在Spring的application context中寻找服务：</FONT></LI></UL><FONT face="verdana, arial, helvetica" size=2></FONT>
<BLOCKQUOTE><FONT face="verdana, arial, helvetica" size=2>ServletContext context = FacesUtils.getServletContext();</FONT><BR><FONT face="verdana, arial, helvetica" size=2>this.appContext = WebApplicationContextUtils.getRequiredWebApplicationContext(context);</FONT><BR><FONT face="verdana, arial, helvetica" size=2>this.catalogService = (CatalogService)this.lookupService(CATALOG_SERVICE_BEAN_NAME);</FONT><BR><FONT face="verdana, arial, helvetica" size=2>this.userService = (UserService)this.lookupService(USER_SERVICE_BEAN_NAME);</FONT><BR></BLOCKQUOTE><FONT face="verdana, arial, helvetica" size=2><B>业务逻辑层</B><BR></FONT>
<UL>
<LI><FONT face="verdana, arial, helvetica" size=2>业务对象：由于采用Hibernate提供持久化，因此Product和Category两个业务对象需要为它们的所有field提供getter和setter。</FONT></LI></UL>
<UL>
<LI><FONT face="verdana, arial, helvetica" size=2>业务服务：CatalogService接口中定义了所有的与Catalog management相关的服务：</FONT></LI></UL>
<BLOCKQUOTE><FONT face="verdana, arial, helvetica" size=2>public interface CatalogService {</FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; public Product saveProduct(Product product) throws CatalogException;</FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; public void updateProduct(Product product) throws CatalogException;</FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; public void deleteProduct(Product product) throws CatalogException;</FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; public Product getProduct(String productId) throws CatalogException;</FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; public Category getCategory(String categoryId) throws CatalogException;</FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; public List getAllProducts() throws CatalogException;</FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; public List getAllCategories() throws CatalogException;</FONT><BR><FONT face="verdana, arial, helvetica" size=2>}</FONT><BR></BLOCKQUOTE>
<UL>
<LI><FONT face="verdana, arial, helvetica" size=2>Spring Configuration：这里是CatalogService的Spring配置：</FONT></LI></UL>
<BLOCKQUOTE><FONT face="verdana, arial, helvetica" size=2><!-- Hibernate Transaction Manager Definition --></FONT><BR><FONT face="verdana, arial, helvetica" size=2><BEAN class=org id=transactionManager></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; <property name="sessionFactory"><REF local="sessionFactory"></property></FONT><BR><FONT face="verdana, arial, helvetica" size=2></BEAN></FONT><BR><BR><FONT face="verdana, arial, helvetica" size=2><!-- Cached Catalog Service Definition --></FONT><BR><FONT face="verdana, arial, helvetica" size=2><BEAN class=catalog id=catalogServiceTarget init-method="init"></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; <property name="catalogDao"><REF local="catalogDao"></property></FONT><BR><FONT face="verdana, arial, helvetica" size=2></BEAN></FONT><BR><BR><FONT face="verdana, arial, helvetica" size=2><!-- Transactional proxy for the Catalog Service --></FONT><BR><FONT face="verdana, arial, helvetica" size=2><BEAN class=org id=catalogService></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; <property name="transactionManager"><REF local="transactionManager"></property></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; <property name="target"><REF local="catalogServiceTarget"></property></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; <property name="transactionAttributes"></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <PROPS></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <PROP key="get*">PROPAGATION_REQUIRED,readOnly</PROP></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <PROP key="save*">PROPAGATION_REQUIRED</PROP></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <PROP key="update*">PROPAGATION_REQUIRED</PROP></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <PROP key="delete*">PROPAGATION_REQUIRED</PROP></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </PROPS></FONT><BR><FONT face="verdana, arial, helvetica" size=2>&nbsp;&nbsp; </property></FONT><BR><FONT face="verdana, arial, helvetica" size=2></BEAN></FONT><BR></BLOCKQUOTE>
<UL>
<LI><FONT face="verdana, arial, helvetica" size=2>Spring和Hibernate的整合：下面是HibernateSessionFactory的配置：</FONT></LI></UL>
<BLOCKQUOTE><FONT face="verdana, arial, helvetica" size=2><!-- Hibernate SessionFactory Definition --></FONT><BR><CODE>
<P>&lt;!-- Hibernate SessionFactory Definition --&gt;<BR>&lt;bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean"&gt;<BR>&nbsp;&nbsp; &lt;property name="mappingResources"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;list&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;catalog/model/businessobject/Product.hbm.xml&lt;/value&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;catalog/model/businessobject/Category.hbm.xml&lt;/value&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;catalog/model/businessobject/User.hbm.xml&lt;/value&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/list&gt;<BR>&nbsp;&nbsp; &lt;/property&gt;<BR>&nbsp;&nbsp; &lt;property name="hibernateProperties"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;props&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;prop key="hibernate.dialect"&gt;net.sf.hibernate.dialect.MySQLDialect&lt;/prop&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;prop key="hibernate.show_sql"&gt;true&lt;/prop&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;prop key="hibernate.cglib.use_reflection_optimizer"&gt;true&lt;/prop&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;prop key="hibernate.cache.provider_class"&gt;net.sf.hibernate.cache.HashtableCacheProvider&lt;/prop&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/props&gt;<BR>&nbsp;&nbsp; &lt;/property&gt; <BR>&nbsp;&nbsp; &lt;property name="dataSource"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ref bean="dataSource"/&gt;<BR>&nbsp;&nbsp; &lt;/property&gt;<BR>&lt;/bean&gt;<BR></CODE>
<P><CODE>CatalogDao</CODE> uses <CODE>HibernateTemplate</CODE> to integrate between Hibernate and Spring. Here's the configuration for <CODE>HibernateTemplate</CODE>: 
<P><CODE>
<P>&lt;!-- Hibernate Template Defintion --&gt;<BR>&lt;bean id="hibernateTemplate" class="org.springframework.orm.hibernate.HibernateTemplate"&gt; <BR>&nbsp;&nbsp; &lt;property name="sessionFactory"&gt;&lt;ref bean="sessionFactory"/&gt;&lt;/property&gt; <BR>&nbsp;&nbsp; &lt;property name="jdbcExceptionTranslator"&gt;&lt;ref bean="jdbcExceptionTranslator"/&gt;&lt;/property&gt; <BR>&lt;/bean&gt; <BR></CODE>
<P></FONT><BR><BR></P></BLOCKQUOTE><FONT face="verdana, arial, helvetica" size=2><B>Integration层</B><BR>Hibernate通过xml配置文件来映射业务对象和关系数据库，在JCatalog中，Product.hbm.xml表示了Product对象的映射，Category.hbm.xml则用来表示Category的映射，Product.hbm.xml如下：<BR></FONT>
<BLOCKQUOTE><FONT face="verdana, arial, helvetica" size=2><CODE>
<P>&lt;?xml version="1.0"?&gt;<BR>&lt;!DOCTYPE hibernate-mapping PUBLIC <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"-//Hibernate/Hibernate Mapping DTD 2.0//EN"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd"&gt;<BR>&lt;hibernate-mapping package="catalog.model.businessobject"&gt;<BR>&nbsp;&nbsp; &lt;class name="Product" table="product"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;id name="id" column="ID" unsaved-value="null"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;generator class="assigned"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/id&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;property name="name" column="NAME" unique="true" not-null="true"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;property name="price" column="PRICE"/&gt;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;property name="width" column="WIDTH"/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;property name="height" column="height"/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;property name="description" column="description"/&gt; &nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;set name="categoryIds" table="product_category" cascade="all"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;key column="PRODUCT_ID"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;element column="CATEGORY_ID" type="string"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/set&gt;<BR>&nbsp;&nbsp; &lt;/class&gt;<BR>&lt;/hibernate-mapping&gt;<BR></CODE>
<P><CODE>CatalogDao</CODE> is wired with <CODE>HibernateTemplate</CODE> by Spring: 
<P><CODE>
<P>&lt;!-- Catalog DAO Definition: Hibernate implementation --&gt;<BR>&lt;bean id="catalogDao" class="catalog.model.dao.hibernate.CatalogDaoHibernateImpl"&gt;<BR>&nbsp;&nbsp; &lt;property name="hibernateTemplate"&gt;&lt;ref bean="hibernateTemplate"/&gt;&lt;/property&gt; <BR>&lt;/bean&gt; <BR></CODE>
<P></P></BLOCKQUOTE><FONT face="verdana, arial, helvetica" size=2><B><FONT size=5>结论</FONT></B><BR>本文主要讲述了如何将JSF与Spring、Hibernate整合在一起来构建实际的Web应用，这三种技术的组合提供了一个强大的Web应用开发框架。在Web应用的高层设计中应该采用多层构架体系，JSF非常适合MVC设计模式以实现表示层，Spring可用在业务逻辑层中管理业务对象，并提供事物管理和资源管理等，Spring与Hibernate结合的非常出色，Hibernate是强大的O/R映射框架，它可以在integration层中提供最好的服务。<BR><BR>通过将整个Web应用分割成多层，并借助于“编程到接口”，应用程序的每一层所采用的技术都是可替换的，例如Struts可以用来替换JSF，JDO可替换Hibernate。各层之间的整合不是不值得研究，采用IoC和ServiceLocator设计模式可使得整合非常容易。JSF提供了其它Web框架欠缺的功能，然而，这并不意味着你马上抛弃Struts而开始使用JSF，是否采用JSF取决于项目目前的状况和功能需求，以及开发团队的意见等。<BR><BR></FONT></FONT><img src ="http://www.blogjava.net/raozhh/aggbug/25743.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/raozhh/" target="_blank">饶志华</a> 2005-12-28 16:42 <a href="http://www.blogjava.net/raozhh/articles/25743.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用 Log4j 搭建一个日志服务器</title><link>http://www.blogjava.net/raozhh/articles/25185.html</link><dc:creator>饶志华</dc:creator><author>饶志华</author><pubDate>Fri, 23 Dec 2005 03:14:00 GMT</pubDate><guid>http://www.blogjava.net/raozhh/articles/25185.html</guid><wfw:comment>http://www.blogjava.net/raozhh/comments/25185.html</wfw:comment><comments>http://www.blogjava.net/raozhh/articles/25185.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/raozhh/comments/commentRss/25185.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/raozhh/services/trackbacks/25185.html</trackback:ping><description><![CDATA[<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD vAlign=top height=63>
<TABLE class=zhi12 cellSpacing=0 cellPadding=0 width="96%" align=center border=0>
<TBODY>
<TR>
<TD style="FONT-WEIGHT: normal; FONT-SIZE: 16pt; LINE-HEIGHT: normal; FONT-STYLE: normal; FONT-VARIANT: normal" height=49>
<DIV align=center><B><FONT color=#003399>
<P align=center>使用 Log4j 搭建一个日志服务器</P></FONT></B></DIV></TD></TR><INPUT type=hidden name=location1> 
<TR>
<TD height=28>
<DIV align=center>
<DIV align=center><IMG height=12 src="http://www.chinaitlab.com/www/news/images/c7.gif" width=17> ChinaITLab收集整理 <IMG height=12 src="http://www.chinaitlab.com/www/news/images/c2.gif" width=12> 2005-11-24 <IMG height=12 src="http://www.chinaitlab.com/www/news/images/c3.gif" width=13> <A href="javascript:window.external.AddFavorite('http://java.chinaitlab.com/tools/38093.html','使用 Log4j 搭建一个日志服务器|中国IT实验室')" target=_top>保存本文</A> <IMG height=18 src="http://www.chinaitlab.com/www/news/images/c5.gif" width=11> <A onclick="javascript:window.open('http://www.chinaitlab.com/www/news/sendfriend.asp?id1=32580&amp;location1='+document.all.location1.value)" href="http://java.chinaitlab.com/tools/38093.html">推荐给好友</A> <IMG height=17 src="http://www.chinaitlab.com/www/news/images/c6.gif" width=17> <A class=A01 style="CURSOR: hand" onclick="AddPortal('29987')">QQ上看本站</A> <IMG height=12 src="http://www.chinaitlab.com/www/news/images/c1.gif" width=13> <A href="javascript:window.external.AddFavorite('http://www.chinaitlab.com','中国IT认证实验室---全球中文IT教育门户')" target=_top>收藏本站</A> </DIV></DIV></TD></TR></TBODY></TABLE>
<TABLE style="WIDTH: 85%" cellSpacing=0 cellPadding=0 width="85%" align=center border=0>
<TBODY>
<TR>
<TD>
<HR>
</TD></TR></TBODY></TABLE></TD></TR>
<TR>
<TD vAlign=top>
<TABLE class=zhi14 style="WIDTH: 620px; HEIGHT: 38px" height=38 cellSpacing=0 cellPadding=0 width=620 align=center border=0>
<TBODY>
<TR>
<TD width=648 height=20><BR>　　最近项目要用到日志服务器，需要把所有服务器的日志统一存入一个日志文件服务器中，于是就想到了Log4j的SocketAppender。<BR>　　<BR>　　网上一通搜索，终于找到了相关的只言片语，内容几乎雷同，和examples\lf5\UsingSocketAppenders中提供的例子没有什么区别！<BR>　　<BR>　　只好自己研究了！<BR>　　<BR>　　<B>1.基本使用<BR>　　<BR>　　1.1服务器</B><BR>　　<BR>　　这个日志服务器的服务器端需要运行：<BR>　　<BR>　　log4j jar包中的org.apache.log4j.net.SocketServer<BR>　　<BR>　　加参数 【本地监听端口】【配置文件】【客户端配置文件目录】<BR>　　<BR>　　第三个参数【配置文件目录】其实指的是针对每个客户端的配置文件，等会详细讲！现在用“.”就可以了.<BR>　　<BR>　　服务器端的配置文件可以用这个（引自利用Log4j 创建日志服务器 By ?の?）：<BR>　　<BR>　　#文件名socketserver.properties<BR>　　#如果需要显示日志界面，可以将本行启用<BR>　　#log4j.rootCategory=, A1<BR>　　log4j.rootLogger=DEBUG,A3<BR>　　log4j.category.org.apache.log4j.net=INFO<BR>　　<BR>　　log4j.appender.A1=org.apache.log4j.lf5.LF5Appender<BR>　　log4j.appender.A1.MaxNumberOfRecords=700<BR>　　<BR>　　log4j.appender.A4=org.apache.log4j.DailyRollingFileAppender<BR>　　log4j.appender.A4.file=server.log<BR>　　log4j.appender.A4.DatePattern='.'yyyyMMdd<BR>　　log4j.appender.A4.layout=org.apache.log4j.PatternLayout<BR>　　log4j.appender.A4.layout.ConversionPattern=\n\n[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n<BR>　　<BR>　　log4j.appender.A3=org.apache.log4j.RollingFileAppender<BR>　　log4j.appender.A3.file=server2.log<BR>　　log4j.appender.A3.MaxFileSize=1024KB<BR>　　log4j.appender.A3.MaxBackupIndex=999<BR>　　log4j.appender.A3.layout=org.apache.log4j.PatternLayout<BR>　　log4j.appender.A3.layout.ConversionPattern=\n\n[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n<BR>　　<BR>　　其中A1是启动Lf5的log监视终端，A3限制大小的文件，A4是日期滚动文件。<BR>　　<BR>　　单A3，A4是讲所有客户端的日志都存放到了同一个日志文件中，我觉的这种方法并不好。<BR>　　<BR>　　<B>1.2客户端</B><BR>　　<BR>　　客户端的配置文件是这样的：<BR>　　log4j.rootCategory=,SOCKET<BR>　　log4j.addivity.org.apache=true<BR>　　<BR>　　#应用于socket<BR>　　log4j.appender.SOCKET=org.apache.log4j.net.SocketAppender<BR>　　log4j.appender.SOCKET.RemoteHost=localhost　 #服务器的IP地址<BR>　　log4j.appender.SOCKET.Port=1978　　　　　　　#服务器的监听端口<BR>　　log4j.appender.SOCKET.LocationInfo=true　　　#这个是什么我不知道<BR>　　log4j.appender.SOCKET.layout=org.apache.log4j.PatternLayout<BR>　　log4j.appender.SOCKET.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%t%m%n<BR>　　<BR>　　#A2<BR>　　log4j.appender.A2=org.apache.log4j.DailyRollingFileAppender<BR>　　log4j.appender.A2.file=server.log<BR>　　log4j.appender.A2.DatePattern='.'yyyy-MM-dd<BR>　　log4j.appender.A2.layout=org.apache.log4j.PatternLayout<BR>　　log4j.appender.A2.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n<BR>　　<BR>　　启动服务器端,再运行客户端就可以了!但所有的服务器端/客户端的日志都放到了一个日志文件中!<BR>　　<BR>　　<B>2.稍微高级点的使用</B><BR>　　<BR>　　下面讲如何把各个客户端和服务器端的日志分别放到不同的日志文件中.<BR>　　<BR>　　这个我在网上找了好久也没有找到!<BR>　　<BR>　　<B>2.1服务器</B><BR>　　<BR>　　服务器的配置文件不用怎么改动，如果你不需要在同一个文件中存放所有日志，可以把配置文件第一行的A3去掉。<BR>　　<BR>　　但服务器端有个更大的麻烦：代码有问题！问题够大了吧，不知道算不算是个bug（我用的是1.2.11版log4j）。<BR>　　<BR>　　改吧!<BR>　　<BR>　　打开log4j目录下的src\java\org\apache\log4j\net\SocketServer.java<BR>　　<BR>　　在这段中改动(看下面代码第12行)<BR>　　<BR>　　LoggerRepository configureHierarchy(InetAddress inetAddress) {<BR>　　cat.info("Locating configuration file for "+inetAddress);<BR>　　// We assume that the toSting method of InetAddress returns is in<BR>　　// the format hostname/d1.d2.d3.d4 e.g. torino/192.168.1.1<BR>　　String s = inetAddress.toString();<BR>　　int i = s.indexOf("/");<BR>　　if(i == -1) {<BR>　　cat.warn("Could not parse the inetAddress ["+inetAddress+<BR>　　"]. Using default hierarchy.");<BR>　　return genericHierarchy();<BR>　　} else {<BR>　　//这个是什么意思,专门取"/"符号吗?明显是错的!闭掉<BR>　　//　　String key = s.substring(0, i);<BR>　　//改为<BR>　　String key = s.substring(i+1);<BR>　　File configFile = new File(dir, key+CONFIG_FILE_EXT);<BR>　　if(configFile.exists()) {<BR>　　Hierarchy h = new Hierarchy(new RootLogger((Level) Priority.DEBUG));<BR>　　hierarchyMap.put(inetAddress, h);<BR>　　<BR>　　new PropertyConfigurator().doConfigure(configFile.getAbsolutePath(), h);<BR>　　<BR>　　return h;<BR>　　} else {<BR>　　cat.warn("Could not find config file ["+configFile+"].");<BR>　　return genericHierarchy();<BR>　　}<BR>　　}<BR>　　}<BR>　　<BR>　　编译文件!<BR>　　<BR>　　打开log4j目录下的src\java\org\apache\log4j\net\SocketNode.java<BR>　　<BR>　　(改这段是因为我用的时候出错!看不出来改不改有什么区别)<BR>　　<BR>　　改第54行<BR>　　<BR>　　ois = new ObjectInputStream(<BR>　　new BufferedInputStream(socket.getInputStream()));<BR>　　<BR>　　为<BR>　　<BR>　　InputStream is = socket.getInputStream();<BR>　　if (is != null) {<BR>　　ois = new ObjectInputStream(new BufferedInputStream(is));<BR>　　}<BR>　　<BR>　　文件头加 import java.io.InputStream;<BR>　　<BR>　　编译文件!<BR>　　<BR>　　现在为每个配置客户端编配置文件,把配置文件放到【客户端配置文件目录】中:<BR>　　log4j.rootCategory=,A4<BR>　　log4j.appender.A4=org.apache.log4j.DailyRollingFileAppender<BR>　　log4j.appender.A4.file=127.0.0.1.log　 #为每个客户端取不同的名字<BR>　　log4j.appender.A4.DatePattern='.'yyyyMMdd<BR>　　log4j.appender.A4.layout=org.apache.log4j.PatternLayout<BR>　　log4j.appender.A4.layout.ConversionPattern=\n\n[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n<BR>　　<BR>　　保存文件名为[客户端ip地址].lcf　如192.168.0.126.lcf<BR>　　<BR>　　<B>2.2 客户端</B><BR>　　<BR>　　客户端不用改变,太幸运了!!<BR>　　<BR>　　ok啦!<BR>　　<BR>　　启动服务器,启动客户端,现在服务器的日志放到了server.log中,有配置文件的客户端的日志会放到相应的日志文件中,没有配置文件的客户端的日志依然放在server.log中!<BR>　　<BR>　　SocketServer.java 和 SocketNode.java两个文件可以单独做一个工程,把他们的package去掉就行了!</TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/raozhh/aggbug/25185.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/raozhh/" target="_blank">饶志华</a> 2005-12-23 11:14 <a href="http://www.blogjava.net/raozhh/articles/25185.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>log4j参数配置说明</title><link>http://www.blogjava.net/raozhh/articles/25183.html</link><dc:creator>饶志华</dc:creator><author>饶志华</author><pubDate>Fri, 23 Dec 2005 03:10:00 GMT</pubDate><guid>http://www.blogjava.net/raozhh/articles/25183.html</guid><wfw:comment>http://www.blogjava.net/raozhh/comments/25183.html</wfw:comment><comments>http://www.blogjava.net/raozhh/articles/25183.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/raozhh/comments/commentRss/25183.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/raozhh/services/trackbacks/25183.html</trackback:ping><description><![CDATA[<DIV id=category>log4j参数配置说明- -</DIV>
<DIV class=entity>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
<P>
<DIV class=postText>
<P>　　Log4j由三个重要的组件构成：日志信息的优先级，日志信息的输出目的地，日志信息的输出格式。日志信息的优先级从高到低有ERROR、WARN、INFO、DEBUG，分别用来指定这条日志信息的重要程度；日志信息的输出目的地指定了日志将打印到控制台还是文件中；而输出格式则控制了日志信息的显示内容。</P>
<P>　　<STRONG>一、定义配置文件</STRONG></P>
<P>　　其实您也可以完全不使用配置文件，而是在代码中配置Log4j环境。但是，使用配置文件将使您的应用程序更加灵活。Log4j支持两种配置文件格式，一种是XML格式的文件，一种是Java特性文件（键=值）。下面我们介绍使用Java特性文件做为配置文件的方法：</P>
<P>　　1.配置根Logger，其语法为：</P>
<P>　　log4j.rootLogger = [ level ] , appenderName, appenderName, … </P>
<P>　　其中，level 是日志记录的优先级，分为OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL或者您定义的级别。Log4j建议只使用四个级别，优先级从高到低分别是ERROR、WARN、INFO、DEBUG。通过在这里定义的级别，您可以控制到应用程序中相应级别的日志信息的开关。比如在这里定义了INFO级别，则应用程序中所有DEBUG级别的日志信息将不被打印出来。 appenderName就是指B日志信息输出到哪个地方。您可以同时指定多个输出目的地。 </P>
<P>　　2.配置日志信息输出目的地Appender，其语法为：</P>
<P>　　log4j.appender.appenderName = fully.qualified.name.of.appender.class <BR>　　log4j.appender.appenderName.option1 = value1 <BR>　　… <BR>　　log4j.appender.appenderName.option = valueN </P>
<P>　　其中，Log4j提供的appender有以下几种： <BR>　　org.apache.log4j.ConsoleAppender（控制台）， <BR>　　org.apache.log4j.FileAppender（文件）， <BR>　　org.apache.log4j.DailyRollingFileAppender（每天产生一个日志文件），<BR>　　org.apache.log4j.RollingFileAppender（文件大小到达指定尺寸的时候产生一个新的文件）， <BR>　　org.apache.log4j.WriterAppender（将日志信息以流格式发送到任意指定的地方） </P>
<P>　　3.配置日志信息的格式（布局），其语法为：</P>
<P>　　log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class <BR>　　log4j.appender.appenderName.layout.option1 = value1 <BR>　　… <BR>　　log4j.appender.appenderName.layout.option = valueN </P>
<P>　　其中，Log4j提供的layout有以e几种： <BR>　　org.apache.log4j.HTMLLayout（以HTML表格形式布局）， <BR>　　org.apache.log4j.PatternLayout（可以灵活地指定布局模式）， <BR>　　org.apache.log4j.SimpleLayout（包含日志信息的级别和信息字符串）， <BR>　　org.apache.log4j.TTCCLayout（包含日志产生的时间、线程、类别等等信息） </P>
<P>　　Log4J采用类似C语言中的printf函数的打印格式格式化日志信息，打印参数如下： %m 输出代码中指定的消息</P>
<P>　　%p 输出优先级，即DEBUG，INFO，WARN，ERROR，FATAL <BR>　　%r 输出自应用启动到输出该log信息耗费的毫秒数 <BR>　　%c 输出所属的类目，通常就是所在类的全名 <BR>　　%t 输出产生该日志事件的线程名 <BR>　　%n 输出一个回车换行符，Windows平台为“\r\n”，Unix平台为“\n” <BR>　　%d 输出日志时间点的日期或时间，默认格式为ISO8601，也可以在其后指定格式，比如：%d{yyy MMM dd HH:mm:ss,SSS}，输出类似：2002年10月18日 22：10：28，921 <BR>　　%l 输出日志事件的发生位置，包括类目名、发生的线程，以及在代码中的行数。举例：Testlog4.main(TestLog4.java:10) </P>
<P>　　<STRONG>二、在代码中使用Log4j</STRONG> </P>
<P>　　1.得到记录器</P>
<P>　　使用Log4j，第一步就是获取日志记录器，这个记录器将负责控制日志信息。其语法为： </P>
<P>　　public static Logger getLogger( String name) </P>
<P>　　通过指定的名字获得记录器，如果必要的话，则为这个名字创建一个新的记录器。Name一般取本类的名字，比如： </P>
<P>　　static Logger logger = Logger.getLogger ( ServerWithLog4j.class.getName () )</P>
<P>　　2.读取配置文件 </P>
<P>　　当获得了日志记录器之后，第二步将配置Log4j环境，其语法为：</P>
<P>　　BasicConfigurator.configure ()： 自动快速地使用缺省Log4j环境。<BR>　　PropertyConfigurator.configure ( String configFilename) ：读取使用Java的特性文件编写的配置文件。<BR>　　DOMConfigurator.configure ( String filename ) ：读取XML形式的配置文件。</P>
<P>　　3.插入记录信息（格式化日志信息）</P>
<P>　　当上两个必要步骤执行完毕，您就可以轻松地使用不同优先级别的日志记录语句插入到您想记录日志的任何地方，其语法如下：</P>
<P>　　Logger.debug ( Object message ) ;<BR>　　Logger.info ( Object message ) ;<BR>　　Logger.warn ( Object message ) ;<BR>　　Logger.error ( Object message ) ;</P></DIV>
<P></P>
<P class=diaryFoot>- 作者： <A onclick="window.open('http://publishblog.blogdriver.com/blog/postMessage.b?receiver=27938','发送短消息','width=520, height=455')" href="javascript:void(0);">icefire</A> 2005年05月18日, 星期三 17:22 </P></DIV><img src ="http://www.blogjava.net/raozhh/aggbug/25183.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/raozhh/" target="_blank">饶志华</a> 2005-12-23 11:10 <a href="http://www.blogjava.net/raozhh/articles/25183.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java调用存储过程 </title><link>http://www.blogjava.net/raozhh/articles/25167.html</link><dc:creator>饶志华</dc:creator><author>饶志华</author><pubDate>Fri, 23 Dec 2005 02:14:00 GMT</pubDate><guid>http://www.blogjava.net/raozhh/articles/25167.html</guid><wfw:comment>http://www.blogjava.net/raozhh/comments/25167.html</wfw:comment><comments>http://www.blogjava.net/raozhh/articles/25167.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/raozhh/comments/commentRss/25167.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/raozhh/services/trackbacks/25167.html</trackback:ping><description><![CDATA[<P><SPAN class=style2><STRONG>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD colSpan=2><SPAN class=style2><STRONG>java调用存储过程 </STRONG></SPAN><STRONG><BR></STRONG>
<HR align=left width="95%" color=#597282 noShade SIZE=1>
<SPAN class=style3>By&nbsp;zxjygx 发表于 2005-10-17 21:40:00</SPAN></TD></TR>
<TR>
<TD colSpan=2><BR><SPAN class=style6>
<P align=center><FONT size=5>Java 调用存储过程</FONT></P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 本文阐述了怎么使用DBMS存储过程。我阐述了使用存储过程的基本的和高级特性，比如返回ResultSet。本文假设你对DBMS和JDBC已经非常熟悉，也假设你能够毫无障碍地阅读其它语言写成的代码（即不是Java的语言），但是，并不要求你有任何存储过程的编程经历。 <BR>存储过程是指保存在数据库并在数据库端执行的程序。你可以使用特殊的语法在Java类中调用存储过程。在调用时，存储过程的名称及指定的参数通过JDBC连接发送给DBMS，执行存储过程并通过连接（如果有）返回结果。 <BR>使用存储过程拥有和使用基于EJB或CORBA这样的应用服务器一样的好处。区别是存储过程可以从很多流行的DBMS中免费使用，而应用服务器大都非常昂贵。这并不只是许可证费用的问题。使用应用服务器所需要花费的管理、编写代码的费用，以及客户程序所增加的复杂性，都可以通过DBMS中的存储过程所整个地替代。 <BR>你可以使用Java，Python，Perl或C编写存储过程，但是通常使用你的DBMS所指定的特定语言。Oracle使用PL/SQL，PostgreSQL使用pl/pgsql，DB2使用Procedural&nbsp;SQL。这些语言都非常相似。在它们之间移植存储过程并不比在Sun的EJB规范不同实现版本之间移植Session&nbsp;Bean困难。并且，存储过程是为嵌入SQL所设计，这使得它们比Java或C等语言更加友好地方式表达数据库的机制。 <BR>因为存储过程运行在DBMS自身，这可以帮助减少应用程序中的等待时间。不是在Java代码中执行4个或5个SQL语句，而只需要在服务器端执行1个存储过程。网络上的数据往返次数的减少可以戏剧性地优化性能。 <BR><BR><FONT color=#5ea294><STRONG>使用存储过程 </STRONG><BR><BR></FONT>简单的老的JDBC通过CallableStatement类支持存储过程的调用。该类实际上是PreparedStatement的一个子类。假设我们有一个poets数据库。数据库中有一个设置诗人逝世年龄的存储过程。下面是对老酒鬼Dylan&nbsp;Thomas（old&nbsp;soak&nbsp;Dylan&nbsp;Thomas，不指定是否有关典故、文化，请批评指正。译注）进行调用的详细代码： <BR><BR>try{ 
<P>&nbsp;&nbsp;&nbsp;&nbsp; int&nbsp;age&nbsp;=&nbsp;39;&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp; String&nbsp;poetName&nbsp;=&nbsp;"dylan&nbsp;thomas";&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp; CallableStatement&nbsp;proc&nbsp;=&nbsp;connection.prepareCall("{&nbsp;call&nbsp;set_death_age(?,&nbsp;?)&nbsp;}");&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp; proc.setString(1,&nbsp;poetName);&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp; proc.setInt(2,&nbsp;age);&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp; cs.execute();</P>
<P>}catch&nbsp;(SQLException&nbsp;e){&nbsp;//&nbsp;....} <BR><BR>传给prepareCall方法的字串是存储过程调用的书写规范。它指定了存储过程的名称，？代表了你需要指定的参数。 <BR>和JDBC集成是存储过程的一个很大的便利：为了从应用中调用存储过程，不需要存根（stub）类或者配置文件，除了你的DBMS的JDBC驱动程序外什么也不需要。 <BR>当这段代码执行时，数据库的存储过程就被调用。我们没有去获取结果，因为该存储过程并不返回结果。执行成功或失败将通过例外得知。失败可能意味着调用存储过程时的失败（比如提供的一个参数的类型不正确），或者一个应用程序的失败（比如抛出一个例外指示在poets数据库中并不存在“Dylan&nbsp;Thomas”） <BR><BR><FONT color=#4db3b3>结合SQL操作与存储过程 <BR><BR></FONT>映射Java对象到SQL表中的行相当简单，但是通常需要执行几个SQL语句；可能是一个SELECT查找ID，然后一个INSERT插入指定ID的数据。在高度规格化（符合更高的范式，译注）的数据库模式中，可能需要多个表的更新，因此需要更多的语句。Java代码会很快地膨胀，每一个语句的网络开销也迅速增加。 <BR>将这些SQL语句转移到一个存储过程中将大大简化代码，仅涉及一次网络调用。所有关联的SQL操作都可以在数据库内部发生。并且，存储过程语言，例如PL/SQL，允许使用SQL语法，这比Java代码更加自然。下面是我们早期的存储过程，使用Oracle的PL/SQL语言编写： <BR><BR>create&nbsp;procedure&nbsp;set_death_age(poet&nbsp;VARCHAR2,&nbsp;poet_age&nbsp;NUMBER)&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp; poet_id&nbsp;NUMBER;</P>
<P>&nbsp;&nbsp;&nbsp; begin&nbsp;SELECT&nbsp;id&nbsp;INTO&nbsp;poet_id&nbsp;FROM&nbsp;poets&nbsp;WHERE&nbsp;name&nbsp;=&nbsp;poet;&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp; INSERT&nbsp;INTO&nbsp;deaths&nbsp;(mort_id,&nbsp;age)&nbsp;VALUES&nbsp;(poet_id,&nbsp;poet_age);</P>
<P>end&nbsp;set_death_age; <BR><BR>很独特？不。我打赌你一定期待看到一个poets表上的UPDATE。这也暗示了使用存储过程实现是多么容易的一件事情。set_death_age几乎可以肯定是一个很烂的实现。我们应该在poets表中添加一列来存储逝世年龄。Java代码中并不关心数据库模式是怎么实现的，因为它仅调用存储过程。我们以后可以改变数据库模式以提高性能，但是我们不必修改我们代码。 <BR>下面是调用上面存储过程的Java代码： <BR><BR>public&nbsp;static&nbsp;void&nbsp;setDeathAge(Poet&nbsp;dyingBard,&nbsp;int&nbsp;age)&nbsp;throws&nbsp;SQLException{&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp; Connection&nbsp;con&nbsp;=&nbsp;null;&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp; CallableStatement&nbsp;proc&nbsp;=&nbsp;null;&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp; try&nbsp;{</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; con&nbsp;=&nbsp;connectionPool.getConnection();&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; proc&nbsp;=&nbsp;con.prepareCall("{&nbsp;call&nbsp;set_death_age(?,&nbsp;?)&nbsp;}");</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; proc.setString(1,&nbsp;dyingBard.getName());&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; proc.setInt(2,&nbsp;age);&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;proc.execute();&nbsp;</P>
<P>}&nbsp;</P>
<P>&nbsp;&nbsp; finally&nbsp;{&nbsp; </P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try&nbsp;{&nbsp;proc.close();&nbsp;}&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;catch&nbsp;(SQLException&nbsp;e)&nbsp;{}&nbsp; </P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; con.close();&nbsp;</P>
<P>&nbsp;&nbsp; }</P>
<P>} <BR><BR>为了确保可维护性，建议使用像这儿这样的static方法。这也使得调用存储过程的代码集中在一个简单的模版代码中。如果你用到许多存储过程，就会发现仅需要拷贝、粘贴就可以创建新的方法。因为代码的模版化，甚至也可以通过脚本自动生产调用存储过程的代码。 <BR><BR>Functions <BR><BR>存储过程可以有返回值，所以CallableStatement类有类似getResultSet这样的方法来获取返回值。当存储过程返回一个值时，你必须使用registerOutParameter方法告诉JDBC驱动器该值的SQL类型是什么。你也必须调整存储过程调用来指示该过程返回一个值。 <BR>下面接着上面的例子。这次我们查询Dylan&nbsp;Thomas逝世时的年龄。这次的存储过程使用PostgreSQL的pl/pgsql： <BR><BR>create&nbsp;function&nbsp;snuffed_it_when&nbsp;(VARCHAR)&nbsp;returns&nbsp;integer&nbsp;'declare&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; poet_id&nbsp;NUMBER;&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; poet_age&nbsp;NUMBER;</P>
<P>begin&nbsp;</P>
<P>--first&nbsp;get&nbsp;the&nbsp;id&nbsp;associated&nbsp;with&nbsp;the&nbsp;poet.&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SELECT&nbsp;id&nbsp;INTO&nbsp;poet_id&nbsp;FROM&nbsp;poets&nbsp;WHERE&nbsp;name&nbsp;=&nbsp;$1;</P>
<P>&nbsp;--get&nbsp;and&nbsp;return&nbsp;the&nbsp;age.&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SELECT&nbsp;age&nbsp;INTO&nbsp;poet_age&nbsp;FROM&nbsp;deaths&nbsp;WHERE&nbsp;mort_id&nbsp;=&nbsp;poet_id;&nbsp;</P>
<P>return&nbsp;age;</P>
<P>end;'&nbsp;language&nbsp;'pl/pgsql'; <BR><BR>另外，注意pl/pgsql参数名通过Unix和DOS脚本的$n语法引用。同时，也注意嵌入的注释，这是和Java代码相比的另一个优越性。在Java中写这样的注释当然是可以的，但是看起来很凌乱，并且和SQL语句脱节，必须嵌入到Java&nbsp;String中。 <BR>下面是调用这个存储过程的Java代码： <BR><BR>connection.setAutoCommit(false);</P>
<P>CallableStatement&nbsp;proc&nbsp;=&nbsp;connection.prepareCall("{&nbsp;?&nbsp;=&nbsp;call&nbsp;snuffed_it_when(?)&nbsp;}");</P>
<P>proc.registerOutParameter(1,&nbsp;Types.INTEGER);</P>
<P>proc.setString(2,&nbsp;poetName);</P>
<P>cs.execute();</P>
<P>int&nbsp;age&nbsp;=&nbsp;proc.getInt(2); <BR><BR>如果指定了错误的返回值类型会怎样？那么，当调用存储过程时将抛出一个RuntimeException，正如你在ResultSet操作中使用了一个错误的类型所碰到的一样。 <BR><BR><FONT color=#4d9eb3>复杂的返回值 <BR><BR></FONT>关于存储过程的知识，很多人好像就熟悉我们所讨论的这些。如果这是存储过程的全部功能，那么存储过程就不是其它远程执行机制的替换方案了。存储过程的功能比这强大得多。 <BR>当你执行一个SQL查询时，DBMS创建一个叫做cursor（游标）的数据库对象，用于在返回结果中迭代每一行。ResultSet是当前时间点的游标的一个表示。这就是为什么没有缓存或者特定数据库的支持，你只能在ResultSet中向前移动。 <BR>某些DBMS允许从存储过程中返回游标的一个引用。JDBC并不支持这个功能，但是Oracle、PostgreSQL和DB2的JDBC驱动器都支持在ResultSet上打开到游标的指针（pointer）。 <BR>设想列出所有没有活到退休年龄的诗人，下面是完成这个功能的存储过程，返回一个打开的游标，同样也使用PostgreSQL的pl/pgsql语言： <BR><BR>create&nbsp;procedure&nbsp;list_early_deaths&nbsp;()&nbsp;return&nbsp;refcursor&nbsp;as&nbsp;'declare&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp; toesup&nbsp;refcursor;</P>
<P>begin&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp; open&nbsp;toesup&nbsp;for&nbsp;SELECT&nbsp;poets.name,&nbsp;deaths.age&nbsp;FROM&nbsp;poets,&nbsp;deaths&nbsp;--&nbsp;all&nbsp;entries&nbsp;in&nbsp;deaths&nbsp;are&nbsp;for&nbsp;poets.&nbsp;--&nbsp;but&nbsp;the&nbsp;table&nbsp;might&nbsp;become&nbsp;generic.&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp; WHERE&nbsp;poets.id&nbsp;=&nbsp;deaths.mort_id&nbsp;AND&nbsp;deaths.age&nbsp;&lt;&nbsp;60;&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp; return&nbsp;toesup;</P>
<P>end;'&nbsp;language&nbsp;'plpgsql'; <BR><BR>下面是调用该存储过程的Java方法，将结果输出到PrintWriter： <BR>PrintWriter: <BR><BR>static&nbsp;void&nbsp;sendEarlyDeaths(PrintWriter&nbsp;out){&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp; Connection&nbsp;con&nbsp;=&nbsp;null;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;CallableStatement&nbsp;toesUp&nbsp;=&nbsp;null;</P>
<P>&nbsp; &nbsp; try&nbsp;{</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; con&nbsp;=&nbsp;ConnectionPool.getConnection();&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;PostgreSQL&nbsp;needs&nbsp;a&nbsp;transaction&nbsp;to&nbsp;do&nbsp;this...&nbsp;con.</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setAutoCommit(false);&nbsp;//&nbsp;Setup&nbsp;the&nbsp;call.&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CallableStatement&nbsp;toesUp&nbsp;=&nbsp;connection.prepareCall("{&nbsp;?&nbsp;=&nbsp;call&nbsp;list_early_deaths&nbsp;()&nbsp;}");&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; toesUp.registerOutParameter(1,&nbsp;Types.OTHER);&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;toesUp.execute();&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ResultSet&nbsp;rs&nbsp;=&nbsp;(ResultSet)&nbsp;toesUp.getObject(1);&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while&nbsp;(rs.next())&nbsp;{</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String&nbsp;name&nbsp;=&nbsp;rs.getString(1);</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int&nbsp;age&nbsp;=&nbsp;rs.getInt(2);&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.println(name&nbsp;+&nbsp;"&nbsp;was&nbsp;"&nbsp;+&nbsp;age&nbsp;+&nbsp;"&nbsp;years&nbsp;old.");&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rs.close();&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp; </P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;catch&nbsp;(SQLException&nbsp;e)&nbsp;{&nbsp;//&nbsp;We&nbsp;should&nbsp;protect&nbsp;these&nbsp;calls.&nbsp;toesUp.close();&nbsp;con.close();</P>
<P>&nbsp;&nbsp;&nbsp; }</P>
<P>} <BR><BR>因为JDBC并不直接支持从存储过程中返回游标，我们使用Types.OTHER来指示存储过程的返回类型，然后调用getObject()方法并对返回值进行强制类型转换。 <BR>这个调用存储过程的Java方法是mapping的一个好例子。Mapping是对一个集上的操作进行抽象的方法。不是在这个过程上返回一个集，我们可以把操作传送进去执行。本例中，操作就是把ResultSet打印到一个输出流。这是一个值得举例的很常用的例子，下面是调用同一个存储过程的另外一个方法实现： <BR><BR>public&nbsp;class&nbsp;ProcessPoetDeaths{&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp; public&nbsp;abstract&nbsp;void&nbsp;sendDeath(String&nbsp;name,&nbsp;int&nbsp;age);</P>
<P>}</P>
<P>&nbsp;&nbsp;&nbsp; static&nbsp;void&nbsp;mapEarlyDeaths(ProcessPoetDeaths&nbsp;mapper){&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp; Connection&nbsp;con&nbsp;=&nbsp;null;&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp; CallableStatement&nbsp;toesUp&nbsp;=&nbsp;null;&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp; try&nbsp;{</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; con&nbsp;=&nbsp;ConnectionPool.getConnection();&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; con.setAutoCommit(false);&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CallableStatement&nbsp;toesUp&nbsp;=&nbsp;connection.prepareCall("{&nbsp;?&nbsp;=&nbsp;call&nbsp;list_early_deaths&nbsp;()&nbsp;}");</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; toesUp.registerOutParameter(1,&nbsp;Types.OTHER);&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; toesUp.execute();&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ResultSet&nbsp;rs&nbsp;=&nbsp;(ResultSet)&nbsp;toesUp.getObject(1);&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while&nbsp;(rs.next())&nbsp;{&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String&nbsp;name&nbsp;=&nbsp;rs.getString(1);&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int&nbsp;age&nbsp;=&nbsp;rs.getInt(2);&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mapper.sendDeath(name,&nbsp;age);&nbsp;</P>
<P>}&nbsp;</P>
<P>rs.close();&nbsp;</P>
<P>}&nbsp;catch&nbsp;(SQLException&nbsp;e)&nbsp;{&nbsp;//&nbsp;We&nbsp;should&nbsp;protect&nbsp;these&nbsp;calls.&nbsp;toesUp.close();&nbsp;</P>
<P>con.close();&nbsp;</P>
<P>}</P>
<P>} <BR><BR>这允许在ResultSet数据上执行任意的处理，而不需要改变或者复制获取ResultSet的方法： <BR><BR>static&nbsp;void&nbsp;sendEarlyDeaths(final&nbsp;PrintWriter&nbsp;out){&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ProcessPoetDeaths&nbsp;myMapper&nbsp;=&nbsp;new&nbsp;ProcessPoetDeaths()&nbsp;{&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;void&nbsp;sendDeath(String&nbsp;name,&nbsp;int&nbsp;age)&nbsp;{&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.println(name&nbsp;+&nbsp;"&nbsp;was&nbsp;"&nbsp;+&nbsp;age&nbsp;+&nbsp;"&nbsp;years&nbsp;old.");&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };&nbsp;</P>
<P>mapEarlyDeaths(myMapper);</P>
<P>} <BR><BR>这个方法使用ProcessPoetDeaths的一个匿名实例调用mapEarlyDeaths。该实例拥有sendDeath方法的一个实现，和我们上面的例子一样的方式把结果写入到输出流。当然，这个技巧并不是存储过程特有的，但是和存储过程中返回的ResultSet结合使用，是一个非常强大的工具<FONT color=#f70968><FONT color=#808080>。 <BR><BR></FONT>结论 <BR></FONT><BR>存储过程可以帮助你在代码中分离逻辑，这基本上总是有益的。这个分离的好处有： <BR>&amp;#8226;&nbsp;快速创建应用，使用和应用一起改变和改善的数据库模式。&nbsp; <BR>&amp;#8226;&nbsp;数据库模式可以在以后改变而不影响Java对象，当我们完成应用后，可以重新设计更好的模式。 <BR>&amp;#8226;&nbsp;存储过程通过更好的SQL嵌入使得复杂的SQL更容易理解。 <BR>&amp;#8226;&nbsp;编写存储过程比在Java中编写嵌入的SQL拥有更好的工具－－大部分编辑器都提供语法高亮！ <BR>&amp;#8226;&nbsp;存储过程可以在任何SQL命令行中测试，这使得调试更加容易。 <BR><BR>并不是所有的数据库都支持存储过程，但是存在许多很棒的实现，包括免费/开源的和非免费的，所以移植并不是一个问题。Oracle、PostgreSQL和DB2都有类似的存储过程语言，并且有在线的社区很好地支持。 <BR>存储过程工具很多，有像TOAD或TORA这样的编辑器、调试器和IDE，提供了编写、维护PL/SQL或pl/pgsql的强大的环境。 <BR>存储过程确实增加了你的代码的开销，但是它们和大多数的应用服务器相比，开销小得多。如果你的代码复杂到需要使用DBMS，我建议整个采用存储过程的方式。</P>
<P> </P></SPAN></TD></TR></TBODY></TABLE></STRONG></SPAN></P><img src ="http://www.blogjava.net/raozhh/aggbug/25167.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/raozhh/" target="_blank">饶志华</a> 2005-12-23 10:14 <a href="http://www.blogjava.net/raozhh/articles/25167.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>