﻿<?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/TrampEagle/category/4708.html</link><description>学习和生活
</description><language>zh-cn</language><lastBuildDate>Wed, 26 Mar 2008 07:49:57 GMT</lastBuildDate><pubDate>Wed, 26 Mar 2008 07:49:57 GMT</pubDate><ttl>60</ttl><item><title>jboss seam 组件驱动的事件</title><link>http://www.blogjava.net/TrampEagle/archive/2008/03/25/188607.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Tue, 25 Mar 2008 14:49:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/archive/2008/03/25/188607.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/188607.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/archive/2008/03/25/188607.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/188607.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/188607.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: jboss seam 组件驱动的事件&nbsp;&nbsp;<a href='http://www.blogjava.net/TrampEagle/archive/2008/03/25/188607.html'>阅读全文</a><img src ="http://www.blogjava.net/TrampEagle/aggbug/188607.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2008-03-25 22:49 <a href="http://www.blogjava.net/TrampEagle/archive/2008/03/25/188607.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>jboss_seam初学入门建议</title><link>http://www.blogjava.net/TrampEagle/archive/2008/03/09/184803.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Sun, 09 Mar 2008 03:00:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/archive/2008/03/09/184803.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/184803.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/archive/2008/03/09/184803.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/184803.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/184803.html</trackback:ping><description><![CDATA[刚开始学习seam，太多东西需要了解，但是刚开始的一个入门HelloWorld搞了挺长时间，却一直没有跑起来，很是郁闷，后来还是老老实实的使用seam-gen生成了一个小例子，然后在上面一步步地展开，终于开始比较顺畅起来。<br />
<br />
另：jboss_seam2.1自带的很多例子都跑不起来，郁闷阿，不过可以当作学习事例，可以自己修改，呵呵，这样一想，也挺高兴的，也算是锻炼吧。<br />
<br />
现在发现，其实seam学习起来也不是那么困难，主要就是第一步，但第一步，对于我这个总喜欢自己敲代码而不喜欢借助工具的人来说，网上介绍的东西也太少了。<br />
<br />
所以，希望初学者，最好不要自己直接写代码，因为很容易遗漏某些jar包或配置文件，再说了，如果自己去写一个个的配置文件确实也太麻烦了一点。<br />
另外，seam-gen确实和Ruby-on-Rails有得一拼，确实也非常简单的。<br />
具体内容可以参考<br />
<a href="http://www.redsaga.com/opendoc/Seam2.0/html_single/">http://www.redsaga.com/opendoc/Seam2.0/html_single/</a> 
<img src ="http://www.blogjava.net/TrampEagle/aggbug/184803.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2008-03-09 11:00 <a href="http://www.blogjava.net/TrampEagle/archive/2008/03/09/184803.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Oracle数据库备份与恢复的三种方法（转）</title><link>http://www.blogjava.net/TrampEagle/archive/2006/11/19/82112.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Sun, 19 Nov 2006 14:05:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/archive/2006/11/19/82112.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/82112.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/archive/2006/11/19/82112.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/82112.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/82112.html</trackback:ping><description><![CDATA[
		<p>引自：<a href="http://searchdatabase.techtarget.com.cn/tips/421/2630421.shtml">http://searchdatabase.techtarget.com.cn/tips/421/2630421.shtml</a><br /><br /><br />Oracle数据库有三种标准的备份方法，它们分别是导出/导入(EXP/IMP)、热备份和冷备份。导出备件是一种逻辑备份，冷备份和热备份是物理备份。</p>
		<p>　　<strong>一、 导出/导入(Export/Import)</strong></p>
		<p>　　利用Export可将数据从数据库中提取出来，利用Import则可将提取出来的数据送回到Oracle数据库中去。</p>
		<p>　　<strong>1、 简单导出数据(Export)和导入数据(Import)</strong></p>
		<p>　　Oracle支持三种方式类型的输出:</p>
		<p>　　(1)、表方式(T方式)，将指定表的数据导出。</p>
		<p>　　(2)、用户方式(U方式)，将指定用户的所有对象及数据导出。</p>
		<p>　　(3)、全库方式(Full方式)，瘵数据库中的所有对象导出。</p>
		<p>　　数据导入(Import)的过程是数据导出(Export)的逆过程，分别将数据文件导入数据库和将数据库数据导出到数据文件。</p>
		<p>　　<strong>2、 增量导出/导入</strong></p>
		<p>　　增量导出是一种常用的数据备份方法，它只能对整个数据库来实施，并且必须作为SYSTEM来导出。在进行此种导出时，系统不要求回答任何问题。导出文件名缺省为export.dmp，如果不希望自己的输出文件定名为export.dmp，必须在命令行中指出要用的文件名。</p>
		<p>　　增量导出包括三种类型:</p>
		<p>　　(1)、“完全”增量导出(Complete)</p>
		<p>　　即备份三个数据库，比如: 
</p>
		<table style="BORDER-RIGHT: #cccccc 1px dotted; TABLE-LAYOUT: fixed; BORDER-TOP: #cccccc 1px dotted; BORDER-LEFT: #cccccc 1px dotted; BORDER-BOTTOM: #cccccc 1px dotted" cellspacing="0" cellpadding="6" width="95%" align="center" border="0">
				<tbody>
						<tr>
								<td style="WORD-WRAP: break-word" bgcolor="#f3f3f3">exp system/manager inctype=complete file=040731.dmp</td>
						</tr>
				</tbody>
		</table>
		<p>　　(2)、“增量型”增量导出</p>
		<p>　　备份上一次备份后改变的数据，比如: 
</p>
		<table style="BORDER-RIGHT: #cccccc 1px dotted; TABLE-LAYOUT: fixed; BORDER-TOP: #cccccc 1px dotted; BORDER-LEFT: #cccccc 1px dotted; BORDER-BOTTOM: #cccccc 1px dotted" cellspacing="0" cellpadding="6" width="95%" align="center" border="0">
				<tbody>
						<tr>
								<td style="WORD-WRAP: break-word" bgcolor="#f3f3f3">exp system/manager inctype=incremental file=040731.dmp</td>
						</tr>
				</tbody>
		</table>
		<p>　　(3)、“累积型”增量导出</p>
		<p>　　累计型导出方式是导出自上次“完全”导出之后数据库中变化了的信息。比如: 
</p>
		<table style="BORDER-RIGHT: #cccccc 1px dotted; TABLE-LAYOUT: fixed; BORDER-TOP: #cccccc 1px dotted; BORDER-LEFT: #cccccc 1px dotted; BORDER-BOTTOM: #cccccc 1px dotted" cellspacing="0" cellpadding="6" width="95%" align="center" border="0">
				<tbody>
						<tr>
								<td style="WORD-WRAP: break-word" bgcolor="#f3f3f3">exp system/manager inctype=cumulative file=040731.dmp</td>
						</tr>
				</tbody>
		</table>
		<p>　　数据库管理员可以排定一个备份日程表，用数据导出的三个不同方式合理高效的完成。</p>
		<p>　　比如数据库的被封任务可以做如下安排:</p>
		<p>　　星期一:完全备份(A)</p>
		<p>　　星期二:增量导出(B)</p>
		<p>　　星期三:增量导出(C)</p>
		<p>　　星期四:增量导出(D)</p>
		<p>　　星期五:累计导出(E)</p>
		<p>　　星期六:增量导出(F)</p>
		<p>　　星期日:增量导出(G)</p>
		<p>　　如果在星期日，数据库遭到意外破坏，数据库管理员可按一下步骤来回复数据库:</p>
		<p>　　第一步:用命令CREATE DATABASE重新生成数据库结构;</p>
		<p>　　第二步:创建一个足够大的附加回滚。</p>
		<p>　　第三步:完全增量导入A: 
</p>
		<table style="BORDER-RIGHT: #cccccc 1px dotted; TABLE-LAYOUT: fixed; BORDER-TOP: #cccccc 1px dotted; BORDER-LEFT: #cccccc 1px dotted; BORDER-BOTTOM: #cccccc 1px dotted" cellspacing="0" cellpadding="6" width="95%" align="center" border="0">
				<tbody>
						<tr>
								<td style="WORD-WRAP: break-word" bgcolor="#f3f3f3">imp system/manager inctype=RESTORE FULL=y FILE=A</td>
						</tr>
				</tbody>
		</table>
		<p>　　第四步:累计增量导入E: 
</p>
		<table style="BORDER-RIGHT: #cccccc 1px dotted; TABLE-LAYOUT: fixed; BORDER-TOP: #cccccc 1px dotted; BORDER-LEFT: #cccccc 1px dotted; BORDER-BOTTOM: #cccccc 1px dotted" cellspacing="0" cellpadding="6" width="95%" align="center" border="0">
				<tbody>
						<tr>
								<td style="WORD-WRAP: break-word" bgcolor="#f3f3f3">imp system/manager inctype=RESTORE FULL=Y FILE=E</td>
						</tr>
				</tbody>
		</table>
		<p>　　第五步:最近增量导入F: 
</p>
		<table style="BORDER-RIGHT: #cccccc 1px dotted; TABLE-LAYOUT: fixed; BORDER-TOP: #cccccc 1px dotted; BORDER-LEFT: #cccccc 1px dotted; BORDER-BOTTOM: #cccccc 1px dotted" cellspacing="0" cellpadding="6" width="95%" align="center" border="0">
				<tbody>
						<tr>
								<td style="WORD-WRAP: break-word" bgcolor="#f3f3f3">imp system/manager inctype=RESTORE FULL=Y FILE=F</td>
						</tr>
				</tbody>
		</table>
		<strong>二、 冷备份</strong>
		<p>　　冷备份发生在数据库已经正常关闭的情况下，当正常关闭时会提供给我们一个完整的数据库。冷备份时将关键性文件拷贝到另外的位置的一种说法。对于备份Oracle信息而言，冷备份时最快和最安全的方法。冷备份的优点是:</p>
		<p>　　1、 是非常快速的备份方法(只需拷文件)</p>
		<p>　　2、 容易归档(简单拷贝即可)</p>
		<p>　　3、 容易恢复到某个时间点上(只需将文件再拷贝回去)</p>
		<p>　　4、 能与归档方法相结合，做数据库“最佳状态”的恢复。</p>
		<p>　　5、 低度维护，高度安全。</p>
		<p>　　但冷备份也有如下不足:</p>
		<p>　　1、 单独使用时，只能提供到“某一时间点上”的恢复。</p>
		<p>　　2、 再实施备份的全过程中，数据库必须要作备份而不能作其他工作。也就是说，在冷备份过程中，数据库必须是关闭状态。</p>
		<p>　　3、 若磁盘空间有限，只能拷贝到磁带等其他外部存储设备上，速度会很慢。</p>
		<p>　　4、 不能按表或按用户恢复。</p>
		<p>　　如果可能的话(主要看效率)，应将信息备份到磁盘上，然后启动数据库(使用户可以工作)并将备份的信息拷贝到磁带上(拷贝的同时，数据库也可以工作)。冷备份中必须拷贝的文件包括:</p>
		<p>　　1、 所有数据文件</p>
		<p>　　2、 所有控制文件</p>
		<p>　　3、 所有联机REDO LOG文件</p>
		<p>　　4、 Init.ora文件(可选)</p>
		<p>　　值得注意的使冷备份必须在数据库关闭的情况下进行，当数据库处于打开状态时，执行数据库文件系统备份是无效的。</p>
		<p>　　下面是作冷备份的完整例子。</p>
		<p>　　(1) 关闭数据库 
</p>
		<table style="BORDER-RIGHT: #cccccc 1px dotted; TABLE-LAYOUT: fixed; BORDER-TOP: #cccccc 1px dotted; BORDER-LEFT: #cccccc 1px dotted; BORDER-BOTTOM: #cccccc 1px dotted" cellspacing="0" cellpadding="6" width="95%" align="center" border="0">
				<tbody>
						<tr>
								<td style="WORD-WRAP: break-word" bgcolor="#f3f3f3">
										<font face="Verdana">sqlplus /nolog <br />sql&gt;;connect /as sysdba <br />sql&gt;;shutdown normal;</font>
								</td>
						</tr>
				</tbody>
		</table>
		<p>　　(2) 用拷贝命令备份全部的时间文件、重做日志文件、控制文件、初始化参数文件 
</p>
		<table style="BORDER-RIGHT: #cccccc 1px dotted; TABLE-LAYOUT: fixed; BORDER-TOP: #cccccc 1px dotted; BORDER-LEFT: #cccccc 1px dotted; BORDER-BOTTOM: #cccccc 1px dotted" cellspacing="0" cellpadding="6" width="95%" align="center" border="0">
				<tbody>
						<tr>
								<td style="WORD-WRAP: break-word" bgcolor="#f3f3f3">sql&gt;;cp <file>; <backup directory="">;</backup></file></td>
						</tr>
				</tbody>
		</table>
		<p>　　(3) 重启Oracle数据库 
</p>
		<table style="BORDER-RIGHT: #cccccc 1px dotted; TABLE-LAYOUT: fixed; BORDER-TOP: #cccccc 1px dotted; BORDER-LEFT: #cccccc 1px dotted; BORDER-BOTTOM: #cccccc 1px dotted" cellspacing="0" cellpadding="6" width="95%" align="center" border="0">
				<tbody>
						<tr>
								<td style="WORD-WRAP: break-word" bgcolor="#f3f3f3">sql&gt;;startup</td>
						</tr>
				</tbody>
		</table>
		<p>　　<strong>三、 热备份</strong></p>
		<p>　　热备份是在数据库运行的情况下，采用archivelog mode方式备份数据库的方法。所以，如果你有昨天夜里的一个冷备份而且又有今天的热备份文件，在发生问题时，就可以利用这些资料恢复更多的信息。热备份要求数据库在Archivelog方式下操作，并需要大量的档案空间。一旦数据库运行在archivelog状态下，就可以做备份了。热备份的命令文件由三部分组成:</p>
		<p>　　1. 数据文件一个表空间一个表空间的备份。</p>
		<p>　　(1) 设置表空间为备份状态</p>
		<p>　　(2) 备份表空间的数据文件</p>
		<p>　　(3) 回复表空间为正常状态</p>
		<p>　　2. 备份归档log文件</p>
		<p>　　(1) 临时停止归档进程</p>
		<p>　　(2) log下那些在archive rede log目标目录中的文件</p>
		<p>　　(3) 重新启动archive进程</p>
		<p>　　(4) 备份归档的redo log文件</p>
		<p>　　3. 用alter database bachup controlfile命令来备份控制文件</p>
		<p>　　热备份的优点是:</p>
		<p>　　1. 可在表空间或数据库文件级备份，备份的时间短。</p>
		<p>　　2. 备份时数据库仍可使用。</p>
		<p>　　3. 可达到秒级恢复(恢复到某一时间点上)。</p>
		<p>　　4. 可对几乎所有数据库实体做恢复</p>
		<p>　　5. 恢复是快速的，在大多数情况下爱数据库仍工作时恢复。</p>
		<p>　　热备份的不足是:</p>
		<p>　　1. 不能出错，否则后果严重</p>
		<p>　　2. 若热备份不成功，所得结果不可用于时间点的恢复</p>
		<p>　　3. 因难于维护，所以要特别仔细小心，不允许“以失败告终”。</p>
<img src ="http://www.blogjava.net/TrampEagle/aggbug/82112.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-11-19 22:05 <a href="http://www.blogjava.net/TrampEagle/archive/2006/11/19/82112.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>oracle在windows平台上如何突破2g内存(转)</title><link>http://www.blogjava.net/TrampEagle/archive/2006/11/15/81168.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Wed, 15 Nov 2006 01:37:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/archive/2006/11/15/81168.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/81168.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/archive/2006/11/15/81168.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/81168.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/81168.html</trackback:ping><description><![CDATA[
		<p>引自：<a href="http://blog.oracle.com.cn/2446/viewspace_2056.html">http://blog.oracle.com.cn/2446/viewspace_2056.html</a><br /><br /><br /><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Arial; mso-fareast-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><br style="mso-special-character: line-break" /></span><font face="Arial">　　众所周知，在32位的操作系统如win2K上，操作系统能管理的内存为4GB(power(2,32) =4G) ，oracle使用的总内存有2G限制。目前大多数信息系统都能为数据库服务器配上4G物理内存甚至更多，但无论你为系统配置多大的物理内存，正常情况下都不能使oracle使用超过2G的内存(包含SGA、PGA等)，从而造成系统资源浪费。那么，有没有什么办法能使运行在windows系统上的oracle使用超过2G的内存呢？windows 提供了一种叫4GT(4G Tuning)的技术，使得oracle使用超过2G(不超过3G)的内存成为可能。而为了让应用程序使用更大的内存，还有一种PSE36的技术，可以让oracle使用超过3G的内存。这里，只对我们使用4GT特性让oracle使用超过2G的内存进行讨论。 </font></p>
		<p>
				<font face="Arial">一 使用4GT特性的基本要求： <br />....尽管可以在不超过2G物理内存的系统上使用4GT特性，但oracle并不推荐这种做法，因为这将会严重降低系统性能。以我的经验来讲，如果系统未安装4G物理内存，最好也不要使用这种方法来让oracle使用更多的内存。此外，在需要你的oracle使用超过2G的内存时，数据库服务器最好不要再运行其他的服务，以减小系统的压力，让oracle工作得更好。 <br />....另外，据MS的文档，4GT只能用于 Advanced Server , Datacenter版本上，Server 版以及 Professional 版不能使用4GT特性(实际上是不是也没有必要？) </font>
		</p>
		<p>
				<font face="Arial">二 为什么4GT能让应用程序使用超过2G的内存 <br />....在正常情况下，windows系统对内存的分配是这样的：在内存地址0x00000000 到 0x7FFFFFFF之间的空间，交给应用程序使用，操作系统内核及其支持则使用内存地址 0x80000000 到 0xFFFFFFFF之间的空间。在使用4GT之后，操作系统将内核及其支持程序使用的内存地址空间压缩到 0xC0000000 到 0xFFFFFFFF之间，从而为应用程序“让”出来1G的空间。但是，仅仅“让”出来了这1G的空间还是不够的，还需要指定哪一个应用程序来使用这“多出来的”1G空间，以及如何分配份额等。后面将结合具体的参数设置来讨论。为便于讨论，我们将正常情况下应用程序可以使用的2G内存(即地址在 0x00000000 到 0x7FFFFFFF 之间的内存)称作普通内存，而将windows “让”出来的1G内存(地址在 0x80000000 到 0xBFFFFFFF之间)称作间接内存。 </font>
		</p>
		<p>
				<font face="Arial">三 oracle使用超过2G内存，不超过3G内存的具体设置步骤： <br />1 首先应卸载实例，关闭oracle服务。 <br />2 打开操作系统3G开关： <br />....修改boot.ini文件，在启动windows项中添加 /3G 参数。修改过后的boot.ini文件应该类似以下内容： <br />[boot loader] <br />timeout=8 <br />default=multi(0)disk(0)rdisk(0)partition(1)<br />[operating systems] <br />multi(0)disk(0)rdisk(0)partition(1)="Microsoft Windows 2000 Advanced Server" /3G /fastdetect <br />注意在启动win2K AdvServer的一栏里，多了一个 /3G 参数。这个参数的作用，就是让windows启动时，将自己的内核及支持程序装载到内存地址 0xC0000000 到 0xFFFFFFFF 之间，给应用程序留出3G的空间来。 <br />3 修改oralce 的 init.ora 文件 <br />..1) 确定oracle的缓冲区及共享池大小，假设作如下定义： <br />........ db_block_size = 4096 <br />........ db_block_buffers = 262144 # 缓冲区大小为1G <br />........ share_pool_size = 314572800 # 使用300M共享池 <br />..2) 添加下列各项内容： <br />........ use_indirect_data_buffers = true <br />........ # 告诉oracle可以使用间接内存(即可以使用windows让出来的1G内存作为数据缓冲区) <br />........ pre_page_sga = true <br />........ # 把oracle SGA锁定到内存中，不产生页面交换文件(8i的参数可能是lock_sga = true) <br />........ # 对于一个有4G物理内存的系统来讲，可能这一个参数并不是必须的 <br />4 修改注册表，定义oracle的DBbuffer使用常规内存大小 <br />....在注册表 _Local_Machine中添加一个二进制值，名称为AWE_WINDOW_MEMORY ，值的单位为字节，大小为你需要让oracle使用普通内存作为缓存的大小(不是windows让出来的1G，而是内存地址在0x00000000 到 0x7FFFFFFF 之间的内存大小)。假如设为209715200，即200M大小，那么oracle的数据缓冲区将占用200M的普通内存，其余部分(1G - 200M = 824M)则使用间接内存。 <br />5 重新启动操作系统，启动数据库。OK，你现在的 oracle 可以使用2G + 824M内存了。 </font>
		</p>
		<p>
				<font face="Arial">四 几个补充讨论 <br />1 windows系统“让”出来的1G间接内存，只能用于数据缓冲区 <br />....在4GT特性测试中发现，间接内存只能用于数据缓冲区，而不能用于共享池，也不能分配给用户作为PGA。或许有其他的参数可以定义，但我查到的文献中没有任何一篇讲间接内存可以用于哪些地方，而在我们的测试中发现按上面的修改后，间接内存只能用于数据缓冲区。此结论只作为一个经验，不是定论，请各位大侠补充修正。 <br />2 注册表中 AWE_WINDOW_MEMORY 参数大小的定义 <br />....这个参数定义缓冲池使用普通内存的大小，不能太小。在数据块大小为4K，缓冲池为1G(即使用262144个块作缓冲池)大小的情况下，此参数定义为100M时，oracle不能启动，定义为200M时正常启动。根据我阅读文献后对这个情况的理解，数据缓冲区的每一个块的块头信息都将存放于普通内存中，不能存放于间接内存中。如果此参数定义过小，导致缓冲区块头信息都不能存放，则可导致数据库启动失败。那么，是不是同样大小的缓冲区，数据库块越大，则这个参数就可以定义得越小，因而可以占用更少的普通内存呢？有待验证。 <br />....在具体的应用中，如何定义此参数，应综合考虑最大并发连接数(专用服务器模式下)、用户重用的堆栈大小、排序区、共享池、大池等内存参数的设置情况，尽可能的将数据缓冲区放到间接内存中，充分利用系统的资源。 <br />3 间接内存的性能 <br />....据oracle的文献讲，间接内存的性能(我想主要是指速度和效率吧？)不如直接内存，绝不推荐在未安装有4G物理内存的系统上使用4GT特性。我不知道如何比较间接内存与直接内存的速度和效率，因而未作测试，姑且信之。 <br />....一点小结，期望能对大家有所启发。还请各位大侠补充指正。 <br />以上内容的测试环境： <br />IBM X360 +4G内存 + RAID 5阵列 <br />Windows 2000 Advaced Server SP3 + oracle 8.1.6 专用服务器模式<br /></font>
		</p>
<img src ="http://www.blogjava.net/TrampEagle/aggbug/81168.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-11-15 09:37 <a href="http://www.blogjava.net/TrampEagle/archive/2006/11/15/81168.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Print Templates</title><link>http://www.blogjava.net/TrampEagle/archive/2006/11/02/78792.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Thu, 02 Nov 2006 14:42:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/archive/2006/11/02/78792.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/78792.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/archive/2006/11/02/78792.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/78792.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/78792.html</trackback:ping><description><![CDATA[
		<a href="http://msdn.microsoft.com/library/default.asp?url=/workshop/browser/hosting/printpreview/reference/reference.asp">http://msdn.microsoft.com/library/default.asp?url=/workshop/browser/hosting/printpreview/reference/reference.asp</a>
<img src ="http://www.blogjava.net/TrampEagle/aggbug/78792.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-11-02 22:42 <a href="http://www.blogjava.net/TrampEagle/archive/2006/11/02/78792.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>打印时能够分页打印的CSS控制参考（转）</title><link>http://www.blogjava.net/TrampEagle/archive/2006/11/02/78791.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Thu, 02 Nov 2006 14:41:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/archive/2006/11/02/78791.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/78791.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/archive/2006/11/02/78791.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/78791.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/78791.html</trackback:ping><description><![CDATA[原文引自：<a href="http://zhaohuiwuhan.spaces.live.com/blog/cns!73C6682D4AF70DBA!393.entry?_c=BlogPart">http://zhaohuiwuhan.spaces.live.com/blog/cns!73C6682D4AF70DBA!393.entry?_c=BlogPart</a><br /><br /><h4 class="TextColor1" id="subjcns!73C6682D4AF70DBA!393" style="MARGIN-BOTTOM: 0px">CSS 与 打印控制</h4><div id="msgcns!73C6682D4AF70DBA!393"><div>打印时如何控制不显示某些页面元素</div><div>&lt;style&gt;<br />@media print{<br />INPUT {display:none}<br />}<br />&lt;/style&gt;</div><div>打印时如何控制分页</div><div>&lt;TR style="page-break-after:always;"&gt;<br /><br /><h4 class="TextColor1" id="subjcns!73C6682D4AF70DBA!394" style="MARGIN-BOTTOM: 0px">打印时能够分页打印的CSS控制参考</h4><div id="msgcns!73C6682D4AF70DBA!394"><div>Pagebreak：在打印的时候强迫在样式控制的对象前后换页。 
<p>Before：设置对象前出现的页分割符。设置为always时，始终在对象之前插入页分割符。相对应的CSS属性是”page-break-before”。 </p><p>After：设置对象后出现的页分割符。设置为always时，始终在对象之后插入页分割符。相对应的CSS属性是”'&gt;。 </p><p>用page-break-after <br />page-break-after版本：CSS2 兼容性：IE4+ 继承性：无 <br />语法： <br />page-break-after : auto | always | avoid | left | right | null <br />取值： <br />auto : 假如需要在对象之后插入页分割符 <br />always : 始终在对象之后插入页分割符 <br />avoid : 未支持。避免在对象后面插入页分割符 <br />left : 未支持。在对象后面插入页分割符直到它到达一个空白的左页边 <br />right : 未支持。在对象后面插入页分割符直到它到达一个空白的右页边 <br />null : 空白字符串。取消页分割符设置 </p><p>说明： <br />检索或设置对象后出现的页分割符。 <br />此属性在打印文档时发生作用。此属性不作用于 BR 或 HR 对象。 <br />假如在浏览器已显示的对象上此属性和 page-break-before 属性的值之间发生冲突，则导致最大数目分页的值被使用。 <br />页分隔符不允许出现在定位对象内部。 <br />在IE6及之前版本浏览器中， left 和 right 值的作用结果等同于 always 。 <br />此属性对于 currentStyle 对象而言是只读的。对于其他对象而言是可读写的。 <br />对应的脚本特性为 pageBreakAfter 。 <br />示例： <br />p { page-break-after: always;} </p></div></div></div></div><img src ="http://www.blogjava.net/TrampEagle/aggbug/78791.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-11-02 22:41 <a href="http://www.blogjava.net/TrampEagle/archive/2006/11/02/78791.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>IE6.0浏览器打印机制解析(转)</title><link>http://www.blogjava.net/TrampEagle/archive/2006/11/02/78788.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Thu, 02 Nov 2006 14:28:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/archive/2006/11/02/78788.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/78788.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/archive/2006/11/02/78788.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/78788.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/78788.html</trackback:ping><description><![CDATA[原文引自：<a href="http://zhaohuiwuhan.spaces.live.com/blog/cns!73c6682d4af70dba!395.entry">http://zhaohuiwuhan.spaces.live.com/blog/cns!73c6682d4af70dba!395.entry</a><br /><br /><br /><div id="msgcns!73c6682d4af70dba!395">　网页打印，可以通过<a href="http://tech.sina.com.cn/introduction/focus/msie.shtml"><u><font color="#0000ff">浏览器</font></u></a>的"打印"功能实现，但"打印模板"机制，却是 <a href="http://tech.sina.com.cn/introduction/focus/msie.shtml"><u><font color="#0000ff">IE</font></u></a> 5.5 /6.0 以及 Netscape 6.0 所独有的；准确一点， IE 5.5 只是一个机制雏形，在 IE 6.0 中才得以完全体现。IE 6.0 的打印功能模块，在精确控制页面边界，文本间隔，以及打印的统一性上，功能更为完备。 
<p>　　通过创建打印模板，你可以精确控制：</p><table cellspacing="0" cellpadding="0" align="right" border="0"><tbody><tr><td><a href="http://ad.doubleclick.net/jump/minisite.sina.com.cn/tech;sz=1x1;num=28729036424615452?"><font color="#274306" size="1">[AD]</font><ad- border="0" ck.net="" ad="" minisite.sina.com.cn="" tech;sz="1x1;num=28729036424615452?&quot;"></ad-></a></td></tr></tbody></table><p>　　网页打印及预览时的页面风格与内容编排风格；</p><p>　　打印属性，如自动为打印的页面添加卷标或编号；</p><p>　　精确控制打印预览界面的各个元素与变量。</p><p>　　通过打印模板，你可以：</p><p>　　自动为所有打印页面添加固定内容，如公司标识，版权申明，或者指定广告；</p><p>　　自定义页面标头与尾注等元素，比如页码或卷标；</p><p>　　指定打印历史与任务；</p><p>　　书本化奇偶分页映射打印......</p><p>　　打印模板机制是建立在动态 HTML 语言基础上的，涉及到主要两个行为：DeviceRect, LayoutRect ，下面我们就这两个行为深入地探讨 IE 6.0 的<a href="http://tech.sina.com.cn/introduction/focus/print.shtml"><u><font color="#0000ff">打印机</font></u></a>制。</p><p>　　另外需要说明的是，DHTML (动态超文本标识语言)的行为跟其他语言的"行为"一样，都是一种应用编程接口，初始状态下有自己的默认属性，在一定的事件下，由用户决定调用其承认的功能模块，从而产生相对应的"行为"。而且，"行为"可以自己编写，不过得以".htc"为其扩展名以供调用。</p><p>　　一.DeviceRect ，定义打印总体风格：</p><p>　　打印总体风格，包括为打印页面添加如公司标识的固定内容(网页上不一定有，只体现在打印纸张上或预览页面上，后同)；打印页面的颜色风格；打印页面的边缘属性或图案；等等。</p><p>　　在进行 DeviceRect 引用前，先得确定页面风格，方法是用＜Style＞进行设置。</p><p>　　例一：我们来定制如下的打印模板(详情见 1.htm)：</p><p>　　8.5 inch 宽</p><p>　　11 inch 高</p><p>　　黄色背景</p><p>　　1 pixel 宽的黑色实心左边界</p><p>　　1 pixel 宽的黑色实心上边界</p><p>　　4 pixels 宽的黑色实心右边界</p><p>　　4 pixels 宽的黑色实心下边界</p><p>　　所有边界与纸张边缘为 10 pixels 的距离</p><p>　　现在我们用 Style 进行设定，假设这个 Style 名为 Mystyle1：</p><p>　　＜STYLE TYPE="text/css"＞</p><p>　　.Mystyle1</p><p>　　{</p><p>　　width:8.5in;</p><p>　　height:11in;</p><p>　　background:#FFFF99;</p><p>　　border-left:1 solid black;</p><p>　　border-top:1 solid black;</p><p>　　border-right:4 solid black;</p><p>　　border-bottom:4 solid black;</p><p>　　margin:10px;</p><p>　　}</p><p>　　＜/STYLE＞ </p><p>　　下面我们给出 DeviceRect 引用的完全页面代码，详细页面请看： 1.htm</p><p>　　＜HTML XMLNS:IE＞</p><p>　　＜HEAD＞</p><p>　　＜?IMPORT NAMESPACE="IE" IMPLEMENTATION="#default"＞</p><p>　　＜STYLE TYPE="text/css"＞</p><p>　　.Mystyle1</p><p>　　{</p><p>　　width:8.5in;</p><p>　　height:11in;</p><p>　　background:#FFFF99;</p><p>　　border-left:1 solid black;</p><p>　　border-top:1 solid black;</p><p>　　border-right:4 solid black;</p><p>　　border-bottom:4 solid black;</p><p>　　margin:10px;</p><p>　　}</p><p>　　＜/STYLE＞</p><p>　　＜/HEAD＞</p><p>　　＜BODY＞</p><p>　　＜IE:DEVICERECT ID="page1" CLASS="Mystyle1" MEDIA="print"＞</p><p>　　＜/IE:DEVICERECT＞</p><p>　　＜IE:DEVICERECT ID="page2" CLASS="Mystyle1" MEDIA="print"＞</p><p>　　＜/IE:DEVICERECT＞</p><p>　　＜/BODY＞</p><p>　　＜/HTML＞ </p><p>　　在这个页面中，共进行了两个 DeviceRect 引用。作为一种规则，每一个单独的打印页面，必须有一个相对应的 DeviceRect 标记，如果有 1000 个页面，那就得有 1000 个 DeviceRect 标记！吓住了？别担心，后面我们会教你一个方法，让所有的 DeviceRect 标记自动完成！</p><p>　　在上面的代码中，ID 是标志属性，不同的页面必须有自己不同的标识；CLASS 引用了 Style 属性；MEDIA 属性则指明了本页面的最终用途是进行打印；＜?IMPORT NAMESPACE="IE" IMPLEMENTATION="#default"＞这句话则是指输入默认的行为，它们分别是 DeviceRect, LayoutRect。</p><p>　　二.LayoutRect ，定义页面内容风格：</p><p>　　跟 DeviceRect 一样，不同的页面，要进行 LayoutRect 引用时都需要添加 LayoutRect 标记，其智能添加方法将在后面介绍； LayoutRect 与 DeviceRect 如果在同一个页面中同时出现，则前者需放在后者之内；另外， LayoutRect 对内容风格的设定，也通过 Style 得以实现。</p><p>　　例二：我们来定制如下的内容风格的打印模板：(详情见 2.htm)</p><p>　　5.5 inches 宽</p><p>　　8 inches 高</p><p>　　与打印纸张边缘，四边保持 1 inch 的宽度(加上页面本身的边缘宽度，为实际的打印边缘宽度)</p><p>　　白色背景</p><p>　　1 inch 宽的虚线边界</p><p>　　先定制名为 contentstyle 的风格：</p><p>　　＜STYLE TYPE="text/css"＞</p><p>　　.contentstyle</p><p>　　{</p><p>　　width:5.5in;</p><p>　　height:8in;</p><p>　　margin:1in;</p><p>　　background:white;</p><p>　　border:1 dashed gray;</p><p>　　}</p><p>　　＜/STYLE＞ </p><p>　　然后下面是进行引用的完整网页代码：</p><p>　　＜HTML＞</p><p>　　＜HEAD＞</p><p>　　＜?IMPORT NAMESPACE="IE" IMPLEMENTATION="#default"＞</p><p>　　＜STYLE TYPE="text/css"＞</p><p>　　.contentstyle</p><p>　　{</p><p>　　width:5.5in;</p><p>　　height:8in;</p><p>　　margin:1in;</p><p>　　background:white;</p><p>　　border:1 dashed gray;</p><p>　　}</p><p>　　＜/STYLE＞</p><p>　　＜/HEAD＞</p><p>　　＜BODY＞</p><p>　　＜IE:LAYOUTRECT ID="layoutrect1" CONTENTSRC="2.html" CLASS="contentstyle"　NEXTRECT="layoutrect2"/＞</p><p>　　＜IE:LAYOUTRECT ID="layoutrect2" CLASS="contentstyle"/＞</p><p>　　＜/BODY＞</p><p>　　＜/HTML＞</p><p>　　跟例一中的源代码相比，例二中只是以 LayoutRect 代替了原来的 DeviceRect 标记；DeviceRect 定制的是模板整体风格，而 LayoutRect 定制的是具体内容的版面风格；LayoutRect 的 ID 属性也具有唯一性； CONTENTSRC 属性则指明了具体的将起作用网页文件；CLASS 指明了风格的引用对象；跟 DeviceRect 不同，在进行 LayoutRect 引用时，必须在每个页面指定 NEXTREC ，即依次排列的下一个内容风格，这里的"下一个内容"用其页面的相应 ID 进行标识，如本例中的 LayoutRect2 。</p><p>　　三.DeviceRect 与 LayoutRect 的协同作战：</p><p>　　上面我们分别讨论了 DeviceRect 与 LayoutRect 的作用与引用方法，现在我们来看一下，如何在同一个打印模板中进行定制与引用。</p><p>　　在每一个打印模板上，必然包含两方面的内容，一个是整体的模板风格(DeviceRect)，另一个是内容风格(LayoutRect)；第一个打印页面跟其他页面是不同的，因为第一个页面中必须指明 CONTENTSRC 属性，而同一打印任务中的其他页面不再需要进行 CONTENTSRC 的指定。</p><p>　　例三：(详细情况见 3.htm)</p><p>　　下面是第一个页面中的 DeviceRect 代码：</p><p>　　＜IE:DEVICERECT ID="page1" CLASS="masterstyle" MEDIA="print"＞</p><p>　　＜IE:LAYOUTRECT ID="layoutrect1" CONTENTSRC="2.html" CLASS="contentstyle" NEXTRECT="layoutrect2"/＞</p><p>　　＜/IE:DEVICERECT＞</p><p>　　下面是其他页面中的 DeviceRect 代码：</p><p>　　＜IE:DEVICERECT ID="page2" CLASS="masterstyle" MEDIA="print"＞</p><p>　　＜IE:LAYOUTRECT ID="layoutrect2" CLASS="contentstyle"/＞</p><p>　　＜/IE:DEVICERECT＞ </p><p>　　下面我们将 DeviceRect 与 LayoutRect 结合起来使用，页面情况见 3.htm ，其源代码如下：</p><p>　　＜HTML XMLNS:IE＞</p><p>　　＜HEAD＞</p><p>　　＜?IMPORT NAMESPACE="IE" IMPLEMENTATION="#default"＞</p><p>　　＜STYLE TYPE="text/css"＞</p><p>　　.contentstyle</p><p>　　{</p><p>　　width:5.5in;</p><p>　　height:8in;</p><p>　　margin:1in;</p><p>　　background:white;</p><p>　　border:1 dashed gray;</p><p>　　}</p><p>　　.Mystyle1</p><p>　　{</p><p>　　width:8.5in;</p><p>　　height:11in;</p><p>　　background:#FFFF99;</p><p>　　border-left:1 solid black;</p><p>　　border-top:1 solid black;</p><p>　　border-right:4 solid black;</p><p>　　border-bottom:4 solid black;</p><p>　　margin:10px;</p><p>　　}</p><p>　　＜/STYLE＞</p><p>　　＜/HEAD＞</p><p>　　＜BODY＞</p><p>　　＜IE:DEVICERECT ID="page1" CLASS="Mystyle1" MEDIA="print"＞</p><p>　　＜IE:LAYOUTRECT ID="layoutrect1" CONTENTSRC="2.html" CLASS="contentstyle" NEXTRECT="layoutrect2"/＞</p><p>　　＜/IE:DEVICERECT＞</p><p>　　＜IE:DEVICERECT ID="page2" CLASS="Mystyle1" MEDIA="print"＞</p><p>　　＜IE:LAYOUTRECT ID="layoutrect2" CLASS="contentstyle"/＞</p><p>　　＜/IE:DEVICERECT＞</p><p>　　＜/BODY＞</p><p>　　＜/HTML＞ </p><p>　　四.DeviceRect 与 LayoutRect 标记的动态自动添加：</p><p>　　前面我们说到，每个单独的打印页面都需要各自的 DeviceRect 与 LayoutRect 标记，那么，如果我们有 1000 个页面需要打印，是否就要在每个页面上重复繁琐的 Copy &amp; Paste 操作？</p><p>　　答案是否定的，我们完全可以通过 JavaScript 脚本来完成这一繁琐的工作。</p><p>　　要实现 HTML 声明的动态创建，关键在于 ＜DIV＞ 标记的定义，下面是其定义规则。</p><p>　　＜DIV ID="devicecontainer"＞</p><p>　　......</p><p>　　＜/DIV＞</p><p>　　＜DIV＞与＜/DIV＞之间，采用 insertAdjacentHTML() 方式，并主要利用了其 afterBegin 与 BeforeEnd 两个变量，现在我们将第一个页面"插入"到＜DIV＞＜/DIV＞之间：</p><p>　　devicecontainer.insertAdjacentHTML("afterBegin", newHTML);</p><p>　　具有继承属性的后续页面，调用 beforeEnd 变量：</p><p>　　devicecontainer.insertAdjacentHTML("beforeEnd", newHTML);</p><p>　　要装载 devicecontainer 页面，还需在 ＜Body＞中添加：</p><p>　　＜BODY ONLOAD="addFirstPage()"＞</p><p>　　现在我们在 JavaScript 中添加包含前面详细介绍的 LayoutRect 与 DeviceRect 元素，用到的命令是 addFirstPage() 。需要注意的是，newHTML 标记后使用的是双引号，而 LayoutRect 与 DeviceRect 标记后的变量使用单引号。如下：</p><p>　　function addFirstPage() {</p><p>　　newHTML = "＜IE:DEVICERECT ID='devicerect1' MEDIA='print' CLASS='mystyle1'＞";</p><p>　　newHTML += "＜IE:LAYOUTRECT ID='layoutrect1' CONTENTSRC='2.html'" + "ONLAYOUTCOMPLETE='onPageComplete()' NEXTRECT='layoutrect2'" + "CLASS='contentstyle'/＞";</p><p>　　newHTML += "＜/IE:DEVICERECT＞";</p><p>　　devicecontainer.insertAdjacentHTML("afterBegin", newHTML);</p><p>　　} </p><p>　　细心的读者一定会发现，LayoutRect 后出现了一个新的属性：LayoutRect:onLayoutComplete ，这个属性主要指定了 LayoutRect 停止响应的后续事件，如系统资源消耗殆尽而停止响应，或者 LayoutRect 指定的变量溢出。</p><p>　　好了，有了上面的原理，下面我们来编写具有自动"插入"功能的 JavaScript 代码：</p><p>　　function onPageComplete() {</p><p>　　if (event.contentOverflow) {</p><p>　　newHTML = "＜IE:DEVICERECT ID='devicerect" + (lastPage + 1) + "' MEDIA='print' CLASS='mystyle1'＞";</p><p>　　newHTML += "＜IE:LAYOUTRECT ID='layoutrect" + (lastPage + 1) + "' ONLAYOUTCOMPLETE='onPageComplete()' NEXTRECT='layoutrect" + (lastPage + 2) + "' CLASS='contentstyle'/＞";</p><p>　　newHTML += "＜/IE:DEVICERECT＞";</p><p>　　devicecontainer.insertAdjacentHTML("beforeEnd", newHTML);</p><p>　　lastPage++;</p><p>　　} </p><p>　　在上面的代码中，contentOverflow 代表的是由于页面信息过长，本页的 LayoutRect 停止响应，则直接跳到下一个页面，让 LayoutRect 重新定义下一个页面的版面；onPageComplete() 则不管页面是否过长，LayoutRect 是否停止响应，只要到了页面尾部则自动跳到下一页，这也是最常见的情况。</p><p>　　在编写本脚本时，关键处在于保持清醒，不能让任意一个变量出错。其中，ID 不仅针对 DeviceRect 与 LayoutRect ，还为 NextRect 所引用，页面指向不能出错；当前页面的页码应该是 lastPage+1 ，下一个页面的页码应该是 lastPage+2 ；NextRect 标记需要下一个页面的 LayoutRect 属性支持，因此它的值应该为 "layoutRect"+(lastPage+2)；打开第一个页面时，这个 LastPage 初始值为 1 。(责任编辑 Ray) </p><div></div></div><img src ="http://www.blogjava.net/TrampEagle/aggbug/78788.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-11-02 22:28 <a href="http://www.blogjava.net/TrampEagle/archive/2006/11/02/78788.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>oracle数据安全面面观（转）</title><link>http://www.blogjava.net/TrampEagle/archive/2006/07/31/60959.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Mon, 31 Jul 2006 02:28:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/archive/2006/07/31/60959.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/60959.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/archive/2006/07/31/60959.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/60959.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/60959.html</trackback:ping><description><![CDATA[引自：<a href="http://www.zebcn.com/html/200605/3352.html">http://www.zebcn.com/html/200605/3352.html</a><br /><br /><p>随着计算机的普及以及网络的发展，数据库已经不再仅仅是那些程序员所专有的话题。而Oracle数据库更是凭借其性能卓越，操作方便灵活的特点，在数据库的市场中已经占据了一席之地。但是同样随着网络技术的不断进步，数据信息的不断增加，数据安全已经不再是以前的“老生长谈”，也更不是以前书本上那些“可望不可及”的条条框框。<br />或许很久以前，大家都觉得Oracle数据库的安全并不存在隐患，因为Oracle公司在去年11月份开始促销其数据库软件时提出的口号是“只有Oracle9i能够做到绝对安全”。但是不管它这么说是为了促销，还是为了扩大知名度，总之伴去年12 月份，英国的安全专家 David Litchfield 发现的9iAS 中存在的程序错误导致的缓冲溢出漏洞以及后来，PenTest Limited 和 eEye Digital Security 各自提出了一个小的漏洞，所有使用Oracle公司产品的人都不由地紧张了原本松弛的大脑--这个对于用户来说，毕竟关系到了自己的“身家性命”。<br />下面笔者将带着大家走进Oracle数据安全的世界。由于笔者水平有限，所以不足之处在所难免，望大家不吝赐教。<br />（一）Oracle数据库的一些基本常识</p><p>这里仅仅是为了以后的安全奠定一些基础，因为我们后面要用到它们。呵呵~！<br />1.Oracle所包含的组件： <br />在 Oracle，数据库是指整个 Oracle RDBMS 环境，它包括以下组件： <br />·Oracle 数据库进程和缓冲（实例）。 <br />·SYSTEM 表空间包含一个集中系统类目，它可以由一个或多个数据文件构成。 <br />·其它由数据库管理员 (DBA)（可选）定义的表空间，每个都由一个或多个数据文件构成。 <br />·两个以上的联机恢复日志。 <br />·归档恢复日志（可选）。 <br />·其它文件（控制文件、Init.ora、Config.ora 等）。 <br />每个 Oracle 数据库都在一个中央系统类目和数据字典上运行，它位于SYSTEM 表空间。<br />2.关于“日志”<br />Oracle数据库使用几种结构来保护数据：数据库后备、日志、回滚段和控制文件。下面我们将大体上了解一下作为主要结构之一的“日志”：<br />每一个Oracle数据库实例都提供日志，记录数据库中所作的全部修改。每一个运行的Oracle数据库实例相应地有一个在线日志，它与Oracle后台进程LGWR一起工作，立即记录该实例所作的全部修改。归档（离线）日志是可选择的，一个Oracle数据库实例一旦在线日志填满后，可形成在线日志归档文件。归档的在线日志文件被唯一标识并合并成归档日志。 <br />·关于在线日志：一个Oracle数据库的每一实例有一个相关联的在线日志。一个在线日志由多个在线日志文件组成。在线日志文件（online redo log file）填入日志项（redo entry），日志项记录的数据用于重构对数据库所作的全部修改。 <br />·关于归档日志：Oracle要将填满的在线日志文件组归档时，则要建立归档日志（archived redo log）。其对数据库备份和恢复有下列用处：<br />&lt;1&gt;数据库后备以及在线和归档日志文件，在操作系统和磁盘故障中可保证全部提交的事物可被恢复。 <br />&lt;2&gt;在数据库打开和正常系统使用下，如果归档日志是永久保存，在线后备可以进行和使用。 <br />数据库可运行在两种不同方式下：NOARCHIVELOG方式或ARCHIVELOG 方式。数据库在NOARCHIVELOG方式下使用时，不能进行在线日志的归档。如果数据库在ARCHIVELOG方式下运行，可实施在线日志的归档。 <br />3.物理和逻辑存储结构：<br />Oracle RDBMS是由表空间组成的，而表空间又是由数据文件组成的。表空间数据文件被格式化为内部的块单位。块的大小，是由DBA在Oracle第一次创建的时候设置的，可以在512到8192个字节的范围内变动。当一个对象在Oracle表空间中创建的时候，用户用叫做长度的单位（初始长度（(initial extent)、下一个长度（next extent）、最小长度（min extents）、以及最大长度（max extents））来标明该对象的空间大小。一个Oracle长度的大小可以变化，但是要包含一个由至少五个连续的块构成的链。<br />4.Oracle与Microsoft SQL Server比较下的联网协议：</p><p>（二）Oracle数据安全的维护<br />记得某位哲学家说过：“事物的变化离不开内因和外因。”那么对于Oracle数据安全这个话题而言，也势必分为“内”和“外”两个部分。那么好，我们就先从“内”开始说起：<br />§1.从Oracle系统本身说起<br />我们先抛开令人闻风色变的“hacker”和其他一些外部的原因，先想一下我们的数据库。什么硬盘损坏，什么软件受损，什么操作事物……一系列由于我们的“疏忽”而造成的系统问题就完全可以让我们辛苦建立的数据库中的数据一去不复返。那么，我们就先从自己身上找找原因吧。<br />【一】解决系统本身问题的方法--数据库的备份及恢复<br />·数据库的备份：<br />关于Oracle数据库的备份，标准地有三中办法：导出/导入（Export/Import）、冷备份、热备份。导出备份是一种逻辑备份，冷备份和热备份是物理备份。 <br />&lt;1&gt;导出/导入（Export/Import） <br />利用Export可将数据从数据库中提取出来，利用Import则可将提取出来的数据送回Oracle数据库中去。 <br />a.简单导出数据（Export）和导入数据（Import）<br />Oracle支持三种类型的输出： <br />（1）表方式（T方式），将指定表的数据导出。 <br />（2）用户方式（U方式），将指定用户的所有对象及数据导出。 <br />（3）全库方式（Full方式），将数据库中的所有对象导出。 <br />数据导出（Import）的过程是数据导入（Export）的逆过程，它们的数据流向不同。 <br />b.增量导出/导入 <br />增量导出是一种常用的数据备份方法，它只能对整个数据库来实施，并且必须作为SYSTEM来导出。在进行此种导出时，系统不要求回答任何问题。导出文件名缺省为export.dmp，如果不希望自己的输出文件定名为export.dmp，必须在命令行中指出要用的文件名。 <br />增量导出包括三个类型： <br />（1）“完全”增量导出（Complete） <br />即备份整个数据库，比如： <br />＄exp system/manager inctype=complete file=990702.dmp <br />（2）“增量型”增量导出 <br />备份上一次备份后改变的数据。比如： <br />＄exp system/manager inctype=incremental file=990702.dmp <br />（3）“累计型”增量导出（Cumulative） <br />累计型导出方式只是导出自上次“完全” 导出之后数据库中变化了的信息。比如： <br />＄exp system/manager inctype=cumulative file=990702.dmp <br />数据库管理员可以排定一个备份日程表，用数据导出的三个不同方式合理高效地完成。比如数据库的备份任务可作如下安排： <br />·星期一：完全导出（A） <br />·星期二：增量导出（B） <br />·星期三：增量导出（C） <br />·星期四：增量导出（D） <br />·星期五：累计导出（E） <br />·星期六：增量导出（F） <br />·星期日：增量导出（G） <br />如果在星期日，数据库遭到意外破坏，数据库管理员可按以下步骤来恢复数据库： <br />第一步：用命令CREATE DATABASE重新生成数据库结构； <br />第二步：创建一个足够大的附加回段。 <br />第三步：完全增量导入A： <br />＄imp system./manager inctype= RECTORE FULL=Y FILE=A <br />第四步：累计增量导入E： <br />＄imp system/manager inctype= RECTORE FULL=Y FILE =E <br />第五步：最近增量导入F： <br />＄imp system/manager inctype=RESTORE FULL=Y FILE=F <br />&lt;2&gt;冷备份 <br />冷备份发生在数据库已经正常关闭的情况下，当正常关闭时会提供给我们一个完整的数据库。冷备份是将关键性文件拷贝到另外位置的一种说法。对于备份Oracle信息而言，冷备份是最快和最安全的方法。冷备份的优点是： <br />·是非常快速的备份方法（只需拷贝文件） <br />·容易归档（简单拷贝即可） <br />·容易恢复到某个时间点上（只需将文件再拷贝回去） <br />·能与归档方法相结合，作数据库“最新状态”的恢复。 <br />·低度维护，高度安全。 <br />但冷备份也有如下不足： <br />·单独使用时，只能提供到“某一时间点上”的恢复。 <br />·在实施备份的全过程中，数据库必须要作备份而不能作其它工作。也就是说，在冷备份过程中，数据库必须是关闭状态。 <br />·若磁盘空间有限，只能拷贝到磁带等其它外部存储设备上，速度会很慢。 <br />·不能按表或按用户恢复。 <br />如果可能的话（主要看效率），应将信息备份到磁盘上，然后启动数据库（使用户可以工作）并将所备份的信息拷贝到磁带上（拷贝的同时，数据库也可以工作）。冷备份中必须拷贝的文件包括： <br />·所有数据文件 <br />·所有控制文件 <br />·所有联机REDO LOG文件 <br />·Init.ora文件（可选） <br />值得注意的是冷备份必须在数据库关闭的情况下进行，当数据库处于打开状态时，执行数据库文件系统备份是无效的 <br />下面是做冷备份的完整例子： <br />（1） 关闭数据库＄sqldba lmode=y <br />SQLDBA &gt;connect internal; <br />SQLDBA &gt;shutdown normal; <br />（2） 用拷贝命令备份全部的时间文件、重做日志文件、控制文件、初始化参数文件 <br />SQLDBA &gt;! cp &lt; file &gt; &lt; backup directory &gt; <br />（3） 重启Oracle数据库 <br />＄sqldba lmode=y <br />SQLDBA &gt;connect internal; <br />SQLDBA &gt;startup; <br />&lt;3&gt;热备份 <br />热备份是在数据库运行的情况下，采用archivelog mode方式备份数据的方法。所以，如果你有昨天夜里的一个冷备份而且又有今天的热备份文件，在发生问题时，就可以利用这些资料恢复更多的信息。热备份要求数据库在Archivelog方式下操作，并需要大量的档案空间。一旦数据库运行在archivelog状态下，就可以做备份了。热备份的命令文件由三部分组成： <br />1．数据文件一个表空间一个表空间地备份。 <br />（1）设置表空间为备份状态 <br />（2）备份表空间的数据文件 <br />（3）恢复表空间为正常状态 <br />2．备份归档log文件。 <br />（1）临时停止归档进程 <br />（2）log下那些在archive redo log目标目录中的文件 <br />（3）重新启动archive进程 <br />（4）备份归档的redo log 文件 <br />3．用alter database backup controlfile命令来备份拷贝文件 <br />热备份的优点是： <br />·可在表空间或数据文件级备份，备份时间短。 <br />·备份时数据库仍可使用。 <br />·可达到秒级恢复（恢复到某一时间点上）。 <br />·可对几乎所有数据库实体作恢复。 <br />·恢复是快速的，在大多数情况下在数据库仍工作时恢复。 <br />热备份的不足是： <br />·不能出错，否则后果严重。 <br />·若热备份不成功，所得结果不可用于时间点的恢复。 <br />·因难于维护，所以要特别仔细小心，不允许“以失败而告终”。 </p><p><br />【二】来自内部的另外一个隐患--用户管理以及密码问题<br />在这里，其实作为一个差不多点的数据库管理员都很清楚，Oracle数据库本身就使用了很多种手段来加强数据库的安全性，经常见到的就有密码，角色，权限等等。那么我们就从最简单的DBSNMP<br />说起：<br />Oralce数据库如果采用典型安装后，自动创建了一个叫做DBSNMP的用户，该用户负责运行Oracle系统的智能代理（Intelligent Agent），该用户的缺省密码也是“DBSNMP”。如果忘记修改该用户的口令，任何人都可以通过该用户存取数据库系统。现在我们来看一下该用户具有哪些权限和角色，然后来分析一下该用户对数据库系统可能造成的损失。 <br />启动SQL/PLUS程序，使用该用户登录进入：<br />SQL&gt; select * from session_privs; <br />CREATE SESSION <br />ALTER SESSION <br />UNLIMITED TABLESPACE <br />CREATE TABLE <br />CREATE CLUSTER <br />CREATE SYNONYM <br />CREATE PUBLIC SYNONYM <br />CREATE VIEW <br />CREATE SEQUENCE <br />CREATE DATABASE LINK <br />CREATE PROCEDURE <br />CREATE TRIGGER <br />ANALYZE ANY <br />CREATE TYPE <br />CREATE OPERATOR <br />CREATE INDEXTYPE <br />可以看到该用户不是SYS或SYSTEM管理用户，然而，它却具有两个系统级权限：UNLIMITED TABLESPACE和CREATE PUBLIC SYNONYM。 <br />看到这两个权限你应该马上想到，这些都是安全隐患，尤其是UNLIMITED TABLESPACE，它是破坏数据库系统的攻击点之一。如果这时候你还依然认为，即使有人利用这个没有修改的口令登录进数据库也造成不了什么损失的话，我就不得不提醒你：该用户具有UNLIMITED TABLESPACE的系统权限，它可以写一个小的脚本，然后恶意将系统用垃圾数据填满，这样数据库系统也就无法运行，并将直接导致最终的瘫痪。目前很多数据库系统都要求7X24的工作，如果出现了系统用垃圾数据填满的情况，那么，等数据库系统恢复时，恐怕不可挽回的损失已经造成了。<br />可是除了 DBSNMP 还有很多其他的用户，怎么办呢？让我们先看一下目前普遍存在于Oracle数据库中的用户管理问题：<br />（1）权限过大：对ORACLE数据库编程和浏览的一般用户常常具有DBA (数据库管理员权限)，<br />能对数据库系统做任何修改或删除。<br />（2）安全性差：很多ORACLE用户缺省存储位置都在系统表空间，这样不仅影响系统的正常工<br />作，而且不同用户的数据信息互相影响、透明，保密性差。随着数据的不断加入，<br />有可能使整个数据库系统崩溃。<br />（3）密码有规律：在ORACLE调试初期形成的用户名和密码一致的不良习惯保留到现在；系统用户SYS和SYSTEM的密码也众所皆知。<br />知道了这些普遍的“毛病”，我们怎么做呢？下面是我的一些建议：<br />（1）ORACLE DBA (数据库管理员)的规范<br />·SUN Solaris操作系统下ORACLE用户密码应严格保密，绝不该把密码设成<br />ORACLE；并指定专门的数据库管理员定期修改。<br />·ORACLE初始化建立的SYS和SYSTEM系统管理员用户密码应由原来MANAGER改成别的不易被记忆的字符串。<br />·ORACLE WEB SERVER的管理端口具备DBA浏览数据库的能力，因此其管理者<br />ADMIN的密码也应保密，不该把密码设成MANAGER；并指定专门的数据库管理员定<br />期修改。<br />·ORACLE DBA最好在SUN SPARC服务器控制台上用窗口式界面实现管理。前提<br />是ORACLE用户启动服务器，然后在窗口式命令行下输入SVRMGRM，即启动了ORACLE SERVER MANAGER菜单式管理；用SYSDBA身份登录后，就可做数据库系统维护工作了</p><p><br />（2）SQL*PLUS编程用户的规范<br />·存储结构的规范<br />考虑到用SQL*PLUS编程可实现各行各业、各公司、各部门多种多样的应用需求，我们的SQL*PLUS编程用户也应该朝这个方向规范：不同种类的应用必须有不同的用户；不同种类的应用必须有不同的存储位置，包括物理文件、缺省表空间、临时表空间的创建和规划：当准备编写某一较大规模(从ORACLE数据量和面向用户量考虑)应用程序时，首先应该创建一个逻辑的存储位置-表空间，同时定义物理文件的存放路径和所占硬盘的大小。<br />①、物理文件缺省的存放路径在/oracle_home/dbs下，在命令行下用UNIX指令df -k 可查看硬盘资源分区的使用情况。如果oracle_home使用率达90‰以上，而且有一个或多个较为空闲的硬盘资源分区可以利用，我们最好把物理文件缺省的存放路径改到较为空闲的硬盘资源分区路径下。在此路径下我们可以这样规划资源物理文件的存储：<br />xxx表空间 <br />xxx行业/ xxx公司/ xxx 部门/ xxx 服务.dbf<br />DEMO表空间<br />default_datafile_home1/col /elec/sys4/demo1.dbf <br />default_datafile_home1/col /elec/sys4/demo2.dbf <br />公司系统四部摹拟演示系统物理文件<br />HUMAN表空间<br />default_datafile_home1/col/elec/human/human.dbf<br />公司人事部人事管理系统物理文件<br />BOOK表空间<br />default_datafile_home1/col/elec/book/book.dbf<br />公司资料室图书管理系统物理文件<br />QUESTION表空间<br />default_datafile_home1/col/elec/client/question.dbf<br />公司客户服务部问题库系统物理文件<br />PC表空间<br />default_datafile_home1/col/chaoxun/client/pc.dbf<br />公司PC机售后服务系统物理文件<br />……表空间<br />default_datafile_home2/……………………………<br />等等<br />说明：其中default_datafile_home1指oracle_home/dbs；<br />default_datafile_home2指较为空闲的硬盘资源分区路径。<br />②、物理文件的大小根据应用系统的数据量、数据对象、程序包的多少来定。一般用于摹拟演示的小系统，表空间初始的物理文件为2M即能满足要求，如果信息量满，还可以增加物理文件，扩充表空间(每次扩充大小也可暂定为2M)；一般实际运行的应用系统可适当增加表空间初始的物理文件大小，但也不要一次分配太大(因为不易回收空间，却易扩充空间)，这也需要根据具体情况具体分析：信息量大、需长时间保存的应用在条件允许情况下，表空间可以大到几百M甚至上G；信息量小、短期经常刷新的应用，表空间可以控制在2M以下。</p><p>③、表空间的名称应该采用同系统应用相似的英文字符或字符缩写，表空间所对应的一个或多个物理文件名也应有相关性。不同用户所处的缺省表空间不同，存储的信息就不能互相访问。这比把所有用户信息都储存在系统表空间，安全性大大提高了。如果用ORACLE WEB SERVER管理端口创建的用户，其缺省和临时表空间一定是系统表空间，DBA切记要改变用户的缺省表空间。临时表空间存放临时数据段，处理一些排序、合并等中间操作，根据实际应用的需求可以把它们放在专门创建的表空间里；如果系统表空间大，也可以把它们放在系统表空间。用户创建的数据索引最好和数据文件分开存放在不同表空间，以减少数据争用和提高响应速度。</p><p>·密码和用户名的规范<br />有相当数量的ORACLE用户名和密码一致，这是个很不安全的因素。我们建议ORACLE用户名和密码一定不要一样，密码最好在五，六位字符以上。不同用户间不应该使用相同的密码。用户名的定义可根据实际应用的英文名来设，而依据编程人员的姓名定义的用户名实际上不规范，可在日后的工作中结合上述有关存储结构规范的说明逐步改进。<br />（3）特殊要求用户的规范<br />在ORACLE数据库使用过程中，还会遇到一些有特殊要求的用户：非编程人员需要对某个表有查询、增加、删除、修改的权利。DBA应创建一个这样的用户，先确定用户名和密码，再规定相关应用所在缺省表空间(包含某个表)和临时表空间，最后TABLE属主给其授权：赋予CONNECT角色SELECT、INSERT、DELETE、UPDATE ON THE TABLE的对象级权限，这可根据实际需求自由取舍。<br />举例：●给新用户授于对象级权限(命令行方式)：<br />假设新用户NEW2需要有查询、删除、修改DCD用户的表EMP。<br />%svrmgrl<br />SVRMGR&gt;connect internal; 以系统管理员登录<br />SVRMGR&gt;create user new2 identified by new2345 default tablespace app;<br />SVRMGR&gt;connect dcd/dcdpwd; 以dcd用户登录<br />SVRMGR&gt;grant connect to new2;<br />SVRMGR&gt;grant select on emp to new2;<br />SVRMGR&gt;grant delete on emp to new2;<br />SVRMGR&gt;grant update on emp to new2;<br />说了这么多关于用户的问题，那么接下来我们就详细得说一下关于密码文件的使用以及维护--在Oracle数据库系统中，用户如果要以特权用户身份（INTERNAL／SYSDBA／SYSOPER）登录Oracle数据库可以有两种身份验证的方法：即使用与操作系统集成的身份验证或使用Oracle数据库的密码文件进行身份验证。因此，管理好密码文件，对于控制授权用户从远端或本机登录Oracle数据库系统，执行数据库管理工作，具有重要的意义。<br />Oracle数据库的密码文件存放有超级用户INTERNAL／SYS的口令及其他特权用户的用户名／口令，它一般存放在ORACLE_HOME＼DATABASE目录下。<br />·密码文件的创建： <br />在使用Oracle Instance Manager创建一数据库实例的时侯，在ORACLE_HOME＼DATABASE目录下还自动创建了一个与之对应的密码文件，文件名为PWDSID.ORA，其中SID代表相应的Oracle数据库系统标识符。此密码文件是进行初始数据库管理工作的基础。在此之后，管理员也可以根据需要，使用工具ORAPWD.EXE手工创建密码文件，命令格式如下：<br />C:＼ &gt;ORAPWD　FILE=＜ FILENAME ＞　PASSWORD =＜ PASSWORD ＞ ENTRIES=&lt; MAX_USERS &gt; <br />各命令参数的含义为： <br />FILENAME：密码文件名； <br />PASSWORD：设置INTERNAL／SYS帐号的口令； <br />MAX_USERS：密码文件中可以存放的最大用户数，对应于允许以SYSDBA／SYSOPER权限登录数据库的最大用户数。由于在以后的维护中，若用户数超出了此限制，则需要重建密码文件，所以此参数可以根据需要设置得大一些。 <br />有了密码文件之后，需要设置初始化参数REMOTE_LOGIN_PASSWORDFILE来控制密码文件的使用状态。<br />·设置初始化参数REMOTE_LOGIN_PASSWORDFILE：<br />在Oracle数据库实例的初始化参数文件中，此参数控制着密码文件的使用及其状态。它可以有以下几个选项：<br />NONE：指示Oracle系统不使用密码文件，特权用户的登录通过操作系统进行身份验证；<br />EXCLUSIVE：指示只有一个数据库实例可以使用此密码文件。只有在此设置下的密码文件可以包含有除INTERNAL／SYS以外的用户信息，即允许将系统权限SYSOPER／SYSDBA授予除INTERNAL／SYS以外的其他用户。<br />SHARED：指示可有多个数据库实例可以使用此密码文件。在此设置下只有INTERNAL／SYS帐号能被密码文件识别，即使文件中存有其他用户的信息，也不允许他们以SYSOPER／SYSDBA的权限登录。此设置为缺省值。<br />在REMOTE_LOGIN_PASSWORDFILE参数设置为EXCLUSIVE、SHARED情况下，Oracle系统搜索密码文件的次序为：在系统注册库中查找ORA_SID_PWFILE参数值（它为密码文件的全路径名）；若未找到，则查找ORA_PWFILE参数值；若仍未找到，则使用缺省值ORACLE_HOME＼DATABASE＼PWDSID.ORA；其中的SID代表相应的Oracle数据库系统标识符。<br />·向密码文件中增加、删除用户：<br />当初始化参数REMOTE_LOGIN_PASSWORDFILE设置为EXCLUSIVE时，系统允许除INTERNAL／SYS以外的其他用户以管理员身份从远端或本机登录到Oracle数据库系统，执行数据库管理工作；这些用户名必须存在于密码文件中，系统才能识别他们。由于不管是在创建数据库实例时自动创建的密码文件，还是使用工具ORAPWD.EXE手工创建的密码文件，都只包含INTERNAL／SYS用户的信息；为此，在实际操作中，可能需要向密码文件添加或删除其他用户帐号。 <br />由于仅被授予SYSOPER／SYSDBA系统权限的用户才存在于密码文件中，所以当向某一用户授予或收回SYSOPER／SYSDBA系统权限时，他们的帐号也将相应地被加入到密码文件或从密码文件中删除。由此，向密码文件中增加或删除某一用户，实际上也就是对某一用户授予或收回SYSOPER／SYSDBA系统权限。 <br />要进行此项授权操作，需使用SYSDBA权限（或INTERNAL帐号）连入数据库，且初始化参数REMOTE_LOGIN_PASSWORDFILE的设置必须为EXCLUSIVE。具体操作步骤如下： <br />创建相应的密码文件； <br />设置初始化参数REMOTE_LOGIN_PASSWORDFILE＝EXCLUSIVE； <br />使用SYSDBA权限登录： CONNECT　SYS／internal_user_passsword　AS　SYSDBA； <br />启动数据库实例并打开数据库； <br />创建相应用户帐号，对其授权（包括SYSOPER和SYSDBA）： 授予权限：GRANT　SYSDBA　TO　user_name； <br />收回权限：REVOKE　SYSDBA　FROM　user_name； <br />现在这些用户可以以管理员身份登录数据库系统了； <br />·使用密码文件登录：<br />有了密码文件后，用户就可以使用密码文件以SYSOPER／SYSDBA权限登录Oracle数据库实例了，注意初始化参数REMOTE_LOGIN_PASSWORDFILE应设置为EXCLUSIVE或SHARED。任何用户以SYSOPER／SYSDBA的权限登录后，将位于SYS用户的Schema之下，以下为两个登录的例子： <br />1. 以管理员身份登录： <br />假设用户scott已被授予SYSDBA权限，则他可以使用以下命令登录： <br />CONNECT　scott／tiger　AS　SYSDBA <br />2. 以INTERNAL身份登录： <br />CONNECT　INTERNAL／INTERNAL_PASSWORD <br />·密码文件的维护：<br />1. 查看密码文件中的成员：<br />可以通过查询视图V$PWFILE_USERS来获取拥有SYSOPER／SYSDBA系统权限的用户的信息，表中SYSOPER／SYSDBA列的取值TRUE／FALSE表示此用户是否拥有相应的权限。这些用户也就是相应地存在于密码文件中的成员。<br />2. 扩展密码文件的用户数量：<br />当向密码文件添加的帐号数目超过创建密码文件时所定的限制（即ORAPWD.EXE工具的MAX_USERS参数）时，为扩展密码文件的用户数限制，需重建密码文件，具体步骤如下：<br />a) 查询视图V$PWFILE_USERS，记录下拥有SYSOPER／SYSDBA系统权限的用户信息； <br />b) 关闭数据库； <br />c) 删除密码文件；<br />d) 用ORAPWD.EXE新建一密码文件； <br />e) 将步骤a中获取的用户添加到密码文件中。 <br />3. 修改密码文件的状态： <br />密码文件的状态信息存放于此文件中，当它被创建时，它的缺省状态为SHARED。可以通过改变初始化参数REMOTE_LOGIN_PASSWORDFILE的设置改变密码文件的状态。当启动数据库事例时，Oracle系统从初始化参数文件中读取REMOTE_LOGIN_PASSWORDFILE参数的设置；当加载数据库时，系统将此参数与口令文件的状态进行比较，如果不同，则更新密码文件的状态。若计划允许从多台客户机上启动数据库实例，由于各客户机上必须有初始化参数文件，所以应确保各客户机上的初始化参数文件的一致性，以避免意外地改变了密码文件的状态，造成数据库登陆的失败。 <br />4. 修改密码文件的存储位置：<br />密码文件的存放位置可以根据需要进行移动，但作此修改后，应相应修改系统注册库有关指向密码文件存放位置的参数或环境变量的设置。<br />5. 删除密码文件： <br />在删除密码文件前,应确保当前运行的各数据库实例的初始化参数REMOTE_LOGIN_PASSWORDFILE皆设置为NONE。在删除密码文件后，若想要以管理员身份连入数据库的话，则必须使用操作系统验证的方法进行登录。<br />但是管理员都觉得乏味，因为在管理员中流行一种很简单的加密办法--就是经常，很频繁地修改自己的密码。可是，每次修改都跟打一次仗似的--因为更新程序并不是每个人都愿意做的事情。<br />那么有没有什么简单点的办法呢？请往下看：<br />模型：Oracle7.3；开发工具：Develope2000。收费系统（在数据库中的名称是SFYY），其Client端分散在市区的数个营业点，通过城域网与主机(小型 机)相连。<br />过程：<br />·在收费小型机Oracle系统的system用户（DBA）下，创建新用户test;<br />create user test<br />identified by carton<br />default tablespace dataspace1<br />quota 100K<br />·对test用户授以权限；<br />grant create session to test;<br />grant resource to test;<br />·在test用户下建立一个存储函数mmtranslate,它其实是一个加密程序。下面是一个简 单的例子。<br />function mmtranslate(m varchar2)<br />return varchar2<br />as<br />i number(2);<br />kk varchar2(10);<br />begin<br />kk:=′′;<br />i:=1;<br />loop<br />if i&lt;=length(m) then<br />if instr(′1234567890′,substr(m,i,1),1,1)&gt;0 then<br />kk:=kk||chr(100+to_number(substr(m,i,1)));<br />elseif instr(‘wxyz‘,substr(m,i,1),1,1)&gt;0 then<br />kk:=kk||chr(-8+ascii(substr(m,i,1)));<br />else<br />kk:=kk||chr(4+ascii(substr(m,i,1)));<br />end if;<br />else<br />exit;<br />end if;<br />i:=i+1;<br />end loop;<br />return kk;<br />exception<br />when others then<br />return ′-1′;<br />end;<br />·在test用户下建表mmtest并插入记录：<br />create table mmtest<br />(usnamevarchar2(6),------用户名称<br />mimavarchar2(6)------加密前的密码);<br />insert into mmtest values( ‘sfyy‘,‘eds2‘);<br />commit;<br />·执行以下语句<br />SQL&gt;select mmtranslate(‘eds2‘) from dual;<br />MMTRANSLATE(‘EDS2‘)<br />----------------------------------------<br />ihwf<br />利用DBA权限更改sfyy的密码为上面语句的执行结果：<br />alter user sffy<br />identified by ihwf; ;<br />·修改应用程序，对于开发环境是Develope2000的程序来说，主要是修改主程序的on-lo gon触发器：<br />declare<br />mm varchar2(6);<br />begin<br />logon(‘test‘,‘carton‘);<br />select mima into mm from mmtest where usname=‘sfyy‘;<br />mm:=mmtranslate(mm);<br />logout;<br />logon(‘sfyy‘,mm);<br />end;<br />然后再利用触发器WHEN-NEW-FROM-INSTANCE执行Callfrom或Newform等 命令，进入业务处理程序。这个主程序应当仅仅由管理员来掌握,编译之后将执行文件下发 到各收费点的Clien端。<br />·在System用户下，利用Oracle提供的pupbld.sql,建立表Productuserprofile,执行下面这样的命令，限制在非开发状态Sql命令的使用,例如<br />insert into productuserprofile<br />(product,userid,attribute,charvalue) values<br />(‘SQL*Plus‘,‘TEST‘,‘CONNECT‘,‘DISABLED‘);<br />insert into productuserprofile<br />(product,userid,attribute,charvalue) values<br />(‘SQL*Plus‘,‘SFYY‘,‘DELETE‘,‘DISABLED‘)；这样，在SQL状态下，根本无法连接到TEST用户，而在 sfyy用户下，delete命令将不能执行。当然，DBA可以改变这些设置。<br />当然了，这个仅仅是属于一种“应用技巧”，但是足可以把那些每天忙于更新系统的管理员舒服好几天了。但是另一方面，还要加强对源程序的管理，在Client端只存放执行程序。加强审计，发现异常现象，及时处理。这样才可以做到更高一层的“安全”。<br />在下面，我主要是向大家介绍一个REM对GHXXB制立数据库触发子,密码的加密程序。<br />REM 对GHXXB制立数据库触发子(当INSERT OR UPDATE GHXXB时触发) <br />drop trigger scjmmm; <br />create or replace trigger scjmmm <br />before insert or update of mm On ghxxb For each Row <br />Begin <br />:new.mm:=ENCRYPT(:new.mm,:NEW.GH,TO_CHAR(SYSDATE,‘SS‘)); <br />End; <br />/ <br />---------------------------密码的加密程序ENCRYPT---------------------- <br />Create or Replace <br />Function ENCRYPT (Inpass In Varchar2,IN_GH In Varchar2,IN_SS In Varchar2) <br />Return Varchar2 Is <br />bcs varchar2(20); <br />bcs1 number; <br />cs number; <br />jg number; <br />m_gh VARCHAR2(4); <br />m_mm VARCHAR2(20); <br />Begin <br />m_gh:=IN_GH; <br />m_mm:=INPASS; <br />cs:=TO_NUMBER(IN_SS); <br />If cs&lt;=1 then cs:=77 ;end if; <br />bcs:=substr(to_char(ascii(substr(m_gh,1,1))),1,2); <br />If bcs&lt;‘1‘ then bcs:=‘7‘ ;end if; <br />m_gh:=substr(m_gh,2); <br />Loop EXIT WHEN nvl(length(m_gh),0)=0 ; <br />bcs:=bcs||substr(to_char(ascii(substr(m_gh,1,1))),-1,1); <br />m_gh:=substr(m_gh,2); <br />End loop; <br />Loop EXIT WHEN nvl(length(m_mm),0)=0 ; <br />bcs:=bcs||substr(to_char(ascii(substr(m_mm,1,1))),-1,1); <br />m_mm:=substr(m_mm,2); <br />End loop; <br />bcs1:=to_number(bcs); <br />jg:=cs*bcs1; <br />Loop EXIT WHEN length(to_char(jg))&gt;13; <br />jg:=jg*cs ; <br />End loop; <br />RETURN(IN_SS||substr(to_char(jg),1,14)); <br />End; <br />/ <br />总结上面的东西，我们仅仅是从自身做起，知道了怎么维护Oracle数据库安全这个话题的“皮毛”。可是，对于这个似乎永远也说不完的话题，我们光知道怎么从内部“防御”就够了吗？不要忘了，在外面，还有一群虎视耽耽的“hacker”在盯着你的数据库--因为这里面有他们想要的东西。<br />所以，请大家关注好下一个话题：<br />§2.不被“hacker”入侵的几个建议 <br />我们的目标是：没有蛀牙！（开个玩笑~！呵呵）其实应该是：它应尽可能地堵住潜在的各种漏洞，防止非法用户利用它们侵入数据库系统。对于数据库数据的安全问题，数据库管理员可以参考有关系统双机热备份功能以及数据库的备份和恢复的资料。 <br />以下就数据库系统不被非法用户侵入这个问题作进一步的阐述。 <br />·组和安全性：在操作系统下建立用户组也是保证数据库安全性的一种有效方法。Oracle程序为了安全性目的一般分为两类：一类所有的用户都可执行，另一类只DBA可执行。在Unix环境下组设置的配置文件是/etc/group，关于这个文件如何配置，请参阅Unix的有关手册，以下是保证安全性的几种方法： <br />(1)在安装Oracle Server前，创建数据库管理员组(DBA)而且分配root和Oracle软件拥有者的用户ID给这个组。DBA能执行的程序只有710权限。在安装过程中SQL*DBA系统权限命令被自动分配给DBA组。 <br />(2)允许一部分Unix用户有限制地访问Oracle服务器系统，增加一个由授权用户组的Oracle组，确保给Oracle服务器实用例程Oracle组ID，公用的可执行程序，比如SQL*Plus，SQL*forms等，应该可被这组执行，然后该这个实用例程的权限为710，它将允许同组的用户执行，而其他用户不能。 <br />(3)改那些不会影响数据库安全性的程序的权限为711。（注：在我们的系统中为了安装和调试的方便，Oracle数据库中的两个具有DBA权限的用户Sys和System的缺省密码是manager。为了您数据库系统的安全，我们强烈建议您该掉这两个用户的密码，具体操作如下： <br />在SQL*DBA下键入： <br />alter user sys indentified by password; <br />alter user system indentified by password; <br />其中password为您为用户设置的密码。 <br />·Oracle服务器实用例程的安全性： <br />以下是保护Oracle服务器不被非法用户使用的几条建议： <br />(1) 确保$ORACLE_HOME/bin目录下的所有程序的拥有权归Oracle软件拥有者所有； <br />(2) 给所有用户实用便程(sqiplus,sqiforms,exp,imp等)711权限，使服务器上所有的用户都可访问Oracle服务器； <br />(3) 给所有的DBA实用例程(比如SQL*DBA)700权限。Oracle服务器和Unix组当访问本地的服务时，您可以通过在操作系统下把Oracle服务器的角色映射到Unix的组的方式来使用Unix管理服务器的安全性，这种方法适应于本地访问。 <br />在Unix中指定Oracle服务器角色的格式如下： <br />ora_sid_role[_dla] <br />其中 sid 是您Oracle数据库的oracle_sid； <br />role 是Oracle服务器中角色的名字； <br />d (可选)表示这个角色是缺省值；a (可选)表示这个角色带有WITH ADMIN选项，您只可以把这个角色授予其他角色，不能是其他用户。 <br />以下是在/etc/group文件中设置的例子： <br />ora_test_osoper_d:NONE:1:jim,narry,scott <br />ora_test_osdba_a:NONE:3:pat <br />ora_test_role1:NONE:4:bob,jane,tom,mary,jim <br />bin: NONE:5:root,oracle,dba <br />root:NONE:7:root <br />词组“ora_test_osoper_d”表示组的名字；词组“NONE”表示这个组的密码；数字1表示这个组的ID；接下来的是这个组的成员。前两行是Oracle服务器角色的例子，使用test作为sid，osoper和osdba作为Oracle服务器角色的名字。osoper是分配给用户的缺省角色，osdba带有WITH ADMIN选项。为了使这些数据库角色起作用，您必须shutdown您的数据库系统，设置Oracle数据库参数文件initORACLE_SID.ora中os_roles参数为True，然后重新启动您的数据库。如果您想让这些角色有connect internal权限，运行orapwd为这些角色设置密码。当您尝试connect internal时，您键入的密码表示了角色所对应的权限。 <br />·SQL*DBA命令的安全性： <br />如果您没有SQL*PLUS应用程序，您也可以使用SQL*DBA作SQL查权限相关的命令只能分配给Oracle软件拥有者和DBA组的用户，因为这些命令被授予了特殊的系统权限。 <br />(1) startup <br />(2) shutdown <br />(3) connect internal <br />·数据库文件的安全性： <br />Oracle软件的拥有者应该这些数据库文件($ORACLE_HOME/dbs/*.dbf)设置这些文件的使用权限为0600：文件的拥有者可读可写，同组的和其他组的用户没有写的权限。 <br />Oracle软件的拥有者应该拥有包含数据库文件的目录，为了增加安全性，建议收回同组和其他组用户对这些文件的可读权限。 <br />·网络安全性： <br />当处理网络安全性时，以下是额外要考虑的几个问题。 <br />(1) 在网络上使用密码在网上的远端用户可以通过加密或不加密方式键入密码，当您用不加密方式键入密码时，您的密码很有可能被非法用户截获，导致破坏了系统的安全性。 <br />(2) 网络上的DBA权限控制您可以通过下列两种方式对网络上的DBA权限进行控制： <br />A 设置成拒绝远程DBA访问； <br />B 通过orapwd给DBA设置特殊的密码。 <br />·建立安全性策略： <br />系统安全性策略 <br />(1)管理数据库用户：数据库用户是访问Oracle数据库信息的途径，因此，应该很好地维护管理数据库用户的安全性。按照数据库系统的大小和管理数据库用户所需的工作量，数据库安全性管理者可能只是拥有create，alter，或drop数据库用户的一个特殊用户，或者是拥有这些权限的一组用户，应注意的是，只有那些值得信任的个人才应该有管理数据库用户的权限。 <br />(2) 用户身份确认：数据库用户可以通过操作系统，网络服务，或数据库进行身份确认，通过主机操作系统进行用户身份认证的优点有： <br />A 用户能更快，更方便地联入数据库； <br />B 通过操作系统对用户身份确认进行集中控制：如果操作系统与数据库用户信息一致，Oracle无须存储和管理用户名以及密码； <br />C 用户进入数据库和操作系统审计信息一致。 <br />(3) 操作系统安全性 <br />A 数据库管理员必须有create和delete文件的操作系统权限； <br />B 一般数据库用户不应该有create或delete与数据库相关文件的操作系统权限； <br />C 如果操作系统能为数据库用户分配角色，那么安全性管理者必须有修改操作系统帐户安全性区域的操作系统权限。 <br />·数据的安全性策略： <br />数据的生考虑应基于数据的重要性。如果数据不是很重要，那么数据的安全性策略可以稍稍放松一些。然而，如果数据很重要，那么应该有一谨慎的安全性策略，用它来维护对数据对象访问的有效控制。 <br />·用户安全性策略： <br />(1) 一般用户的安全性： <br />A 密码的安全性：如果用户是通过数据库进行用户身份的确认，那么建议使用密码加密的方式与数据库进行连接。这种方式的设置方法如下： <br />在客户端的oracle.ini文件中设置ora_encrypt_login数为true； <br />在服务器端的initORACLE_SID.ora文件中设置dbling_encypt_login参数为true。 <br />B 权限管理：对于那些用户很多，应用程序和数据对象很丰富的数据库，应充分利用“角色”这个机制所带的方便性对权限进行有效管理。对于复杂的系统环境，“角色”能大大地简化权限的理。 <br />(2) 终端用户的安全性： <br />您必须针对终端用户制定安全性策略。例如，对于一个有很多用户的大规模数据库，安全性管理者可以决定用户组分类为这些用户组创建用户角色，把所需的权限和应用程序角色授予每一个用户角色，以及为用户分配相应的用户角色。当处理特殊的应用要求时，安全性管理者也必须明确地把一些特定的权限要求授予给用户。您可以使用“角色”对终端用户进行权限管理。 <br />·数据库管理者安全性策略： <br />(1) 保护作为sys和system用户的连接： <br />当数据库创建好以后，立即更改有管理权限的sys和system用户的密码，防止非法用户访问数据库。当作为sys和system用户连入数据库后，用户有强大的权限用各种方式对数据库进行改动。 <br />(2) 保护管理者与数据库的连接：<br />应该只有数据库管理者能用管理权限连入数据库，当以sysdba或startup，shutdown，和recover或数据库对象(例如create,drop，和delete等)进行没有任何限制的操作。 <br />(3) 使用角色对管理者权限进行管理 <br />·应用程序开发者的安全性策略： <br />(1) 应用程序开发者和他们的权限数据库应用程序开发者是唯一一类需要特殊权限组完成自己工作的数据库用户。开发者需要诸如create table,create，procedure等系统权限，然而，为了限制开发者对数据库的操作，只应该把一些特定的系统权限授予开发者。 <br />(2) 应用程序开发者的环境： <br />A 程序开发者不应与终端用户竞争数据库资源； <br />B 用程序开发者不能损害数据库其他应用产品。 <br />(3) free和controlled应用程序开发应用程序开发者有一下两种权限： <br />A free development <br />应用程序开发者允许创建新的模式对象，包括table,index,procedure,package等，它允许应用程序开发者开发独立于其他对象的应用程序。 <br />B controlled development <br />应用程序开发者不允许创建新的模式对象。所有需要table,indes procedure等都由数据库管理者创建，它保证了数据库管理者能完全控制数据空间的使用以及访问数据库信息的途径。但有时应用程序开发者也需这两种权限的混和。 <br />(4) 应用程序开发者的角色和权限数据库安全性管理者能创建角色来管理典型的应用程序开发者的权限要求。 <br />A create系统权限常常授予给应用程序开发者，以至于他们能创建他的数据对象。 <br />B 数据对象角色几乎不会授予给应用程序开发者使用的角色。 <br />(5) 加强应用程序开发者的空间限制作为数据库安全性管理者，您应该特别地为每个应用程序开发者设置以下的一些限制： <br />A 开发者可以创建table或index的表空间； <br />B 在每一个表空间中，开发者所拥有的空间份额。应用程序管理者的安全在有许多数据库应用程序的数据库系统中，您可能需要一应用程序管理者，应用程序管理者应负责起以下的任务：<br />a）为每一个应用程序创建角色以及管理每一个应用程序的角色； <br />b）创建和管理数据库应用程序使用的数据对象； <br />c）需要的话，维护和更新应用程序代码和Oracle的存储过程和程序包。<br />我相信有了以上的这些建议，作为一个Oracle的管理者绝对可以做好他本职的工作了。可是，我们再怎么努力，都始终得面对这样一个现实，那就是Oracle毕竟是其他人开发的，而我们却在使用。所以，Oracle到底有多少漏洞--我想这个不是你和我所能解决的。不过既然作为一篇讨论Oracle数据安全的文章，我认为有必要把漏洞这一块也写进去，毕竟这也是“安全”必不可少的一部分。呵呵！<br />所以……<br />【Oracle漏洞举例】：<br />·Oracle9iAS Web Cache远程拒绝服务攻击漏洞（2002-10-28）<br />·Oracle 8.1.6的oidldapd中的漏洞 <br />·Oracle 9iAS OracleJSP 泄漏JSP文件信息漏洞 <br />·Linux ORACLE 8.1.5漏洞 <br />想必我没有理由再往下举了，因为读者肯定已经从其他有效的途径得到了关于Oracle漏洞的最新情报。我这里就不再赘述了。<br />总而言之一句话--“Oracle数据安全是一个博大而又精深的话题；如果你没有耐心，就永远不会得到它的精髓之所在。”<br />常用日期格式是 2005-10-13 怎样能转换成 20051013 呢?</p><p>我自己经常用到,怕自己忘记了,所以用BLOG记录下来.</p><p><br />convert(char(12),datatime,112) </p><img src ="http://www.blogjava.net/TrampEagle/aggbug/60959.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-07-31 10:28 <a href="http://www.blogjava.net/TrampEagle/archive/2006/07/31/60959.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WebLogic Server 管理最佳实践（转）</title><link>http://www.blogjava.net/TrampEagle/archive/2006/07/31/60955.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Mon, 31 Jul 2006 02:26:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/archive/2006/07/31/60955.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/60955.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/archive/2006/07/31/60955.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/60955.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/60955.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 引自：http://www.zebcn.com/html/200603/474.html创建 WebLogic 配置/域域是一组逻辑上相关的WebLogic Server资源，您可以把它当作单个管理单元进行管理。域将所有的资源和应用程序信息保存在一个基于XML的配置库中。为了在WebLogic Server上部署并运行应用程序，首先要创建域。 推荐使用域配置向导作为创建新域的工具。如果您准备编写脚...&nbsp;&nbsp;<a href='http://www.blogjava.net/TrampEagle/archive/2006/07/31/60955.html'>阅读全文</a><img src ="http://www.blogjava.net/TrampEagle/aggbug/60955.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-07-31 10:26 <a href="http://www.blogjava.net/TrampEagle/archive/2006/07/31/60955.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Jakarta Commons:巧用类和组件(转)</title><link>http://www.blogjava.net/TrampEagle/archive/2006/05/20/47182.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Sat, 20 May 2006 08:34:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/archive/2006/05/20/47182.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/47182.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/archive/2006/05/20/47182.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/47182.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/47182.html</trackback:ping><description><![CDATA[原文引自：<a href="http://linux.ccidnet.com/art/322/20030805/57869_1.html">http://linux.ccidnet.com/art/322/20030805/57869_1.html</a><br /><font color="#000000">作者：Vikram Goyal；仙人掌工作室译　来源：赛迪网　发布时间：2003.08.05</font><br /><br />Jakarta Commons是Jakarta的子项目，它创建和维护着许多独立软件包，这些包一般与其他框架或产品无关，其中收集了大量小型、实用的组件，大部分面向服务器端编程。 
<p style="TEXT-INDENT: 2em">　　Commons的包分成两部分：Sandbox，Commons代码库。Sandbox是一个测试平台，用来检验各种设想、计划。本文介绍的组件属于Commons代码库，文章将展示各个组件的功能、适用场合，并通过简单的例子介绍其用法。 
</p><p style="TEXT-INDENT: 2em">　　<b>一、概述</b></p><p style="TEXT-INDENT: 2em">　　可重用性是Jakarta Commons项目的灵魂所在。这些包在设计阶段就已经考虑了可重用性问题。其中一些包，例如Commons里面用来记录日志的Logging包，最初是为其他项目设计的，例如Jakarta Struts项目，当人们发现这些包对于其他项目也非常有用，能够极大地帮助其他项目的开发，他们决定为这些包构造一个"公共"的存放位置，这就是Jakarta Commons项目。 
</p><p style="TEXT-INDENT: 2em">　　为了真正提高可重用性，每一个包都必须不依赖于其他大型的框架或项目。因此，Commons项目的包基本上都是独立的，不仅是相对于其他项目的独立，而且相对于Commons内部的大部分其他包独立。虽然存在一些例外的情况，例如Betwixt包要用到XML API，但绝大部分只使用最基本的API，其主要目的就是要能够通过简单的接口方便地调用。 
</p><p style="TEXT-INDENT: 2em">　　不过由于崇尚简洁，许多包的文档变得过于简陋，缺乏维护和支持，甚至有一部分还有错误的链接，文档也少得可怜。大部分的包需要我们自己去找出其用法，甚至有时还需要我们自己去分析其适用场合。本文将逐一介绍这些包，希望能够帮助你迅速掌握这一积累了许多人心血的免费代码库。 
</p><p style="TEXT-INDENT: 2em">　　说明：Jakarta Commons和<a href="http://commons.apache.org/" target="_blank">Apache Commons</a>是不同的，后者是Apache Software Foundation的一个顶层项目，前者则是Jakarta项目的一个子项目，同是也是本文要讨论的主角。本文后面凡是提到Commons的地方都是指Jakarta的Commons。 
</p><p style="TEXT-INDENT: 2em">　　为了便于说明，本文把Commons项目十八个成品级的组件（排除了EL、Latka和Jexl）分成5类，如下表所示。 
</p><p style="TEXT-INDENT: 2em"></p><center><img style="BORDER-RIGHT: black 1px solid; BORDER-TOP: black 1px solid; BORDER-LEFT: black 1px solid; BORDER-BOTTOM: black 1px solid" src="http://linux.ccidnet.com/col/attachment/2003/8/247208.gif" /></center><p style="TEXT-INDENT: 2em">　　必须指出的是，这种分类只是为了方便文章说明，Commons项目里面实际上并不存在这种分类，同时这些分类的边界有时也存在一定的重叠。 
</p><p style="TEXT-INDENT: 2em">　　本文首先介绍Web相关类和其他类里面的组件，下一篇文章将涉及XML相关、包装这两类，最后一篇文章专门介绍属于工具类的包。 
</p><p style="TEXT-INDENT: 2em">　　<b>二、其他类</b></p><p style="TEXT-INDENT: 2em">　　CLI、Discovery、Lang和Collections包归入其他类，这是因为它们都各自针对某个明确、实用的小目标，可谓专而精。 
</p><p style="TEXT-INDENT: 2em">　　<b>2.1 CLI</b></p><p style="TEXT-INDENT: 2em">　　■ 概况：CLI即Command Line Interface，也就是"命令行接口"，它为Java程序访问和解析命令行参数提供了一种统一的接口。 
</p><p style="TEXT-INDENT: 2em">　　■ 官方资源：<a href="http://jakarta.apache.org/commons/cli/index.html" target="_blank">主页</a>，<a href="http://www.apache.org/dist/jakarta/commons/discovery/binaries/commons-discovery-0.2.zip" target="_blank">二进制</a>，<a href="http://www.apache.org/dist/jakarta/commons/discovery/source/commons-discovery-0.2-src.zip" target="_blank">源代码</a></p><p style="TEXT-INDENT: 2em">　　■ 何时适用：当你需要以一种一致的、统一的方式访问命令行参数之时。 
</p><p style="TEXT-INDENT: 2em">　　■ 示例应用：CLIDemo.java。CLASSPATH中必须包含commons-cli-1.0.jar。 
</p><p style="TEXT-INDENT: 2em">　　■ 说明： 
</p><p style="TEXT-INDENT: 2em">　　有多少次你不得不为一个新的应用程序重新设计新的命令行参数处理方式？如果能够只用某个单一的接口，统一完成诸如定义输入参数（是否为强制参数，数值还是字符串，等等）、根据一系列规则分析参数、确定应用要采用的路径等任务，那该多好！答案就在CLI。 
</p><p style="TEXT-INDENT: 2em">　　在CLI中，每一个想要在命令中指定的参数都是一个Option对象。首先创建一个Options对象，将各个Option对象加入Options对象，然后利用CLI提供的方法来解析用户的输入参数。Option对象可以要求用户必须输入某个参数，例如必须在命令行提供文件名字。如果某个参数是必须的，创建Option对象的时候就要显式地指定。 
</p><p style="TEXT-INDENT: 2em">　　下面是使用CLI的步骤。 
</p><p style="TEXT-INDENT: 2em"><ccid_nobr><table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="540" align="center" bordercolorlight="black" border="1"><tbody><tr><td class="code" bgcolor="#e6e6e6"><pre><ccid_code>// …
// ①  创建一个Options：
Options options = new Options();
options.addOption("t", false, "current time");
// …
// ② 创建一个解析器，分析输入：
CommandLineParser parser = new BasicParser();
CommandLine cmd;
try {
	cmd = parser.parse(options, args); 
} catch (ParseException pe) {
	usage(options);
	return;
}
// …
// ③ 最后就可以根据用户的输入，采取相应的操作：
if (cmd.hasOption("n")) {
	System.err.println("Nice to meet you: " +
	cmd.getOptionValue('n'));
}</ccid_code></pre></td></tr></tbody></table></ccid_nobr></p><p style="TEXT-INDENT: 2em">　　这就是使用CLI的完整过程了。当然，CLI还提供了其他高级选项，例如控制格式和解析过程等，但基本的使用思路仍是一致的。请参见本文最后提供的示例程序。 
</p><p style="TEXT-INDENT: 2em">　　<b>2.2 Discovery</b></p><p style="TEXT-INDENT: 2em">　　■ 概况：Discovery组件是发现模式（Discovery Pattern）的一个实现，它的目标是按照一种统一的方式定位和实例化类以及其他资源。 
</p><p style="TEXT-INDENT: 2em">　　■ 官方资源：<a href="http://jakarta.apache.org/commons/discovery/" target="_blank">主页</a>，<a href="http://www.apache.org/dist/jakarta/commons/discovery/binaries/commons-discovery-0.2.zip" target="_blank">二进制</a>，<a href="http://www.apache.org/dist/jakarta/commons/discovery/source/commons-discovery-0.2-src.zip" target="_blank">源代码</a>。 
</p><p style="TEXT-INDENT: 2em">　　■ 何时适用：当你想用最佳的算法在Java程序中查找Java接口的各种实现之时。 
</p><p style="TEXT-INDENT: 2em">　　■ 应用实例：DiscoveryDemo.java，MyInterface.java，MyImpl1.java，MyImpl2.java，MyInterface。要求CLASSPATH中必须包含commons-discovery.jar和commons-logging.jar。 
</p><p style="TEXT-INDENT: 2em">　　■ 说明： 
</p><p style="TEXT-INDENT: 2em">　　Discovery的意思就是"发现"，它试图用最佳的算法查找某个接口的所有已知的实现。在使用服务的场合，当我们想要查找某个服务的所有已知的提供者时，Discovery组件尤其有用。 
</p><p style="TEXT-INDENT: 2em">　　考虑一下这种情形：我们为某个特别复杂的任务编写了一个接口，所有该接口的实现都用各不相同的方式来完成这个复杂任务，最终用户可以根据需要来选择完成任务的具体方式。那么，在这种情形下，最终用户应该用什么办法来找出接口的所有可用实现（即可能的完成任务的方式）呢？ 
</p><p style="TEXT-INDENT: 2em">　　上面描述的情形就是所谓的服务-服务提供者体系。服务的功能由接口描述，服务提供者则提供具体的实现。现在的问题是最终用户要用某种办法来寻找系统中已经安装了哪些服务提供者。在这种情形下，Discovery组件就很有用了，它不仅可以用来查找那些实现了特定接口的类，而且还可以用来查找资源，例如图片或其他文件等。在执行这些操作时，Discovery遵从Sun的服务提供者体系所定义的规则。 
</p><p style="TEXT-INDENT: 2em">　　由于这个原因，使用Discovery组件确实带来许多方便。请读者参阅本文后面示例程序中的接口MyInterface.java和两个实现类MyImpl1.java、MyImple2.java，了解下面例子的细节。在使用Discovery的时候要提供MyInterface文件，把它放入META-INF/services目录，注意该文件的名字对应接口的完整限定名称（Fully Qualified Name），如果接口属于某个包，该文件的名字也必须相应地改变。 
</p><p style="TEXT-INDENT: 2em"><ccid_nobr><table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="540" align="center" bordercolorlight="black" border="1"><tbody><tr><td class="code" bgcolor="#e6e6e6"><pre><ccid_code>// …
// ① 创建一个类装入器的实例。
ClassLoaders loaders =
	ClassLoaders.getAppLoaders(MyInterface.class, getClass(), false);
// …
// ② 用DiscoverClass的实例来查找实现类。
DiscoverClass discover = new DiscoverClass(loaders);
// …
// ③ 查找实现了指定接口的类：
Class implClass = discover.find(MyInterface.class);
System.err.println("Implementing Provider: " + implClass.getName());</ccid_code></pre></td></tr></tbody></table></ccid_nobr></p><p style="TEXT-INDENT: 2em">　　运行上面的代码，就可以得到在MyInterface文件中注册的类。再次提醒，如果你的实现是封装在包里面的，在这里注册的名字也应该作相应地修改，如果该文件没有放在正确的位置，或者指定名字的实现类不能找到或实例化，程序将抛出DiscoverException，表示找不到符合条件的实现。下面是MyInterface文件内容的一个例子：MyImpl2 # Implementation 2。 
</p><p style="TEXT-INDENT: 2em">　　当然，实现类的注册办法并非只有这么一种，否则的话Discovery的实用性就要大打折扣了！实际上，按照Discovery内部的类查找机制，按照这种方法注册的类将是Discovery最后找到的类。另一种常用的注册方法是通过系统属性或用户定义的属性来传递实现类的名字，例如，放弃META-INF/services目录下的文件，改为执行java -DMyInterface=MyImpl1 DiscoveryDemo命令来运行示例程序，这里的系统属性是接口的名字，值是该接口的提供者，运行的结果是完全一样的。 
</p><p style="TEXT-INDENT: 2em">　　Discovery还可以用来创建服务提供者的(singleton)实例并调用其方法，语法如下：((MyInterface)discover.newInstance(MyInterface.class)).myMethod();。注意在这个例子中，我们并不知道到底哪一个服务提供者实现了myMethod，甚至我们根本不必关心这一点。具体的情形与运行这段代码的方式以及运行环境中已经注册了什么服务提供者有关，在不同的环境下运行，实际得到的服务提供者可能不同。 
</p><p style="TEXT-INDENT: 2em">　　<b>2.3 Lang</b></p><p style="TEXT-INDENT: 2em">　　■ 概况：Lang是java.lang的一个扩展包，增加了许多操作String的功能，另外还支持C风格的枚举量。 
</p><p style="TEXT-INDENT: 2em">　　■ 官方资源：<a href="http://jakarta.apache.org/commons/lang.html" target="_blank">主页</a>，<a href="http://jakarta.apache.org/builds/jakarta-commons/release/commons-lang/v1.0.1/commons-lang-1.0.1.zip" target="_blank">二进制</a>，<a href="http://jakarta.apache.org/builds/jakarta-commons/release/commons-lang/v1.0.1/commons-lang-1.0.1-src.zip" target="_blank">源代码</a>。 
</p><p style="TEXT-INDENT: 2em">　　■ 何时适用：当java.lang包提供的方法未能满足需要，想要更多的功能来处理String、数值和System属性时；还有，当你想要使用C风格的枚举量时。 
</p><p style="TEXT-INDENT: 2em">　　■ 示例应用：LangDemo.java，Mortgage.java，OnTV.java。CLASSPATH中必须包含commons-lang.jar。 
</p><p style="TEXT-INDENT: 2em">　　■ 说明： 
</p><p style="TEXT-INDENT: 2em">　　这个包提供了许多出于方便目的而提供的方法，它们中的大多数是静态的，简化了日常编码工作。StringUtils类是其中的一个代表，它使得开发者能够超越标准的java.lang.String包来处理字符串。使用这些方法很简单，通常只要在调用静态方法时提供适当的参数就可以了。例如，如果要将某个单词的首字符改为大写，只需调用：StringUtils.capitalise("name")，调用的输出结果是Name。请浏览StringUtils API文档了解其他静态方法，也许你会找到一些可以直接拿来使用的代码。本文提供的示例程序示范了其中一些方法的使用。 
</p><p style="TEXT-INDENT: 2em">　　另一个值得注意的类是RandomStringUtils，它提供了生成随机字符串的方法，用来创建随机密码实在太方便了。 
</p><p style="TEXT-INDENT: 2em">　　NumberUtils类提供了处理数值数据的方法，许多方法值得一用，例如寻找最大、最小数的方法，将String转换成数值的方法，等等。NumberRange和CharRange类分别提供了创建和操作数值范围、字符范围的方法。 
</p><p style="TEXT-INDENT: 2em">　　Builder包里的类提供了一些特殊的方法，可用来构造类的toString、hashCode、compareTo和equals方法，其基本思路就是构造出类的高质量的toString、hashCode、compareTo和equals方法，从而免去了用户自己定义这些方法之劳，只要调用一下Builder包里面的方法就可以了。例如，我们可以用ToStringBuilder来构造出类的toString描述，如下例所示： 
</p><p style="TEXT-INDENT: 2em"><ccid_nobr><table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="540" align="center" bordercolorlight="black" border="1"><tbody><tr><td class="code" bgcolor="#e6e6e6"><pre><ccid_code>public class Mortgage {
	private float rate;
	private int years;
	....
	public String toString() {
		return new ToStringBuilder(this).
			append("rate",  this.rate).
			append("years", this.years).
			toString();
	}
}</ccid_code></pre></td></tr></tbody></table></ccid_nobr></p>使用这类方法有什么好处呢？显然，它使得我们有可能通过一种统一的方式处理所有数据类型。所有Builder方法的用法都和上例相似。 
<p style="TEXT-INDENT: 2em">　　Java没有C风格的枚举量，为此，lang包提供了一个类型安全的Enum类型，填补了空白。Enum类是抽象的，如果你要创建枚举量，就要扩展Enum类。下面的例子清楚地说明了Enum的用法。 
</p><p style="TEXT-INDENT: 2em"><ccid_nobr><table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="540" align="center" bordercolorlight="black" border="1"><tbody><tr><td class="code" bgcolor="#e6e6e6"><pre><ccid_code>import org.apache.commons.lang.enum.Enum;
import java.util.Map;
import java.util.List;
import java.util.Iterator;
    
public final class OnTV extends Enum {
            
	public static final OnTV IDOL= 
	  new OnTV("Idol");
	public static final OnTV SURVIVOR =
	  new OnTV("Survivor");
	public static final OnTV SEINFELD = 
	  new OnTV("Seinfeld");

	private OnTV(String show) {
		super(show);
	}
	public static OnTV getEnum(String show){
		return (OnTV) getEnum(OnTV.class, show);
	}
	public static Map getEnumMap() {
		return getEnumMap(OnTV.class);
	}
	public static List getEnumList() {
		return getEnumList(OnTV.class);
	}
	public static Iterator iterator() {
		return iterator(OnTV.class);
	}
}</ccid_code></pre></td></tr></tbody></table></ccid_nobr></p><p style="TEXT-INDENT: 2em">　　以后我们就可以按照下面的方式使用枚举变量：OnTV.getEnum("Idol")。该调用从前面创建的枚举数据类型返回Idol。这个例子比较简单，实际上Enum类还提供了许多有用的方法，请参见本文后面提供的完整实例。 
</p><p style="TEXT-INDENT: 2em">　　<b>2.4 Collections</b></p><p style="TEXT-INDENT: 2em">　　■ 概况：扩展了Java Collection框架，增添了新的数据结构、迭代机制和比较操作符。 
</p><p style="TEXT-INDENT: 2em">　　■ 官方资源：<a href="http://jakarta.apache.org/commons/collections.html" target="_blank">主页</a>，<a href="http://jakarta.apache.org/builds/jakarta-commons/release/commons-collections/v2.1/commons-collections-2.1.zip" target="_blank">二进制</a>，<a href="http://jakarta.apache.org/builds/jakarta-commons/release/commons-collections/v2.1/commons-collections-2.1-src.zip" target="_blank">源代码</a>。 
</p><p style="TEXT-INDENT: 2em">　　■ 何时适用：几乎所有需要操作数据结构的重要Java开发项目都可以使用Collections API。和Java的标准实现相比，Collections API有着诸多优势。 
</p><p style="TEXT-INDENT: 2em">　　■ 示例应用：CollectionsDemo.java。要求CLASSPATH中包含commons-collections.jar。 
</p><p style="TEXT-INDENT: 2em">　　■ 说明： 
</p><p style="TEXT-INDENT: 2em">　　要在有限的文章篇幅之内详尽地介绍 Collections API实在是太困难了，不过这里仍将涵盖大多数最重要的类，希望能够引起你的兴趣，认真了解一下其余的类。Collections本身的文档也提供了许多资料并解释了每一个类的用法。 
</p><p style="TEXT-INDENT: 2em">　　Bag接口扩展标准的Java Collection，允许生成计数器来跟踪Bag里面的所有元素。当你想要跟踪进出某个集合的元素的总数时，Bag是非常有用的。由于Bag本身是一个接口，所以实际使用的应该是实现了该接口的类，例如HashBag或TreeBag--从这些类的名字也可以看出，HashBag实现的是一个HashMap的Bag，而TreeBag实现的是TreeMap的Bag。Bag接口中两个最重要的方法是：getCount(Object o)，用来返回Bag里面特定对象的出现次数；uniqueSet()，返回所有唯一元素。 
</p><p style="TEXT-INDENT: 2em">　　Buffer接口允许按照预定义的次序删除集合中的对象，删除次序可以是LIFO（Last In First Out，后进先出），或FIFO（First In First Out，先进先出），另外还可以是自定义的次序。下面来看看如何实现一个Buffer，按照自然次序删除元素。 
</p><p style="TEXT-INDENT: 2em">　　BinaryHeap类实现了Buffer接口，能够按照自然次序删除元素。如果要颠倒次序，则必须传入一个false，告诉Heap采用自然次序的逆序。 
</p><p style="TEXT-INDENT: 2em"><ccid_nobr><table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="540" align="center" bordercolorlight="black" border="1"><tbody><tr><td class="code" bgcolor="#e6e6e6"><pre><ccid_code>BinaryHeap heap = new BinaryHeap();
// …
// 将元素加入该Heap
heap.add(new Integer(-1));
heap.add(new Integer(-10));
heap.add(new Integer(0));
heap.add(new Integer(-3));
heap.add(new Integer(5));
//…
// 删除一个元素
heap.remove();</ccid_code></pre></td></tr></tbody></table></ccid_nobr></p><p style="TEXT-INDENT: 2em">　　调用该Heap的remove，按照自然次序，元素集合中的-10将被删除。如果我们要求按照逆序排序，则被删除的将是5。 
</p><p style="TEXT-INDENT: 2em">　　FastArrayList、FastHashMap和FastTreeMap类能够按照两种模式操作，超越了与它们对应的标准Collection。第一种模式是"慢模式"，类的修改操作（添加、删除元素）是同步的。与此相对，另一种模式是"快模式"，对这些类的访问假定为只读操作，因此不需要同步，速度较快。在快模式中，结构性的改动通过下列方式完成：首先克隆现有的类，修改克隆得到的类，最后用克隆得到的类替换原有的类。FastArrayList、FastHashMap和FastTreeMap类特别适合于那种初始化之后大部分操作都是只读操作的多线程环境。 
</p><p style="TEXT-INDENT: 2em">　　iterators包为各种集合和对象提供标准Java Collection包没有提供的迭代器。本文的示例应用示范了ArrayIterator，通过迭代方式访问Array的内容。iterators包里面各种迭代器的用法基本上与标准Java迭代器一样。 
</p><p style="TEXT-INDENT: 2em">　　最后，comparators包提供了一些实用的比较符。所谓比较符其实也是一个类，它定义的是如何比较两个属于同一类的对象，决定它们的排序次序。例如，在前面提到的Buffer类中，我们可以定义自己的比较符，用自定义的比较符来决定元素的排序次序，而不是采用元素的自然排序次序。下面来看看具体的实现经过。 
</p><p style="TEXT-INDENT: 2em"><ccid_nobr><table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="540" align="center" bordercolorlight="black" border="1"><tbody><tr><td class="code" bgcolor="#e6e6e6"><pre><ccid_code>// …
// ① 创建一个BinaryHeap类，但这一次参数中
//    指定NullComparator。NullComparator比较
//    null与其他对象，根据nullsAreHigh标记来
//    判断null值比其他对象大还是小：如果
//    nullsAreHigh的值是false，则认为null要比
//    其他对象小。
BinaryHeap heap2 = new BinaryHeap
 (new NullComparator(false));
// …
// ② 将一些数据（包括几个null值）加入heap：
heap2.add(null);
heap2.add(new Integer("6"));
heap2.add(new Integer("-6"));
heap2.add(null);
// …
// ③ 最后删除一个元素，Bag包含的null将减少
//    一个，因为null要比其他对象小。
heap2.remove();</ccid_code></pre></td></tr></tbody></table></ccid_nobr></p><p style="TEXT-INDENT: 2em">　　有关其他类Commons组件的介绍就到这里结束。如果你想了解更多细节信息，请参见API文档，最好再看看这些包的源代码。 
</p><p style="TEXT-INDENT: 2em">　　<b>三、Web类</b></p><p style="TEXT-INDENT: 2em">　　Web类的组件用来执行与Web相关的任务。 
</p><p style="TEXT-INDENT: 2em">　　<b>3.1 FileUpload</b></p><p style="TEXT-INDENT: 2em">　　■ 概况：一个可以直接使用的文件上载组件。 
</p><p style="TEXT-INDENT: 2em">　　■ 官方资源：<a href="http://jakarta.apache.org/commons/fileupload/index.html" target="_blank">主页</a>。由于这个组件尚未正式发布，今年二月发布的Beta版又有许多BUG，所以建议从<a href="http://cvs.apache.org/builds/jakarta-commons/nightly/commons-fileupload/" target="_blank">nightly builds</a>下载最新的版本。 
</p><p style="TEXT-INDENT: 2em">　　■ 何时适用：当你想要在Java服务器环境中加入一个易用、高性能的文件上载组件之时。 
</p><p style="TEXT-INDENT: 2em">　　■ 示例应用：fileuploaddemo.jsp，fileuploaddemo.htm，和msg.jsp。要求服务器端应用目录的WEB-INF/lib下面有commons-fileupload-1.0-dev.jar。 
</p><p style="TEXT-INDENT: 2em">　　■ 说明： 
</p><p style="TEXT-INDENT: 2em">　　FileUpload组件解决了常见的文件上载问题。它提供了一个易用的接口来管理上载到服务器的文件，可用于JSP和Servlet之中。FileUpload组件遵从RFC1867，它分析输入请求，向应用程序提供一系列上载到服务器的文件。上载的文件可以保留在内存中，也可以放入一个临时位置（允许配置一个表示文件大小的参数，如果上载的文件超过了该参数指定的大小，则把文件写入一个临时位置）。另外还有一些参数可供配置，包括可接受的最大文件、临时文件的位置等。 
</p><p style="TEXT-INDENT: 2em">　　下面介绍一下使用FileUpload组件的步骤。 
</p><p style="TEXT-INDENT: 2em">　　首先创建一个HTML页面。注意，凡是要上载文件的表单都必须设置enctype属性，且属性的值必须是multipart/form-data，同时请求方法必须是POST。下面的表单除了上载两个文件，另外还有一个普通的文本输入框： 
</p><p style="TEXT-INDENT: 2em"><ccid_nobr><table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="540" align="center" bordercolorlight="black" border="1"><tbody><tr><td class="code" bgcolor="#e6e6e6"><pre><ccid_code>&lt;form name="myform" action="fileuploaddemo.jsp"
 method="post" enctype="multipart/form-data"&gt;
    输入你的名字:&lt;br /&gt;
      &lt;input type="text" name="name" size="15"/&gt;&lt;br /&gt;
  图形:&lt;br /&gt;
      &lt;input type="file" name="myimage"&gt;&lt;br/&gt;
  文件:&lt;br /&gt;
      &lt;input type="file" name="myfile"&gt;&lt;br /&gt;&lt;br /&gt;
    &lt;input type="submit" name="Submit" 
     value="Submit your files"/&gt;</ccid_code></pre></td></tr></tbody></table></ccid_nobr></p><p style="TEXT-INDENT: 2em">　　接下来创建JSP页面。 
</p><p style="TEXT-INDENT: 2em"><ccid_nobr><table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="540" align="center" bordercolorlight="black" border="1"><tbody><tr><td class="code" bgcolor="#e6e6e6"><pre><ccid_code>// …
// ① 检查输入请求是否为multipart的表单数据。
boolean isMultipart = FileUpload.
  isMultipartContent(request);
// …
// ② 为该请求创建一个句柄，通过它来解析请求。执行
//    解析后，所有的表单项目都保存在一个List中。
DiskFileUpload upload = new DiskFileUpload();
// 通过句柄解析请求，解析得到的项目保存在一个List中
List items = upload.parseRequest(request);
// …
// ③ 通过循环依次获得List里面的文件项目。要区分表示
//    文件的项目和普通的表单输入项目，使用isFormField()
//    方法。根据处理请求的要求，我们可以保存上载的文
//    件，或者一个字节一个字节地处理文件内容，或者打
//    开文件的输入流。
Iterator itr = items.iterator();

while(itr.hasNext()) {
	FileItem item = (FileItem) itr.next();
        
// 检查当前的项目是普通的表单元素，还是一个上载的文件
	if(item.isFormField()) {
// 获得表单域的名字
	String fieldName = item.getFieldName();
// 如果表单域的名字是name…
	if(fieldName.equals("name"))
		request.setAttribute("msg", 
		"Thank You: " + item.getString());
		
	} else {
// 该项目是一个上载的文件，把它保存到磁盘。
// 注意item.getName()
// 会返回上载文件在客户端的完整路径名称，这似乎是一个BUG。
// 为解决这个问题，这里使用了fullFile.getName()。
		File fullFile  = new File(item.getName());  
		File savedFile = new File
		(getServletContext().getRealPath("/"),
		fullFile.getName());
		item.write(savedFile);
	}
}</ccid_code></pre></td></tr></tbody></table></ccid_nobr></p><p style="TEXT-INDENT: 2em">　　我们可以通过上载句柄的upload.setSizeMax来限制上载文件的大小。当上载文件的大小超过允许的值时，程序将遇到异常。在上面的例子中，文件大小的限制值是-1，表示允许上载任意大小的文件。 
</p><p style="TEXT-INDENT: 2em">　　还有其他一些略有变化的使用形式，正如前面所指出的，我们可以在上载的文件上打开一个输入流，或者让它们驻留在内存中直至空间占用达到一定的限制值，或者在判断文件类型的基础上，以String或Byte数组的形式获取其内容，或者直接删除文件。这一切都只要使用FileItem类提供的方法就可以方便地做到（DefaultFileItem是FileItem的一个实现）。 <br /><strong>3.2 HttpClient</strong></p><p style="TEXT-INDENT: 2em">　　■ 概况：这个API扩展了java.net包，提供了模拟浏览器的功能。 
</p><p style="TEXT-INDENT: 2em">　　■ 官方资源：<a href="http://jakarta.apache.org/commons/httpclient/index.html" target="_blank">主页</a>，<a href="http://jakarta.apache.org/builds/jakarta-commons/release/commons-httpclient/v2.0/commons-httpclient-2.0-beta1.zip" target="_blank">二进制</a>，<a href="http://jakarta.apache.org/builds/jakarta-commons/release/commons-httpclient/v2.0/commons-httpclient-2.0-beta1-src.zip" target="_blank">源代码</a>。 
</p><p style="TEXT-INDENT: 2em">　　■ 何时适用：当你要构造Web浏览器的功能；当你的应用需要一种高效的办法进行HTTP/HTTPS通信时。 
</p><p style="TEXT-INDENT: 2em">　　■ 示例应用：HttpClientDemo.java。要求CLASSPATH中有commons-httpclient.jar，common-logging.jar。要求使用JDK 1.4或更高版本。 
</p><p style="TEXT-INDENT: 2em">　　■ 说明： 
</p><p style="TEXT-INDENT: 2em">　　HttpClient扩展和增强了标准java.net包，是一个内容广泛的代码库，功能极其丰富，能够构造出各种使用HTTP协议的分布式应用，或者也可以嵌入到现有应用，为应用增加访问HTTP协议的能力。在Commons稳定版中，HttpClient的文档似乎要比其他包更完善一些，而且还带有几个实例。下面我们通过一个简单的例子来了解如何提取一个Web页面，HttpClient文档中也有一个类似的例子，我们将扩充那个例子使其支持SSL。注意本例需要JDK 1.4支持，因为它要用到Java Secure Socket Connection库，而这个库只有JDK 1.4及更高的版本才提供。 
</p><p style="TEXT-INDENT: 2em">　　① 首先确定一个可以通过HTTPS下载的页面，本例使用的是https://www.paypal.com/。同时确保%JAVA_HOME%/jre/lib/security/java.security文件包含了下面这行代码：security.provider.2=com.sun.net.ssl.internal.ssl.Provider。 
</p><p style="TEXT-INDENT: 2em">　　除了这些设置之外，HTTPS连接的处理方式没有其他特别的地方--至少对于本例来说如此。不过，如果远程网站使用的根证书不被你使用的Java认可，则首先必须导入它的证书。 
</p><p style="TEXT-INDENT: 2em">　　② 创建一个HttpClient的实例。HttpClient类可以看成是应用的主驱动程序，所有针对网络的功能都依赖于它。HttpClient类需要一个Connection Manager来管理连接。HttpConnectionManager允许我们创建自己的连接管理器，或者，我们也可以直接使用内建的SimpleHttpConnectionManager或MultiThreadedHttpConnectionManager类。如果在创建HttpClient时没有指定连接管理器，HttpClient默认使用SimpleHttpConnectionManager。 
</p><p style="TEXT-INDENT: 2em"><ccid_nobr><table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="540" align="center" bordercolorlight="black" border="1"><tbody><tr><td class="code" bgcolor="#e6e6e6"><pre><ccid_code>// 创建一个HttpClient的实例
HttpClient client = new HttpClient();</ccid_code></pre></td></tr></tbody></table></ccid_nobr></p><p style="TEXT-INDENT: 2em">　　③ 创建一个HttpMethod的实例，即确定与远程服务器的通信要采用哪种传输方式，HTTP允许采用的传输方式包括：GET，POST，PUT，DELETE，HEAD，OPTIONS，以及TRACE。这些传输方式分别作为一个独立的类实现，但所有这些类都实现HttpMethod接口。在本例中，我们使用的是GetMethod，创建GetMethod实例时在参数中指定我们想要GET的URL。 
</p><p style="TEXT-INDENT: 2em"><ccid_nobr><table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="540" align="center" bordercolorlight="black" border="1"><tbody><tr><td class="code" bgcolor="#e6e6e6"><pre><ccid_code>// 创建一个HttpMethod的实例
HttpMethod method = new GetMethod(url);</ccid_code></pre></td></tr></tbody></table></ccid_nobr></p><p style="TEXT-INDENT: 2em">　　④ 执行HttpMethod定义的提取操作。执行完毕后，executeMethod方法将返回远程服务器报告的状态代码。注意executeMethod属于HttpClient，而不是HttpMethod。 
</p><p style="TEXT-INDENT: 2em"><ccid_nobr><table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="540" align="center" bordercolorlight="black" border="1"><tbody><tr><td class="code" bgcolor="#e6e6e6"><pre><ccid_code>// 执行HttpMethod定义的提取操作
statusCode = client.executeMethod(method);</ccid_code></pre></td></tr></tbody></table></ccid_nobr></p><p style="TEXT-INDENT: 2em">　　⑤ 读取服务器返回的应答。如果前面的连接操作失败，程序将遇到HttpException或IOException，其中IOException一般意味着网络出错，继续尝试也不太可能获得成功。服务器返回的应答可以按照多种方式读取，例如作为一个字节数组，作为一个输入流，或者作为一个String。获得服务器返回的应答后，我们就可以按照自己的需要任意处置它了。 
</p><p style="TEXT-INDENT: 2em"><ccid_nobr><table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="540" align="center" bordercolorlight="black" border="1"><tbody><tr><td class="code" bgcolor="#e6e6e6"><pre><ccid_code>byte[] responseBody = method.getResponseBody();</ccid_code></pre></td></tr></tbody></table></ccid_nobr></p><p style="TEXT-INDENT: 2em">　　⑥ 最后要做的就是释放连接。 
</p><p style="TEXT-INDENT: 2em"><ccid_nobr><table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="540" align="center" bordercolorlight="black" border="1"><tbody><tr><td class="code" bgcolor="#e6e6e6"><pre><ccid_code>method.releaseConnection();</ccid_code></pre></td></tr></tbody></table></ccid_nobr></p><p style="TEXT-INDENT: 2em">　　以上只是非常简单地介绍了一下HttpClient库，HttpClient实际的功能要比本文介绍的丰富得多，不仅健壮而且高效，请参阅API文档了解详情。 
</p><p style="TEXT-INDENT: 2em">　　<b>3.3 Net</b></p><p style="TEXT-INDENT: 2em">　　■ 概况：一个用于操作Internet基础协议的底层API。 
</p><p style="TEXT-INDENT: 2em">　　■ 官方资源：<a href="http://jakarta.apache.org/commons/net/" target="_blank">主页</a>，<a href="http://apache.planetmirror.com/dist/jakarta/commons/net/binaries/" target="_blank">二进制</a>，<a href="http://apache.planetmirror.com.au/dist/jakarta/commons/net/source/" target="_blank">源代码</a>。 
</p><p style="TEXT-INDENT: 2em">　　■ 何时适用：当你想要访问各种Internet底层协议之时（Finger，Whois，TFTP，Telnet，POP3，FTP，NNTP，以及SMTP）。 
</p><p style="TEXT-INDENT: 2em">　　■ 示例应用：NetDemo.java。要求CLASSPATH中包含commons-net-1.0.0.jar。 
</p><p style="TEXT-INDENT: 2em">　　■ 说明： 
</p><p style="TEXT-INDENT: 2em">　　Net包是一个强大、专业的类库，类库里的类最初属于一个叫做NetComponents的商业产品。 
</p><p style="TEXT-INDENT: 2em">　　Net包不仅支持对各种低层次协议的访问，而且还提供了一个高层的抽象。大多数情况下，Net包提供的抽象已能满足一般需要，它使得开发者不再需要直接面对各种协议的Socket级的低层命令。使用高层抽象并不减少任何功能，Net API在这方面做得很出色，既提供了足够的功能，又不至于在特色方面作过多的妥协。 
</p><p style="TEXT-INDENT: 2em">　　SocketClient是支持所有协议的基础类，它是一个抽象类，聚合了各种协议都需要的公用功能。各种不同协议的使用过程其实很相似，首先利用connect方法建立一个指向远程服务器的连接，执行必要的操作，最后终止与服务器的连接。下面通过实例介绍具体的使用步骤。 
</p><p style="TEXT-INDENT: 2em"><ccid_nobr><table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="540" align="center" bordercolorlight="black" border="1"><tbody><tr><td class="code" bgcolor="#e6e6e6"><pre><ccid_code>// …
// ① 创建一个客户端。我们将用NNTPClient
//　　从新闻服务器下载新闻组清单。
client = new NNTPClient();
// …
// ② 利用前面创建的客户端连接到新闻服务器。
//　　这里选用的是一个新闻组较少的服务器。
client.connect("aurelia.deine.net");
// …
// ③ 提取新闻组清单。下面的命令将返回一个
//　　NewsGroupInfo对象的数组。如果指定的服
//　　务器上不包含新闻组，返回的数组将是空的，
//　　如果遇到了错误，则返回值是null。
list = client.listNewsgroups();
//...
// ④ 最后终止与服务器的连接。
 if (client.isConnected())
   client.disconnect();</ccid_code></pre></td></tr></tbody></table></ccid_nobr></p><p style="TEXT-INDENT: 2em">　　必须说明的是，listNewsgroups命令可能需要较长的时间才能返回，一方面是因为网络速度的影响，另外也可能是由于新闻组清单往往是很庞大的。NewsGroupInfo对象包含有关新闻组的详细信息，并提供了一些操作新闻组的命令，比如提取文章总数、最后发布的文章、发布文章的权限，等等。 
</p><p style="TEXT-INDENT: 2em">　　其他客户端，例如FingerClient、POP3Client、TelnetClient等，用法也差不多。 
</p><p style="TEXT-INDENT: 2em">　　结束语：有关Web相关类和其他类的介绍就到此结束。在下一篇文章中，我们将探讨XML类和包装类，最后一篇文章则介绍工具类。 
</p><p style="TEXT-INDENT: 2em">　　希望读者有兴趣试试本文提供的程序实例。很多时候Jakarta Commons给人以混乱的感觉，希望本文使你加深了对Jakarta Commons了解，或者至少引起了你对Commons子项目以及它提供的各种实用API和库的兴趣。 </p><img src ="http://www.blogjava.net/TrampEagle/aggbug/47182.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-05-20 16:34 <a href="http://www.blogjava.net/TrampEagle/archive/2006/05/20/47182.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java 平台中的增补字符（转）</title><link>http://www.blogjava.net/TrampEagle/archive/2006/05/18/46810.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Thu, 18 May 2006 06:23:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/archive/2006/05/18/46810.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/46810.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/archive/2006/05/18/46810.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/46810.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/46810.html</trackback:ping><description><![CDATA[原文引自：<a href="http://java.sun.com/developer/technicalArticles/Intl/Supplementary/index_zh_CN.html">http://java.sun.com/developer/technicalArticles/Intl/Supplementary/index_zh_CN.html</a><br /><br /><p><span class="byline">作者：Sun Microsystems, Inc. 的 <a href="http://java.sun.com/developer/technicalArticles/Intl/Supplementary/index_zh_CN.html#authorbio">Norbert Lindenberg 和 Masayoshi Okutsu</a></span></p><p>2004 年 5 月</p><p><a href="http://java.sun.com/developer/technicalArticles/Intl/Supplementary/index.html">English: Supplementary Characters in the Java Platform</a><br /><a lang="ja" href="http://java.sun.com/developer/technicalArticles/Intl/Supplementary/index_ja.html">日本語: Java プラットフォームにおける補助文字のサポート</a></p><h3>摘要</h3><p>本文介绍 Java 平台支持增补字符的方式。增补字符是 Unicode 标准中代码点超出 U+FFFF 的字符，因此它们无法在 Java 编程语言中描述为单个的 16 位实体（例如 <code>char</code> 数据类型）。这些字符一般极少用，但是，有些会在诸如中文或日文人名中用到，因此，在东亚国家，政府应用程序通常会要求支持这些字符。</p><p>Java 平台目前正在改进，以便支持对增补字符的处理，这种改进对现有的应用程序影响微乎其微。新的低层 API 在需要时能够使用单个的字符运行。不过，大多数文本处理 API 均使用字符序列，例如 <code>String</code> 类或字符数组。现在，这些均解释为 UTF-16 序列，而且，这些 API 实现已转变为正确地处理增补字符。这些改进已融入 Java 2 平台 5.0 版，标准版 (J2SE)。</p><p>除详细解释这些改进之外，本文同时为应用程序开发人员确定和实现必要的更改提供指导，以支持整个 Unicode 字符集的使用。</p><h3>背景</h3><p>Unicode 最初设计是作为一种固定宽度的 16 位字符编码。在 Java 编程语言中，基本数据类型 <code>char</code> 初衷是通过提供一种简单的、能够包含任何字符的数据类型来充分利用这种设计的优点。不过，现在看来，16 位编码的所有 65,536 个字符并不能完全表示全世界所有正在使用或曾经使用的字符。于是，Unicode 标准已扩展到包含多达 1,112,064 个字符。那些超出原来的 16 位限制的字符被称作增补字符。Unicode 标准 2.0 版是第一个包含启用增补字符设计的版本，但是，直到 3.1 版才收入第一批增补字符集。由于 J2SE 的 5.0 版必须支持 Unicode 标准 4.0 版，因此它必须支持增补字符。</p><p>对增补字符的支持也可能会成为东亚市场的一个普遍商业要求。政府应用程序会需要这些增补字符，以正确表示一些包含罕见中文字符的姓名。出版应用程序可能会需要这些增补字符，以表示所有的古代字符和变体字符。中国政府要求支持 GB18030（一种对整个 Unicode 字符集进行编码的字符编码标准），因此，如果是 Unicode 3.1 版或更新版本，则将包括增补字符。台湾标准 CNS-11643 包含的许多字符在 Unicode 3.1 中列为增补字符。香港政府定义了一种针对粤语的字符集，其中的一些字符是 Unicode 中的增补字符。最后，日本的一些供应商正计划利用增补字符空间中大量的专用空间收入 50,000 多个日文汉字字符变体，以便从其专有系统迁移至基于 Java 平台的解决方案。</p><p>因此，Java 平台不仅需要支持增补字符，而且必须使应用程序能够方便地做到这一点。由于增补字符打破了 Java 编程语言的基础设计构想，而且可能要求对编程模型进行根本性的修改，因此，Java Community Process 召集了一个专家组，以期找到一个适当的解决方案。该小组被称为 JSR-204 专家组，使用 <a href="http://jcp.org/en/jsr/detail?id=204">Unicode 增补字符支持的 Java 技术规范请求</a>的编号。从技术上来说，该专家组的决定仅适用于 J2SE 平台，但是由于 Java 2 平台企业版 (J2EE) 处于 J2SE 平台的最上层，因此它可以直接受益，我们期望 Java 2 平台袖珍版 (J2ME) 的配置也采用相同的设计方法。</p><p>不过，在了解 JSR-204 专家组确定的解决方案之前，我们需要先理解一些术语。</p><h3>代码点、字符编码方案、UTF-16：这些是指什么？ </h3><p>不幸的是，引入增补字符使字符模型变得更加复杂了。在过去，我们可以简单地说“字符”，在一个基于 Unicode 的环境（例如 Java 平台）中，假定字符有 16 位，而现在我们需要更多的术语。我们会尽量介绍得相对简单一些 — 如需了解所有详细的讨论信息，您可以阅读 <a href="http://www.unicode.org/versions/Unicode4.0.0/ch02.pdf">Unicode 标准第 2 章</a>或 Unicode 技术报告 17“<a href="http://www.unicode.org/reports/tr17/">字符编码模型</a>”。Unicode 专业人士可略过所有介绍直接参阅本部分中的最后定义。</p><p>字符是抽象的最小文本单位。它没有固定的形状（可能是一个字形），而且没有值。“A”是一个字符，“€”（德国、法国和许多其他欧洲国家通用货币的标志）也是一个字符。</p><p>字符集是字符的集合。例如，汉字字符是中国人最先发明的字符，在中文、日文、韩文和越南文的书写中使用。</p><p>编码字符集是一个字符集，它为每一个字符分配一个唯一数字。Unicode 标准的核心是一个编码字符集，字母“A”的编码为 0041<sub>16</sub> 和字符“€”的编码为 20AC<sub>16</sub>。Unicode 标准始终使用十六进制数字，而且在书写时在前面加上前缀“U+”，所以“A”的编码书写为“U+0041”。</p><p>代码点是指可用于编码字符集的数字。编码字符集定义一个有效的代码点范围，但是并不一定将字符分配给所有这些代码点。有效的 Unicode 代码点范围是 U+0000 至 U+10FFFF。Unicode 4.0 将字符分配给一百多万个代码点中的 96,382 代码点。</p><p>增补字符是代码点在 U+10000 至 U+10FFFF 范围之间的字符，也就是那些使用原始的 Unicode 的 16 位设计无法表示的字符。从 U+0000 至 U+FFFF 之间的字符集有时候被称为基本多语言面 (BMP)。因此，每一个 Unicode 字符要么属于 BMP，要么属于增补字符。</p><p>字符编码方案是从一个或多个编码字符集到一个或多个固定宽度代码单元序列的映射。最常用的代码单元是字节，但是 16 位或 32 位整数也可用于内部处理。UTF-32、UTF-16 和 UTF-8 是 Unicode 标准的编码字符集的字符编码方案。</p><p>UTF-32 即将每一个 Unicode 代码点表示为相同值的 32 位整数。很明显，它是内部处理最方便的表达方式，但是，如果作为一般字符串表达方式，则要消耗更多的内存。 </p><p>UTF-16 使用一个或两个未分配的 16 位代码单元的序列对 Unicode 代码点进行编码。值 U+0000 至 U+FFFF 编码为一个相同值的 16 位单元。增补字符编码为两个代码单元，第一个单元来自于高代理范围（U+D800 至 U+DBFF），第二个单元来自于低代理范围（U+DC00 至 U+DFFF）。这在概念上可能看起来类似于多字节编码，但是其中有一个重要区别：值 U+D800 至 U+DFFF 保留用于 UTF-16；没有这些值分配字符作为代码点。这意味着，对于一个字符串中的每个单独的代码单元，软件可以识别是否该代码单元表示某个单单元字符，或者是否该代码单元是某个双单元字符的第一个或第二单元。这相当于某些传统的多字节字符编码来说是一个显著的改进，在传统的多字节字符编码中，字节值 0x41 既可能表示字母“A”，也可能是一个双字节字符的第二个字节。</p><p>UTF-8 使用一至四个字节的序列对编码 Unicode 代码点进行编码。U+0000 至 U+007F 使用一个字节编码，U+0080 至 U+07FF 使用两个字节，U+0800 至 U+FFFF 使用三个字节，而 U+10000 至 U+10FFFF 使用四个字节。UTF-8 设计原理为：字节值 0x00 至 0x7F 始终表示代码点 U+0000 至 U+007F（Basic Latin 字符子集，它对应 ASCII 字符集）。这些字节值永远不会表示其他代码点，这一特性使 UTF-8 可以很方便地在软件中将特殊的含义赋予某些 ASCII 字符。</p><p>下表所示为几个字符不同表达方式的比较：</p><blockquote><p></p><table cellspacing="0" cellpadding="2" border="1"><tbody><tr><td>Unicode 代码点 </td><td><center>U+0041</center></td><td><center>U+00DF</center></td><td><center>U+6771</center></td><td><center>U+10400</center></td></tr><tr><td>表示字形 </td><td><center><img height="18" src="http://java.sun.com/developer/technicalArticles/Intl/Supplementary/000041.gif" width="18" align="bottom" /></center></td><td><center><img height="18" src="http://java.sun.com/developer/technicalArticles/Intl/Supplementary/0000df.gif" width="18" align="bottom" /></center></td><td><center><img height="23" src="http://java.sun.com/developer/technicalArticles/Intl/Supplementary/006771.gif" width="23" align="bottom" /></center></td><td><center><img height="19" src="http://java.sun.com/developer/technicalArticles/Intl/Supplementary/010400.gif" width="13" align="bottom" /></center></td></tr><tr><td>UTF-32 代码单元 </td><td><center><table cellspacing="0" cellpadding="2" border="1"><tbody><tr><td><code>00000041</code></td></tr></tbody></table></center></td><td><center><table cellspacing="0" cellpadding="2" border="1"><tbody><tr><td><code>000000DF</code></td></tr></tbody></table></center></td><td><center><table cellspacing="0" cellpadding="2" border="1"><tbody><tr><td><code>00006771</code></td></tr></tbody></table></center></td><td><center><table cellspacing="0" cellpadding="2" border="1"><tbody><tr><td><code>00010400</code></td></tr></tbody></table></center></td></tr><tr><td>UTF-16 代码单元 </td><td><center><table cellspacing="0" cellpadding="2" border="1"><tbody><tr><td><code>0041</code></td></tr></tbody></table></center></td><td><center><table cellspacing="0" cellpadding="2" border="1"><tbody><tr><td><code>00DF</code></td></tr></tbody></table></center></td><td><center><table cellspacing="0" cellpadding="2" border="1"><tbody><tr><td><code>6771</code></td></tr></tbody></table></center></td><td><center><table cellspacing="0" cellpadding="2" border="1"><tbody><tr><td><code>D801</code></td><td><code>DC00</code></td></tr></tbody></table></center></td></tr><tr><td>UTF-8 代码单元 </td><td><center><table cellspacing="0" cellpadding="2" border="1"><tbody><tr><td><code>41</code></td></tr></tbody></table></center></td><td><center><table cellspacing="0" cellpadding="2" border="1"><tbody><tr><td><code>C3</code></td><td><code>9F</code></td></tr></tbody></table></center></td><td><center><table cellspacing="0" cellpadding="2" border="1"><tbody><tr><td><code>E6</code></td><td><code>9D</code></td><td><code>B1</code></td></tr></tbody></table></center></td><td><center><table cellspacing="0" cellpadding="2" border="1"><tbody><tr><td><code>F0</code></td><td><code>90</code></td><td><code>90</code></td><td><code>80</code></td></tr></tbody></table></center></td></tr></tbody></table></blockquote><p>另外，本文在许多地方使用术语字符序列或 <code>char</code> 序列概括 Java 2 平台识别的所有字符序列的容器：<code>char[]</code>, <code>java.lang.CharSequence</code> 的实现（例如 <code>String</code> 类），和 <code>java.text.CharacterIterator</code> 的实现。</p><p>这么多术语。它们与在 Java 平台中支持增补字符有什么关系呢？</p><h3>Java 平台中增补字符的设计方法 </h3><p>JSR-204 专家组必须作出的主要决定是如何在 Java API 中表示增补字符，包括单个字符和所有形式的字符序列。专家组考虑并排除了多种方法：</p><ul><li>重新定义基本类型 <code>char</code>，使其具有 32 位，这样也会使所有形式的 <code>char</code> 序列成为 UTF-32 序列。 
</li><li>在现有的 16 位类型 <code>char</code> 的基础上，为字符引入一种新的 32 位基本类型（例如，<code>char32</code>）。所有形式的 Char 序列均基于 UTF-16。 
</li><li>在现有的 16 位类型 <code>char</code> 的基础上，为字符引入一种新的 32 位基本类型（例如，<code>char32</code>）。<code>String</code> 和 <code>StringBuffer</code> 接受并行 API，并将它们解释为 UTF-16 序列或 UTF-32 序列；其他 <code>char</code> 序列继续基于 UTF-16。 
</li><li>使用 <code>int</code> 表示增补的代码点。<code>String</code> 和 <code>StringBuffer</code> 接受并行 API，并将它们解释为 UTF-16 序列或 UTF-32 序列；其他 <code>char</code> 序列继续基于 UTF-16。 
</li><li>使用代理 <code>char</code> 对，表示增补代码点。所有形式的 <code>char</code> 序列基于 UTF-16。 
</li><li>引入一种封装字符的类。<code>String</code> 和 <code>StringBuffer</code> 接受新的 API，并将它们解释为此类字符的序列。 
</li><li>使用一个 <code>CharSequence</code> 实例和一个索引的组合表示代码点。 </li></ul><p>在这些方法中，一些在早期就被排除了。例如，重新定义基本类型 <code>char</code>，使其具有 32 位，这对于全新的平台可能会非常有吸引力，但是，对于 J2SE 来说，它会与现有的 Java 虚拟机<sup><a href="http://java.sun.com/developer/technicalArticles/Intl/Supplementary/index_zh_CN.html#TJVM">1</a></sup>、序列化和其他接口不兼容，更不用说基于 UTF-32 的字符串要使用两倍于基于 UTF-16 的字符串的内存了。添加一种新类型的 <code>char32</code> 可能会简单一些，但是仍然会出现虚拟机和序列化方面的问题。而且，语言更改通常需要比 API 更改有更长的提前期，因此，前面两种方法会对增补字符支持带来无法接受的延迟。为了在余下的方法中筛选出最优方案，实现小组使用四种不同的方法，在大量进行低层字符处理的代码（<code>java.util.regex</code> 包）中实现了对增补字符支持，并对这四种方法的难易程度和运行表现进行了比较。</p><p>最终，专家组确定了一种分层的方法：</p><ul><li>使用基本类型 <code>int</code> 在低层 API 中表示代码点，例如 <code>Character</code> 类的静态方法。 
</li><li>将所有形式的 <code>char</code> 序列均解释为 UTF-16 序列，并促进其在更高层级 API 中的使用。 
</li><li>提供 API，以方便在各种 <code>char</code> 和基于代码点的表示法之间的转换。 </li></ul><p>在需要时，此方法既能够提供一种概念简明且高效的单个字符表示法，又能够充分利用通过改进可支持增补字符的现有 API。同时，还能够促进字符序列在单个字符上的应用，这一点一般对于国际化的软件很有好处。</p><p>在这种方法中，一个 <code>char</code> 表示一个 UTF-16 代码单元，这样对于表示代码点有时并不够用。您会注意到，J2SE 技术规范现在使用术语代码点和 UTF-16 代码单元（表示法是相关的）以及通用术语字符（表示法与该讨论没有关系）。API 通常使用名称 <code>codePoint</code> 描述表示代码点的类型 <code>int</code> 的变量，而 UTF-16 代码单元的类型当然为 <code>char</code>。</p><p>我们将在下面两部分中了解到 J2SE 平台的实质变化 — 其中一部分介绍单个代码点的低层 API，另一部分介绍采用字符序列的高层接口。</p><h3>开放的增补字符：基于代码点的 API</h3><p>新增的低层 API 分为两大类：用于各种 <code>char</code> 和基于代码点的表示法之间转换的方法和用于分析和映射代码点的方法。 </p><p>最基本的转换方法是 <code>Character.toCodePoint(char high, char low)</code>（用于将两个 UTF-16 代码单元转换为一个代码点）和 <code>Character.toChars(int codePoint)</code>（用于将指定的代码点转换为一个或两个 UTF-16 代码单元，然后封装到一个 <code>char[]</code> 内。不过，由于大多数情况下文本以字符序列的形式出现，因此，另外提供 <code>codePointAt</code> 和 <code>codePointBefore</code> 方法，用于将代码点从各种字符序列表示法中提取出来：<code>Character.codePointAt(char[] a, int index)</code> 和 <code>String.codePointBefore(int index)</code> 是两种典型的例子。在将代码点插入字符序列时，大多数情况下均有一些针对 <code>StringBuffer</code> 和 <code>StringBuilder</code> 类的 <code>appendCodePoint(int codePoint)</code> 方法，以及一个用于提取表示代码点的 <code>int[]</code> 的 <code>String</code> 构建器。</p><p>几种用于分析代码单元和代码点的方法有助于转换过程：Character 类中的 <code>isHighSurrogate</code> 和 <code>isLowSurrogate</code> 方法可以识别用于表示增补字符的 <code>char</code> 值；<code>charCount(int codePoint)</code> 方法可以确定是否需要将某个代码点转换为一个或两个 <code>char</code>。</p><p>但是，大多数基于代码点的方法均能够对所有 Unicode 字符实现基于 <code>char</code> 的旧方法对 BMP 字符所实现的功能。以下是一些典型例子：</p><ul><li><code>Character.isLetter(int codePoint)</code> 可根据 Unicode 标准识别字母。 
</li><li><code>Character.isJavaIdentifierStart(int codePoint)</code> 可根据 Java 语言规范确定代码点是否可以启动标识符。 
</li><li><code>Character.UnicodeBlock.of(int codePoint)</code> 可搜索代码点所属的 Unicode 字符子集。 
</li><li><code>Character.toUpperCase(int codePoint)</code> 可将给定的代码点转换为其大写等值字符。尽管此方法能够支持增补字符，但是它仍然不能解决根本的问题，即在某些情况下，逐个字符的转换无法正确完成。例如，德文字符“"ß"”应该转换为“SS”，这需要使用 <code>String.toUpperCase</code> 方法。 </li></ul><p>注意大多数接受代码点的方法并不检查给定的 <code>int</code> 值是否处于有效的 Unicode 代码点范围之内（如上所述，只有 0x0 至 0x10FFFF 之间的范围是有效的）。在大多数情况下，该值是以确保其有效的方法产生的，在这些低层 API 中反复检查其有效性可能会对系统性能造成负面的影响。在无法确保有效性的情况下，应用程序必须使用 <code>Character.isValidCodePoint</code> 方法确保代码点有效。大多数方法对于无效的代码点采取的行为没有特别加以指定，不同的实现可能会有所不同。</p><p>API 包含许多简便的方法，这些方法可使用其他低层的 API 实现，但是专家组觉得，这些方法很常用，将它们添加到 J2SE 平台上很有意义。不过，专家组也排除了一些建议的简便方法，这给我们提供了一次展示自己实现此类方法能力的机会。例如，专家组经过讨论，排除了一种针对 <code>String</code> 类的新构建器（该构建器可以创建一个保持单个代码点的 <code>String</code>）。以下是使应用程序使用现有的 API 提供功能的一种简便方法：</p><blockquote><pre><a name="code1"></a>/**
 * 创建仅含有指定代码点的新 String。
 */
String newString(int codePoint) {
    return new String(Character.toChars(codePoint));
}</pre></blockquote><p>您会注意到，在这个简单的实现中，<code>toChars</code> 方法始终创建一个中间数列，该数列仅使用一次即立即丢弃。如果该方法在您的性能评估中出现，您可能会希望将其优化为针对最为普通的情况，即该代码点为 BMP 字符：</p><blockquote><pre><a name="code2"></a>/**
 * 创建仅含有指定代码点的新 String。
 * 针对 BMP 字符优化的版本。
 */
String newString(int codePoint) {
    if (Character.charCount(codePoint) == 1) {
        return String.valueOf((char) codePoint);
    } else {
        return new String(Character.toChars(codePoint));
    }
}</pre></blockquote><p>或者，如果您需要创建许多个这样的 string，则可能希望编写一个重复使用 <code>toChars</code> 方法所使用的数列的通用版本：</p><blockquote><pre><a name="code3"></a>/**
 * 创建每一个均含有一个指定
 * 代码点的新 String。
 * 针对 BMP 字符优化的版本。
 */
String[] newStrings(int[] codePoints) {
    String[] result = new String[codePoints.length];
    char[] codeUnits = new char[2];
    for (int i = 0; i &lt; codePoints.length; i++) { 
         int count = Character.toChars(codePoints[i], codeUnits, 0); 
         result[i] = new String(codeUnits, 0, count);
    }
    return result;
}</pre></blockquote><p>不过，最终您可能会发现，您需要的是一个完全不同的解决方案。新的构建器 <code>String(int codePoint)</code> 实际上建议作为 <code>String.valueOf(char)</code> 的一个基于代码点的备选方案。在很多情况下，此方法用于消息生成的环境，例如：</p><blockquote><pre><a name="code4"></a>System.out.println("Character " + String.valueOf(char) + " is invalid.");</pre></blockquote><p>新的<a href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/Formatter.html">格式化 API</a> 支持增补文字，提供一种更加简单的备选方案：</p><blockquote><pre><a name="code5"></a>System.out.printf("Character %c is invalid.%n", codePoint);</pre></blockquote><p>使用此高层 API 不仅简捷，而它有很多特殊的优点：它可以避免串联（串联会使消息很难本地化），并将需要移进资源包 (resource bundle) 的字符串数量从两个减少到一个。</p><h3>增补字符透视：功能增强</h3><p>在支持使用增补字符的 Java 2 平台中的大部分更改没有反映到新的 API 内。一般预期是，处理字符序列的所有接口将以适合其功能的方式处理增补字符。本部分着重讲述为达到此预期所作一些功能增强。</p><h4>Java 编程语言中的标识符</h4><p>Java 语言规范指出所有 Unicode 字母和数字均可用于标识符。许多增补字符是字母或数字，因此 Java 语言规范已经参照新的基于代码点的方法进行更新，以在标识符内定义合法字符。为使用这些新方法，需要检测标识符的 javac 编译器和其他工具都进行了修订。 </p><h4>库内的增补字符支持</h4><p>许多 J2SE 库已经过增强，可以通过现有接口支持增补字符。以下是一些例子：</p><ul><li>字符串大小写转换功能已更新，可以处理增补字符，也可以实现 Unicode 标准中规定的特殊大小写规则。 
</li><li><code>java.util.regex</code> 包已更新，这样模式字符串和目标字符串均可以包含增补字符并将其作为完整单元处理。 
</li><li>现在，在 <code>java.text</code> 包内进行整理处理时，会将增补字符看作完整单元。 
</li><li><code>java.text.Bidi</code> 类已更新，可以处理增补字符和 Unicode 4.0 中新增的其他字符。请注意，Cypriot Syllabary 字符子集内的增补字符具有从右至左的方向性。 
</li><li>Java 2D API 内的字体渲染和打印技术已经过增强，可以正确渲染和测量包含增补字符的字符串。 
</li><li>Swing 文本组件实现已更新，可以处理包含增补字符的文本。 </li></ul><h4>字符转换</h4><p>只有很少的字符编码可以表示增补字符。如果是基于 Unicode 的编码（如 UTF-8 和 UTF-16LE），则旧版的 J2RE 内的字符转换器已经按照正确处理增补字符的方式实现转换。对于 J2RE 5.0，可以表示增补字符的其他编码的转换器已更新：GB18030、x-EUC-TW（现在实现所有 CNS 11643 层面）和 Big5-HKSCS（现在实现 HKSCS-2001）。</p><h3><a name="source"></a>在源文件内表示增补字符</h3><p>在 Java 编程语言源文件中，如果使用可以直接表示增补字符的字符编码，则使用增补字符最为方便。UTF-8 是最佳的选择。在所使用的字符编码无法直接表示字符的情况下，Java 编程语言提供一种 Unicode 转义符语法。此语法没有经过增强，无法直接表示增补字符。而是使用两个连续的 Unicode 转义符将其表示为 UTF-16 字符表示法中的两个编码单元。例如，字符 U+20000 写作“\uD840\uDC00”。您也许不愿意探究这些转义序列的含义；最好是写入支持所需增补字符的编码，然后使用一种工具（如 native2ascii）将其转换为转义序列。</p><p>遗憾的是，由于其编码问题，属性文件仍局限于 ISO 8859-1（除非您的应用程序使用新的 XML 格式）。这意味着您始终必须对增补字符使用转义序列，而且可能要使用不同的编码进行编写，然后使用诸如 native2ascii 的工具进行转换。</p><h3><a name="Modified_UTF-8"></a>经修订的 UTF-8</h3><p>Java 平台对经修订的 UTF-8 已经很熟悉，但是，问题是应用程序开发人员在可能包含增补字符的文本和 UTF-8 之间进行转换时需要更加留神。需要特别注意的是，某些 J2SE 接口使用的编码与 UTF-8 相似但与其并不兼容。以前，此编码有时被称为“Java modified UTF-8”（经 Java 修订的 UTF-8） 或（错误地）直接称为“UTF-8”。对于 J2SE 5.0，其说明文档正在更新，此编码将统称为“modified UTF-8”（经修订的 UTF-8）。</p><p>经修订的 UTF-8 和标准 UTF-8 之间之所以不兼容，其原因有两点。其一，经修订的 UTF-8 将字符 U+0000 表示为双字节序列 0xC0 0x80，而标准 UTF-8 使用单字节值 0x0。其二，经修订的 UTF-8 通过对其 UTF-16 表示法的两个代理代码单元单独进行编码表示增补字符 。每个代理代码单元由三个字节来表示，共有六个字节。而标准 UTF-8 使用单个四字节序列表示整个字符。</p><p>Java 虚拟机及其附带的接口（如 Java 本机接口、多种工具接口或 Java 类文件）在 <code>java.io.DataInput</code> 和 <code>DataOutput</code> 接口和类中使用经修订的 UTF-8 实现或使用这些接口和类 ，并进行序列化。Java 本机接口提供与经修订的 UTF-8 之间进行转换的例程。而标准 UTF-8 由 <code>String</code> 类、<code>java.io.InputStreamReader</code> 和 <code>OutputStreamWriter</code> 类、<code>java.nio.charset</code> 设施 (facility) 以及许多其上层的 API 提供支持。</p><p>由于经修订的 UTF-8 与标准的 UTF-8 不兼容，因此切勿同时使用这两种版本的编码。经修订的 UTF-8 只能与上述的 Java 接口配合使用。在任何其他情况下，尤其对于可能来自非基于 Java 平台的软件的或可能通过其编译的数据流，必须使用标准的 UTF-8。需要使用标准的 UTF-8 时，则不能使用 Java 本机接口例程与经修订的 UTF-8 进行转换。</p><h3>在应用程序内支持增补字符</h3><p>现在，对大多数读者来说最为重要的问题是：必须对应用程序进行哪些更改才能支持增补字符？</p><p>答案取决于在应用程序中进行哪种类型的文本处理和使用哪些 Java 平台 API。</p><p>对于仅以各种形式 <code>char</code> 序列（[<code>char[]</code>、<code>java.lang.CharSequence</code> 实现、<code>java.text.CharacterIterator</code> 实现）处理文本和仅使用接受和退回序列（如 <code>char</code> 序列）的 Java API 的应用程序，可能根本不需要进行任何更改。Java 平台 API 的实现应该能够处理增补字符。</p><p>对于本身解释单个字符、将单个字符传送给 Java 平台 API 或调用能够返回单个字符的方法的应用程序，则需要考虑这些字符的有效值。在很多情况下，往往不要求支持增补字符。例如，如果某应用程序搜索 <code>char</code> 序列中的 HTML 标记，并逐一检查每个 <code>char</code>，它会知道这些标记仅使用 Basic Latin 字符子集中的字符。如果所搜索的文本含有增补字符，则这些字符不会与标记字符混淆，因为 UTF-16 使用代码单元表示增补字符，而代码单元的值不会用于 BMP 字符。</p><p>只有在某应用程序本身解释单个字符、将单个字符传送给 Java 平台 API 或调用能够返回单个字符的方法且这些字符可能为增补字符时，才必须更改该应用程序。在提供使用 <code>char</code> 序列的并行 API 时，最好转而使用此类 API。在其他情况下，有必要使用新的 API 在 <code>char</code> 和基于代码点的表示法之间进行转换，并调用基于代码点的 API。当然，如果您发现在 J2SE 5.0 中有更新、更方便的 API，使您能够支持增补字符并同时简化代码（如上 <a href="http://java.sun.com/developer/technicalArticles/Intl/Supplementary/index_zh_CN.html#code5">格式化范例</a> 中所述），则没有必要这样做。</p><p>您可能会犹豫，是将所有文本转换为代码点表示法（即 <code>int[]</code>）然后在该表示法中处理，还是在大多数情况下仍采用 <code>char</code> 序列，仅在需要时转换为代码点，两者之间孰优孰劣很难确定。当然，总体来说，Java 平台 API 相对于 <code>char</code> 序列肯定具有一定的优势，而且采用 Java 平台 API 可以节省内存空间。</p><p>对于需要与 UTF-8 之间进行转换的应用程序，还需要认真考虑是需要标准的 UTF-8 还是经修订的 UTF-8，并针对每种 UTF-8 采用适当的 Java 平台。“<a href="http://java.sun.com/developer/technicalArticles/Intl/Supplementary/index_zh_CN.html#Modified_UTF-8">经修订的 UTF-8</a>”部分介绍进行正确选择所需的信息。</p><h3>使用增补字符测试应用程序</h3><p>经过前面部分的介绍后，无论您是否需要修订应用程序，测试应用程序是否运行正常始终是一种正确的做法。对于不含有图形用户界面的应用程序，有关“<a href="http://java.sun.com/developer/technicalArticles/Intl/Supplementary/index_zh_CN.html#source">在源文件内表示增补字符</a>”　的信息有助于设计测试用例。以下是有关使用图形用户界面进行测试的补充信息。</p><p>对于文本输入，<a href="http://java.sun.com/j2se/1.5.0/download.jsp">Java 2 SDK</a> 提供用于接受“\Uxxxxxx”格式字符串的代码点输入方法，这里大写的“U”表示转义序列包含六个十六进制数字，因此允许使用增补字符。小写的“u”表示转义序列“\uxxxx”的原始格式。您可以在 J2SDK 目录 demo/jfc/CodePointIM 内找到此输入方法及其说明文档。</p><p>对于字体渲染，您需要至少能够渲染一些增补字符的字体。其中一种此类字体为 James Kass 的 <a href="http://home.att.net/~jameskass/code2001.htm">Code2001</a> 字体，它提供手写体字形（如 Deseret 和 Old Italic）。利用 Java 2D 库中提供新功能，您只需将该字体安装到 J2RE 的 lib/fonts/fallback 目录内即可，然后它可自动添加至在 2D 和 XAWT 渲染时使用的所有逻辑字体 — 无需编辑字体配置文件。 </p><p>至此，您就可以确认，您的应用程序能够完全支持增补字符了！</p><h3>结论</h3><p>对增补字符的支持已经引入 Java 平台，大部分应用程序无需更改代码即可处理这些字符。解释单个字符的应用程序可以在 <code>Character</code> 类和多种 <code>CharSequence</code> 子类中使用基于代码点的新 API。</p><h3>鸣谢</h3><p>Java 平台中的增补字符支持由 Java Community Process 的 JSR-204 专家组设计。技术规范设计主持为 Masayoshi Okutsu 和 Brian Beck (Sun Microsystems)，其他专家组成员有 Craig Cummings (Oracle)、Mark Davis (IBM)、Markus Eble (SAP AG)、Jere Käpyaho (Nokia Corp.)、Kazuhiro Kazama (NTT)、Kenji Kazumura (Fujitsu Limited)、Eiichi Kimura (NEC Corp.)、Changshin Lee (Tmax Soft Inc.) 和 Toshiki Murata (Oki Electric Industry Co.)。参考实现由 Sun Microsystems 的 Java Internationalization 团队完成，并承蒙位于圣何塞的 IBM Globalization Center of Competency 的协助。技术规范的技术兼容套件为 Java Compatibility Kit，由 Sun Microsystems 的 JCK 团队实现。</p><h3>参考书目</h3><p>Masayoshi Okutsu, Brian Beck (ed.): <a href="http://jcp.org/aboutJava/communityprocess/first/jsr204/index.html" target="_blank">Unicode Supplementary Character Support. Proposed Final Draft</a>. Sun Microsystems, 2004.</p><p><a href="http://java.sun.com/j2se/1.5.0/docs/api/index.html">Java 2 Platform Standard Edition 5.0 API Specification</a>. Sun Microsystems, 2004.</p><p>The Unicode Consortium: <a href="http://www.awprofessional.com/catalog/product.asp?product_id=%7BDD012C36-4893-47EE-91C8-20F6B0FA6FF7%7D">The Unicode Standard, Version 4.0</a>. Addison-Wesley, 2003.</p><p>Ken Whistler, Mark Davis: <a href="http://www.unicode.org/reports/tr17/">Character Encoding Model</a>. Unicode Technical Report #17. The Unicode Consortium, 2000.</p><p>James Kass: <a href="http://home.att.net/~jameskass/code2001.htm">Code2001, a Plane 1 Unicode-based Font</a>.</p><h3><a name="authorbio"></a>关于作者</h3><p>Norbert Lindenberg 是 Sun Microsystems 的 Java Web Services 团队内 Java Internationalization 技术主管。在加盟 Sun 之前，曾经供职于 General Magic 和 Apple Computer，参与过多个国际化项目。他毕业于德国的卡尔斯鲁厄大学，拥有计算机科学理科硕士学位。</p><p>Masayoshi Okutsu 是 Sun Microsystems 的 Java Web Services 团队的一名国际化工程师，目前担任 Unicode Supplementary Character Support 的 Java Specification Request 204 的技术规范主管。在加盟 Sun Microsystems 之前，供职于 Digital Equipment Corporation，期间曾经参与多个国际化项目。他毕业于日本山形大学，拥有电子工程理学士学位。</p><p></p><hr align="left" width="5%" /><br /><a name="TJVM"></a><sup>1</sup> 本网站中使用的术语“Java 虚拟机”或“JVM”是指针对 Java 平台的虚拟机。<img src ="http://www.blogjava.net/TrampEagle/aggbug/46810.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-05-18 14:23 <a href="http://www.blogjava.net/TrampEagle/archive/2006/05/18/46810.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Windows 系统服务详细介绍（转载）</title><link>http://www.blogjava.net/TrampEagle/archive/2006/04/22/42466.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Sat, 22 Apr 2006 03:39:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/archive/2006/04/22/42466.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/42466.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/archive/2006/04/22/42466.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/42466.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/42466.html</trackback:ping><description><![CDATA[原文引自：<a href="http://zhoujianhua.bokee.com/">http://zhoujianhua.bokee.com/</a><br /><br /><div class="diaryContent" id="diary2834126" style="DISPLAY: block"><p><font size="2">1、Alerter <br />服务名称：Alerter <br />进程名称：services.exe <br />功能说明：这个服务是当系统发生故障时向管理员发送警报，或向用户发送出错信息。除非你的电脑处在局域网中，而且配有网络管理员，一般情况下可以设置为“手动”或“已禁用”。 <br />默认设置：手动 <br />普通配置：已禁用 <br />网络配置：已禁用 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />2、Application Layer Gateway Service <br />服务名称：ALG <br />进程名称：alg.exe <br />功能说明：为Internet共享和防火墙的第三方插件提供支持如果你使用了WindowsXP的Internet共享和防火墙，那么这个服务必须启用。它占用大约1.5MB的内存资源。 <br />默认设置：手动 <br />普通配置：手动 <br />网络配置：自动 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />3、Application Management <br />服务名称：AppMgmt <br />进程名称：svchost.exe <br />功能说明：Windows2000/XP引入了一种基于msi文件格式（应用程序安装信息程序包）的软件管理方案——应用程序管理组件服务，它不仅管理软件的安装、删除，而且可以使用此项服务修改、修复现有应用程序，监视文件复原并通过复原排除基本故障等。 <br />默认设置：手动 <br />普通配置：手动 <br />网络配置：手动 <br />单机配置：手动 <br />极端配置：手动 <br /><br />4、Automatic Updates服务名称：wuauserv <br />进程名称：svchost.exe <br />功能说明：Windows自动升级的服务。要注意的是，不管是通过Windows Update手动或自动升级，都需要Background Intelligent Transfer Service和System Event Notification这两个服务的支持。 <br />默认设置：自动 <br />普通配置：自动 <br />网络配置：自动 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />5、Background Intelligent服务名称：BITS <br />进程名称：svchost.exe <br />功能说明：用于通过Http1.1服务器来异步传输数据，也就是利用闲置网络带宽在后台传输文件，Windows Update需要用到这个服务，从而实现升级数据断点续传功能。另外，在使用MSN Explorer、Windows Messenger、Windows Media Player或.NET功能等时，它的自动更新需要这个服务。 <br />默认设置：手动 <br />普通配置：已禁用 <br />网络配置：已禁用 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />6、Transfer Service服务名称： <br />进程名称：svchost.exe <br />功能说明：用于通过Http1.1服务器来异步传输数据，也就是利用闲置网络带宽在后台传输文件，Windows Update需要用到这个服务，从而实现升级数据断点续传功能。另外，在使用MSN Explorer、Windows Messenger、Windows Media Player或.NET功能等时，它的自动更新需要这个服务。 <br />默认设置：手动 <br />普通配置：已禁用 <br />网络配置：已禁用 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />7、ClipBook服务名称：ClipSrv <br />进程名称：clipsrv.exe <br />功能说明：这个服务通过Network DDE和Network DDE DSDM提供的网络动态数据交换服务，查阅远程电脑中的剪贴薄，即使你的电脑在局域网中，为了安全起见还是强烈建议将其设置为“手动”，而对于普通用户来说，完全可以将其设置为“已禁用”。这样可以节省大约1.3MB的内存空间。 <br />默认设置：手动 <br />普通配置：已禁用 <br />网络配置：已禁用 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />8、Com+System Application服务名称：COMSysApp <br />进程名称：dllhost.exe <br />功能说明：主要为那些使用了COM+的程序提供主持，同时系统日志也需要它才能运行，因此建议改为手动。 <br />默认设置：手动 <br />普通配置：手动 <br />网络配置：手动 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />9、COM+ Event System服务名称：EventSystem <br />进程名称：svchost.exe <br />功能说明：为使用COM+程序提供系统事件支持，或曰自动发布到订阅 COM 组件。建议保持Windows的默认设置。 <br />默认设置：手动 <br />普通配置：手动 <br />网络配置：手动 <br />单机配置：手动 <br />极端配置：已禁用 <br /><br />10、Computer Browser服务名称：Browser <br />进程名称：svchost.exe <br />功能说明：维护网络邻居中计算机的最新列表，其中包括基于Windows 的域、工作组和计算机，还有其它NetBIOS协议的网络设备，“网上邻居”显示的内容是来源于此。并将这个列表通知给请求的程序。普通单机用户设置为“已禁用”，局域网用户设置为“自动”。如果，在“网上邻居”中看不到其它电脑，很可能是这个服务没有开启。 <br />默认设置：自动 <br />普通配置：已禁用 <br />网络配置：自动 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />11、Cryptographic Services服务名称：CryptSvc <br />进程名称：svchost.exe <br />功能说明：它主要用来确认Windows 文件签名的，如果将其禁用，那么你会经常遇到报告驱动程序未被微软鉴定的警告框。同时这个服务也是Windows Update手动或自动更新时所需要的。另外，在升级SP1 或SP2、DirectX 9.0 必须要这个服务处于运行状态。而Windows Media Player和一些.NET应用程序也需要该服务。它大约占用1.9MB内存空间。 <br />默认设置：自动 <br />普通配置：自动 <br />网络配置：自动 <br />单机配置：已禁用 <br /><br />12、DHCP Client服务名称：Dhcp <br />进程名称：svchost.exe <br />功能说明：全称Dynamic Host Configuration Protocol（动态主机配置协议），该协议可以自动为局域网中的每一台计算机自动分配IP 地址，并完成每台计算机的TCP/IP协议配置，包括IP地址、子网掩码、网关，以及DNS 服务器等。这样在局域网中特别是大型局域网中，管理员就不必为每一台计算机手工配置TCP/IP 协议了，也避免了IP 地址重复的问题。作为普通用户，如果已通过DSL、Cable等方式连入Internet，那么还是保持自动状态为好，因为我们在Internet 上的IP地址是由ISP动态分配的。如果系统不应用于任何网络，那么你可以将其设为“已禁用”。在Windows中要启用DHCP协议，只要将IP地址设置为“自动获得IP地址”即可。具体的方法如下：以Windows XP为例，打开“本地连接”属性，双击“Internet协议（TCP/IP）”打开属性窗口。接着，在“常规”选项卡中选中“自动获得IP地址”，单击右下角的“高级”。下面在打开窗口的“IP设置”选项卡中，就可以看到“DHCP被启用”。 <br />默认设置：自动 <br />普通配置：自动 <br />网络配置：已禁用 <br />单机配置：自动 <br />极端配置：已禁用 <br /><br />13、Distributed Link Tracking Client服务名称：TrkWks <br />进程名称：svchost.exe <br />功能说明：分布式连接跟踪客户端，它能跟踪文件在网络域的NTFS卷中移动状况，并发出通知。普通用户设置为：“已禁用”，局域网用户（硬盘分区文件系统为NTFS）设为：“自动”。比如：你在电脑A上创建了一个文件，接着在电脑B上创建了这个文件的快捷方式或链接，如果你在电脑A 中移动了文件的位置，那么这个服务将会告诉电脑B 这个文件的新位置并进行修正。对于单机用户，这个服务用处不大，而且它会占用大约3.5MB～4MB 的内存资源。 <br />默认设置：自动 <br />普通配置：手动 <br />网络配置：手动 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />14、Distributed Transaction Coordinator服务名称：MSDTC <br />进程名称：msdtc.exe <br />功能说明：并列事务，是分布于两个以上的数据库，消息队列，文件系统，或其它事务保护资源管理器。这个服务禁用后，事件日志会报告相关错误。不过，经测试发现，并不会影响系统运行。而一些.NET程序会用到它。 <br />默认设置：手动 <br />普通配置：手动 <br />网络配置：手动 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />15、DNS Client服务名称：Dnscache <br />进程名称：svchost.exe <br />功能说明：域名系统客户端，它可将域名解析为IP 地址。除非你没有连入任何网络，否则应设为“自动”。 <br />默认设置：自动 <br />普通配置：自动 <br />网络配置：自动 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />16、Error Reporting Service服务名称：ERSvc <br />进程名称：svchost.exe <br />功能说明：当应用程序出现错误时，这个服务可以向微软提交相关报告。因为它的用处并不大，还白白占用内存和系统资源，因此推荐将其禁用。 <br />默认设置：自动 <br />普通配置：已禁用 <br />网络配置：已禁用 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />17、Event Log服务名称：Eventlog <br />进程名称：services.exe <br />功能说明：该服务能记录程序和系统发送的出错消息。日志包含了对诊断问题有所帮助的信息。有时禁用这个服务重启系统后，会导致了与几个网络有关的服务无法启动，并出现无法拨号上网的现象。因此如果你存在Internet 或局域网连接，建议还是把设为“自动”吧。 <br />默认设置：自动 <br />普通配置：自动 <br />网络配置：自动 <br />单机配置：自动 <br />极端配置：自动 <br /><br />18、Fast User Switching Compatibility服务名称：FastUserSwitc hing Compatibility <br />进程名称：svchost.exe <br />功能说明：在Windows XP中提供了用户快速切换功能，让你可以在保持当前用户登录的情况下，再用另外的账户登录，实现同时登录状态。除非你的电脑有多人使用，并且真的需要这个功能，否则还是将其改为手动或已禁用。 <br />默认设置：手动 <br />普通配置：手动 <br />网络配置：手动 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />19、Fax Service服务名称：FAX <br />进程名称：fxssvc.exe <br />功能说明：传真服务，在Windows 95中的传真功能在Windows 2000中重新被予以支持，而且与系统集成得更好。如果你不用它，就设为“已禁用”吧。 <br />默认设置：手动 <br />普通配置：已禁用 <br />网络配置：已禁用 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />20、Help and Support服务名称：helpsvc <br />进程名称：svchost.exe <br />功能说明：WindowsXP 搜索和帮助功能所必需的服务，但我们并不是每天都要用到这个功能，因此建议设置为“已禁用”，需要时再启动它。 <br />默认设置：自动 <br />普通配置：已禁用 <br />网络配置：已禁用 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />21、Human Interface Device Access服务名称：HidServ <br />进程名称：svchost.exe <br />功能说明：主要是那些带有功能按钮（传真、复印）扫描仪、或者多媒体键盘等需要使用这个服务，因此，建议先将其设置为“已禁用”，如果你的某些设备出现问题，再将其设置为“自动”。 <br />默认设置：已禁用 <br />普通配置：已禁用 <br />网络配置：已禁用 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />22、HTTP SSL服务名称：HTTPFilter <br />进程名称：svchost.exe <br />功能说明：此服务通过安全套接字层(SSL)实现 HTTP 服务的安全超文本传送协议(HTTPS)。 <br />默认设置：手动 <br />普通配置：手动 <br />网络配置：手动 <br />单机配置：手动 <br />极端配置：手动 <br /><br />23、IMAPI CD-Burning COM Service服务名称：ImapiService <br />进程名称：imapi.exe <br />功能说明：这是Windows XP的刻录功能的服务，让你可以通过拖放来进行光盘刻录，非常方便。同时，一些软件也会用到这个服务，因此建议将其设置为“自动”，如果你安装了Nero 等刻录软件，也可以尝试将其禁用，这样可以加速Nero的运行速度。这个服务占用1.6MB的内存资源。 <br />默认设置：手动 <br />普通配置：自动 <br />网络配置：自动 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />24、Indexing Service服务名称：cisvc <br />进程名称：cisvc.exe <br />功能说明：索引服务能针对本地硬盘或共享网络驱动器上的文档内容和属性建立索引，并通过Windows 2000/XP特有的文档过滤器快速定位到你所需要的文档，它大大强化了Windows 2000/XP的搜索能力。但索引服务要持续不断进行，所以会消耗了大量系统资源。微软建议在仅有64MB内存的系统中，如需索引的文档超过十万个，就应该禁用这个服务。但实际测试中，我们发现即使文档数量远远低于十万个，系统资源的消耗也非常惊人，而且在128MB的系统上情况也好不了多少，因此我们强烈建议禁用它。另外，虽然微软称它只在系统空闲时运行，但我们发现，它经常并不那么规矩，如果你的电脑经常会出现迟缓的问题，那么试着禁用它看看效果。 <br />默认设置：手动 <br />普通配置：已禁用 <br />网络配置：已禁用 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />25、Internet Connection Firewall/Internet Connection Sharing服务名称：SharedAccess <br />进程名称：svchost.exe <br />功能说明：Internet防火墙及为局域网计算机提供Internet共享连接。这个服务为多台联网的电脑共享一个拨号网络访问Internet 提供了捷径，对不使用该功能的普通用户可将其设为“已禁用”。 <br />默认设置：自动 <br />普通配置：自动 <br />网络配置：自动 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />26、IPSEC Services服务名称：PolicyAgent <br />进程名称：lsass.exe <br />功能说明：IP 安全策略代理服务，它允许IP 安全策略对两台计算机之间传输的数据包进行加密，从而防止在网上看到它的人对它进行更改和破译。IPSEC 是一种用来保护内部网、专用网络以及外部网 <br />（Internet、Extranet）免遭攻击的重要防御方法。使用IPSEC 前必须需要首先定义两台计算机之间相互信任和通信安全的方式。请注意：在Windows 2000默认情况下“IP安全策略代理”是自动启动的，而“IP安全策略”并没有启动，既然如此，一般用户就完全可以禁用该服务。 <br />默认设置：自动 <br />普通配置：已禁用 <br />网络配置：已禁用 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />27、Logical Disk Manager服务名称：dmserver <br />进程名称：svchost.exe <br />功能说明：它是Windows 磁盘管理的重要服务，如果你在通过“控制面板→管理工具→计算机管理→磁盘管理”时遇到问题，那么最好看看这个服务是否启动。 <br />默认设置：自动 <br />普通配置：手动 <br />网络配置：手动 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />28、Logical Disk Manager Administrative Service服务名称：dmadmin <br />进程名称：dmadmin.exe <br />功能说明：与上一个服务相似，磁盘管理请求的系统管理服务。 <br />默认设置：手动 <br />普通配置：手动 <br />网络配置：手动 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />29、Messenger服务名称：Messenger <br />进程名称：services.exe <br />功能说明：发送和接收由系统管理员或由Alerter 服务所发送消息的服务。由于Alerter服务需要依存本服务，因此如果已将Alerter禁止，那么这项可以设置为“手动”或“已禁用”。这个服务提供了服务器和客户机之间传输信息的功能，它对于家庭用户来说没有任何用处，因此建议将其设置为“已禁用”，这样还可以将来自互联网的垃圾信息禁掉。另外，你还可以在命令提示符中输入“netsend 127.0.0.1 hi”来测试你的电脑是否存在相关漏洞，如果收到了弹出信息“Hi”，那么你应该禁用这个服务。如果收到的是错误提示“网络找不到此消息别名”，那么你的电脑是安全的。如果你实在需要这个服务，那么可以通过防火墙关闭UDP 的135、137端口和TCP 的135、139、445 端口来过滤来自互联网的信息。 <br />默认设置：自动 <br />普通配置：已禁用 <br />网络配置：已禁用 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />30、MS Software Shadow Copy Provider服务名称：SwPrv <br />进程名称：dllhost.exe <br />功能说明：和“Volume Shadow Copy”服务联合使用，主要用来进行备份的。另外，有些磁盘克隆、镜像等软件需要使用这个服务才能运行。 <br />默认设置：手动 <br />普通配置：手动 <br />网络配置：手动 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />31、Net Logon <br />服务名称：Netlogon <br />进程名称：lsass.exe <br />功能说明：简单说就是在局域网上验证登录信息的服务。一般家庭用户可以将其设为“已禁用”或“手动”。有时中了病毒后，这个服务会出现无法启动的问题。 <br />默认设置：自动 <br />普通配置：已禁用 <br />网络配置：已禁用 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />32、NetMeeting Remote Desktop Sharing服务名称：mnmsrvc <br />进程名称：mnmsrvc.exe <br />功能说明：远程桌面共享，该服务能通过NetMeeting 允许有权限的用户远程访问Windows桌面。这个功能对一般用处不大，可以设为“已禁用”。 <br />默认设置：手动 <br />普通配置：已禁用 <br />网络配置：已禁用 <br /><br />33、Network Connections服务名称：Netman <br />进程名称：svchost.exe <br />功能说明：网络连接管理着“网络和拨号连接”文件夹中的所有对象。如果你有任何网络连接（包括Internet拨号连接）就保持“手动”状态。否则若你禁用它，在“网络和拨号连接”文件夹中将什么都看不到，更不用说新建连接和拨号上网了。 <br />默认设置：手动 <br />普通配置：手动 <br />网络配置：自动 <br />单机配置：手动 <br />极端配置：已禁用 <br /><br />34、Network DDE <br />服务名称：NetDDE <br />进程名称：netdde.exe <br />功能说明：网络动态数据交换服务是一种为DDE 对话提供网络传输和安全的服务。DDE（动态数据交换）是实现进程通讯的一种形式，它允许支持DDE 的两个或多个程序交换信息和命令。对一般用户可设为“已禁用”。 <br />默认设置：手动 <br />普通配置：已禁用 <br />网络配置：已禁用 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />35、Network DDE DSDM服务名称：NetDDE dsdm <br />进程名称：netdde.exe <br />功能说明：网络动态数据交换服务是一种为DDE 对话提供网络传输和安全的服务。DDE（动态数据交换）是实现进程通讯的一种形式，它允许支持DDE 的两个或多个程序交换信息和命令。对一般用户可设为“已禁用”。 <br />默认设置：手动 <br />普通配置：已禁用 <br />网络配置：已禁用 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />36、Network Location Awareness (NLA)服务名称：Nla <br />进程名称：svchost.exe <br />功能说明：这个服务是Internet 网络连接共享所必需的，不过只要在服务端开启即可。 <br />默认设置：手动 <br />普通配置：手动 <br />网络配置：自动 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />37、NT LM Security Support Provider <br />进程名称：lsass.exe <br />功能说明：为没有使用命名管道传输的远程过程调用 (RPC) 程序提供安全性。管理系统安全信息，如果你启动了Telnet 服务，那么这个服务必须打开。 <br />默认设置：手动 <br />普通配置：手动 <br />网络配置：手动 <br />单机配置：手动 <br />极端配置：已禁用 <br /><br />38、Performance Logs and Alerts服务名称：SysmonLog <br />进程名称：smlogsvc.exe <br />功能说明：按照计划收集系统性能数据，并将信息发送到日志或进行提醒。一般用户根本不需要它。 <br />默认设置：手动 <br />普通配置：已禁用 <br />网络配置：已禁用 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />39、Plug and Play服务名称：PlugPlay <br />进程名称：services.exe <br />功能说明：打开Windows 的即插即用功能，建议将其设置为：自动，如不然会导致注册表混乱。 <br />默认设置：自动 <br />普通配置：自动 <br />网络配置：自动 <br />单机配置：自动 <br />极端配置：自动 <br /><br />40、Portable Media Serial Number服务名称：WmdmPmSp <br />进程名称：svchost.exe <br />功能说明：通过联网的计算机重新找回连接在你电脑上的移动播放器（MP3播放器等）的数字序号。一般情况下禁用即可。如果禁用后，你的Windows Media Player出现问题，需要将其开启。 <br />默认设置：自动 <br />普通配置：已禁用 <br />网络配置：已禁用 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />41、Print Spooler服务名称：Spooler <br />进程名称：spoolsv.exe <br />功能说明：打印后台处理，该服务的作用是将多个请求打印的文档统一进行保存和管理，待打印机空闲后，再将数据送往打印机处理。无任何打印设备的用户设置为“已禁用”，否则设为“自动”。请注意： <br />如果你启用了传真服务（Fax Service）的话，就应该保持自动状态，因为传真服务依赖Print Spooler的运行。 <br />默认设置：自动 <br />普通配置：自动 <br />网络配置：自动 <br />单机配置：自动 <br />极端配置：已禁用 <br /><br />42、Protected Storage服务名称：Protected Storage <br />进程名称：lsass.exe <br />功能说明：该服务主要用来保存本地密码，比如：拨号密码和Outlook密码等，或网络站点用户名、密码等信息（IE 的自动完成需要它的支持）。 <br />默认设置：自动 <br />普通配置：自动 <br />网络配置：自动 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />43、QoS RSVP服务名称：RSVP <br />进程名称：rsvp.exe <br />功能说明：QoS是网络的一种安全机制。在正常情况下并不需要QoS，但是对关键应用和多媒体应用就十分必要。当网络过载或拥塞时，QoS 能确保重要业务量不受延迟或丢弃，同时保证网络的高效运行。如果你经常使用Windows Media Player收看或收听网络电视或广播，或者使用NetMeeting，那么就得将其设置为“自动”。 <br />默认设置：手动 <br />普通配置：已禁用 <br />网络配置：已禁用 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />44、Remote Access Auto Connection Manager服务名称：RasAuto <br />进程名称：svchost.exe <br />功能说明：当某个程序需要远程地址时，该服务会创建到网络的连接。有时你的网络连接需要这个服务。同时，一些Cable或DSL服务提供商需要这个服务，你可以尝试将其设置为禁用，并测试网络连接是否正常。如果你使用了硬件路由器和网关，那么此服务就不必要了。 <br />默认设置：手动 <br />普通配置：手动 <br />网络配置：自动 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />45、Remote Access Connection Manager <br />服务名称：RasMan <br />进程名称：svchost.exe <br />功能说明：用于创建网络连接，如果你使用了Internet共享，那么该服务就是必需的了。另外，一些Cable 和DSL 连接需要此服务才能正常运行。如果你使用了硬件网关和路由器，那么它就不必开启了。 <br />默认设置：手动 <br />普通配置：手动 <br />网络配置：自动 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />46、Remote Desktop Help Session Manager <br />服务名称：RDSessMgr <br />进程名称：sessmgr.exe <br />功能说明：管理和控制远程助手。它大约会占用3.4～4MB的内存资源。 <br />默认设置：手动 <br />普通配置：已禁用 <br />网络配置：已禁用 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />47、Remote Procedure Call (RPC) <br />服务名称：RpcSs <br />进程名称：svchost.exe <br />功能说明：这个服务极为重要，许多其他服务需要它才能正常运行。而且如果你禁用了它，那么很可能导致电脑无法启动。 <br />默认设置：自动 <br />普通配置：自动 <br />网络配置：自动 <br />单机配置：自动 <br />极端配置：自动 <br /><br />48、Remote Procedure Call (RPC) Locator服务名称：RpcLocator <br />进程名称：locator.exe <br />功能说明：用于管理RPC 名称服务数据库。一般情况下禁用即可，如果遇到网络断线，那么可以将其设置为手动。它将占用大约1.2MB的内存空间。 <br />默认设置：手动 <br />普通配置：手动 <br />网络配置：手动 <br />单机配置：手动 <br />极端配置：已禁用 <br /><br />49、Remote Registry Service服务名称：RemoteRegistry <br />进程名称：svchost.exe <br />功能说明：远程注册表服务能使你在本机上编辑远程的另一台计算机上的注册表。普通用户根本没必要使用这个服务。 <br />默认设置：自动 <br />普通配置：已禁用 <br />网络配置：已禁用 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />50、Removable Storage <br />服务名称：NtmsSvc <br />进程名称：svchost.exe <br />功能说明：用于管理可移动存储媒体，比如：磁带机等，如果在禁用后，你的软盘、CD或DVD光驱出现问题（比如：如果自动播放等），那么可以将其设置为“自动”。 <br />默认设置：自动 <br />普通配置：自动 <br />网络配置：自动 <br />单机配置：自动 <br />极端配置：自动 <br /><br />51、Routing and Remote Access服务名称：RemoteAccess <br />进程名称：svchost.exe <br />功能说明：提供通过Modem（或其他设备拨号）或VPN远程连接到局域网络的路由服务。启用后，控制面板中的网络连接的接入的连接图标将被激活。 <br />默认设置：手动 <br />普通配置：已禁用 <br />网络配置：已禁用 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />52、Run As Service服务名称：seclogon <br />进程名称：services.exe <br />功能说明：以其他用户身份运行服务的服务，当你以一般权限用户身份登录系统，而在使用中又需要修改只有系统管理员才能修改的系统设置项时，该服务提供了不重启系统以管理员身份登录的捷径。你只需要在命令提示符下运行RunAs 命令就可达到更改目的。对于一般用户在未熟悉RunAs 命令用法之前可以将其设为“已禁用”。另外，按住Shift键再右击程序快捷方式后，在弹出菜单中选择“运行方式”可打开它的窗口。 <br />默认设置： 已禁用 <br />普通配置：已禁用 <br />网络配置：已禁用 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />53、Secondary Logon服务名称：seclogon <br />进程名称：svchost.exe <br />功能说明：是Run As服务的辅助服务，在Windows XP中已经不再有Run As Service，而直接使用这个服务来实现，让你在当初账户下使用另外的账户启动程序。比如：当前的账户权限较低，无法使用一些系统工具，而利用这个服务，则可以直接右击程序文件，然后选择“Run As”，接着就可以用管理员帐户启动系统工具，而不必重新登录。 <br />默认设置：自动 <br />普通配置：已禁用 <br />网络配置：已禁用 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />54、Security Accounts Manager <br />服务名称：SamSs <br />进程名称：lsass.exe <br />功能说明：与“Protected Storage”服务相似，用来保存本地用户的一些安全信息。如果你经常使用组策略，那么最好将其设置为“自动”。 <br />默认设置：自动 <br />普通配置：自动 <br />网络配置：自动 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />55、Server服务名称：lanmanserver <br />进程名称：svchost.exe <br />功能说明：用来管理网络中文件和打印服务器，对于一般用户而言可以将其设置为：手动，如果你是局域网用户则需将其设置为：自动。 <br />默认设置：自动 <br />普通配置：已禁用 <br />网络配置：自动 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />56、Shell Hardware Detection服务名称：ShellHWDetection <br />进程名称：svchost.exe <br />功能说明：用于支持设备的自动播放，比如：闪存、各种记忆卡、光驱等。如果你的笔记本电脑在使用坞后，出现问题、在“我的电脑”中看不到DVD光驱、自动运行出现问题等，可以将其设置为“自动”看看是否能解决。 <br />默认设置：自动 <br />普通配置：自动 <br />网络配置：自动 <br />单机配置：自动 <br />极端配置：已禁用 <br /><br />57、Smart Card/Smart Card Helper <br />服务名称：SCardSvr <br />进程名称：SCardSvr.exe <br />功能说明：这两个服务提供对智能卡设备的支持。若没有这种卡，应将其设为“已禁用”。 <br />默认设置：手动 <br />普通配置：已禁用 <br />网络配置：已禁用 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />58、SSDP Discovery Service服务名称：SSDPSRV <br />进程名称：svchost.exe <br />功能说明：用于定位你的家庭网络中的UPnP设备，与“Universal Plug and Play Device Host”服务依存，它能够检测并配置你的UPnP设备，如果你没有这样的设备，出于安全考虑，最好将其禁用。另外，如果你使用了支持UPnP 的NAT 防火墙或路由器，那么MSN Messenger 需要使用此服务。如果你在玩DirectX 的游戏时，发现它们进行网络连接多人对战时很困难，那么最好设置为“自动”，同时下载所有Windows的安全补丁。这个服务还存在一些问题，比如：它会向UDP端口1900发送太多广播，如果你发现自己并没有进行什么网络操作，但系统托盘右下角的网络连接图标总是闪个不停，那么很可能是这个服务造成的。 <br />默认设置：手动 <br />普通配置：已禁用 <br />网络配置：已禁用 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />59、Still Image Service服务名称：StiSvc <br />进程名称：stisvc.exe <br />功能说明：主要用于控制连接到你电脑上的扫描仪和数码相机。如果停用这个服务，那么大部分扫描仪和数码相机可能无法工作。因为它们的驱动程序往往和这个服务紧密结合。有网友报告HP的扫描仪驱动程序会导致StiSvc进程占用99％的CPU资源，因此，如果你在使用HP扫描仪进行扫描时，系统突然变得非常慢，最好下载、安装其最新的驱动程序。 如果这样还不能解决问题，就得先卸载驱动程序，然后停用这个服务，接着重启电脑，重装驱动程序即可。当然，如果不经常使用扫描仪或数码相机，也可以将其设置为“手动”或“已禁用”。 <br />默认设置：自动 <br />普通配置：手动 <br />网络配置：手动 <br />单机配置：手动 <br />极端配置：已禁用 <br /><br />60、System Event Notification服务名称：SENS <br />进程名称：svchost.exe <br />功能说明：和“COM+ Event System”是依存关系，系统日志主要用来记录登录和电源事件。 <br />默认设置：自动 <br />普通配置：自动 <br />网络配置：自动 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />61、System Restore Service服务名称：srservice <br />进程名称：svchost.exe <br />功能说明：这个服务用来支持Windows XP的系统还原服务，它会占用大量的系统资源。如果禁用了这个服务，那么原来的创建的还原点也会一并被删除。 <br />默认设置：自动 <br />普通配置：已禁用 <br />网络配置：已禁用 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />62、Task Scheduler服务名称：Schedule <br />进程名称：svchost.exe <br />功能说明：用来管理计划任务的，比如每周按时整理磁盘等。如果你不使用Windows的计划任务，那么可以将其设置为：已禁用。 <br />默认设置：自动 <br />普通配置：已禁用 <br />网络配置：已禁用 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />63、TCP/IP NetBIOS Helper Service <br />服务名称：LmHosts <br />进程名称：svchost.exe <br />功能说明：TCP/IP NetBIOS支持服务，该服务能在TCP/IP上提供NetBIOS支持。由于NetBIOS是基于局域网的，因此作为访问Internet资源的一般用户可以禁用它，除非你的系统处在局域网中。 <br />默认设置：自动 <br />普通配置：已禁用 <br />网络配置：已禁用 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />64、Telephony服务名称：TapiSrv <br />进程名称：svchost.exe <br />简单地说这个服务能为计算机提供电话拨号的能力。如果你使用拨号方式连接到Internet或通过电话线连接其他计算机，就应将其设为“手动”。 <br />默认设置：手动 <br />普通配置：手动 <br />网络配置：自动 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />65、Telnet <br />服务名称：TlntSvr <br />进程名称：tlntsvr.exe <br />功能说明：该服务允许你从远程计算机上登录以本系统并且使用命令行方式操作这台计算机。对于一般单机用户该服务并不重要，可以设为“手动”或“已禁用”。 <br />默认设置：手动 <br />普通配置：已禁用 <br />网络配置：已禁用 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />66、Terminal Services服务名称：TermService <br />进程名称：svchost.exe <br />功能说明：实现远程登录本地电脑，快速用户切换和远程桌面功能需要该服务。 <br />默认设置：手动 <br />普通配置：手动 <br />网络配置：手动 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />67、Themes服务名称：Themes <br />进程名称：svchost.exe <br />功能说明：Windows XP桌面主题服务，一个主题将占用大约4～12MB。 <br />默认设置：自动 <br />普通配置：自动 <br />网络配置：手动 <br />单机配置：手动 <br />极端配置：已禁用 <br /><br />68、Uninterruptible Power Supply服务名称：UPS <br />进程名称：ups.exe <br />功能说明：这个服务是用来管理你的UPS的，如果你没有UPS就将它设置为：“手动”或“已禁用”。 <br />默认设置：手动 <br />普通配置：已禁用 <br />网络配置：已禁用 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />69、Universal Plug and Play Device Host服务名称：UPNPhost <br />进程名称：svchost.exe <br />功能说明：与“SSDP Discovery Service”服务为依存关系，见前述“SSDP Discovery Service”。 <br />默认设置：手动 <br />普通配置：已禁用 <br />网络配置：已禁用 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br /><br />70、Upload Manager服务名称：uploadmgr <br />进程名称：svchost.exe <br />功能说明：用于发送硬件驱动程序信息到微软，使其能够跟踪驱动信息的可用性。对于简单文件和打印共享这个服务并不必要。因此完全可以将其禁用。 <br />默认设置：自动 <br />普通配置：已禁用 <br />网络配置：已禁用 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />71、Volume Shadow Copy服务名称：VSS <br />进程名称：vssvc.exe <br />功能说明：与“MS Software Shadow Copy Provider service”互为依存关系，微软备份功能也需要使用这个服务。占用大约3.0 MB的内存资源。 <br />默认设置：手动 <br />普通配置：手动 <br />网络配置：手动 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />72、WebClient服务名称：WebClient <br />进程名称：svchost.exe <br />功能说明：可能与以后的.Net软件有关联，不过一般禁用即可。如果某些微软的产品因此而出现问题，比如：MSN Explorer,、Media Player、NetMeeting、Messenger等，那么可以将其设置为自动。 <br />默认设置：自动 <br />普通配置：已禁用 <br />网络配置：已禁用 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />73、Windows Audio服务名称：AudioSrv <br />进程名称：svchost.exe <br />功能说明：控制声音。 <br />默认设置：自动 <br />普通配置：自动 <br />单机配置：自动 <br />极端配置：自动 <br /><br />74、Windows Image Acquisition (WIA)服务名称：stisvc <br />进程名称：svchost.exe <br />功能说明：Windows图像获取服务，这是Windows XP加入的一个新功能，主要包括了Windows XP对图像设备的支持以及对图像信息的处理编辑等功能。从最终用户一端来看，WIA 主要作用是：支持图像设备，如扫描仪、数码照相机、视频摄像机等；处理图像，包括将图像信息从移动设备中转存到桌面计算机中， <br />对图像进行编辑排版直至打印等一系列的操作。 WIA系统设有一个操作向导，在向导的指引下，你可以轻松完成上述的各种工作。一些扫描仪、摄像头、数码相机需要使用此服务。 <br />默认设置：手动 <br />普通配置：手动 <br />网络配置：手动 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />75、Windows Installer服务名称：MSIServer <br />进程名称：msiexec.exe <br />功能说明：这个服务对于那些使用MSI 文件（Windows 安装程序）进行安装的软件是必需的。现在越来越多的软件使用Windows 安装程序进行安装，所以最好将其设置为“手动”。它会占用大约3.4 MB的内存资源。 <br />默认设置：手动 <br />普通配置：手动 <br />网络配置：手动 <br />单机配置：手动 <br />极端配置：手动 <br /><br />76、Windows Management Instrumentation服务名称：Winmgmt <br />进程名称：svchost.exe <br />功能说明：Windows管理规范和Windows管理规范驱动程序扩展，WMI是Windows 2000 中的基础管理结构，它通过一组常用接口控制和监视系统（如对系统属性的查看与更改、设置用户权限等）。为加快系统启动速度，我们可以将这两个服务设置为“手动”，待系统启动后在需要的时候再自动用它们。 <br />默认设置：自动 <br />普通配置：自动 <br />网络配置：自动 <br />单机配置：自动 <br />极端配置：自动 <br /><br />77、Windows Management Instrumentation Driver Extension服务名称：Wmi <br />进程名称：svchost.exe <br />功能说明：Windows管理规范和Windows管理规范驱动程序扩展，WMI是Windows 2000 中的基础管理结构，它通过一组常用接口控制和监视系统（如对系统属性的查看与更改、设置用户权限等）。为加快系统启动速度，我们可以将这两个服务设置为“手动”，待系统启动后在需要的时候再自动用它们。 <br />默认设置：手动 <br />普通配置：手动 <br />网络配置：手动 <br />单机配置：手动 <br />极端配置：已禁用 <br /><br />78、Windows Time服务名称：W32Time <br />进程名称：svchost.exe <br />功能说明：自动连接到Internet（或局域网中）的服务器来校对你的系统时间。一般每隔七天自动较对一次，非网络服务器感觉意义不大。 <br />默认设置：自动 <br />普通配置：已禁用 <br />网络配置：已禁用 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />79、Wireless Zero Configuration服务名称：WZCSVC <br />进程名称：svchost.exe <br />功能说明：用于自动配置无线网络设备和连接品质反馈。如果你没有无线网络，那么将其设置为禁用即可。 <br />默认设置：自动 <br />普通配置：手动 <br />网络配置：手动 <br />单机配置：手动 <br />极端配置：已禁用 <br /><br />80、WMI Performance Adapter服务名称：WmiApSrv <br />进程名称：wmiapsrv.exe <br />功能说明：WMI 的性能适配器。一般用户根本不需要它，禁用它可以节省大约2.5～6MB的内存资源。 <br />默认设置：手动 <br />普通配置：已禁用 <br />网络配置：已禁用 <br />单机配置：已禁用 <br />极端配置：已禁用 <br /><br />81、Workstation服务名称：lanmanworkst ation <br />进程名称：svchost.exe <br />功能说明：它是用来管理其他网络功能的，如果你的电脑上网，那么应该选择：自动。 <br />默认设置：自动 <br />普通配置：自动 <br />网络配置：自动 <br />单机配置：自动 <br />极端配置：自动</font> 1、Alerter <br />服务名称：Alerter <br />进程名称：services.exe <br />功能说明：这个服务是当系统发生故障时向管理员发送警报，或向用户发送出错信息。除非你的电脑处在局域网中，而且配有网络管理员，一般情况下可以设置为“手动”或“已禁用”。 <br />默认设置：手动 <br />普通配置：已禁用 <br />网络配置：已禁用 <br />单机配置：已禁用 <br />极端配置：已禁用 </p></div><img src ="http://www.blogjava.net/TrampEagle/aggbug/42466.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-04-22 11:39 <a href="http://www.blogjava.net/TrampEagle/archive/2006/04/22/42466.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>彻底明白Java的IO系统(转载)</title><link>http://www.blogjava.net/TrampEagle/archive/2006/04/22/42465.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Sat, 22 Apr 2006 03:36:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/archive/2006/04/22/42465.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/42465.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/archive/2006/04/22/42465.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/42465.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/42465.html</trackback:ping><description><![CDATA[原文引自：<a href="http://zhoujianhua.bokee.com/">http://zhoujianhua.bokee.com/</a><br /><br />一．    Input和Output<br />1.    stream代表的是任何有能力产出数据的数据源，或是任何有能力接收数据的接收源。在Java的IO中，所有的stream（包括Input和Out stream）都包括两种类型：<br />1.1    以字节为导向的stream<br />以字节为导向的stream，表示以字节为单位从stream中读取或往stream中写入信息。以字节为导向的stream包括下面几种类型：<br />1)    input　stream：<br />1)    ByteArrayInputStream：把内存中的一个缓冲区作为InputStream使用<br />2)    StringBufferInputStream：把一个String对象作为InputStream<br />3)    FileInputStream：把一个文件作为InputStream，实现对文件的读取操作<br />4)    PipedInputStream：实现了pipe的概念，主要在线程中使用<br />5)    SequenceInputStream：把多个InputStream合并为一个InputStream<br />2)    Out　stream<br />1)    ByteArrayOutputStream：把信息存入内存中的一个缓冲区中<br />2)    FileOutputStream：把信息存入文件中<br />3)    PipedOutputStream：实现了pipe的概念，主要在线程中使用<br />4)    SequenceOutputStream：把多个OutStream合并为一个OutStream<br />1.2    以Unicode字符为导向的stream<br />以Unicode字符为导向的stream，表示以Unicode字符为单位从stream中读取或往stream中写入信息。以Unicode字符为导向的stream包括下面几种类型：<br />1)    Input　Stream<br />1)    CharArrayReader：与ByteArrayInputStream对应<br />2)    StringReader：与StringBufferInputStream对应<br />3)    FileReader：与FileInputStream对应<br />4)    PipedReader：与PipedInputStream对应<br />2)    Out　Stream<br />1)    CharArrayWrite：与ByteArrayOutputStream对应<br />2)    StringWrite：无与之对应的以字节为导向的stream<br />3)    FileWrite：与FileOutputStream对应<br />4)    PipedWrite：与PipedOutputStream对应<br />以字符为导向的stream基本上对有与之相对应的以字节为导向的stream。两个对应类实现的功能相同，字是在操作时的导向不同。如CharArrayReader：和ByteArrayInputStream的作用都是把内存中的一个缓冲区作为InputStream使用，所不同的是前者每次从内存中读取一个字节的信息，而后者每次从内存中读取一个字符。<br />1.3    两种不现导向的stream之间的转换<br />InputStreamReader和OutputStreamReader：把一个以字节为导向的stream转换成一个以字符为导向的stream。<br />2.    stream添加属性<br />2.1     "为stream添加属性"的作用<br />运用上面介绍的Java中操作IO的API，我们就可完成我们想完成的任何操作了。但通过FilterInputStream和FilterOutStream的子类，我们可以为stream添加属性。下面以一个例子来说明这种功能的作用。<br />如果我们要往一个文件中写入数据，我们可以这样操作：<br />    FileOutStream fs = new FileOutStream("test.txt");<br />然后就可以通过产生的fs对象调用write()函数来往test.txt文件中写入数据了。但是，如果我们想实现"先把要写入文件的数据先缓存到内存中，再把缓存中的数据写入文件中"的功能时，上面的API就没有一个能满足我们的需求了。但是通过FilterInputStream和FilterOutStream的子类，为FileOutStream添加我们所需要的功能。<br />2.2    FilterInputStream的各种类型<br />2.2.1    用于封装以字节为导向的InputStream<br />1)    DataInputStream：从stream中读取基本类型（int、char等）数据。<br />2)    BufferedInputStream：使用缓冲区<br />3)    LineNumberInputStream：会记录input stream内的行数，然后可以调用getLineNumber()和setLineNumber(int)<br />4)    PushbackInputStream：很少用到，一般用于编译器开发<br />2.2.2    用于封装以字符为导向的InputStream<br />1)    没有与DataInputStream对应的类。除非在要使用readLine()时改用BufferedReader，否则使用DataInputStream<br />2)    BufferedReader：与BufferedInputStream对应<br />3)    LineNumberReader：与LineNumberInputStream对应<br />4)    PushBackReader：与PushbackInputStream对应<br />2.3    FilterOutStream的各种类型<br />2.2.3    用于封装以字节为导向的OutputStream<br />1)    DataIOutStream：往stream中输出基本类型（int、char等）数据。<br />2)    BufferedOutStream：使用缓冲区<br />3)    PrintStream：产生格式化输出<br />2.2.4    用于封装以字符为导向的OutputStream<br />1)    BufferedWrite：与对应<br />2)    PrintWrite：与对应<br />3.    RandomAccessFile<br />1)    可通过RandomAccessFile对象完成对文件的读写操作<br />2)    在产生一个对象时，可指明要打开的文件的性质：r，只读；w，只写；rw可读写<br />3)    可以直接跳到文件中指定的位置<br />4.    I/O应用的一个例子<br />import java.io.*;<br />public class TestIO{<br />    public static void main(String[] args)<br />        throws IOException{<br />        //1.以行为单位从一个文件读取数据<br />        BufferedReader in = <br />            new BufferedReader(<br />                new FileReader("F:\\nepalon\\TestIO.java"));<br />        String s, s2 = new String();<br />        while((s = in.readLine()) != null)<br />            s2 += s + "\n";<br />        in.close();<br /><br />        //1b. 接收键盘的输入<br />        BufferedReader stdin = <br />            new BufferedReader(<br />                new InputStreamReader(System.in));<br />        System.out.println("Enter a line:");<br />        System.out.println(stdin.readLine());<br /><br />        //2. 从一个String对象中读取数据<br />        StringReader in2 = new StringReader(s2);<br />        int c;<br />        while((c = in2.read()) != -1)<br />            System.out.println((char)c);<br />        in2.close();<br /><br />        //3. 从内存取出格式化输入<br />        try{<br />            DataInputStream in3 = <br />                new DataInputStream(<br />                    new ByteArrayInputStream(s2.getBytes()));<br />            while(true)<br />                System.out.println((char)in3.readByte());           <br />        }<br />        catch(EOFException e){<br />            System.out.println("End of stream");<br />        }<br /><br />        //4. 输出到文件<br />        try{<br />            BufferedReader in4 =<br />                new BufferedReader(<br />                    new StringReader(s2));<br />            PrintWriter out1 =<br />                new PrintWriter(<br />                    new BufferedWriter(<br />                        new FileWriter("F:\\nepalon\\ TestIO.out")));<br />            int lineCount = 1;<br />            while((s = in4.readLine()) != null)<br />                out1.println(lineCount++ + "：" + s);<br />            out1.close();<br />            in4.close();<br />        }<br />        catch(EOFException ex){<br />            System.out.println("End of stream");<br />        }<br /><br />        //5. 数据的存储和恢复<br />        try{<br />            DataOutputStream out2 = <br />                new DataOutputStream(<br />                    new BufferedOutputStream(<br />                        new FileOutputStream("F:\\nepalon\\ Data.txt")));<br />            out2.writeDouble(3.1415926);<br />            out2.writeChars("\nThas was pi:writeChars\n");<br />            out2.writeBytes("Thas was pi:writeByte\n");<br />            out2.close();<br />            DataInputStream in5 =<br />                new DataInputStream(<br />                    new BufferedInputStream(<br />                        new FileInputStream("F:\\nepalon\\ Data.txt")));<br />            BufferedReader in5br =<br />                new BufferedReader(<br />                    new InputStreamReader(in5));<br />            System.out.println(in5.readDouble());<br />            System.out.println(in5br.readLine());<br />            System.out.println(in5br.readLine());<br />        }<br />        catch(EOFException e){<br />            System.out.println("End of stream");<br />        }<br /><br />        //6. 通过RandomAccessFile操作文件<br />        RandomAccessFile rf =<br />            new RandomAccessFile("F:\\nepalon\\ rtest.dat", "rw");<br />        for(int i=0; i&lt;10; i++)<br />            rf.writeDouble(i*1.414);<br />        rf.close();<br />        <br />        rf = new RandomAccessFile("F:\\nepalon\\ rtest.dat", "r");<br />        for(int i=0; i&lt;10; i++)<br />            System.out.println("Value " + i + "：" + rf.readDouble());<br />        rf.close();<br />        <br />        rf = new RandomAccessFile("F:\\nepalon\\ rtest.dat", "rw");<br />        rf.seek(5*8);<br />        rf.writeDouble(47.0001);<br />        rf.close();<br /><br />        rf = new RandomAccessFile("F:\\nepalon\\ rtest.dat", "r");<br />        for(int i=0; i&lt;10; i++)<br />            System.out.println("Value " + i + "：" + rf.readDouble());<br />        rf.close();<br />    }<br />}<br />关于代码的解释（以区为单位）：<br />1区中，当读取文件时，先把文件内容读到缓存中，当调用in.readLine()时，再从缓存中以字符的方式读取数据（以下简称"缓存字节读取方式"）。<br />1b区中，由于想以缓存字节读取方式从标准IO（键盘）中读取数据，所以要先把标准IO（System.in）转换成字符导向的stream，再进行BufferedReader封装。<br />2区中，要以字符的形式从一个String对象中读取数据，所以要产生一个StringReader类型的stream。<br />4区中，对String对象s2读取数据时，先把对象中的数据存入缓存中，再从缓冲中进行读取；对TestIO.out文件进行操作时，先把格式化后的信息输出到缓存中，再把缓存中的信息输出到文件中。<br />5区中，对Data.txt文件进行输出时，是先把基本类型的数据输出屋缓存中，再把缓存中的数据输出到文件中；对文件进行读取操作时，先把文件中的数据读取到缓存中，再从缓存中以基本类型的形式进行读取。注意in5.readDouble()这一行。因为写入第一个writeDouble()，所以为了正确显示。也要以基本类型的形式进行读取。<br />6区是通过RandomAccessFile类对文件进行操作。<img src ="http://www.blogjava.net/TrampEagle/aggbug/42465.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-04-22 11:36 <a href="http://www.blogjava.net/TrampEagle/archive/2006/04/22/42465.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>How to make you struts application support I18N?（转载）</title><link>http://www.blogjava.net/TrampEagle/archive/2006/04/20/42196.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Thu, 20 Apr 2006 09:03:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/archive/2006/04/20/42196.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/42196.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/archive/2006/04/20/42196.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/42196.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/42196.html</trackback:ping><description><![CDATA[原文引自：<a href="http://fjy26.itpub.net/">http://fjy26.itpub.net/</a><br /><br /><ol><li>configure web.xml,insert following tag:<br />&lt;init-param&gt;<br />&lt;param-name&gt;locale&lt;/param-name&gt;<br />&lt;param-value&gt;true&lt;/param-value&gt;<br />&lt;/init-param&gt;<br />codes above enable localization. 
</li><li>configure web.xml,insert following tag:<br />&lt;init-param&gt;<br />&lt;param-name&gt;application&lt;/param-name&gt;<br />&lt;paramvalue&gt;ApplicationResources&lt;/param-value&gt;<br />&lt;/init-param&gt;<br />codes above indicate where default resource(ApplicationResources.properties) file reside in.If a specific resource locale is required, the actionServlet will look for resource file named ApplicationResources_xx_XX.properties according to locale. for example:ApplicationResources_en_us.properties <br />hint:the default path of resource file is WEB-INF/classes/. 
</li><li>using the struts framework locale object.<br />after above two steps, struts stores a locale object in session context,you can get the object using following codes:<br />Locale locale=(Locale)request.getSession().getAttribute(Action.LOCALE_KEY); 
</li><li>changing user's locale<br />Since locale is immutable ,you must create a new locale object to replace existing one:<br />Locale locale=new Locale(myForm.getLanguage(),myForm.getCountry());<br />HttpSession session=request.getSession(true);<br />session.setAttribute(Action.LOCALE_KEY,locale); 
</li><li>placing labels and messages in properties files. 
</li><li>creating language-specified properties file for every locale you will support. if properly named,these files will be loaded automatically.if the application can't find a key in the resource for a user's locale,it will use the resource for the default locale. 
</li><li>localization-aware components VS other components<br />usually,most struts components support I18N automatically.for the components that do not have lacalization built in,you can usually use&lt;bean:message&gt; to provide localized message. 
</li><li>localizing other components: validation and tiles<br />validation:all the labels and messages used by validator are linked to resource file and will be localized automatically.if additional localization be needed ,say to verify the format of a postal code or telephone number ,you can define a locale-specfic formset element like this:<br />&lt;form name="testform" locale="fr" country="CA"&gt;...<br />tiles:you can define xml file according to locale like resource file :<br />tiles_fr_CA.xml</li></ol><img src ="http://www.blogjava.net/TrampEagle/aggbug/42196.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-04-20 17:03 <a href="http://www.blogjava.net/TrampEagle/archive/2006/04/20/42196.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转载：关于war包中文件的读取（原创：bruce ）</title><link>http://www.blogjava.net/TrampEagle/archive/2006/04/20/42195.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Thu, 20 Apr 2006 09:01:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/archive/2006/04/20/42195.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/42195.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/archive/2006/04/20/42195.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/42195.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/42195.html</trackback:ping><description><![CDATA[本文引自：<a href="http://fjy26.itpub.net/">http://fjy26.itpub.net/</a><br /><br />war包中的文件的读取<br />在开发J2EE Web应用时，在开发阶段通常采用目录的部署方式，而在正式运行时通常把web应用打包为单个的.war文件进行方便地部署。也就是在你的应用目录（比如WebLogic的DefaultWebApp）下，执行下面的命令：<br /><div class="codeStyle"><ol><li>jar cf0 mywebapp.war ** </li></ol></div><br />这样，要部署到正式系统时就非常方便，只需要把这个.war文件拷贝到WebLogic的applications目录或Tomcat的webapps目录下即可自动进行部署。Tomcat会对部署的.war应用包进行自动监控、解包，所以不会出现下面提到的问题。而WebLogic并不会自动解包.war，所以如果在你的应用中，需要读取原来应用中的配置文件或其它资源文件时，就会发现，在解包部署时，正常运行的程序，在WebLogic中打包部署时，运行却出错，会报告找不到该文件。例如下面的应用：<br />[pre] |--DefaultWebApp<br />|--index.jsp<br />|--.....jsp<br />|--WEB-INF<br />|-- web.xml<br />|-- log4j.properties <br />|-- classes<br />......[/pre]<br />其中使用到了Log4J作为日志输出工具，Log4J的配置文件log4j.propertes放在DefaultWebAppWEB-INF目录下。Log4J通过一个自动加载的Servlet进行初始化，初始化代码如下：<br /><div class="codeStyle"><ol><li>ServletContext context = getServletContext(); 
</li><li>org.apache.log4j.PropertyConfigurator.configure(context.getRealPath(<font color="#ff33ff">"/"</font>) + 
</li><li><font color="#ff33ff">"/WEB-INF/log4j.properties"</font>); </li></ol></div><br />其中，context.getRealPath("/")得到当前Web应用的真实根目录，比如，如果你的WebLogic安装在D:bea下，在Windows下context.getRealPath("/")通常会返回：<br />D:beawlserver6.1configmydomainapplicationsDefaultWebApp<br />在UNIX下类似：<br />/bea/wlserver6.1/config/mydomain/applications/DefaultWebApp<br />这样，和"/ WEB-INF /log4j.properties"拼接后，就得到了log4j.properties文件的真实路径，Log4J通过文件IO读取这个配置文件，完成初始化。<br />现在一切正常！测试通过后，将DefaultWebApp下的所有文件打为一个.war包，进行部署时，发现系统报告找不到“D:beawlserver6.1null WEB-INF log4j.properties”文件！如果你的应用中还需要读取其它已经被打包到war包中的文件，都会报告找不到文件。并且，系统并不会到D:beawlserver6.1configmydomainapplicationsDefaultWebApp目录下寻找，而会到D:beawlserver6.1null下寻找。这是因为context.getRealPath("/")返回了null。查看ServletContext的API文档，<br /><br />public String getRealPath(String path)<br />……<br />The real path returned will be in a form appropriate to the computer and operating system on which the servlet container is running, including the proper path separators. This method returns null if the servlet container cannot translate the virtual path to a real path for any reason <b>(such as when the content is being made available from a .war archive)</b>.<br /><br />原来，对一个打包的应用来说，是没有RealPath的概念的，调用getRealPath只会简单地返回null。其实，也很好理解，一个文件被打包入了.war文件，就不存在目录结构了（虽然包中仍然存在目录结构，但这不等同于文件系统中的目录结构）。所以，对war包中的资源是无法得到RealPath的。这样也就无从通过文件IO进行读取了。那么，如何读取war包中的资源呢？答案是使用ServletContext.getResourceAsStream(String)方法。<br />对于org.apache.log4j.PropertyConfigurator，有如下几种配置方法：<br /><div class="codeStyle"><ol><li><b><font color="#0000ff">static</font></b><b><font color="#0000ff">void</font></b> configure(<font color="#ff0000">Properties</font> properties); 
</li><li><b><font color="#0000ff">static</font></b><b><font color="#0000ff">void</font></b> configure(<b><a href="http://blog.itpub.net/source/jdk142/java/lang/String.java.html" target="_blank"><font class="classLink"><u>String</u></font></a></b> configFilename); 
</li><li><b><font color="#0000ff">static</font></b><b><font color="#0000ff">void</font></b> configure(<font color="#ff0000">URL</font> configURL); </li></ol></div><br />既然，现在不能得到war包中的Log4J的配置文件，那么可以通过读入InputStream，构造一个Properties，通过configure(Properties properties)方法同样可以完成配置。示例代码如下：<br /><div class="codeStyle"><ol><li><font color="#ff0000">InputStream</font> is = getServletContext(). 
</li><li>getResourceAsStream(<font color="#ff33ff">"/WEB-INF/log4j.properties"</font>); 
</li><li><font color="#ff0000">Properties</font> props = <b><font color="#0000ff">new</font></b><font color="#ff0000">Properties</font>(); 
</li><li><b><font color="#0000ff">try</font></b> { 
</li><li>props.load(is); 
</li><li>} <b><font color="#0000ff">catch</font></b> (<font color="#ff0000">IOException</font> e) { 
</li><li><b><a href="http://blog.itpub.net/source/jdk142/java/lang/System.java.html" target="_blank"><font class="classLink"><u>System</u></font></a></b>.err.println(<font color="#ff33ff">"Load log4j configuration failed"</font>); 
</li><li>} 
</li><li>PropertyConfigurator.configure(props); </li></ol></div><br />那么，现在对于war应用可以成功运行，但如果现在不通过war部署，直接通过目录结构部署应用会不会又出现找不到资源的错误呢？请来看看ServletContext.getResourceAsStream的API文档，<br /><br />Returns a URL to the resource that is mapped to a specified path. The path must begin with a "/" and is interpreted as relative to the current context root. <br />This method allows the servlet container to make a resource available to servlets from any source. <b>Resources can be located on a local or remote file system, in a database, or in a .war file.</b><br /><br />可见，通过getResourceAsStream可以获取包括本地文件系统、远程文件系统、war包等资源。不会出现上面担心的问题。<br /><br /><b>结论：在开发J2EE Web应用时，如果需要读取本应用中的文件，尽量使用ServletContext.getResourceAsStream进行，而不要使用文件IO。</b><br /><img src ="http://www.blogjava.net/TrampEagle/aggbug/42195.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-04-20 17:01 <a href="http://www.blogjava.net/TrampEagle/archive/2006/04/20/42195.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>案例分析：得到单据流水号</title><link>http://www.blogjava.net/TrampEagle/archive/2006/03/20/36169.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Mon, 20 Mar 2006 00:51:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/archive/2006/03/20/36169.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/36169.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/archive/2006/03/20/36169.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/36169.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/36169.html</trackback:ping><description><![CDATA[
		<a class="f1" href="http://searchdatabase.techtarget.com.cn/tips/44/2333544.shtml?BLK=030001&amp;NODE=1003"> 本文摘自：http://searchdatabase.techtarget.com.cn/tips/44/2333544.shtml?BLK=030001&amp;NODE=1003
<div class="guanggao"><span id="contentAdv"></span></div></a>
		<p>
				<font face="Verdana">-- ==================================================<br />-- 名称：得到单据流水号<br />-- 实现功能：取得对应表的计数器，实现流水号功能．<br />-- 调用示例：SELECT F_LT_GetOrderNo(FId) as FID, * from Tab1 T1<br />                    left outer join T_OrderList T2 on T1.FTabID = T2.FID<br />-- ==================================================<br />CREATE TABLE T_OrderList(<br />FID int IDENTITY (1, 1) NOT NULL,<br />FIncCount int -- 计数器<br />)</font>
		</p>
		<p>
				<font face="Verdana">CREATE FUNCTION F_LT_GetOrderNo(@ID int)<br />AS RETURN VARCHAR(32)<br />DECLARE @OrderNo int<br />SELECT @OrderNo = FIncCount FROM T_OrderList WHERE FID = @ID<br />    -- 取得编号后，计数器加１<br />UPDATE T_OrderList SET FIncCount = FIncCount +1 -- 函数中不允许执行UPDATE，这种情况要怎么处理．<br />RETURNS @OrderNo</font>
		</p>
		<p>
				<font face="Verdana">-- 系统单据表，存放系统所以业务单据列表，存有生成流水号计数器<br />CREATE  TABLE  T_OrderList(  <br />           FID  int  IDENTITY  (1,  1)  NOT  NULL,  <br />           FIncCount  int  --  计数器  <br />           FOrder varchar(30) not Null<br />           )  </font>
		</p>
		<p>
				<font face="Verdana">-- 系统业务单据，存放企业日常业务数据，具体每单有一个单据流水号<br />CREATE  TABLE  T_Order(  <br />           FID  int  IDENTITY  (1,  1)  NOT  NULL,  <br />           FNumber varchar(40),  -- 单据流水号  <br />           FOrderInfo varchar(30)<br />           )  </font>
		</p>
		<p>
				<font face="Verdana">-- 现系统要求自动运算，将运算后的数据填充到T_Order业务表中．填充时各记录要生成不同的单据流水号．我原先的实现想法是用存储过程：<br />CREATE    PROCEDURE P_OnlyC<br />  @CodeC VARCHAR(48) OUTPUT<br />AS<br />DECLARE @OnlyC VARCHAR(48)<br />,@FIncCount INTEGER</font>
		</p>
		<p>
				<font face="Verdana">-- 取出当前单据流水号<br />SELECT @FIncCount=FIncCount FROM T_OrderList WHERE FID=@CodeC<br />-- 流水号加１<br />SELECT @FIncCount = @FIncCount +1</font>
		</p>
		<p>
				<font face="Verdana">UPDATE T_OrderList SET FIncCount = @FIncCount WHERE FID= @CodeC</font>
		</p>
		<p>
				<font face="Verdana">-- 组织各个编码<br />SELECT @OnlyC = @CodeC  + '-' + @OnlyC<br />SELECT @CodeC = @OnlyC; SELECT @OnlyC AS FNumber<br />-- print @CodeC<br />GO</font>
		</p>
		<p>
				<font face="Verdana">但这程方法不能在SELECT语句运算出的结果中调用．如前面写的SELECT P_OnlyC(FId)  as  流水号,  *  from (select sum(..) from tab..) Tab1  所以我想用函数,但函数里又没办法执行递增流水号：</font>
		</p>
		<p>
				<font face="Verdana">CREATE  FUNCTION  F_LT_GetOrderNo(@ID  int)  <br />AS  RETURN  VARCHAR(32)  <br />           DECLARE  @OrderNo  int  <br />           SELECT  @OrderNo  =  FIncCount  FROM  T_OrderList  WHERE  FID  =  @ID  <br />       --  取得编号后，计数器加１  <br />           UPDATE  T_OrderList  SET  FIncCount  =  FIncCount  +1  --  函数中不允许执行UPDATE  <br />           RETURNS  @OrderNo <br />           <br />           <br />           <br />CREATE PROCEDURE n_GetBillNo <br />@billType  char(2),--单据类型<br />@BillOutNo  nvarchar(50) Output</font>
		</p>
		<p>
				<font face="Verdana"> AS</font>
		</p>
		<p>
				<font face="Verdana">Begin</font>
		</p>
		<p>
				<font face="Verdana">declare @NowNO int<br />declare @date char(10)<br />declare @Symbol nvarchar(10)<br />declare @ErrorMsg nvarchar(200)</font>
		</p>
		<p>
				<font face="Verdana">Set NoCount On<br />Begin Tran<br />--设定延时<br />SET LOCK_TIMEOUT 5000</font>
		</p>
		<p>
				<font face="Verdana">--取当前序号<br />Select @NowNO=fnumber,@Symbol=fCode,@date=Convert(char(8),fDate,112) from n_BillNo  With(xLock) where fcode=@BillType<br />if @@Error&lt;&gt;0<br />   begin<br />   Set @ErrorMsg='数据被锁定，请求超时！'<br />   Goto Failed<br />   end<br />--是否是新的一月<br />if Convert(char(8),getdate(),112)&lt;&gt;@date<br /> Set @NowNo=1<br /> else<br /> Set @NowNo=@NowNo+1</font>
		</p>
		<p>
				<font face="Verdana">--更新当前序列号和设置最后更新日期<br />update n_BillNo set fNumber=@NowNO,fDate=GetDate() where fcode=@BillType<br />if @@Error&lt;&gt;0<br />   begin<br />   Set @ErrorMsg='更新外部序列号失败！'<br />   Goto Failed<br />   end</font>
		</p>
		<p>
				<font face="Verdana">--取得单号<br />Set @BillOutNo=lTrim(@SymBol)+Convert(char(8),GetDate(),112)+Right(('0000'+Convert(varchar,@NowNO)),4)</font>
		</p>
		<p>
				<font face="Verdana">Goto Succeed</font>
		</p>
		<p>
				<font face="Verdana">Failed:<br />  RaisError(@ErrorMsg,16,1)<br />  Rollback Tran  <br />  Set NoCount Off<br />  Return 1</font>
		</p>
		<p>
				<font face="Verdana">Succeed:<br />  Commit Tran<br />  Set NoCount Off<br />  Return 0</font>
		</p>
		<p>
				<font face="Verdana">End<br />GO</font>
		</p>
<img src ="http://www.blogjava.net/TrampEagle/aggbug/36169.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-03-20 08:51 <a href="http://www.blogjava.net/TrampEagle/archive/2006/03/20/36169.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>X.509证书请求消息格式</title><link>http://www.blogjava.net/TrampEagle/archive/2006/03/19/36092.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Sun, 19 Mar 2006 11:34:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/archive/2006/03/19/36092.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/36092.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/archive/2006/03/19/36092.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/36092.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/36092.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 本文引自：										http://www.infosecurity.org.cn/article/pki/standard/23547.html																								 																		此备忘录的状况																												此文档为因特网...&nbsp;&nbsp;<a href='http://www.blogjava.net/TrampEagle/archive/2006/03/19/36092.html'>阅读全文</a><img src ="http://www.blogjava.net/TrampEagle/aggbug/36092.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-03-19 19:34 <a href="http://www.blogjava.net/TrampEagle/archive/2006/03/19/36092.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>keytool - 密钥和证书管理工具</title><link>http://www.blogjava.net/TrampEagle/archive/2006/03/18/35942.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Sat, 18 Mar 2006 07:11:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/archive/2006/03/18/35942.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/35942.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/archive/2006/03/18/35942.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/35942.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/35942.html</trackback:ping><description><![CDATA[
		<h1> </h1>
		<blockquote>本文引自：<a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html">http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html</a><br /><br />说明<br />keytool是个密钥和证书管理工具。它使用户能够管理自己的公钥/私钥对及相关证书，用于（通过数字签名）自我认证（用户向别的用户/服务认证自己）或数据完整性以及认证服务。它还允许用户储存他们的通信对等者的公钥（以证书形式）。 
<p><i>证书</i>是来自一个实体（个人、公司等）的经数字签名的声明，它声明某些其它实体的公钥（及其它信息）具有某一的特定值（参见<a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#Certificates">证书</a>）。当数据被数字化签名后，校验签名即可检查数据的完整性和真实性。<i>完整性</i>的意思是数据没有被修改或损坏过，<i>真实性</i>的意思是数据的确是来自声称创建了该数据和对它进行了签名的实体。 </p><p>keytool将密钥和证书储存在一个所谓的<i>密钥仓库</i>中。缺省的<a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#KeystoreImplementation">密钥仓库实现</a>将密钥仓库实现为一个文件。它用口令来保护私钥。 </p><p><b><a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/jarsigner.html">jarsigner</a></b> 工具利用密钥仓库中的信息来产生或校验 Java 存档 (JAR) 文件的数字签名 （JAR 文件将类文件、图象、声音和/或其它数字化数据打包在一个文件中）。<b>jarsigner</b> 用 JAR 文件所附带的证书（包含于 JAR 文件的签名块文件中）来校验 JAR 文件的数字签名，然后检查该证书的公钥是否“可信任”，即是否包括在指定的密钥仓库中。 </p><p>请注意：keytool和 <b>jarsigner</b> 工具完全取代了 JDK 1.1 中提供的 <b>javakey</b> 工具。这些新工具所提供的功能比 <b>javakey</b> 提供的多，包括能够用口令来保护密钥仓库和私钥，以及除了能够生成签名外还可以校验它们。新的密钥仓库体系结构取代了 <b>javakey</b> 所创建和管理的身份数据库。可以利用 <tt>-identitydb</tt>keytool命令将信息从身份数据库导入 密钥仓库。 </p><h3>密钥仓库项</h3><blockquote>在密钥仓库中有两种不同类型的项： 
<ol>  
<li><b>密钥项</b> - 每项存放极为敏感的加密密钥信息，这种信息以一种受保护的格式储存以防止未授权的访问。通常，储存在这类项中的密钥是机密密钥，或是伴有用于认证相应公钥用的<a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#CertChains">证书“链”</a>的私钥。<b>keytool</b> 和 <b>jarsigner</b> 工具只处理后一类型的项，即私钥及其关联的证书链。 
</li><li><b>可信任的证书项</b> - 每项包含一个属于另一团体的公钥证书。它之所以叫做“可信任的证书”，是因为密钥仓库的拥有者相信证书中的公钥确实属于证书“主体”（拥有者）识别的身份。证书签发人通过对证书签名来保证这点。 </li></ol></blockquote><h3>密钥仓库使用的别名</h3><blockquote>对所有的密钥仓库项（密钥项和可信任的证书项）的访问都要通过唯一的<i>别名</i>来进行。别名不区分大小写，即别名 <tt>Hugo</tt> 和 <tt>hugo</tt> 指的是同一密钥仓库项。 
<p>当用 <a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#genkeyCmd">-genkey</a> 命令来生成密钥对（公钥和私钥）或用 <a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#importCmd">-import</a> 命令来将证书或证书链加到可信任证书的清单中，以增加一个实体到密钥仓库中，必须指定了一个别名。后续 <b>keytool</b> 命令必须使用这一相同的别名来引用该实体。 </p><p>例如，假设您用别名 <tt>duke</tt> 生成了新的公钥/私钥密钥对并将公钥用以下命令打包到自签名证书中（参见<a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#CertChains">证书链</a>）： </p><pre>    keytool -genkey -alias duke -keypass dukekeypasswd</pre>这指定了一个初始口令“dukekeypasswd”，接下来的命令都要使用该口令才能访问与别名 <tt>duke</tt> 相关联的私钥。以后如果您想更改 duke 的 私钥口令，可用类似下述的命令： <pre>    keytool -keypasswd -alias duke -keypass dukekeypasswd -new newpass</pre>这将把口令从“dukekeypasswd”改为“newpass”。 
<p>请注意：实际上，除非是作为测试目的或是在安全的系统上，否则不应在命令行或脚本中指定口令。如果没有在命令行上指定所要求的口令选项，您将会得到要求输入口令的提示。当在口令提示符下键入口令时，口令将被即时显示出来（键入什么就显示什么），因此，要小心，不要当着任何人的面键入口令。</p></blockquote><h3><a name="KeystoreLocation"></a>密钥仓库位置</h3><blockquote>每个 <b>keytool</b> 命令都有一个 <tt>-keystore</tt> 选项，用于指定 <b>keytool</b> 管理的密钥仓库的永久密钥仓库文件名称及其位置。缺省情况下，密钥仓库储存在用户宿主目录（由系统属性的“user.home”决定）中名为 <i>.keystore</i> 的文件中。在 Solaris 系统中“user.home”缺省为用户的宿主目录。</blockquote><h3>密钥仓库的创建</h3><blockquote>当用 <a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#genkeyCmd">-genkey</a>、<a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#importCmd">-import</a> 或 <a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#identitydbCmd">-identitydb</a> 命令向某个尚不存在的密钥仓库添加数据时，就创建了一个密钥仓库。 
<p>具体地说，如果在 <tt>-keystore</tt> 选项中指定了一个并不存在的密钥仓库，则该密钥仓库将被创建。 </p><p>如果不指定 <tt>-keystore</tt> 选项，则缺省密钥仓库将是宿主目录中名为 <tt>.keystore</tt> 的文件。如果该文件并不存在，则它将被创建。</p></blockquote><h3><a name="KeystoreImplementation"></a>密钥仓库实现</h3><blockquote><tt>java.security</tt> 包中提供的 <tt>KeyStore</tt> 类为访问和修改密钥仓库中的信息提供了相当固定的接口。可以有多个不同的具体实现，其中每个实现都是对某个特定<i>类型</i>的密钥仓库的具体实现。 
<p>目前，有两个命令行工具（<b>keytool</b> 和 <b>jarsigner</b>）以及一个名为 <b>Policy Tool</b> 的基于 GUI 的工具使用密钥仓库实现。由于密钥仓库是公用的，JDK 用户可利用它来编写其它的安全性应用程序。 </p><p>Sun Microsystems 公司提供了一个内置的缺省实现。它利用名为“JKS” 的专用密钥仓库类型（格式），将密钥仓库实现为一个文件。它用个人口令保护每个私钥，也用口令（可能为另一个口令）保护整个密钥仓库的完整性。 </p><p>密钥仓库的实现基于提供者 (provider)。更具体地说，由<tt>密钥仓库</tt>所提供的应用程序接口是借助于“服务提供者接口”(SPI) 来实现的。也就是说，在 <tt>java.security</tt> 包中还有一个对应的抽象 <tt>KeystoreSpi</tt> 类，它定义了“提供者”必须实现的服务提供者接口方法。（术语“提供者”指的是一个或一组包，这个或这组包提供了一部份可由 Java 安全 API 访问的服务子集的具体实现。因此，要提供某个密钥仓库实现，客户机必须实现一个“提供者”并实现 KeystoreSpi 子类，如<a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/guide/security/HowToImplAProvider.html">如何为 Java 加密体系结构实现 Provider</a> 中所述。 </p><p>通过使用<tt> KeyStore</tt> 类中提供的“getInstance”工厂方法，应用程序可从不同的提供者中挑选不同<i>类型</i>的密钥仓库实现。密钥仓库类型定义密钥仓库信息的存储和数据格式，以及用于保护密钥仓库中的私钥和密钥仓库自身完整性的算法。不同类型的密钥仓库实现是不兼容的。 </p><p><b>keytool</b> 使用基于文件的密钥仓库实现 （它把在命令行中传递给它的密钥仓库位置当成文件名处理并将之转换为文件输入流，从该文件输入流中加载密钥仓库信息）。另一方面，<b>jarsigner</b> 和 <b>policytool</b> 工具可从任何可用 URL 指定的位置读取某个密钥仓库。 </p><p>对于 <b>keytool</b> 和 <b>jarsigner</b>，可在命令行用 <i>-storetype</i> 选项指定密钥仓库类型。对于 <b>Policy Tool</b>，可通过 “编辑”菜单中的“更改密钥仓库”命令来指定密钥仓库类型。 </p><p>如果没有明确指定一个密钥仓库类型，这些工具将只是根据安全属性文件中指定的 <tt>keystore.type</tt> 属性值来选择密钥仓库实现。安全属性文件名为 <tt>java.security</tt>，它位于 JDK 安全属性目录 <tt><i>java.home</i>/lib/security</tt> 中，其中 <i>java.home</i> 为 JDK 的安装目录。 </p><p>每个工具都先获取 <tt>keystore.type</tt> 的值，然后检查所有当前已安装的提供者直到找到一个实现所要求类型的密钥仓库的实现为止。然后就使用该提供者的密钥仓库实现。 </p><p><tt>KeyStore</tt> 类定义了一个名为 <tt>getDefaultType</tt> 的静态方法，它可让应用程序或 applet 检索 <tt>keystore.type</tt> 属性的值。以下代码将创建缺省密钥仓库类型（此类型由 <tt>keystore.type</tt> 属性所指定。）的一个实例： </p><pre>    KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());</pre>缺省的密钥仓库类型是“jks”（这是由“SUN”提供者提供的密钥仓库实现的专用类型）。它在安全性属性文件中由下行进行指定： <pre>    keystore.type=jks</pre>要让工具使用不同于缺省类型的密钥仓库实现，可更改此行，指定不同的密钥仓库类型。 
<p>例如，如果您有一个这样的提供者包，它给出一个名为“pkcs12”的密钥仓库类型的密钥仓库实现，则可将上面那行改为： </p><pre>    keystore.type=pkcs12</pre>注意：密钥仓库类型的命名中大小写无关紧要。例如，“JKS”将被认为是与“jks”相同的。</blockquote><h3><a name="DefaultAlgs"></a>支持的算法和密钥大小</h3><blockquote><b>keytool</b> 允许用户指定任何注册了的加密服务提供者所提供的密钥对生成和签名算法。也就是说，各种命令中的 <i>keyalg</i> 和 <i>sigalg</i> 选项必须得到提供者的实现的支持。缺省的密钥对生成算法是“DSA”。签名算法是从所涉及私钥的算法推导来的：如果所涉及的私钥是“DSA”类型，则缺省的签名算法为“SHA1withDSA”，如果所涉及的私钥是“RSA”类型，则缺省的签名算法为“MD5withRSA”。 
<p>在生成 DSA 密钥对时，密钥大小的范围必须在 512 到 1024 位之间，且必须是 64 的倍数。缺省的密钥大小为 1024 位。</p></blockquote><h3><a name="Certificates"></a>证书</h3><blockquote><b>证书</b>（也叫<b>公钥证书</b>）是来自某个实体（签<i>发人</i>）的经数字签名的声明，它声明另一实体（<i>主体</i>）的公钥（及其它信息）具有某一特定的值。 
<p>下面详细解释本句中使用的主要术语： </p><dl>  
<dt><i>公钥</i></dt><dd>是与特定实体相关联的数字。所有需要与该实体进行信任交互的人都应知道该数字。公钥用于校验签名。 
</dd><dt><i>经数字签名</i></dt><dd>如果某些数据<i>经数字签名</i>，说明它们已与某一实体的“身份”存储在一起，而且证明该实体的签名知道这些数据。通过用该实体的私钥进行绘制，这些数据就是不可伪造的了。 
</dd><dt><i>身份</i></dt><dd>用于声明实体的一种手段。某些系统中，身份是公钥，而在另一些系统中则可以是 Unix UID、电子邮件地址或 X.509 特征名等等。 
</dd><dt><i>签名</i></dt><dd>所谓签名，就是用实体的（<i>签名人</i>，在证书中也称为签<i>发人</i>）私钥对某些数据进行计算。 
</dd><dt><i>私钥</i></dt><dd>是一些数字，每个数字都应仅被以该数字作为私钥的特定实体所知（即该数字应保密）。在所有公钥密码系统中，私钥和公钥均成对出现。在 DSA 等具体的公钥密码系统中，一个私钥只对应一个公钥。私钥用于计算签名。 
</dd><dt><i>实体</i></dt><dd>实体是您在某种程度上对其加以信任的个人、组织、程序、计算机、企业、银行等。 </dd></dl>通常，公钥密码系统需要访问用户的公钥。在大型联网环境中，并不能确保通信实体之间已经预先建立起关系，也无法确保受信任的储存库与所用的公钥都存在。于是人们发明了证书作为公钥分配问题的解决办法。现在，<i>认证机构</i> (CA) 可充当可信任的第三方。CA 是可信任的向其它实体签名（发放）证书的实体（例如企业）。由于 CA 受法律协议约束，因此可认为它们只提供可靠有效的证书。公共认证机构数量很多，例如 <a href="http://www.verisign.com/">VeriSign</a>、<a href="http://www.thawte.com/">Thawte</a>、<a href="http://www.entrust.com/">Entrust</a> 等等。您还可以使用诸如 Netscape/Microsoft Certificate Servers 或 Entrust CA 等产品来自己运营认证机构。 
<p>使用 <b>keytool</b> 可以显示、导入和导出证书。还可以产生自签名证书。 </p><p><b>keytool</b> 目前处理 X.509 证书。 </p><h3><a name="DName"></a>X.509 证书</h3><blockquote>X.509 标准规定了证书可以包含什么信息，并说明了记录信息的方法（数据格式）。除了签名外，所有 X.509 证书还包含以下数据： 
<dl>  
<dt><b>版本</b></dt><dd>识别用于该证书的 X.509 标准的版本，该版本影响证书中所能指定的信息。迄今为止，已定义的版本有三个。<b>keytool</b> 可导入和导出 v1、v2 和 v3 版的证书。它只能生成 v1 版证书。 
</dd><dt><b>序列号</b></dt><dd>发放证书的实体有责任为证书指定序列号，以使其区别于该实体发放的其它证书。此信息用途很多。例如，如果某一证书被撤消，其序列号将放到证书撤消清单 (CRL) 中。 
</dd><dt><b>签名算法标识符</b></dt><dd>用于标识 CA 签名证书时所用的算法。 
</dd><dt><b>签发人名称</b></dt><dd>签名证书的实体的 <a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#DName">X.500 特征名</a>。它通常为一个 CA。使用该证书意味着信任签名该证书的实体。注意：有些情况下（例如<i>根或顶层</i> CA 证书），签发人会签名自己的证书。 
</dd><dt><b>有效期</b></dt><dd>每个证书均只能在一个有限的时间段内有效。该有效期以起始日期和时间及终止日期和时间表示，可以短至几秒或长至一世纪。所选有效期取决于许多因素，例如用于签名证书的私钥的使用频率及愿为证书支付的金钱等。它是在没有危及相关私钥的条件下，实体可以依赖公钥值的预计时间。 
</dd><dt><b>主体名</b></dt><dd>证书可以识别其公钥的实体名。此名称使用 X.500 标准，因此在Internet中应是唯一的。它是实体的 <a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#DName">X.500 特征名 </a>(DN)，例如， <pre>    CN=Java Duke, OU=Java Software Division, O=Sun Microsystems Inc, C=US</pre>（这些指主体的通用名、组织单位、组织和国家。） 
</dd><dt><b>主体公钥信息</b></dt><dd>这是被命名实体的公钥，同时包括指定该密钥所属公钥密码系统的算法标识符及所有相关的密钥参数。 </dd></dl><i>X.509 1 版</i> 1988 年发布，已得到广泛使用，是最常用的版本。 
<p><i>X.509 2 版</i>引入了主体和签发人唯一标识符的概念，以解决主体和/或签发人名称在一段时间后可能重复使用的问题。大多数证书监视文档都极力建议不要重复使用主体或签发人名称，而且建议证书不要使用唯一标识符。版本 2 证书尚未得到广泛使用。 </p><p><i>X.509 3 版</i>是最新的版本（1996 年）。它支持扩展的概念，因此任何人均可定义扩展并将其纳入证书中。现在常用的扩展包括：<i>KeyUsage</i>（仅限密钥用于特殊目的，例如“只签名”）和 <i>AlternativeNames</i>（允许其它身份也与该公钥关联，例如 DNS 名、电子邮件地址、IP 地址）。扩展可标记为“极重要”，以表示该扩展应被检查并执行或使用。例如，如果某一证书将 KeyUsage 扩展标记为“极重要”，而且设置为“keyCertSign”，则在 SSL 通讯期间该证书出现时将被拒绝，因为该证书扩展表示相关私钥应只用于签名证书，而不应该用于 SSL。 </p><p>证书中的所有数据均用两个名为 ASN.1/DER 的相关标准进行编码。<i>抽象语法注释 1</i><i>(Abstract Syntax Notation 1)</i> 描述数据。<i>确定性编码规则 (Definite Encoding Rules)</i> 描述储存和传输该数据的唯一方式。</p></blockquote><h4><a name="DName"></a>X.500 特征名</h4><blockquote>X.500 特征名用于标识实体，例如 X.509 证书的 主体和签发人（签名人）域所命名的实体。<b>keytool</b> 支持以下的子组件： 
<ul>  
<li><i>commonName</i> - 个人常用名，例如“Susan Jones” 
</li><li><i>organizationUnit</i> - 小型组织（例如部门或分部）的名称，例如“Purchasing” 
</li><li><i>organizationName</i> - 大型组织的名称，例如“ABCSystems, Inc.” 
</li><li><i>localityName</i> - 地方（城市）名，例如“Palo Alto” 
</li><li><i>stateName</i> - 州或省份名，例如“California” 
</li><li><i>country</i> - 两个字母的国家代码，例如“CH” </li></ul>当给出一个特征名字符串作为 <tt>-dname</tt> 选项的值时，例如 <tt>-genkey</tt> 或 <tt>-selfcert</tt> 命令中的该选项，字符串必须为以下格式： <pre>CN=<i>cName</i>, OU=<i>orgUnit</i>, O=<i>org</i>, L=<i>city</i>, S=<i>state</i>, C=<i>countryCode</i></pre>其中所有的斜体字代表实际值而上面的关键字是以下缩写： <pre>        CN=commonName
        OU=organizationUnit
        O=organizationName
        L=localityName
        S=stateName
        C=country</pre>以下是特征名字符串样本： <pre>CN=Mark Smith, OU=JavaSoft, O=Sun, L=Cupertino, S=California, C=US</pre>以下是使用这一字符串的样本命令： <pre>keytool -genkey -dname "CN=Mark Smith, OU=JavaSoft, O=Sun, L=Cupertino,
S=California, C=US" -alias mark</pre>大小写对关键字缩写无关紧要。例如，“CN”、“cn”和“Cn”都将被当作是一样的。 
<p>但顺序是有关系的；每个子组件必须按设计好的顺序出现。但是，不是所有子组件都必须有。可以只用一部分，例如： </p><pre>CN=Steve Meier, OU=SunSoft, O=Sun, C=US</pre>如果特征名字符串的值含有逗号，当在命令行指定该字符串时，逗号必须用“\”字符来进行转义，如下所示： <pre>   cn=peter schuster, o=Sun Microsystems\, Inc., o=sun, c=us</pre>在命令行中指定特征名字符串是不必要的。如果某一命令需要指定特征名字符串，而在命令行中又未提供，则用户将得到每个子组件的提示。这种情况下，逗号不需要用“\”来转义。</blockquote><h4><a name="EncodeCertificate"></a>Internet RFC 1421 证书编码标准</h4><blockquote>证书常用 Internet RFC 1421 标准中定义的可打印的编码格式来存储，而不是用其二进制编码来存储。这种证书格式，也称“Base 64 编码”，便于通过电子邮件或其它机制将证书导出到别的应用程序中。 
<p>用 <tt>-import</tt> 和 <tt>-printcert</tt> 命令读入的证书可以是这种格式的编码或是二进制格式的编码。 </p><p>缺省情况下，<tt>-export</tt> 命令将以二进制编码格式输出证书，但如果指定了 <tt>-rfc</tt> 选项，则将以可打印的编码格式输出证书。 </p><p>缺省情况下，<tt>-list</tt> 命令打印证书的 MD5 指纹。而如果指定了 <tt>-v</tt> 选项，将以可读格式打印证书，如果指定了 <tt>-rfc</tt> 选项，将以可打印的编码格式输出证书。 </p><p>在其可打印的编码格式中，已编码证书的起始行是： </p><pre>-----BEGIN CERTIFICATE-----</pre>结束行是： <pre>-----END CERTIFICATE-----</pre></blockquote><h3><a name="CertChains"></a>证书链</h3><blockquote><b>keytool</b> 可创建和管理密钥仓库的“密钥”项，每个密钥项都含有私钥和相关证书“链”。链中的第一个证书含有与私钥对应的公钥。 
<p>当第一次产生密钥时（参见 <a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#genkeyCmd">-genkey</a> 命令），链中只含有一个元素，即<i>自签名证书</i>。自签名证书是一个这样的证书：其签发人（签名人）与主体（证书所认证的公钥所属的实体）相同。当调用 <tt>-genkey</tt> 命令来生成新的公钥/私钥对时，它同时也把公钥打包进自签名证书中。 </p><p>之后，当证书签名请求 (CSR) （参见 <a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#certreqCmd">-certreq</a> 命令）被生成并送至认证机构 (CA) 后，CA 的答复将被导入（参见 <a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#importCmd">-import</a>），证书链将取代自签名证书。在链的底部是认证主体公钥的 CA 所发放的证书（答复）。链中下一个证书是用于认证 <i>CA </i>公钥的证书。 </p><p>在许多情况下，这是个自签名证书（即来自认证其自身公钥的 CA 的证书）且是链中的最后一个证书。在其它情况下，CA 也许将返回证书链。这种情况下，链中底部的证书是相同的（由 CA 签名的证书，对密钥项的公钥进行认证），但链中第二个证书是由<i>不同的</i> CA 所签名的，对您向其发送 CSR 的 CA 的公钥进行认证。然后，链中的下一个证书将是对第二个 CA 的公钥进行认证的证书，以此类推，直至到达自签名的“根”证书为止。因此，链中的每个证书（从第一个以后）都对链中前一个证书的签名人的公钥进行认证。 </p><p>许多 CA 只返回所发放的证书，而不支持链，特别是当层次结构较简单时（无中介 CA）。这种情况下，必须用储存在密钥仓库中的可信任的证书信息来建立证书链。 </p><p>另一种答复格式（由 PKCS#7 标准所定义）除了包含所签发的证书外，还支持证书链。两种答复格式都可由 <b>keytool</b> 处理。 </p><p>顶层（根）CA 证书是自签名的。但是，对根公钥的信任并非来自根证书本身（任何人都可用特征名来产生自签名证书！譬如说用 VeriSign 根 CA 的特征名）, 而是来自报纸之类的其它来源。根 CA 的公钥是广为人知的。它被储存在证书中的唯一原因是因为这是大多数工具所能理解的格式，因此这种情况下的证书只是作为一种传输根 CA 的公钥用的“交通工具”。在将根 CA 证书加到您的密钥仓库中之前，应该先对它进行查看（用 <tt>-printcert</tt> 选项）并将所显示的指纹与已知的指纹（从报纸、根 CA 的网页等中获取）进行比较。</p></blockquote><h4><a name="ImportCertificate"></a>导入证书</h4><blockquote>要从一个文件中导入某个证书，可用 <a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#importCmd">-import</a> 命令，如下所示： <pre>    keytool -import -alias joe -file jcertfile.cer</pre>此样本命令导入文件 <i>jcertfile.cer</i> 中的证书并将其存储在由别名 <i>joe</i> 标识的密钥仓库项中。 
<p>导入证书的两个理由如下： </p><ol>  
<li>为将其添加到可信任的证书清单中，或 
</li><li>为导入因向 CA 提交证书签名请求（参见 <a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#certreqCmd">-certreq</a> 命令）而收到的来自该 CA 的认证答复。 </li></ol><tt>-alias</tt> 选项的值指明要进行何种类型的导入。如果数据库中存在别名，且该别名标识具有私钥的项，则将假定您要导入认证答复。<b>keytool</b> 将检查认证答复中的公钥是否与用别名储存的私钥相匹配，如果两者不同，则程序退出。如果别名标识另一种类型的密钥仓库项，则不导入该证书。如果该别名不存在，则它将被创建并与导入的证书关联。 
<h4><a name="TrustedCertWarning"></a>有关导入可信任证书的警告</h4><blockquote>重要：将证书作为可信任的证书导入之前，请务必先仔细检查该证书！ 
<p>先查看一下（用 <tt>-printcert</tt> 命令，或用不带 <tt>-noprompt</tt> 选项的 <tt>-import 命令</tt>），确保所显示的证书指纹与所预计的相匹配。例如，假设某人给您送来或用电子邮件发来一个证书，您将它放在名为 <tt>/tmp/cert</tt> 的文件中。在将它加到可信任证书的清单中之前，可通过执行 <tt>-printcert</tt> 命令来查看它的指纹，如下所示： </p><pre>  keytool -printcert -file /tmp/cert
    Owner: CN=ll, OU=ll, O=ll, L=ll, S=ll, C=ll
    Issuer: CN=ll, OU=ll, O=ll, L=ll, S=ll, C=ll
    Serial Number: 59092b34
    Valid from: Thu Sep 25 18:01:13 PDT 1997 until: Wed Dec 24 17:01:13 PST 1997
    Certificate Fingerprints:
         MD5： 11:81:AD:92:C8:E5:0E:A2:01:2E:D4:7A:D7:5F:07:6F
         SHA1: 20:B6:17:FA:EF:E5:55:8A:D0:71:1F:E8:D6:9D:C0:37:13:0E:5E:FE</pre>然后给向您发送证书的人打电话或用其它方式联系，将您将您所看到的指纹与他们所提供的比较。只有两者相等才可保证证书在传送途中没有被其它人（例如，攻击者）的证书所更换。如果发生了这样的攻击，而您未检查证书即将其导入，您就会信任攻击者所签名的任何东西（例如，一个含有恶意类文件的 JAR 文件）。 
<p>注意：并不要求在导入证书前执行 <tt>-printcert</tt> 命令，因为在将证书添加到密钥仓库中可信任证书的清单中之前，<tt>-import</tt> 命令将会打印出该证书的信息，并提示您进行校验。这时，您可选择中止导入操作。但是注意，只有在调用不带 <tt>-noprompt</tt> 选项的 <tt>-import</tt> 命令时才能这样做。如果给出了 <tt>-noprompt</tt> 选项，则不存在与用户的交互</p></blockquote></blockquote><h4><a name="ExportCertificate"></a>导出证书</h4><blockquote>要将证书导出到文件中，请用 <a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#exportCmd">-export</a> 命令，如下所示： <pre>    keytool -export -alias jane -file janecertfile.cer</pre>该样本命令将 <i>jane</i> 的证书导出到文件 <tt>janecertfile.cer</tt> 中。也就是说，如果 <tt>jane</tt> 是某个密钥项的别名，该命令将导出该密钥仓库项中所含证书链底部的证书。这是认证 <tt>jane</tt> 的公钥用的证书。 
<p>相反，如果 <tt>jane</tt> 是某个可信任证书项的别名，则导出的是该可信任的证书。</p></blockquote><h4><a name="DisplayCertificate"></a>显示证书</h4><blockquote>要打印某个密钥仓库项的内容，请用 <a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#listCmd">-list</a> 命令，如下所示： <pre>    keytool -list -alias joe</pre>如果未指定别名，如下所示： <pre>    keytool -list</pre>则打印整个密钥仓库的内容。 
<p>要显示储存在文件中的证书的内容，请用 <a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#printcertCmd">-printcert</a> 命令，如下所示： </p><pre>    keytool -printcert -file certfile.cer</pre>这将打印储存在文件 <tt>certfile.cer</tt> 中的有关证书的信息。 
<p>注意：此操作与密钥仓库无关，也就是说，<i>不</i>需要密钥仓库即可显示储存在文件中的证书。</p></blockquote><h4><a name="CertificateGeneration"></a>生成自签名证书</h4><blockquote><i>自签名证书</i>是一个这样的证书：其签发人（签名人）与主体（证书所认证的公钥所属的实体）相同。当调用 <tt>-genkey</tt> 命令来生成新的公钥/私钥对时，它同时也把公钥打包进自签名证书中。 
<p>有时您也许希望生成新的自签名证书。例如，您也许想对不同身份（特征名）使用相同的密钥对。例如，假设您换了个部门。此时您可以： </p><ol>  
<li>复制原始的密钥项。请参见 <a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#keycloneCmd">-keyclone</a>。 
</li><li>用新特征名为该复制项生成新的自签名证书。参见下文。 
</li><li>为该复制项生成证书签名请求，并导入答复证书或证书链。参见 <a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#certreqCmd">-certreq</a> 和 <a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#importCmd">-import</a> 命令。 
</li><li>删除原始（现在已过时）项。参见 <a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#deleteCmd">-delete</a> 命令。 </li></ol>要生成自签名证书，请用 <a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#selfcertCmd">-selfcert</a> 命令，如下所示： <pre>    keytool -selfcert -alias dukeNew -keypass b92kqmp
      -dname "cn=Duke Smith, ou=Purchasing, o=BlueSoft, c=US"</pre>所生成的证书作为指定别名（本例中为“dukeNew”）所标识的密钥仓库项中的单元素证书来存储，它将取代现有的证书链。</blockquote></blockquote></blockquote>
		<h2>
				<a name="CommandNotes">
				</a>命令和选项注意事项</h2>
		<blockquote>
				<a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#Commands">下面</a>列出各种命令及其选项，并对它们进行描述。注意： 
<ul>  
<li>所有的命令和选项名之前都有减号 (-) 。 
</li><li>每个命令的选项都可按任意顺序提供。 
</li><li>所有非斜体项或不在花括号和方括号内的项都不能改动。 
</li><li>选项周围的花括号通常表示如果在命令行中没有指定该选项，则使用<a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#OptionDefaults">缺省</a>值。花括号还用在 <tt>-v</tt>、<tt>-rfc</tt> 和 <tt>-J</tt> 选项周围，这些选项只有在命令行中出现时才有意义（也就是说，它们没有任何缺省值，不然就是不存在该选项）。 
</li><li>选项周围的方括号表示如果在命令行中没有指定该选项，则用户将得到要求输入其值的提示。（对于 <tt>-keypass</tt> 选项，如果在命令行中没有指定该选项，<b>keytool</b> 将先是尝试用密钥仓库口令来访问私钥，如果失败，再提示您输入私钥口令。） 
</li><li>斜体项（选项）代表必须提供实际值。例如，下面是 <tt>-printcert</tt> 命令的格式： <pre>  keytool -printcert {-file <i>cert_file</i>} {-v}</pre>当指定 <tt>-printcert</tt> 命令时，请用实际文件名来替代 <i>cert_file</i>，如下所示： <pre>  keytool -printcert -file VScert.cer</pre></li><li>如果选项值含有空白（空格），必须用引号把它们括起来。 
</li><li><tt>-help</tt> 命令是缺省命令。因此，命令行 <pre>  keytool</pre>等价于 <pre>  keytool -help</pre></li></ul><h3><a name="OptionDefaults"></a>选项缺省值</h3><blockquote>下面是各选项的缺省值。 <pre>-alias "mykey"

-keyalg "DSA"

-keysize 1024

-validity 90

-keystore <i>用户宿主目录中名为 .keystore 的文件

</i>-file <i>读时为标准输入，写时为标准输出</i></pre>签名算法（<i>-sigalg</i> 选项）是由所涉及私钥的算法推导而来的：如果所涉及的私钥是“DSA”类型，则 <i>-sigalg</i> 选项将缺省为“带 DSA 的 SHA1”，如果所涉及的私钥是“RSA”类型，则 <i>-sigalg</i> 选项将缺省为“带 RSA 的 MD5”。</blockquote><h3><a name="OptionsInAll"></a>出现在大多数命令中的选项</h3><blockquote><tt>-v</tt> 选项可出现在除 <tt>-help</tt> 之外的所有命令中。如果出现该选项，表示处在“长格式”模式下；将输出详细的证书信息。 
<p><tt>-J<i>javaoption</i></tt> 选项也可在任何命令中出现。如果出现该选项，则所指定的 <i>javaoption</i> 字符串将被直接传给 Java 解释器。（<b>keytool</b> 实际上是解释器周围的一个 “wrapper”。） 该选项不应含有任何空格。它有助于调整执行环境或内存使用。要获得可用的解释器选项的清单，可在命令行键入 <tt>java -h</tt> 或 <tt>java -X</tt>。 </p><p>有三个选项可出现在用于操作密钥仓库的所有命令中： </p><dl>  
<dt><tt>-storetype <i>storetype</i></tt></dt><dd>此限定符指定将被实例化的密钥仓库类型。缺省的密钥仓库类型是安全属性文件中“keystore.type”属性值所指定的那个类型，由 <tt>java.security.KeyStore</tt> 中的静态方法 <tt>getDefaultType</tt> 返回。 
</dd><dt><tt>-keystore <i>keystore</i></tt></dt><dd>密钥仓库（数据库文件）的位置。缺省情况下，密钥仓库指的是用户宿主目录的 <i>.keystore</i> 文件，它是由“user.home”的系统属性确定的。在 Solaris 系统中，“user.home”缺省为用户宿主目录。 
</dd><dt><tt>-storepass <i>storepass</i></tt></dt><dd>口令，用来保护密钥仓库的完整性。 
<p><br /><i>storepass</i> 的长度必须至少为 6 个字符。所有访问密钥仓库内容的命令都必须提供这一选项。对于这些命令，如果没有给出 <tt>-storepass</tt> 选项，则用户将得到要求输入该选项的提示。 </p><p>当从密钥仓库中检索信息时，口令属于可选项；如果未给出口令，就不能检查所检索信息的完整性，而且将出现警告。 </p><p>使用口令时必须小心 - 参见<a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#PasswordWarning">与口令有关的警告</a>。</p></dd></dl></blockquote><h3><a name="PasswordWarning"></a>与口令有关的警告</h3><blockquote>大多数对密钥仓库操作的命令都要求仓库口令。一些命令要求私钥口令。 
<p>口令可以在命令行上（分别在 <tt>-storepass</tt> 和 <tt>-keypass</tt> 选项上）指定。但是，除非是作为测试目的或是在一个安全的系统上，否则不应在命令行或脚本中指定口令。 </p><p>如果没有在命令行上指定所要求的口令选项，您将会得到要求输入口令的提示。当在口令提示符下键入口令时，口令将被即时地显示出来（键入什么就显示什么），因此，要小心，不要当着任何人的面键口令。</p></blockquote></blockquote>
		<h2>
				<a name="Commands">
				</a>命令</h2>
		<blockquote>另请参阅<a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#CommandNotes">命令和选项注释</a>。 
<h3>向密钥仓库添加数据</h3><blockquote><dl><dt><a name="genkeyCmd"></a><tt><b><font size="+1">-genkey </font></b>{-alias <i>alias</i>} {-keyalg <i>keyalg</i>} {-keysize <i>keysize</i>} {-sigalg <i>sigalg</i>} [-dname <i>dname</i>] [-keypass <i>keypass</i>] {-validity <i>valDays</i>} {-storetype <i>storetype</i>} {-keystore <i>keystore</i>} [-storepass <i>storepass</i>] {-v} {-J<i>javaoption</i>}</tt></dt><dd>产生密钥对（公钥和与之关联的私钥）。将公钥打包进 X.509 v1 的自签名证书中，该证书以单元素证书链的形式储存。该证书链和私钥将储存于 <i>alias</i> 所标识的新密钥仓库项中。 
<p><br /><i>keyalg</i> 指定了用于生成密钥对的算法，而 <i>keysize</i> 指定要生成的每个密钥的大小。<i>sigalg</i> 指定签名自签名证书所用的算法；这一算法必须与 <i>keyalg</i> 兼容。参见<a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#DefaultAlgs">支持的算法和密钥大小</a>。 </p><p><i>dname</i> 指定与 <i>alias</i> 关联的 <a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#DName">X.500 特征名</a>，并用作自签名证书中的 <tt>issuer</tt> 和 <tt>subject</tt> 域。如果在命令行中没有提供特征名，用户将得到要求输入该信息的提示。 </p><p><i>keypass</i> 是口令，用来保护所生成密钥对中的私钥。如果没有提供口令，用户将得到要求输入口令的提示。如果在提示符下按 RETURN 键，则密钥口令将被设置为与密钥仓库所用的口令相同。<i>keypass</i> 的长度必须至少为 6 个字符。使用口令时必须小心 - 参见 <a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#PasswordWarning">与口令有关的警告</a>。 </p><p><i>valDays</i> 指定证书的有效期。 </p></dd><dt><a name="importCmd"></a><tt><b><font size="+1">-import </font></b>{-alias <i>alias</i>} {-file <i>cert_file</i>} [-keypass <i>keypass</i>] {-noprompt} {-trustcacerts} {-storetype <i>storetype</i>} {-keystore <i>keystore</i>} [-storepass <i>storepass</i>] {-v} {-J<i>javaoption</i>}</tt></dt><dd>从文件 <i>cert_file</i> 中读取证书或证书链（后者在 PKCS#7 格式的答复所给出）并将其储存在 <i>alias</i> 所标识的密钥仓库项中。如果没有给出文件，则从标准输入设备中读取证书或 PKCS#7 答复。<b>keytool</b> 可导入 X.509 v1、v2 和 v3 的证书以及由该类证书所组成的 PKCS#7 格式的证书链。要导入的数据必须是二进制编码格式或 <a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#EncodeCertificate">Internet RFC 1421 标准</a>所定义的可打印的编码格式（也称 Base64 编码）。在后一种情况下，编码必须用以“-----BEGIN”开头的字符串开始，用以“-----END”结尾的字符串来结束。 
<p><br />当导入<b>新的可信任证书</b>时，密钥仓库中还没有 <i>alias</i>。在把证书添加到密钥仓库中之前，<b>keytool</b> 将尝试用密钥仓库中已有的可信任证书来构造从该证书到自签名证书（属于根 CA）的信任链，以对证书进行校验。 </p><p>如果指定了 <tt>-trustcacerts</tt> 选项，则将为该信任链考虑其它证书，即考虑名为<b>“cacerts”</b>的文件中的证书，该文件位于 JDK 安全属性目录 <tt><i>java.home</i>\lib\security</tt> 中，其中 <i>java.home</i> 为 JDK 安装目录。“cacerts”文件代表含有 CA 证书的系统范围的密钥仓库。通过指定密钥仓库类型为“jks”，系统管理员可用 <b>keytool</b> 来配置和管理该文件。“cacerts”密钥仓库文件发送时附有五个 VeriSign 根 CA 证书，其 X.500 特征名如下： </p><pre>1. OU=Class 1 Public Primary Certification Authority, O="VeriSign, Inc.",
C=US

2. OU=Class 2 Public Primary Certification Authority, O="VeriSign,
Inc.", C=US

3. OU=Class 3 Public Primary Certification Authority,
O="VeriSign, Inc.", C=US

4. OU=Class 4 Public Primary Certification
Authority, O="VeriSign, Inc.", C=US

5. OU=Secure Server Certification
Authority, O="RSA Data Security, Inc.", C=US</pre>“cacerts”密钥仓库文件的初始口令为“changeit” 。系统管理员在安装 JDK 后，就应该立即更改这个口令以及该文件的缺省访问权限。 
<p>如果 <b>keytool</b> 无法建立从要导入的证书到自签名证书的信任路径（利用密钥仓库或“cacerts”文件），则打印出该证书的信息，而用户将得到要求校验的提示，例如，系统将通知用户通过比较显示出的指纹和得自其它（可信任的）信息来源的指纹来进行校验，信息来源可能是证书拥有者本人。在将证书作为一个“可信任”证书导入之前，要十分小心，务必保证该证书是有效的！ -- 参见<a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#TrustedCertWarning">有关导入可信任证书的警告</a>。然后，用户可以选择中止导入操作。但是，如果给了 <tt>-noprompt</tt> 选项，则不会有与用户的交互。 </p><p>当导入<b>认证答复</b>时，该认证答复将用密钥仓库中可信任的证书来确认，有时也使用在“cacerts”密钥仓库文件中配置的证书（如果指定了 <tt>-trustcacerts</tt> 选项）。 </p><p>如果答复是一个 X.509 证书，<b>keytool</b> 将尝试建立信任链，以该认证答复为头，以属于根 CA 的自签名证书为尾。该认证答复和用于认证该认证答复的证书层次形成了 <i>alias</i> 的新证书链。 </p><p>如果答复是 PKCS#7 格式的证书链，则该链应首先被排序（用户证书在最前面，自签名的根 CA 证书在最后面），然后 <b>keytool</b> 尝试将答复中的根 CA 证书与密钥仓库或“cacerts”密钥仓库文件（如果指定了 <tt>-trustcacerts</tt> 选项）中的任何可信任证书进行匹配。如果找不到匹配，则打印出该根 CA 证书的信息，而用户将得到要求校验它的提示，例如，系统将通知用户通过比较显示出的指纹和得自其它（可信任的）信息来源的指纹来进行校验，信息来源可能是证书拥有者本人。因此，用户可以选择中止导入操作。但是，如果给了 <tt>-noprompt</tt> 选项，则不会有与用户的交互。 </p><p><i>alias</i> 的新证书链将取代与该项关联的旧证书链。只有提供了有效的 <i>keypass</i>，即提供了用于保护该项的私钥的口令时，旧链才可被取代。如果没有提供口令，而且私钥口令与密钥仓库口令不同，用户将得到要求输入口令的提示。使用口令时必须小心 --  参见<a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#PasswordWarning">与口令有关的警告</a>。 </p></dd><dt><a name="selfcertCmd"></a><tt><b><font size="+1">-selfcert </font></b>{-alias <i>alias</i>} {-sigalg <i>sigalg</i>} {-dname <i>dname</i>} {-validity <i>valDays</i>} [-keypass <i>keypass</i>] {-storetype <i>storetype</i>} {-keystore <i>keystore</i>} [-storepass <i>storepass</i>] {-v} {-J<i>javaoption</i>}</tt></dt><dd>利用密钥仓库信息（包括与 <i>alias</i> 关联的私钥和公钥）产生 X.509 v1 自签名证书。如果在命令行提供了 <i>dname</i>，它将同时用作该证书的签发人和主体的 <a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#DName">X.500 特征名</a>。否则，将使用与 <i>alias</i> 关联的 X.500 特征名（位于其现有证书链底部）。 
<p><br />所生成的证书作为 <i>alias</i> 所标识的密钥仓库项中的单元素证书链来存储，它将取代现有的证书链。 </p><p><i>sigalg</i> 指定签名证书用的算法。参见<a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#DefaultAlgs">支持的算法和密钥大小</a>。 </p><p>要访问私钥，必须提供正确的口令，因为私钥在密钥仓库中是受口令保护的。如果在命令行中没有提供 <i>keypass</i>，且私钥口令与保护密钥仓库完整性所用的口令不同，则用户将得到要求输入该口令的提示。使用口令时必须小心 -- 参见<a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#PasswordWarning">与口令有关的警告</a>。 </p><p><i>valDays</i> 指定证书的有效期。 </p></dd><dt><a name="identitydbCmd"></a><tt><b><font size="+1">-identitydb </font></b>{-file <i>idb_file</i>} {-storetype <i>storetype</i>} {-keystore <i>keystore</i>} [-storepass <i>storepass</i>] {-v} {-J<i>javaoption</i>}</tt></dt><dd>从 <i>idb_file</i> 文件中读取 JDK 1.1.x 格式的身份数据库，并将它的项加到密钥仓库中。如果没有给出文件名，则从标准输入设备中读取身份数据库。如果不存在密钥仓库，则创建它。 
<p><br />只有被标记为可信任的身份数据库项（“身份”）才能被导入密钥仓库中。所有其它身份都将被略去。对每个可信任的身份，将创建一个密钥仓库项。身份名用作该密钥仓库项的“别名”。 </p><p>所有可信任身份的私钥都将在相同的口令 <i>storepass</i> 下得到加密。该口令与保护密钥仓库完整性所用的口令相同。用户随后可用<b> keytool</b> 命令选项“-keypasswd”来对各私钥赋予单独的口令。 </p><p>身份数据库中的一个身份可以存放多个证书，各证书所认证的都是同一公钥。但一个私钥的密钥仓库密钥项含有该私钥和单一的“证书链”（该链最初只有一个证书），链中的第一个证书含有与该私钥对应的公钥。当从身份导入信息时，只有该身份中的第一个证书被储存到密钥仓库中。这是因为身份数据库中的身份名被用作其相应密钥仓库项的别名，而别名在密钥仓库中是唯一的。</p></dd></dl></blockquote><h3>导出数据</h3><blockquote><dl><dt><a name="certreqCmd"></a><tt><b><font size="+1">-certreq </font></b>{-alias <i>alias</i>} {-sigalg <i>sigalg</i>} {-file <i>certreq_file</i>} [-keypass <i>keypass</i>] {-storetype <i>storetype</i>} {-keystore <i>keystore</i>} [-storepass <i>storepass</i>] {-v} {-J<i>javaoption</i>}</tt></dt><dd>生成 PKCS#10 格式的证书签名请求 (CSR)。 
<p><br />CSR 用来发送给认证机构 (CA)。CA 对认证请求者进行认证（通常是离线的），并返回证书或证书链，以取代密钥仓库中现有的证书链（该证书链最初只含有自签名证书）。 </p><p>私钥和与 <i>alias</i> 关联的 X.500 特征名用于创建 PKCS#10 证书请求。要访问私钥，必须提供正确的口令，因为私钥在库中是受口令保护的。如果在命令行没有提供 <i>keypass</i>，且私钥口令与保护密钥仓库完整性所用的口令不同，则用户将得到要求输入口令的提示。 </p><p>使用口令时必须小心 -- 参见<a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#PasswordWarning">与口令有关的警告</a>。 </p><p><i>sigalg</i> 指定签名 CSR 时用的算法。参见<a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#DefaultAlgs">支持的算法和密钥大小</a>。 </p><p>CSR 存储在文件 <i>certreq_file</i> 中。如果没有给出文件名，CSR 将被输出到标准输出设备中。 </p><p>用 <i>import</i> 命令来导入 CA 所返回的答复。 </p></dd><dt><a name="exportCmd"></a><tt><b><font size="+1">-export </font></b>{-alias <i>alias</i>} {-file <i>cert_file</i>} {-storetype <i>storetype</i>} {-keystore <i>keystore</i>} [-storepass <i>storepass</i>] {-rfc} {-v} {-J<i>javaoption</i>}</tt></dt><dd>从密钥仓库中读取与 <i>alias</i> 关联的证书，并将其储存在文件 <i>cert_file</i> 中。 
<p><br />如果没有给出文件名，证书将被输出到标准输出设备中。 </p><p>缺省情况下，证书被输出为二进制编码格式，但如果指定了 <tt>-rfc</tt> 选项，则将被输出为 <a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#EncodeCertificate">Internet RFC 1421 标准</a>中定义的可打印格式。 </p><p>如果 <i>alias</i> 引用的是可信任证书，则该证书将被输出。否则，<i>alias</i> 引用的是含有相关证书链的密钥项。在这种情况下，链中的第一个证书将被返回。该证书对由 <i>alias</i> 所指定的实体的公钥进行认证。</p></dd></dl></blockquote><h3>显示数据</h3><blockquote><dl><dt><a name="listCmd"></a><tt><b><font size="+1">-list </font></b>{-alias <i>alias</i>} {-storetype <i>storetype</i>} {-keystore <i>keystore</i>} [-storepass <i>storepass</i>] {-v | -rfc} {-J<i>javaoption</i>}</tt></dt><dd>打印（到标准输出设备中）<i>alias</i> 所标识的密钥仓库项的内容。如果没有指定别名，则将打印整个密钥仓库的内容。 
<p><br />缺省情况下，该命令打印证书的 MD5 指纹。如果指定了 <tt>-v</tt> 选项，证书将以可读格式打印，同时包含拥有者、签发人和序列号等附加信息。如果指定了?<tt>-rfc</tt> 选项，证书将以 <a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#EncodeCertificate">Internet RFC 1421 标准</a>所定义的可打印的编码格式打印。 </p><p>不能同时指定 <tt>-v</tt> 和 <tt>-rfc</tt> 两个选项。 </p></dd><dt><a name="printcertCmd"></a><tt><b><font size="+1">-printcert </font></b>{-file <i>cert_file</i>} {-v} {-J<i>javaoption</i>}</tt></dt><dd>从文件 <i>cert_file</i> 中读取证书将以可读格式打印其内容。如果没有给出文件名，则从标准输入设备中读取证书。 
<p><br />证书可以是用二进制编码或 <a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#EncodeCertificate">Internet RFC 1421 标准</a>所定义的可打印编码格式。 </p><p>注意：该选项的使用与密钥仓库无关。</p></dd></dl></blockquote><h3>管理密钥仓库</h3><blockquote><dl><dt><a name="keycloneCmd"></a><tt><b><font size="+1">-keyclone </font></b>{-alias <i>alias</i>} [-dest <i>dest_alias</i>] [-keypass <i>keypass</i>] [-new <i>new_keypass</i>] {-storetype <i>storetype</i>} {-keystore <i>keystore</i>} [-storepass <i>storepass</i>] {-v} {-J<i>javaoption</i>}</tt></dt><dd>生成新的密钥仓库项，该项含有的私钥和证书链与原始项的相同。 
<p><br />原始项由 <i>alias</i> （如果没有提供别名，则其值缺省为“mykey”）标识。新（目标）项由 <i>dest_alias</i> 标识。如果没有在命令行中提供目标别名，用户将得到要求输入该信息的提示。 </p><p>如果私钥口令与密钥仓库口令不同，那么，只有提供了有效的 <i>keypass</i> 时该项才能被复制。<i>keypass</i> 是用于保护与 <i>alias</i> 关联的私钥的口令。如果没有在命令行提供密钥口令，且私钥口令与密钥仓库口令不同，用户将得到要求输入口令的提示。如果愿意，可用不同的口令来保护复制项中的私钥。如果没有在命令行提供 <tt>-new</tt> 选项，用户将得到提示要求输入新项的口令（可以选择让该口令与被复制项的私钥所用的口令相同）。 </p><p>使用口令时必须小心 -- 参见<a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#PasswordWarning">与口令有关的警告</a>。 </p><p>该命令可用于建立多个与给定密钥对相对应的证书链，或用于备份。 </p></dd><dt><tt><b><font size="+1">-storepasswd </font></b>[-new <i>new_storepass</i>] {-storetype <i>storetype</i>} {-keystore <i>keystore</i>} [-storepass <i>storepass</i>] {-v} {-J<i>javaoption</i>}</tt></dt><dd>更改保护密钥仓库内容的完整性所用的口令。新口令为 <i>new_storepass</i>，其长度必须至少是 6 个字符。 
<p><br />使用口令时必须小心 -- 参见<a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#PasswordWarning">与口令有关的警告</a>。 </p></dd><dt><tt><b><font size="+1">-keypasswd </font></b>{-alias <i>alias</i>} [-keypass <i>old_keypass</i>] [-new <i>new_keypass</i>] {-storetype <i>storetype</i>} {-keystore <i>keystore</i>} [-storepass <i>storepass</i>] {-v} {-J<i>javaoption</i>}</tt></dt><dd>把保护 <i>alias</i> 所标识的私钥的口令从 <i>old_keypass</i> 更改为 <i>new_keypass</i>。 
<p><br />如果没有在命令行提供 <tt>keypass</tt> 选项，且私钥口令与密钥仓库口令不同，则用户将得到要求输入该口令的提示。 </p><p>如果没有在命令行给出 <tt>-new</tt> 选项，则用户将得到要求输入新口令的提示。 </p><p>使用口令时必须小心 -- 参见<a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#PasswordWarning">与口令有关的警告</a>。 </p></dd><dt><a name="deleteCmd"></a><tt><b><font size="+1">-delete </font></b>[-alias <i>alias</i>] {-storetype <i>storetype</i>} {-keystore <i>keystore</i>} [-storepass <i>storepass</i>] {-v} {-J<i>javaoption</i>}</tt></dt><dd>从密钥仓库中删除 <i>alias</i> 所标识的项。如果没有在命令行上提供别名，则用户将得到要求输入别名的提示。 </dd></dl></blockquote><h3>获取帮助</h3><blockquote><dl><dt><b><tt><font size="+1">-help</font></tt></b></dt><dd>列出所有的命令及其选项。 </dd></dl></blockquote></blockquote>
		<h2>
				<a name="EXAMPLES">
				</a>示例</h2>
		<blockquote>假设您要创建一个密钥仓库以管理您的公钥/私钥对来自您所信任实体的证书。 
<h3>生成密钥对</h3><blockquote>您首先要做的是创建一个密钥仓库和生成密钥对。您可以使用以下命令： <pre>    keytool -genkey -dname "cn=Mark Jones, ou=JavaSoft, o=Sun, c=US"
      -alias business -keypass kpi135 -keystore /working/mykeystore
      -storepass ab987c -validity 180</pre>（请注意：键入该命令时必须使其成为一行。此处用多行来显示，主要是为了可读性。） 
<p>该命令将在 C 盘的“working”目录（假设它还不存在）中创建名为“mykeystore”的密钥仓库，并赋予它口令“ab987c”。它将为实体生成公钥/私钥对，该实体的“特征名”为：常用名“Mark Jones”、组织单位“JavaSoft”、组织“Sun”和两个字母的国家代码“US”。它使用缺省的“DSA”密钥生成算法来创建密钥，两个密钥（公钥与私钥）的长度都是 1024 位。 </p><p>它创建自签名证书（使用缺省的“带 DSA 的 SHA1”签名算法），该证书包括公钥和特征名信息。该证书的有效期为 180 天，且与别名“business”所代表的密钥仓库项关联。私钥被赋予口令“kpi135”。 </p><p>如果采用选项的缺省值，可以大大缩短该命令。实际上，这些选项并不是必需的；对于有缺省值的选项，未指定时将使用缺省值，对于任何被要求的值，您将会得到要求输入它的提示。因此，您可以只使用下面的命令： </p><pre>    keytool -genkey</pre>这种情况下，将创建一个具有别名“mykey”的密钥仓库项，它含有新生成的密钥对和有效期为 90 天的证书。该项被放在您的宿主目录下一个名为“.keystore”的密钥仓库中（如果该密钥仓库并不存在，则将创建它）。您将得到要求输入特征名信息、密钥仓库口令和私钥口令的提示。 
<p>其余示例假设您执行了未指定选项的 <tt>-genkey</tt> 命令，且用上述第一个 <tt>-genkey</tt> 命令中给出的值来回答提示要求（私钥口令为“kpi135”等等）。</p></blockquote><h3>从认证机构请求已签名的证书</h3><blockquote>目前为止我们所具有的就是自签名证书。证书如果由认证机构 (CA) 签名，将更有可能得到别人的信任。要得到这样的签名，首先要用以下命令生成证书签名请求 (CSR)： <pre>    keytool -certreq -file MarkJ.csr</pre>这将为缺省别名“mykey”所标识的实体生成 CSR，并将此请求放在名为“MarkJ.csr”的文件中。将此文件提交给某个 CA（例如 VeriSign, Inc.）。该 CA 将对您这个请求者进行认证（通常是离线的），然后返回它们所签名的证书，用于认证您的公钥。（某些情况下，它们实际上将返回证书链，链中每个证书都认证前一个证书的签名人的公钥。）</blockquote><h3>导入 CA 的证书</h3><blockquote>自签名证书必须用证书链代替，链中的每个证书都认证前一个证书的签名人的公钥，直到“根”CA 为止。 
<p>在导入 CA 的答复之前，在您的密钥仓库中或 <tt>cacerts</tt> 密钥仓库文件（如<a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/solaris/keytool.html#importCmd">导入命令</a>中所述）中需要有一个或多个“可信任”证书： </p><ul>  
<li>如果该认证答复是个证书链，您只需要链中最顶部的证书（即用于认证根 CA 的公钥的“根” CA 证书）。 
</li><li>如果该认证答复是单个证书，您需要发放 CA（即签名该证书的认证机构）的证书，如果此证书不是自签名的，则需其签名人的证书，以此类推，直到自签名的“根” CA 证书为止。 </li></ul>“cacerts”密钥仓库文件发送时附有五个 VeriSign 根 CA 证书，因此您可能并不需要导入 VeriSign 证书以作为密钥仓库中的可信任证书。但如果您请求由另一个 CA 签名的证书，而认证该 CA 的公钥的证书未被加到“cacerts”中，则您需要将来自该 CA 的证书作为“可信任证书”导入。 
<p>来自 CA 的证书通常是自签名的或是由另一个 CA 签名的（这种情况下您还需要认证该 CA 的公钥的证书）。假设 ABC, Inc. 公司是 CA，而您从该公司获得一个声称是自签名证书的名为“ABCCA.cer”的文件，它用于认证该 CA 的公钥。 </p><p>在将证书作为一个“可信任”证书导入之前，要十分小心，务必保证该证书是有效的！先查看一下（用 <b>keytool</b><tt>-printcert</tt> 命令，或用不带 <b>-noprompt</b> 选项的 <b>keytool</b><tt>-import</tt> 命令）以确保所显示的证书指纹与所预计的相匹配。然后可以给发送证书的人打电话，将您所看到的指纹与他们所提供的（或安全公钥储存库所显示的）进行比较。只有两者相等才可保证证书在传送途中没有被其它人（例如，攻击者）的证书所更换。如果发生了这样的攻击，而您未检查证书即将其导入，那么您就会信任攻击者所签名的任何东西。 </p><p>如果您相信证书是有效的，则您可以用以下命令将其加到密钥仓库中： </p><pre>    keytool -import -alias abc -file ABCCA.cer</pre>这将在密钥仓库中用文件“ABCCA.cer”中的数据创建“可信任证书”项，并将别名“abc”赋予该项。</blockquote><h3>导入来自 CA 的认证答复</h3><blockquote>一旦导入了用于认证 CA（该 CA 是您将证书签名请求送往之处）公钥的证书后，或在“cacerts”文件中已有这种证书时，就可以导入该认证答复，从而用证书链取代您的自签名证书。如果 CA 的答复是证书链，则该链是 CA 响应您的请求而返回的证书链；如果 CA 的答复是一个证书，则该链是用认证答复和可信任证书建立的证书链，这些可信任证书是密钥仓库（您要将认证答复导入之处）或“cacerts”密钥仓库文件中已有的。 
<p>例如，假设您将证书签名请求送往 VeriSign。您可用以下命令来导入认证答复，该命令假定所返回的证书名为“VSMarkJ.cer”： </p><pre>    keytool -import -trustcacerts -file VSMarkJ.cer</pre></blockquote><h3>导出用于认证您的公钥的证书</h3><blockquote>假设您用 <a href="http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/tooldocs/win32/jarsigner.html">jarsigner</a> 工具来签名 Java 归档 (JAR) 文件。需要使用这一文件的客户机将认证您的签名。 
<p>认证签名的一种方法是先将您的公钥证书作为“可信任”项导入它们的密钥仓库中。您可以将证书导出并将其提供给客户机。例如，假设项的别名为“mykey”，您可以用以下命令将您的证书导出到名为 <tt>MJ.cer</tt> 的文件中： </p><pre>    keytool -export -alias mykey -file MJ.cer</pre>有了该证书以及已被签名的 JAR 文件，客户机就可以用 <b>jarsigner</b> 工具来认证您的签名。</blockquote><h3>更改特征名但保留密钥对</h3><blockquote>假设，譬如说因为您换了部门或搬到另一个城市去了而改变了您的特征名。如果愿意，您仍然可以使用您先前使用的公钥/私钥对而只对特征名进行更新。例如，假设您的名字叫 Susan Miller，并用别名 <tt>sMiller</tt> 和以下的特征名创建了初始密钥项： <pre>  "cn=Susan Miller, ou=Finance Department, o=BlueSoft, c=us"</pre>假设您从财务部门换到了会计部门。您仍然可使用先前所生成的公钥/私钥对，而用以下方法对特征名进行更新。首先，复制您的密钥项： <pre>    keytool -keyclone -alias sMiller -dest sMillerNew</pre>（您将得到要求输入密钥仓库口令和初始密钥口令及目标密钥口令的提示，因为在命令行没有提供这些信息。）现在，您需要更改与复制项关联的证书链以使链中的第一个证书使用您的新特征名。先用相应名称生成自签名证书： <pre>    keytool -selfcert -alias sMillerNew
      -dname "cn=Susan Miller, ou=Accounting Department, o=BlueSoft, c=us"</pre>然后根据该新证书中的信息生成证书签名请求： <pre>    keytool -certreq -alias sMillerNew</pre>当您得到 CA 认证答复后，将其导入： <pre>    keytool -import -alias sMillerNew -file VSSMillerNew.cer</pre>导入认证答复后，您也许会要删除使用旧特征名的初始密钥项： <pre>    keytool -delete -alias sMiller</pre></blockquote></blockquote>
<img src ="http://www.blogjava.net/TrampEagle/aggbug/35942.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-03-18 15:11 <a href="http://www.blogjava.net/TrampEagle/archive/2006/03/18/35942.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Oracle与DB2数据类型的对应</title><link>http://www.blogjava.net/TrampEagle/archive/2006/03/10/34689.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Fri, 10 Mar 2006 08:18:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/archive/2006/03/10/34689.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/34689.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/archive/2006/03/10/34689.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/34689.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/34689.html</trackback:ping><description><![CDATA[<P>转自：<A href="http://searchdatabase.techtarget.com.cn/askexperts/190/2239190.shtml?ticket=ST-16422-ztfNySbuR6GzRSaauT6G">http://searchdatabase.techtarget.com.cn/askexperts/190/2239190.shtml?ticket=ST-16422-ztfNySbuR6GzRSaauT6G</A><BR><BR><A class=f1>&nbsp;</P>
<DIV class=guanggao><SPAN id=contentAdv></SPAN></DIV>
<P>　　首先，通过下表介绍<A class=bluekey href="http://www.yesky.com/key/2712/137712.html" target=_blank>ORACLE</A>与DB2/400数据类型之间的对应关系，是一对多的关系，具体采用哪种对应关系，应具体问题具体分析。</P>
<P>　　注意事项:</P>
<P>　　DATE <A class=bluekey href="http://www.yesky.com/key/2217/142217.html" target=_blank>and</A> TIME</P>
<P>　　Oracle中的DATE含有年、月、日、时、分、秒，它和DB2/400中的DATE不相互对应，DB2/400中的DATE只有年、月、日，TIME类型含有时、分、秒，因此日期和时间类型要进行转换，请参照下表。</P>
<P>
<TABLE style="BORDER-RIGHT: #cccccc 1px dotted; TABLE-LAYOUT: fixed; BORDER-TOP: #cccccc 1px dotted; BORDER-LEFT: #cccccc 1px dotted; BORDER-BOTTOM: #cccccc 1px dotted" cellSpacing=0 cellPadding=6 width="95%" align=center border=0>
<TBODY>
<TR>
<TD style="WORD-WRAP: break-word" bgColor=#f3f3f3>
<TABLE cellSpacing=0 cellPadding=2 width="100%" border=1>
<TBODY>
<TR>
<TD colSpan=2>Oracle<BR></TD>
<TD colSpan=2>DB2/400</TD></TR>
<TR>
<TD>Oracle数据类型 <BR></TD>
<TD>注意事项 </TD>
<TD>DB2 UDB数据类型 </TD>
<TD>注意事项</TD></TR>
<TR>
<TD>DATE </TD>
<TD>&nbsp;</TD>
<TD>DATE<BR>TIME<BR>TIMESTAMP l </TD>
<TD>如果只使用MM/DD/YYY,那么使用DATE类型。<BR>l 如果只使用HH:MM:SS, 那么使用TIME类型。<BR>l 如果要使用日期和时间，则使用时间戳类型（TIMESTAMP）<BR>l 可以使用Oracle中的TO_CHAR()函数来取DATE的字串来分别与DB2/400的DATE、TIME相匹配。<BR></TD></TR>
<TR>
<TD>VARCHAR2(n) </TD>
<TD>n&lt;=4000 <BR></TD>
<TD>CHAR(n)<BR>VARCHAR(n) l </TD>
<TD>若n&lt;=32766,则使用DB2/400中的CHAR类型、VARCHAR</TD></TR>
<TR>
<TD>LONG </TD>
<TD>n&lt;=2GB </TD>
<TD>VARCHAR(n)<BR>CLOB(n) l <BR></TD>
<TD>若n&lt;=32K,则使用DB2/400中的CHAR类型、VARCHAR。<BR>l 若32K=&lt; n &lt;=2GB，则使<A class=bluekey href="http://www.yesky.com/key/3310/138310.html" target=_blank>用C</A>LOB。</TD></TR>
<TR>
<TD>ROW&amp;<BR>LONG ROW <BR></TD>
<TD>n&lt;=255 </TD>
<TD>CHAR(n) FOR BIT DATA<BR>VARCHAR(n) FOR BIT DATA <BR>BLOB(n) l </TD>
<TD>若n&lt;=32K, 则使用CHAR(n) FOR BIT DATA 或<BR>VARCHAR(n) FOR BIT DATA <BR>l 若n&lt;=2GB, 则使用BLOB(n)</TD></TR>
<TR>
<TD>BLOB <BR></TD>
<TD>n&lt;=4GB </TD>
<TD>BLOB(n) </TD>
<TD>若n&lt;=2GB, 则使用BLOB(n)</TD></TR>
<TR>
<TD>CLOB <BR></TD>
<TD>n&lt;=4GB </TD>
<TD>CLOB(n) </TD>
<TD>若n&lt;=2GB, 则使用CLOB(n)</TD></TR>
<TR>
<TD>NCLOB <BR></TD>
<TD>n&lt;=4GB </TD>
<TD>DBCLOB(n) </TD>
<TD>若n&lt;=2GB, 则使用DBCLOB(n/2)</TD></TR>
<TR>
<TD>NUMBER </TD>
<TD>&nbsp;</TD>
<TD>SMALLINT/INTEGER/BIGINT<BR><BR>DECIMAL(p,s)/NUMBER(p,s)<BR><BR>Float(n)/ REAL/DOUBLE l </TD>
<TD>若Oracle中定义NUMBER(p) 或 NUMBER(p,s), 则使用SAMLLINT/INTEGER/BIGINT<BR>l 若Oracle中定义NUMBER(p,s), 则使用DECIMAL(p,s)<BR>l 若Oracle中定义NUMBER,则使用FLOAT(n)/REAL/DOUBLE<BR></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE></P>
<P>　　NUMBER</P>
<P>　　Oracle中的NUMBER类型可以对应DB2/400中的很多类型，这种对应关系要依赖于Oracle中number将用于保存何种类型的数据，是整型还是带有小数位的实型数据，另外还要考虑类型所占用的存储空间，例如:SAMLLINT占2 byte, INTEGER占4 byte; BIGINT占8 byte;Oracle中的NUMBER类型所占用的存储空间要根据它的定义而定，例如缺省精度下的NUMBER有38个数字长，占用20 byte的空间。具体的对应关系请参照上表。</P>
<P>　　ROW and LOB类型</P>
<P>　　DB2/400提供VARCHAR和CLOB与ORACLE中的RAW和LONG RAW相对应。ORACLE也支持大对象:BLOB、CLOB、CLOB和NCLOB, ORACLE中的BLOB和CLOB可以提供4GB的空间，而DB2/400中的BLOB和CLOB只能存放2GB的数据;DB2/400中的DBCLOB与ORACLE中的NCLOB 2GB相对应。Oracle 中的BFILE数据类型用于管理数据库以外的二进制数据，数据库中的表将指向数据库外部的存放的BFILE文件，DB2/400也提供一个类似的数据类型DATALINK相对应。</P>
<P>　　ROWID</P>
<P>　　Oracle ROWIND虚拟列用于对表中的某一列进行唯一标示，DB2/400中也有这样的数据类型ROWID，它与ORACLE中的ROWID的功能相似。DB2/400中的ROWID可以存放40 byte的数据用来唯一标示表中的每一行，它没有ccsid属性，这些信息中没有关于datafile、 block 或 row的信息。</P>
<P>　　例如:</P>
<P>　　CREATE TABLE LIBRARYNAME/ORDERS2 (ORDERNO ROWID, SHIPPED_TO VARCHAR (36) , ORDER_DATE DATE) ORDERNO的数据类型为ROWID，用于存放订单号，每当插入一行时，系统自动生成一个值，存放进这个字段。可以用OPERATION NAVIGATOR查看它的内容。</P>
<P>　　Character type</P>
<P>　　DB2/400的CHAR、VARCHAR类型与ORACLE中的VARCHAR2(n)类型相对应，但是ORACLE中的VARCHAR2(n)类型仅用于存放较小的字符串，这里的n小于4000，因此在这种情况下，最好用定长的CHAR(N)类型与ORACLE的VARCHAR2(n)相对应，这样不仅可以提高效率，还可以节省存储空间，若使用VARCHAR(n)类型最好用ALLOCATE参数，这样可以提<A class=bluekey href="http://www.yesky.com/key/290/140290.html" target=_blank>高数</A>据库的性能,它可以减少内存和硬盘之间的输入/输出操作。</P>
<P>　　要注意DB2/400中的字符串中文输入问题，要想在DB2/400上输入中文应用这样的<A class=bluekey href="http://www.yesky.com/key/4661/134661.html" target=_blank>SQL</A>创建表，这里的CCSID 935,代表简体中文。</P>
<P>　　例如:</P>
<P>　　CREATE TABLE MYLIB/ZHONG (HANZI CHAR (30 ) CCSID 935 NOT NULL WITHDEFAULT)</P><img src ="http://www.blogjava.net/TrampEagle/aggbug/34689.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-03-10 16:18 <a href="http://www.blogjava.net/TrampEagle/archive/2006/03/10/34689.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Beehive入门</title><link>http://www.blogjava.net/TrampEagle/archive/2006/03/05/33758.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Sun, 05 Mar 2006 14:35:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/archive/2006/03/05/33758.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/33758.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/archive/2006/03/05/33758.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/33758.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/33758.html</trackback:ping><description><![CDATA[转自：<A href="http://dev2dev.bea.com.cn/bbs/school/index.jsp">http://dev2dev.bea.com.cn/bbs/school/index.jsp</A><BR><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD bgColor=#d9d9d9 colSpan=11><BR>
<TABLE cellSpacing=0 cellPadding=3 width="100%" border=0>
<TBODY>
<TR>
<TD>作者：肖菁（dev2devID: powerise）, dev2dev版主</TD></TR></TBODY></TABLE></TD></TR>
<TR>
<TD bgColor=#d9d9d9 colSpan=11><IMG height=5 alt="" src="file:///E:/web/bea/WebLogic%20Workshop.files/spacer.gif" width=4 border=0></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width="100%" align=center bgColor=#cccccc border=0>
<TBODY>
<TR>
<TD>
<TABLE cellSpacing=1 cellPadding=4 width="100%" bgColor=#cccccc border=0>
<TBODY>
<TR bgColor=white>
<TD vAlign=top width="99%">　　作者在本文中简单的介绍了Beehive项目，分析了Beehive项目正常运行所需要的软件、环境，最后给出了在Tomcat容器上安装、部署Beehive下载包中提供的实例的完整步骤和相关编译指令。 
<P><B>关键词： Beehive 简介 安装 部署</B></P>
<P><B>1 Beehive简介<BR>1.1 Beehive是什么？</B><BR>　　BEA将WebLogic Server 运行时框架的部分实现提取出来，经过修改后提交给Apache，形成了今天的Beehive。<BR>　　Beehive体现了BEA公司在WebLogic8.1中加入的几个特性：JPF（Java Page Flow）、简化的资源访问方式—控件、简化的Web服务开发，这些特性能够让J2EE的编程更加简单；更重要的是，Beehive的运行已经不再依赖于WebLogic Server环境了，而是能够在绝大多数支持Servlet/JSP 2.3的容器中运行，这让J2EE的应用开发商能够提供基于Beehive、同时可以在J2EE平台间无缝移植的应用。<BR><BR><B>1.2 Beehive的组成</B><BR>　　Beehive包括了三部分的内容，分别是：Page Flow、Controls和Web服务。</P>
<P><B>　　1. Page Flow</B><BR>　　基于Struts的一种MVC框架，使用JSR-175解决了Struts配置文件的自动更新，也使Struts开发更容易用IDE工具实现，同时提供了一些更易于使用的特性：</P>
<UL>
<LI><I>状态管理</I><BR>一个JPF中的所有页面和处理动作都被看成是一个会话，JPF中声明的变量在各个页面均可访问，包括JavaBean。<BR>[注] 当用户离开页面流时，会话状态中存储的数据将自动释放以便更有效地使用会话数据。所以多个JPF之间是不能共享会话数据的。 
<LI><I>丰富的数据绑定功能</I><BR>Page Flow中提供了一组标签库来实现数据和JSP页面的显示标记之间的绑定，让显示界面编程更加容易。 
<LI><I>集中的异常处理</I><BR>可以在一个JPF中处理异常，也可以在Globel.app跨越一组页面流处理异常信息。很显然，我们可以很容易为整个应用程序集中的进行异常处理，让程序更具灵活性——比如常见的登录超时信息、权限信息等，传统的方式你应该在每个页面进行判断，现在你可以在入口处进行判断就可以了。 </LI></UL>
<P><B>　　2. Controls</B><BR>　　创建轻量级J2EE组件的一种框架，您可以不用编写大量代码就完成以下常见工作：</P>
<UL>
<LI>壹用于创建访问资源如数据库、本地文件、Web服务等资源的组件。 
<LI>用于封装企业可重用的业务逻辑等。 </LI></UL>
<P><B>　　3. Web服务</B><BR>　　JSR-181的一种实现，支持基于Javadoc 批注的Web服务开发方式。<BR><BR><B>2 开始工作</B><BR>　　作者的操作系统是Windows2000，下面所有步骤的讲解也是针对Windows2000，如果您使用其它操作系统，需要根据下面的步骤做一些相应的调整，如果需要帮助，请通过email：guilaida@163.com和作者取得联系。<BR><BR><B>　　2.1 工具下载</B><BR>　　1. Servlet容器—Tomcat5.5.8<BR>　　为了演示Beehive的可移植性，这里的Servlet不选择WebLogic Server，而是选择了Apache的另一个开源项目—Tomcat5.5.8，后面的例子也主要以Tomcat5.5.8为例来讲解，如果你使用其他的Servlet容器或者Tomcat的其他版本，请按照您的环境进行相应的调整。<BR>　　Tomcat的下载站点：<A href="http://jakarta.apache.org/site/binindex.cgi" target=_blank>http://jakarta.apache.org/site/binindex.cgi</A></P>
<P><B>　　</B>2. JDK1.5.X<BR><B>　　</B>因为Tomcat5.X是基于JDK1.5.X而开发的，如果使用JDK其他版本需要另外做些改动，所以为了简单起见，建议您下载JDK的1.5或者以上版本，我的JDK版本号是1.5.0_01。</P>
<P><B>　　</B>3. Beehive<BR><B>　　</B>Beehive的最新版本是1.0，可以到http://incubator.apache.org/beehive去下载安装包。</P>
<P><B>　　</B>4. Ant<BR><B>　　</B>Beehive环境准备、程序编译采用了Ant作为build工具，所以请到http://ant.apache.org下载Ant的最新版本，作者使用的版本是1.6<BR>[注] 这点应该感谢BEA，他们大大降低了这项工作的复杂度。<BR><BR><B>　　2.2 软件安装</B><BR><B>　　</B>1. 安装JDK<BR><B>　　</B>后面将用%Java_home%来引用JDK的安装目录，如作者安装在C:\jdk150，那么%Java_home%就表示C:\jdk150这个目录。</P>
<P><B>　　</B>2. 安装Tomcat<BR><B>　　</B>将下载的Tomcat压缩包解压缩到某个目录如C:\Tomcat5.5.8下，后面的文章中将用%Tomcat_Home%来表示这个目录。</P>
<P><B>　　</B>3. 解压缩Beehive<BR><B>　　</B>将下载的Beehive压缩包解压缩到某个目录如C:\beehive1.0，后面将用%Beehive_Home%来表示这个目录。</P>
<P><B>　　</B>4. 安装Ant<BR><B>　　</B>将下载的Ant压缩包解压缩到某个目录如C:\ Ant1.6，后面将用%Ant_Home%来表示这个目录。<BR><BR><B>　　2.3 让Beehive的例子跑起来</B><BR><B>　　</B>Beehive中包括了三方面的内容：Page Flow、Controls和Web Services。Beehive下载包中带了8个例子，分别演示了各种不同情况，这些例子都位于%Beehive_Home%\ samples目录中。我们这里先选择其中的三个简单的例子-- netui-blank、controls-blank、wsm-blank，分别代表了这三个方向，现在我们开始工作，让这些例子先跑起来。<BR><B>　　2.3.1 环境设置</B><BR><B>　　</B>1. 修改%Beehive_Home%\beehiveUser.cmd<BR><B>　　</B>将其中的BEEHIVE_HOME、 JAVA_HOME、 ANT_HOME、CATALINA_HOME等变量设置为各软件各自对应的安装目录。<BR><B>　　</B>下面是作者的beehiveUser.cmd文件的内容：<BR><BR>@echo off<BR>REM<BR>REM Customize this file based on where you install various 3rd party components<BR>REM such as the JDK, Ant and Tomcat.<BR>REM</P>
<P>REM the root of Beehive distribution<BR>set BEEHIVE_HOME=c:\beehive1.0</P>
<P>REM location of a JDK<BR>set JAVA_HOME=C:\JDK150</P>
<P>REM location of Ant<BR>set ANT_HOME=Ant1.6</P>
<P>REM location of Tomcat<BR>set CATALINA_HOME=C:\tomcat5.5.8</P>
<P>set PATH=%PATH%;%JAVA_HOME%\bin;%ANT_HOME%\bin</P>
<P><B>　　</B>2. 修改Tomcat的权限设置<BR><B>　　</B>为了让Tomcat能够实时部署，需要修改Tomcat的权限设置，主要是修改%Tomcat_Home%\conf\ tomcat-users.xml文件，增加一个manager角色，同时增加一个属于manager的用户，作者是将tomcat这个用户的权限扩大为manager角色了，下面是修改过的tomcat-users.xml：<BR><BR>&lt;?xml version='1.0' encoding='utf-8'?&gt;<BR>&lt;tomcat-users&gt;<BR>&lt;role rolename="tomcat"/&gt;<BR>&lt;role rolename="role1"/&gt;<BR>&lt;role rolename="manager"/&gt;<BR>&lt;user username="tomcat" password="tomcat" roles="tomcat,manager"/&gt;<BR>&lt;user username="role1" password="tomcat" roles="role1"/&gt;<BR>&lt;user username="both" password="tomcat" roles="tomcat,role1"/&gt;<BR>&lt;/tomcat-users&gt;<BR><BR><B>　　</B>关于Tomcat权限设置，更多的资料请参考Tomcat的帮助文档。<A id=232 name=232></A><BR><BR><B>2.3.2 让PageFlow的例子跑起来</B><BR><B>　　</B>1. 打开一个Dos窗口，进入%Beehive_Home%目录<BR><B>　　</B>2. 执行beehiveUser.cmd，设置相关环境变量<BR><B>　　</B>3. 准备JPF运行所必须的jar文件和标签声明文件(.tld)<BR><B>　　</B>执行ant -f ant\webappRuntimeCore.xml -Dwebapp.dir=%Beehive_home%\samples\netui-blank deploy.beehive.webapp.runtime<BR><B>　　</B>[注] webapp.dir是你的JPF应用的目录，必须是全路径，不支持相对路径<BR><B>　　</B>4. 编译JPF应用<BR><B>　　</B>执行ant -f %BEEHIVE_HOME%\ant\buildWebapp.xml -Dwebapp.dir=%Beehive_home%\samples\netui-blank build.webapp</P>
<P><B>　　</B>如果编译成功，你就可以开始下面的步骤了，如果编译没有成功，你需要检查步骤1~4。<BR><B>　　</B>5. 启动Tomcat<BR><B>　　</B>6. 打开一个浏览器<BR><B>　　</B>在地址栏输入：http://localhost:8080/manager/deploy?path=/pageflow&amp;war=file:C:\beehive1.0\samples\netui-blank&amp;update=true，如果发布成功，浏览器中应该输出OK - Deployed application at context path /pageflow。<BR><B>　　</B>[注] </P>
<UL>
<LI>path=/pageflow中的pageflow是你的web应用使用的上下文路径 
<LI>C:\beehive1.0\samples\netui-blank请替换为你的Beehive中netui_blank例子的绝对路径 </LI></UL>
<P><B>　　</B>7. 测试一下<BR><B>　　</B>在浏览器中输入http://localhost:8080/pageflow，如果浏览器中输出如下信息表示你的Page Flow的例子已经可以运行了：New Web Application Page。<BR><BR><B>2.3.3 让Control的例子跑起来</B><BR><B>　　</B>前面的步骤1～2和<A href="http://dev2dev.bea.com.cn/bbs/school/guide/wlworkshop/2005041801.html#232">2.3.2</A>节中的步骤保持一致，后面的步骤变动如下：<BR><B>　　</B>3. 准备支持Control运行相关的jar文件和标签声明文件(.tld)<BR><B>　　</B>执行ant -f ant\webappRuntimeCore.xml -Dwebapp.dir=%Beehive_home%\samples\controls-blank deploy.beehive.webapp.runtime<BR><B>　　</B>4. 编译控件<BR><B>　　</B>执行ant -f %Beehive_Home%\samples\controls-blank\build.xml build命令编译controls-blank项目中的控件。<BR><B>　　</B>例子中的控件很简单，只有一个方法hello，作用是返回字符串“hello!”<BR><B>　　</B>5. 将控件部署到controls-blank环境中<BR><B>　　</B>执行命令： copy %beehive_home%\samples\controls-blank\build\mycontrols.jar %beehive_home%\samples\controls-blank\web-inf\lib，将步骤5中生成的jar文件发布到controls-blank应用的lib目录下。<BR><B>　　</B>6. 启动Tomcat<BR><B>　　</B>7. 建立一个jsp文件<BR>在%beehive_home%\samples\controls-blank目录下创建一helloworld.jsp文件，文件内容如下：<BR><BR>&lt;%@ page language="java" contentType="text/html;charset=gb2312"%&gt;<BR>&lt;%@ taglib uri="http://beehive.apache.org/netui/tags-databinding-1.0" prefix="netui-data"%&gt;<BR>&lt;%@ taglib uri="http://beehive.apache.org/netui/tags-html-1.0" prefix="netui"%&gt;<BR>&lt;%@ taglib uri="http://beehive.apache.org/netui/tags-template-1.0" prefix="netui-template"%&gt;<BR>&lt;netui:html&gt;<BR>&lt;head&gt;<BR>&lt;title&gt;Control演示实例&lt;/title&gt;<BR>&lt;netui:base/&gt;<BR>&lt;/head&gt;<BR>&lt;netui:body&gt;<BR>&lt;jsp:useBean class="pkg.HelloBean" id="helloBean" scope="session"/&gt;<BR>&lt;h3&gt;Control演示实例--测试页&lt;/h3&gt;<BR>&lt;p&gt;<BR>调用演示用Control的hello方法的返回内容是: &lt;strong&gt; &lt;%= helloBean.hello() %&gt; &lt;/strong&gt;<BR>&lt;/p&gt;<BR>&lt;/netui:body&gt;<BR>&lt;/netui:html&gt;<BR><BR><B>　　</B>8. 发布Web应用<BR><B>　　</B>应用上下文路径设定为control，相应的在浏览器内输入如下地址：<BR>http://localhost:8080/manager/deploy?path=/control&amp;war=file:C:\beehive1.0\samples\controls-blank&amp;update=true<BR><B>　　</B>9. 测试一下<BR><B>　　</B>在浏览器内输入：http://localhost:8080/control/helloworld.jsp，如果环境配置成功，浏览器内的返回信息应该如下图所示：<BR><BR><B>2.3.4 让Web服务的例子跑起来</B><BR><B>　　</B>前面的步骤1～2和2.3.2节中的步骤保持一致，后面的步骤变动如下：<BR><B>　　</B>3. 准备Web服务运行所必须的jar文件和标签声明文件(.tld)<BR>执行ant -f ant\webappRuntimeCore.xml -Dwebapp.dir=%Beehive_home%\samples\wsm-blank deploy.wsm.webapp.runtime<BR><B>　　</B>4. 编译演示用的Web 服务<BR>执行ant -f %BEEHIVE_HOME%\ant\buildWebapp.xml -Dwebapp.dir=%Beehive_home%\samples\wsm-blank build.webapp命令编译wsm-blank项目中的web服务。<BR>例子中的Web服务比较简单，只对外发布了一个不带参数的方法sayHelloWorld，返回字符串“Hello world!”。<BR><B>　　</B>5. 启动Tomcat<BR><B>　　</B>6. 发布Web应用<BR><B>　　</B>应用上下文路径设定为wsm，相应的在浏览器内输入如下地址：<BR>http://localhost:8080/manager/deploy?path=/control&amp;war=file:C:\beehive1.0\samples\wsm-blank&amp;update=true<BR><B>　　</B>7. 测试一下<BR><B>　　</B>在浏览器内输入：http://localhost:8080/wsm/ ，如果环境配置成功，浏览器内的返回信息应该如下图所示：</P>
<P align=center><IMG height=188 src="file:///E:/web/bea/WebLogic%20Workshop.files/image2005041801.gif" width=639></P>
<P><B>　　</B>访问其中的连接“sayHelloWorld”，可以看到访问该Web服务后的SOAP返回消息：</P>
<P align=center><IMG height=98 src="file:///E:/web/bea/WebLogic%20Workshop.files/image2005041802.gif" width=580></P>
<P><B>3 总结</B><BR><B>　　</B>本文的内容集中在三个方面：Beehive项目简介、项目正常运行需要的软件和环境准备以及如果让beehive提供的例子在Tomcat平台上运行的步骤和相关使用命令。<BR><B>　　</B>通过本文的介绍，你应该可以根据不同应用的需要，创建符合要求的Web开发环境，同时能够熟练的编译、部署自己的Web应用。<BR><B>　　</B>下一篇文章将是《页面流入门》，作者将简单分析页面流（JPF）框架的运行机制，还通过具体的例子简单的演示了页面流框架下如何应用标签开发用户界面、实现页面导航、处理表单提交等基本功能。</P>
<P><B>作者简介：</B><BR>姓名：肖菁<BR>E_mail：<A href="mailto:guilaida@163.com">guilaida@163.com</A><BR><B>　　</B>软件工程师，buildfiledesign项目设计师（buildfiledesign.sourceforge.net），Vivianj技术交流站站长（guilaida.go.nease.net），致力于研究J2EE编程技术、Web Service技术以及他们在 WebSphere、WebLogic、 Apache平台上的实现。<BR></P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/TrampEagle/aggbug/33758.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-03-05 22:35 <a href="http://www.blogjava.net/TrampEagle/archive/2006/03/05/33758.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>oracle数据库开发的一些经验积累</title><link>http://www.blogjava.net/TrampEagle/archive/2006/03/01/33020.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Wed, 01 Mar 2006 08:02:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/archive/2006/03/01/33020.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/33020.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/archive/2006/03/01/33020.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/33020.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/33020.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 原文引自：http://www.mikecat.net/blogview.asp?logID=11621、不安装Oracle客户连接Oracle&nbsp;8的方法&nbsp;请将以下文件拷贝到运行文件所在目录&nbsp;一、ODBC动态库&nbsp;:ctl3d32.dll&nbsp;msvcrt40.dll&nbsp;odbc16gt.dll&nbsp;odbc32.dll&nbsp;odbc...&nbsp;&nbsp;<a href='http://www.blogjava.net/TrampEagle/archive/2006/03/01/33020.html'>阅读全文</a><img src ="http://www.blogjava.net/TrampEagle/aggbug/33020.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-03-01 16:02 <a href="http://www.blogjava.net/TrampEagle/archive/2006/03/01/33020.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ORACLE SQL性能优化(十二)</title><link>http://www.blogjava.net/TrampEagle/archive/2006/02/25/32391.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Sat, 25 Feb 2006 03:46:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/archive/2006/02/25/32391.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/32391.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/archive/2006/02/25/32391.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/32391.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/32391.html</trackback:ping><description><![CDATA[引自：<A href="http://www.dbonline.cn/source/oracle/20031218/oracle%20SQL%20performance%20tuning12.html">http://www.dbonline.cn/source/oracle/20031218/oracle%20SQL%20performance%20tuning12.html</A><BR><BR>
<TABLE cellSpacing=0 cellPadding=0>
<TBODY>
<TR>
<TD class=font14 align=middle>
<DIV class="style34 style35" align=center><STRONG>ORACLE SQL性能优化系列 (十二) </STRONG></DIV></TD></TR>
<TR>
<TD class=font12 align=middle>
<DIV class=style36 align=center><BR>作者: black_snail</DIV></TD></TR>
<TR>
<TD class=font12></TD></TR>
<TR>
<TD class=font12 align=middle>
<TABLE cellSpacing=0 cellPadding=0>
<TBODY>
<TR>
<TD>
<P class=style35>&nbsp;</P>
<P class=style35>关键字 ORACLE PERFORMANCE TUNING PL/SQL <BR><BR>出处 http://www.dbasupport.com <BR><BR><BR><BR>39. 总是使用索引的第一个列 <BR><BR>如果索引是建立在多个列上, 只有在它的第一个列(leading column)被where子句引用时,优化器才会选择使用该索引. <BR><BR><BR>译者按: <BR><BR>这也是一条简单而重要的规则. 见以下实例. <BR><BR><BR>SQL&gt; create table multiindexusage ( inda number , indb number , descr varchar2(10)); <BR><BR>Table created. <BR><BR>SQL&gt; create index multindex on multiindexusage(inda,indb); <BR><BR>Index created. <BR><BR>SQL&gt; set autotrace traceonly <BR><BR><BR>SQL&gt; select * from multiindexusage where inda = 1; <BR><BR>Execution Plan <BR><BR>---------------------------------------------------------- <BR><BR>0 SELECT STATEMENT Optimizer=CHOOSE <BR><BR>1 0 TABLE ACCESS (BY INDEX ROWID) OF 'MULTIINDEXUSAGE' <BR><BR>2 1 INDEX (RANGE SCAN) OF 'MULTINDEX' (NON-UNIQUE) <BR><BR><BR>SQL&gt; select * from multiindexusage where indb = 1; <BR><BR>Execution Plan <BR><BR>---------------------------------------------------------- <BR><BR>0 SELECT STATEMENT Optimizer=CHOOSE <BR><BR>1 0 TABLE ACCESS (FULL) OF 'MULTIINDEXUSAGE' <BR><BR><BR>很明显, 当仅引用索引的第二个列时,优化器使用了全表扫描而忽略了索引 <BR><BR><BR><BR>40. ORACLE内部操作 <BR><BR>当执行查询时,ORACLE采用了内部的操作. 下表显示了几种重要的内部操作. <BR><BR>ORACLE Clause <BR>内部操作 <BR><BR>ORDER BY <BR>SORT ORDER BY <BR><BR>UNION <BR>UNION-ALL <BR><BR>MINUS <BR>MINUS <BR><BR>INTERSECT <BR>INTERSECT <BR><BR>DISTINCT,MINUS,INTERSECT,UNION <BR>SORT UNIQUE <BR><BR>MIN,MAX,COUNT <BR>SORT AGGREGATE <BR><BR>GROUP BY <BR>SORT GROUP BY <BR><BR>ROWNUM <BR>COUNT or COUNT STOPKEY <BR><BR>Queries involving Joins <BR>SORT JOIN,MERGE JOIN,NESTED LOOPS <BR><BR>CONNECT BY <BR>CONNECT BY <BR><BR><BR><BR><BR>41. 用UNION-ALL 替换UNION ( 如果有可能的话) <BR><BR><BR>当SQL语句需要UNION两个查询结果集合时,这两个结果集合会以UNION-ALL的方式被合并, 然后在输出最终结果前进行排序. <BR><BR>如果用UNION ALL替代UNION, 这样排序就不是必要了. 效率就会因此得到提高. <BR><BR><BR>举例: <BR><BR>低效： <BR><BR>　　　　 SELECT ACCT_NUM, BALANCE_AMT <BR><BR>FROM DEBIT_TRANSACTIONS <BR><BR>WHERE TRAN_DATE = '31-DEC-95' <BR><BR>UNION <BR><BR>SELECT ACCT_NUM, BALANCE_AMT <BR><BR>FROM DEBIT_TRANSACTIONS <BR><BR>WHERE TRAN_DATE = '31-DEC-95' <BR><BR>高效: <BR><BR>SELECT ACCT_NUM, BALANCE_AMT <BR><BR>FROM DEBIT_TRANSACTIONS <BR><BR>WHERE TRAN_DATE = '31-DEC-95' <BR><BR>UNION ALL <BR><BR>SELECT ACCT_NUM, BALANCE_AMT <BR><BR>FROM DEBIT_TRANSACTIONS <BR><BR>WHERE TRAN_DATE = '31-DEC-95' <BR><BR><BR>译者按: <BR><BR>需要注意的是，UNION ALL 将重复输出两个结果集合中相同记录. 因此各位还是 <BR><BR>要从业务需求分析使用UNION ALL的可行性. <BR><BR>UNION 将对结果集合排序,这个操作会使用到SORT_AREA_SIZE这块内存. 对于这 <BR><BR>块内存的优化也是相当重要的. 下面的SQL可以用来查询排序的消耗量 <BR><BR><BR>Select substr(name,1,25) "Sort Area Name", <BR><BR>substr(value,1,15) "Value" <BR><BR>from v$sysstat <BR><BR>where name like 'sort%' <BR><BR><BR><BR>42. 使用提示(Hints) <BR><BR>对于表的访问,可以使用两种Hints. <BR><BR>FULL 和 ROWID <BR><BR><BR>FULL hint 告诉ORACLE使用全表扫描的方式访问指定表. <BR><BR>例如: <BR><BR>SELECT /*+ FULL(EMP) */ * <BR><BR>FROM EMP <BR><BR>WHERE EMPNO = 7893; <BR><BR><BR>ROWID hint 告诉ORACLE使用TABLE ACCESS BY ROWID的操作访问表. <BR><BR><BR>通常, 你需要采用TABLE ACCESS BY ROWID的方式特别是当访问大表的时候, 使用这种方式, 你需要知道ROIWD的值或者使用索引. <BR><BR>如果一个大表没有被设定为缓存(CACHED)表而你希望它的数据在查询结束是仍然停留 <BR><BR>在SGA中,你就可以使用CACHE hint 来告诉优化器把数据保留在SGA中. 通常CACHE hint 和 FULL hint 一起使用. <BR><BR>例如: <BR><BR>SELECT /*+ FULL(WORKER) CACHE(WORKER)*/ * <BR><BR>FROM WORK; <BR><BR><BR>索引hint 告诉ORACLE使用基于索引的扫描方式. 你不必说明具体的索引名称 <BR><BR>例如: <BR><BR>SELECT /*+ INDEX(LODGING) */ LODGING <BR><BR>FROM LODGING <BR><BR>WHERE MANAGER = ‘BILL GATES'; <BR><BR><BR>在不使用hint的情况下, 以上的查询应该也会使用索引,然而,如果该索引的重复值过多而你的优化器是CBO, 优化器就可能忽略索引. 在这种情况下, 你可以用INDEX hint强制ORACLE使用该索引. <BR><BR><BR>ORACLE hints 还包括ALL_ROWS, FIRST_ROWS, RULE,USE_NL, USE_MERGE, USE_HASH 等等. <BR><BR><BR>译者按: <BR><BR>使用hint , 表示我们对ORACLE优化器缺省的执行路径不满意,需要手工修改. <BR><BR>这是一个很有技巧性的工作. 我建议只针对特定的,少数的SQL进行hint的优化. <BR><BR>对ORACLE的优化器还是要有信心(特别是CBO)&nbsp;<BR><BR><BR><BR>&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;&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;<A HREF="/TrampEagle/archive/2006/02/25/32390.html">《上一页》</A>&nbsp; </P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/TrampEagle/aggbug/32391.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-25 11:46 <a href="http://www.blogjava.net/TrampEagle/archive/2006/02/25/32391.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ORACLE SQL性能优化(十) </title><link>http://www.blogjava.net/TrampEagle/archive/2006/02/25/32389.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Sat, 25 Feb 2006 03:45:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/archive/2006/02/25/32389.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/32389.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/archive/2006/02/25/32389.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/32389.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/32389.html</trackback:ping><description><![CDATA[引自：<A href="http://www.dbonline.cn/source/oracle/20031218/oracle%20SQL%20performance%20tuning10.html">http://www.dbonline.cn/source/oracle/20031218/oracle%20SQL%20performance%20tuning10.html</A><BR><BR>
<TABLE cellSpacing=0 cellPadding=0>
<TBODY>
<TR>
<TD class=font14 align=middle>
<DIV class="style34 style35" align=center><STRONG>ORACLE SQL性能优化系列 (十) </STRONG></DIV></TD></TR>
<TR>
<TD class="style35 style36" align=middle>
<DIV align=center><BR>作者: black_snail</DIV></TD></TR>
<TR>
<TD class=font12></TD></TR>
<TR>
<TD class=font12 align=middle>
<TABLE cellSpacing=0 cellPadding=0>
<TBODY>
<TR>
<TD>
<P class=style35>&nbsp;</P>
<P class=style35>关键字 ORACLE PERFORMANCE TUNING PL/SQL <BR><BR>出处 http://WWW.DBASUPPORT.COM <BR><BR><BR><BR>31. 强制索引失效 <BR><BR><BR>如果两个或以上索引具有相同的等级,你可以强制命令ORACLE优化器使用其中的一个(通过它,检索出的记录数量少) . <BR><BR><BR>举例: <BR><BR><BR>SELECT ENAME <BR><BR>FROM EMP <BR><BR>WHERE EMPNO = 7935 <BR><BR>AND DEPTNO + 0 = 10 /*DEPTNO上的索引将失效*/ <BR><BR>AND EMP_TYPE || ‘' = ‘A' /*EMP_TYPE上的索引将失效*/ <BR><BR><BR>这是一种相当直接的提高查询效率的办法. 但是你必须谨慎考虑这种策略,一般来说,只有在你希望单独优化几个SQL时才能采用它. <BR><BR><BR>这里有一个例子关于何时采用这种策略, <BR><BR><BR>假设在EMP表的EMP_TYPE列上有一个非唯一性的索引而EMP_CLASS上没有索引. <BR><BR><BR>SELECT ENAME <BR><BR>FROM EMP <BR><BR>WHERE EMP_TYPE = ‘A' <BR><BR>AND EMP_CLASS = ‘X'; <BR><BR><BR>优化器会注意到EMP_TYPE上的索引并使用它. 这是目前唯一的选择. 如果,一段时间以后, 另一个非唯一性建立在EMP_CLASS上,优化器必须对两个索引进行选择,在通常情况下,优化器将使用两个索引并在他们的结果集合上执行排序及合并. 然而,如果其中一个索引（EMP_TYPE）接近于唯一性而另一个索引（EMP_CLASS）上有几千个重复的值. 排序及合并就会成为一种不必要的负担. 在这种情况下,你希望使优化器屏蔽掉EMP_CLASS索引. <BR><BR>用下面的方案就可以解决问题. <BR><BR>SELECT ENAME <BR><BR>FROM EMP <BR><BR>WHERE EMP_TYPE = ‘A' <BR><BR>AND EMP_CLASS||'' = ‘X'; <BR><BR><BR><BR>32. 避免在索引列上使用计算． <BR><BR>WHERE子句中，如果索引列是函数的一部分．优化器将不使用索引而使用全表扫描． <BR><BR><BR>举例: <BR><BR><BR>低效： <BR><BR>SELECT … <BR><BR>FROM DEPT <BR><BR>WHERE SAL * 12 &gt; 25000; <BR><BR><BR>高效: <BR><BR>SELECT … <BR><BR>FROM DEPT <BR><BR>WHERE SAL &gt; 25000/12; <BR><BR><BR>译者按: <BR><BR>这是一个非常实用的规则，请务必牢记 <BR><BR><BR><BR>33. 自动选择索引 <BR><BR>如果表中有两个以上（包括两个）索引，其中有一个唯一性索引，而其他是非唯一性． <BR><BR>在这种情况下，ORACLE将使用唯一性索引而完全忽略非唯一性索引． <BR><BR><BR>举例: <BR><BR>SELECT ENAME <BR><BR>FROM EMP <BR><BR>WHERE EMPNO = 2326 <BR><BR>AND DEPTNO = 20 ; <BR><BR><BR>这里，只有EMPNO上的索引是唯一性的，所以EMPNO索引将用来检索记录． <BR><BR>TABLE ACCESS BY ROWID ON EMP <BR><BR>INDEX UNIQUE SCAN ON EMP_NO_IDX <BR><BR><BR><BR>34. 避免在索引列上使用NOT <BR><BR>通常，　我们要避免在索引列上使用NOT, NOT会产生在和在索引列上使用函数相同的 <BR><BR>影响. 当ORACLE”遇到”NOT,他就会停止使用索引转而执行全表扫描. <BR><BR>举例: <BR><BR><BR>低效: (这里,不使用索引) <BR><BR><BR>SELECT … <BR><BR>FROM DEPT <BR><BR>WHERE DEPT_CODE NOT = 0; <BR><BR><BR>高效: (这里,使用了索引) <BR><BR><BR>SELECT … <BR><BR>FROM DEPT <BR><BR>WHERE DEPT_CODE &gt; 0; <BR><BR><BR>需要注意的是,在某些时候, ORACLE优化器会自动将NOT转化成相对应的关系操作符. <BR><BR>NOT &gt; to &lt;= <BR><BR>NOT &gt;= to &lt; <BR><BR>NOT &lt; to &gt;= <BR><BR>NOT &lt;= to &gt; <BR><BR><BR><BR>译者按: <BR><BR>在这个例子中,作者犯了一些错误. 例子中的低效率SQL是不能被执行的. <BR><BR>我做了一些测试: <BR><BR><BR>SQL&gt; select * from emp where NOT empno &gt; 1; <BR><BR>no rows selected <BR><BR>Execution Plan <BR><BR>---------------------------------------------------------- <BR><BR>0 SELECT STATEMENT Optimizer=CHOOSE <BR><BR>1 0 TABLE ACCESS (BY INDEX ROWID) OF 'EMP' <BR><BR>2 1 INDEX (RANGE SCAN) OF 'EMPNO' (UNIQUE) <BR><BR><BR>SQL&gt; select * from emp where empno &lt;= 1; <BR><BR>no rows selected <BR><BR>Execution Plan <BR><BR>---------------------------------------------------------- <BR><BR>0 SELECT STATEMENT Optimizer=CHOOSE <BR><BR>1 0 TABLE ACCESS (BY INDEX ROWID) OF 'EMP' <BR><BR>2 1 INDEX (RANGE SCAN) OF 'EMPNO' (UNIQUE) <BR><BR><BR>两者的效率完全一样，也许这符合作者关于” 在某些时候, ORACLE优化器会自动将NOT转化成相对应的关系操作符” 的观点． <BR><BR><BR><BR>35. 用&gt;=替代&gt; <BR><BR><BR>如果DEPTNO上有一个索引, <BR><BR><BR>高效: <BR><BR><BR>SELECT * <BR><BR>FROM EMP <BR><BR>WHERE DEPTNO &gt;=4 <BR><BR><BR>低效: <BR><BR><BR>SELECT * <BR><BR>FROM EMP <BR><BR>WHERE DEPTNO &gt;3 <BR><BR><BR>两者的区别在于, 前者DBMS将直接跳到第一个DEPT等于4的记录而后者将首先定位到DEPTNO=3的记录并且向前扫描到第一个DEPT大于3的记录. <BR><BR>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <A HREF="/TrampEagle/archive/2006/02/25/32388.html">《上一页》</A>&nbsp; |&nbsp; <A HREF="/TrampEagle/archive/2006/02/25/32390.html">《下一页》</A></P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/TrampEagle/aggbug/32389.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-25 11:45 <a href="http://www.blogjava.net/TrampEagle/archive/2006/02/25/32389.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ORACLE SQL性能优化(十一) </title><link>http://www.blogjava.net/TrampEagle/archive/2006/02/25/32390.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Sat, 25 Feb 2006 03:45:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/archive/2006/02/25/32390.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/32390.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/archive/2006/02/25/32390.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/32390.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/32390.html</trackback:ping><description><![CDATA[引自：<A href="http://www.dbonline.cn/source/oracle/20031218/oracle%20SQL%20performance%20tuning11.html">http://www.dbonline.cn/source/oracle/20031218/oracle%20SQL%20performance%20tuning11.html</A><BR><BR>
<TABLE cellSpacing=0 cellPadding=0>
<TBODY>
<TR>
<TD class=font14 align=middle>
<DIV class="style34 style35" align=center><STRONG>ORACLE SQL性能优化系列 (十一) </STRONG></DIV></TD></TR>
<TR>
<TD class="font12 style35 style36" align=middle>
<DIV align=center><BR>作者: black_snail</DIV></TD></TR>
<TR>
<TD class=font12></TD></TR>
<TR>
<TD class=font12 align=middle>
<TABLE cellSpacing=0 cellPadding=0>
<TBODY>
<TR>
<TD>
<P class=style35>&nbsp;</P>
<P class=style35>关键字 ORACLE PERFORMANCE PL/SQL TUNING <BR><BR>出处 http://www.dbasupport.com <BR><BR><BR><BR>36. 用UNION替换OR (适用于索引列) <BR><BR>通常情况下, 用UNION替换WHERE子句中的OR将会起到较好的效果. 对索引列使用OR将造成全表扫描. 注意, 以上规则只针对多个索引列有效. 如果有column没有被索引, 查询效率可能会因为你没有选择OR而降低. <BR><BR>在下面的例子中, LOC_ID 和REGION上都建有索引. <BR><BR>高效: <BR><BR>SELECT LOC_ID , LOC_DESC , REGION <BR><BR>FROM LOCATION <BR><BR>WHERE LOC_ID = 10 <BR><BR>UNION <BR><BR>SELECT LOC_ID , LOC_DESC , REGION <BR><BR>FROM LOCATION <BR><BR>WHERE REGION = “MELBOURNE” <BR><BR><BR>低效: <BR><BR>SELECT LOC_ID , LOC_DESC , REGION <BR><BR>FROM LOCATION <BR><BR>WHERE LOC_ID = 10 OR REGION = “MELBOURNE” <BR><BR><BR>如果你坚持要用OR, 那就需要返回记录最少的索引列写在最前面. <BR><BR><BR>注意: <BR><BR><BR>WHERE KEY1 = 10 (返回最少记录) <BR><BR>OR KEY2 = 20 (返回最多记录) <BR><BR><BR>ORACLE 内部将以上转换为 <BR><BR>WHERE KEY1 = 10 AND <BR><BR>((NOT KEY1 = 10) AND KEY2 = 20) <BR><BR><BR>译者按: <BR><BR><BR>下面的测试数据仅供参考: (a = 1003 返回一条记录 , b = 1 返回1003条记录) <BR><BR>SQL&gt; select * from unionvsor /*1st test*/ <BR><BR>2 where a = 1003 or b = 1; <BR><BR>1003 rows selected. <BR><BR>Execution Plan <BR><BR>---------------------------------------------------------- <BR><BR>0 SELECT STATEMENT Optimizer=CHOOSE <BR><BR>1 0 CONCATENATION <BR><BR>2 1 TABLE ACCESS (BY INDEX ROWID) OF 'UNIONVSOR' <BR><BR>3 2 INDEX (RANGE SCAN) OF 'UB' (NON-UNIQUE) <BR><BR>4 1 TABLE ACCESS (BY INDEX ROWID) OF 'UNIONVSOR' <BR><BR>5 4 INDEX (RANGE SCAN) OF 'UA' (NON-UNIQUE) <BR><BR>Statistics <BR><BR>---------------------------------------------------------- <BR><BR>0 recursive calls <BR><BR>0 db block gets <BR><BR>144 consistent gets <BR><BR>0 physical reads <BR><BR>0 redo size <BR><BR>63749 bytes sent via SQL*Net to client <BR><BR>7751 bytes received via SQL*Net from client <BR><BR>68 SQL*Net roundtrips to/from client <BR><BR>0 sorts (memory) <BR><BR>0 sorts (disk) <BR><BR>1003 rows processed <BR><BR>SQL&gt; select * from unionvsor /*2nd test*/ <BR><BR>2 where b = 1 or a = 1003 ; <BR><BR>1003 rows selected. <BR><BR>Execution Plan <BR><BR>---------------------------------------------------------- <BR><BR>0 SELECT STATEMENT Optimizer=CHOOSE <BR><BR>1 0 CONCATENATION <BR><BR>2 1 TABLE ACCESS (BY INDEX ROWID) OF 'UNIONVSOR' <BR><BR>3 2 INDEX (RANGE SCAN) OF 'UA' (NON-UNIQUE) <BR><BR>4 1 TABLE ACCESS (BY INDEX ROWID) OF 'UNIONVSOR' <BR><BR>5 4 INDEX (RANGE SCAN) OF 'UB' (NON-UNIQUE) <BR><BR>Statistics <BR><BR>---------------------------------------------------------- <BR><BR>0 recursive calls <BR><BR>0 db block gets <BR><BR>143 consistent gets <BR><BR>0 physical reads <BR><BR>0 redo size <BR><BR>63749 bytes sent via SQL*Net to client <BR><BR>7751 bytes received via SQL*Net from client <BR><BR>68 SQL*Net roundtrips to/from client <BR><BR>0 sorts (memory) <BR><BR>0 sorts (disk) <BR><BR>1003 rows processed <BR><BR><BR><BR>SQL&gt; select * from unionvsor /*3rd test*/ <BR><BR>2 where a = 1003 <BR><BR>3 union <BR><BR>4 select * from unionvsor <BR><BR>5 where b = 1; <BR><BR>1003 rows selected. <BR><BR>Execution Plan <BR><BR>---------------------------------------------------------- <BR><BR>0 SELECT STATEMENT Optimizer=CHOOSE <BR><BR>1 0 SORT (UNIQUE) <BR><BR>2 1 UNION-ALL <BR><BR>3 2 TABLE ACCESS (BY INDEX ROWID) OF 'UNIONVSOR' <BR><BR>4 3 INDEX (RANGE SCAN) OF 'UA' (NON-UNIQUE) <BR><BR>5 2 TABLE ACCESS (BY INDEX ROWID) OF 'UNIONVSOR' <BR><BR>6 5 INDEX (RANGE SCAN) OF 'UB' (NON-UNIQUE) <BR><BR>Statistics <BR><BR>---------------------------------------------------------- <BR><BR>0 recursive calls <BR><BR>0 db block gets <BR><BR>10 consistent gets <BR><BR>0 physical reads <BR><BR>0 redo size <BR><BR>63735 bytes sent via SQL*Net to client <BR><BR>7751 bytes received via SQL*Net from client <BR><BR>68 SQL*Net roundtrips to/from client <BR><BR>1 sorts (memory) <BR><BR>0 sorts (disk) <BR><BR>1003 rows processed <BR><BR>用UNION的效果可以从consistent gets和 SQL*NET的数据交换量的减少看出 <BR><BR><BR><BR>37. 用IN来替换OR <BR><BR><BR>下面的查询可以被更有效率的语句替换: <BR><BR><BR>低效: <BR><BR><BR>SELECT…. <BR><BR>FROM LOCATION <BR><BR>WHERE LOC_ID = 10 <BR><BR>OR LOC_ID = 20 <BR><BR>OR LOC_ID = 30 <BR><BR><BR>高效 <BR><BR>SELECT… <BR><BR>FROM LOCATION <BR><BR>WHERE LOC_IN IN (10,20,30); <BR><BR><BR>译者按: <BR><BR>这是一条简单易记的规则，但是实际的执行效果还须检验，在ORACLE8i下，两者的执行路径似乎是相同的．　 <BR><BR><BR><BR>38. 避免在索引列上使用IS NULL和IS NOT NULL <BR><BR>避免在索引中使用任何可以为空的列，ORACLE将无法使用该索引 ．对于单列索引，如果列包含空值，索引中将不存在此记录. 对于复合索引，如果每个列都为空，索引中同样不存在此记录.　如果至少有一个列不为空，则记录存在于索引中． <BR><BR>举例: <BR><BR>如果唯一性索引建立在表的A列和B列上, 并且表中存在一条记录的A,B值为(123,null) , ORACLE将不接受下一条具有相同A,B值（123,null）的记录(插入). 然而如果 <BR><BR>所有的索引列都为空，ORACLE将认为整个键值为空而空不等于空. 因此你可以插入1000 <BR><BR>条具有相同键值的记录,当然它们都是空! <BR><BR><BR>因为空值不存在于索引列中,所以WHERE子句中对索引列进行空值比较将使ORACLE停用该索引. <BR><BR>举例: <BR><BR><BR>低效: (索引失效) <BR><BR>SELECT … <BR><BR>FROM DEPARTMENT <BR><BR>WHERE DEPT_CODE IS NOT NULL; <BR><BR><BR>高效: (索引有效) <BR><BR>SELECT … <BR><BR>FROM DEPARTMENT <BR><BR>WHERE DEPT_CODE &gt;=0; <BR><BR>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <A HREF="/TrampEagle/archive/2006/02/25/32389.html">《上一页》</A>&nbsp; |&nbsp; <A HREF="/TrampEagle/archive/2006/02/25/32391.html">《下一页》</A></P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/TrampEagle/aggbug/32390.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-25 11:45 <a href="http://www.blogjava.net/TrampEagle/archive/2006/02/25/32390.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ORACLE SQL性能优化(九)</title><link>http://www.blogjava.net/TrampEagle/archive/2006/02/25/32388.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Sat, 25 Feb 2006 03:44:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/archive/2006/02/25/32388.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/32388.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/archive/2006/02/25/32388.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/32388.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/32388.html</trackback:ping><description><![CDATA[引自：<A href="http://www.dbonline.cn/source/oracle/20031218/oracle%20SQL%20performance%20tuning9.html">http://www.dbonline.cn/source/oracle/20031218/oracle%20SQL%20performance%20tuning9.html</A><BR><BR>
<TABLE cellSpacing=0 cellPadding=0>
<TBODY>
<TR>
<TD class=font14 align=middle>
<DIV class="style34 style35" align=center><STRONG>ORACLE SQL性能优化系列 (九) </STRONG></DIV></TD></TR>
<TR>
<TD class=font12 align=middle>
<DIV class=style36 align=center><BR>作者: black_snail</DIV></TD></TR>
<TR>
<TD class=font12></TD></TR>
<TR>
<TD class=font12 align=middle>
<TABLE cellSpacing=0 cellPadding=0>
<TBODY>
<TR>
<TD>
<P class=style35>&nbsp;</P>
<P class=style35>关键字 ORACLE PL/SQL TUNING PERFORMANCE <BR><BR>出处 http://www.dbasupport.com <BR><BR><BR><BR>27. 基础表的选择 <BR><BR><BR>基础表(Driving Table)是指被最先访问的表(通常以全表扫描的方式被访问). 根据优化器的不同, SQL语句中基础表的选择是不一样的. <BR><BR>如果你使用的是CBO (COST BASED OPTIMIZER),优化器会检查SQL语句中的每个表的物理大小,索引的状态,然后选用花费最低的执行路径. <BR><BR>如果你用RBO (RULE BASED OPTIMIZER) , 并且所有的连接条件都有索引对应, 在这种情况下, 基础表就是FROM 子句中列在最后的那个表. <BR><BR>举例: <BR><BR>SELECT A.NAME , B.MANAGER <BR><BR>FROM　WORKER A, <BR><BR>LODGING B <BR><BR>WHERE　A.LODGING = B.LODING; <BR><BR>由于LODGING表的LODING列上有一个索引, 而且WORKER表中没有相比较的索引, WORKER表将被作为查询中的基础表. <BR><BR><BR><BR>28. 多个平等的索引 <BR><BR>当SQL语句的执行路径可以使用分布在多个表上的多个索引时, ORACLE会同时使用多个索引并在运行时对它们的记录进行合并, 检索出仅对全部索引有效的记录. <BR><BR>在ORACLE选择执行路径时,唯一性索引的等级高于非唯一性索引. 然而这个规则只有 <BR><BR>当WHERE子句中索引列和常量比较才有效.如果索引列和其他表的索引类相比较. 这种子句在优化器中的等级是非常低的. <BR><BR>如果不同表中两个想同等级的索引将被引用, FROM子句中表的顺序将决定哪个会被率先使用. FROM子句中最后的表的索引将有最高的优先级. <BR><BR>如果相同表中两个想同等级的索引将被引用, WHERE子句中最先被引用的索引将有最高的优先级. <BR><BR>举例: <BR><BR>DEPTNO上有一个非唯一性索引,EMP_CAT也有一个非唯一性索引. <BR><BR>SELECT ENAME, <BR><BR>FROM EMP <BR><BR>WHERE DEPT_NO = 20 <BR><BR>AND EMP_CAT = ‘A'; <BR><BR>这里,DEPTNO索引将被最先检索,然后同EMP_CAT索引检索出的记录进行合并. 执行路径如下: <BR><BR><BR><BR>TABLE ACCESS BY ROWID ON EMP <BR><BR>AND-EQUAL <BR><BR>INDEX RANGE SCAN ON DEPT_IDX <BR><BR>INDEX RANGE SCAN ON CAT_IDX <BR><BR><BR><BR>29. 等式比较和范围比较 <BR><BR>当WHERE子句中有索引列, ORACLE不能合并它们,ORACLE将用范围比较. <BR><BR><BR>举例: <BR><BR>DEPTNO上有一个非唯一性索引,EMP_CAT也有一个非唯一性索引. <BR><BR>SELECT ENAME <BR><BR>FROM EMP <BR><BR>WHERE DEPTNO &gt; 20 <BR><BR>AND EMP_CAT = ‘A'; <BR><BR><BR><BR>这里只有EMP_CAT索引被用到,然后所有的记录将逐条与DEPTNO条件进行比较. 执行路径如下: <BR><BR>TABLE ACCESS BY ROWID ON EMP <BR><BR>INDEX RANGE SCAN ON CAT_IDX <BR><BR><BR><BR>30. 不明确的索引等级 <BR><BR><BR>当ORACLE无法判断索引的等级高低差别,优化器将只使用一个索引,它就是在WHERE子句中被列在最前面的. <BR><BR>举例: <BR><BR>DEPTNO上有一个非唯一性索引,EMP_CAT也有一个非唯一性索引. <BR><BR><BR>SELECT ENAME <BR><BR>FROM EMP <BR><BR>WHERE DEPTNO &gt; 20 <BR><BR>AND EMP_CAT &gt; ‘A'; <BR><BR><BR>这里, ORACLE只用到了DEPT_NO索引. 执行路径如下: <BR><BR><BR>TABLE ACCESS BY ROWID ON EMP <BR><BR>INDEX RANGE SCAN ON DEPT_IDX <BR><BR><BR>译者按: <BR><BR>我们来试一下以下这种情况: <BR><BR>SQL&gt; select index_name, uniqueness from user_indexes where table_name = 'EMP'; <BR><BR><BR>INDEX_NAME UNIQUENES <BR><BR>------------------------------ --------- <BR><BR>EMPNO UNIQUE <BR><BR>EMPTYPE NONUNIQUE <BR><BR><BR>SQL&gt; select * from emp where empno &gt;= 2 and emp_type = 'A' ; <BR><BR><BR>no rows selected <BR><BR><BR><BR>Execution Plan <BR><BR>---------------------------------------------------------- <BR><BR>0 SELECT STATEMENT Optimizer=CHOOSE <BR><BR>1 0 TABLE ACCESS (BY INDEX ROWID) OF 'EMP' <BR><BR>2 1 INDEX (RANGE SCAN) OF 'EMPTYPE' (NON-UNIQUE) <BR><BR><BR>虽然EMPNO是唯一性索引,但是由于它所做的是范围比较, 等级要比非唯一性索引的等式比较低! <BR><BR>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <A HREF="/TrampEagle/archive/2006/02/25/32387.html">《上一页》</A>&nbsp; |&nbsp; <A HREF="/TrampEagle/archive/2006/02/25/32389.html">《下一页》</A></P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/TrampEagle/aggbug/32388.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-25 11:44 <a href="http://www.blogjava.net/TrampEagle/archive/2006/02/25/32388.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ORACLE SQL性能优化(八)</title><link>http://www.blogjava.net/TrampEagle/archive/2006/02/25/32387.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Sat, 25 Feb 2006 03:43:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/archive/2006/02/25/32387.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/32387.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/archive/2006/02/25/32387.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/32387.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/32387.html</trackback:ping><description><![CDATA[引自：<A href="http://www.dbonline.cn/source/oracle/20031218/oracle%20SQL%20performance%20tuning8.html">http://www.dbonline.cn/source/oracle/20031218/oracle%20SQL%20performance%20tuning8.html</A><BR><BR>
<TABLE cellSpacing=0 cellPadding=0>
<TBODY>
<TR>
<TD class=font14 align=middle>
<DIV class=style35 align=center>ORACLE SQL性能优化系列 (八) </DIV></TD></TR>
<TR>
<TD class=font12 align=middle>
<DIV class=style36 align=center><BR>作者: black_snail</DIV></TD></TR>
<TR>
<TD class=font12></TD></TR>
<TR>
<TD class=font12 align=middle>
<TABLE cellSpacing=0 cellPadding=0>
<TBODY>
<TR>
<TD>
<P class=style37>&nbsp;</P>
<P class=style37>关键字 ORACLE SQL PERFORMANCE TUNING <BR><BR>出处 http://www.dbasupport.com <BR><BR><BR><BR>25. 用索引提高效率 <BR><BR><BR>索引是表的一个概念部分,用来提高检索数据的效率. 实际上,ORACLE使用了一个复杂的自平衡B-tree结构. 通常,通过索引查询数据比全表扫描要快. 当ORACLE找出执行查询和Update语句的最佳路径时, ORACLE优化器将使用索引. 同样在联结多个表时使用索引也可以提高效率. 另一个使用索引的好处是,它提供了主键(primary key)的唯一性验证. <BR><BR>除了那些LONG或LONG RAW数据类型, 你可以索引几乎所有的列. 通常, 在大型表中使用索引特别有效. 当然,你也会发现, 在扫描小表时,使用索引同样能提高效率. <BR><BR>虽然使用索引能得到查询效率的提高,但是我们也必须注意到它的代价. 索引需要空间来 <BR><BR>存储,也需要定期维护, 每当有记录在表中增减或索引列被修改时, 索引本身也会被修改. 这意味着每条记录的INSERT , DELETE , UPDATE将为此多付出4 , 5 次的磁盘I/O . 因为索引需要额外的存储空间和处理,那些不必要的索引反而会使查询反应时间变慢. <BR><BR><BR>译者按: <BR><BR>定期的重构索引是有必要的. <BR><BR>ALTER INDEX &lt;INDEXNAME&gt; REBUILD &lt;TABLESPACENAME&gt; <BR><BR><BR><BR>26. 索引的操作 <BR><BR><BR>ORACLE对索引有两种访问模式. <BR><BR><BR>索引唯一扫描 ( INDEX UNIQUE SCAN) <BR><BR><BR>大多数情况下, 优化器通过WHERE子句访问INDEX. <BR><BR><BR>例如: <BR><BR>表LODGING有两个索引 : 建立在LODGING列上的唯一性索引LODGING_PK和建立在MANAGER列上的非唯一性索引LODGING$MANAGER. <BR><BR><BR><BR>SELECT * <BR><BR>FROM LODGING <BR><BR>WHERE LODGING = ‘ROSE HILL'; <BR><BR><BR>在内部 , 上述SQL将被分成两步执行, 首先 , LODGING_PK 索引将通过索引唯一扫描的方式被访问 , 获得相对应的ROWID, 通过ROWID访问表的方式 执行下一步检索. <BR><BR>如果被检索返回的列包括在INDEX列中,ORACLE将不执行第二步的处理(通过ROWID访问表). 因为检索数据保存在索引中, 单单访问索引就可以完全满足查询结果. <BR><BR>下面SQL只需要INDEX UNIQUE SCAN 操作. <BR><BR><BR>SELECT LODGING <BR><BR>FROM LODGING <BR><BR>WHERE LODGING = ‘ROSE HILL'; <BR><BR><BR>索引范围查询(INDEX RANGE SCAN) <BR><BR>适用于两种情况: <BR><BR>1. 基于一个范围的检索 <BR><BR>2. 基于非唯一性索引的检索 <BR><BR><BR>例1: <BR><BR><BR>SELECT LODGING <BR><BR>FROM LODGING <BR><BR>WHERE LODGING LIKE ‘M%'; <BR><BR><BR>WHERE子句条件包括一系列值, ORACLE将通过索引范围查询的方式查询LODGING_PK . 由于索引范围查询将返回一组值, 它的效率就要比索引唯一扫描 <BR><BR>低一些. <BR><BR><BR>例2: <BR><BR><BR>SELECT LODGING <BR><BR>FROM LODGING <BR><BR>WHERE MANAGER = ‘BILL GATES'; <BR><BR><BR>这个SQL的执行分两步, LODGING$MANAGER的索引范围查询(得到所有符合条件记录的ROWID) 和下一步同过ROWID访问表得到LODGING列的值. 由于LODGING$MANAGER是一个非唯一性的索引,数据库不能对它执行索引唯一扫描. <BR><BR><BR>由于SQL返回LODGING列,而它并不存在于LODGING$MANAGER索引中, 所以在索引范围查询后会执行一个通过ROWID访问表的操作. <BR><BR><BR>WHERE子句中, 如果索引列所对应的值的第一个字符由通配符(WILDCARD)开始, 索引将不被采用. <BR><BR><BR>SELECT LODGING <BR><BR>FROM LODGING <BR><BR>WHERE MANAGER LIKE ‘％HANMAN'; <BR><BR><BR>在这种情况下，ORACLE将使用全表扫描. <BR><BR><BR>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <A HREF="/TrampEagle/archive/2006/02/25/32386.html">《上一页》</A>&nbsp; |&nbsp; <A HREF="/TrampEagle/archive/2006/02/25/32388.html">《下一页》</A></P></TD></TR></TBODY></TABLE><BR><BR></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/TrampEagle/aggbug/32387.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-25 11:43 <a href="http://www.blogjava.net/TrampEagle/archive/2006/02/25/32387.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ORACLE SQL性能优化(七) </title><link>http://www.blogjava.net/TrampEagle/archive/2006/02/25/32386.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Sat, 25 Feb 2006 03:42:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/archive/2006/02/25/32386.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/32386.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/archive/2006/02/25/32386.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/32386.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/32386.html</trackback:ping><description><![CDATA[引自：<A href="http://www.dbonline.cn/source/oracle/20031218/oracle%20SQL%20performance%20tuning7.html">http://www.dbonline.cn/source/oracle/20031218/oracle%20SQL%20performance%20tuning7.html</A><BR><BR>
<TABLE cellSpacing=0 cellPadding=0>
<TBODY>
<TR>
<TD class=font14 align=middle>
<DIV class="style34 style35" align=center><STRONG>ORACLE SQL性能优化系列 (七 ) </STRONG></DIV></TD></TR>
<TR>
<TD class=font12 align=middle>
<DIV class=style36 align=center><BR>作者: black_snail</DIV></TD></TR>
<TR>
<TD class=font12></TD></TR>
<TR>
<TD class=font12 align=middle>
<TABLE cellSpacing=0 cellPadding=0>
<TBODY>
<TR>
<TD>
<P class=style35>&nbsp;</P>
<P class=style35>24. 用EXPLAIN PLAN 分析SQL语句 <BR><BR><BR>EXPLAIN PLAN 是一个很好的分析SQL语句的工具,它甚至可以在不执行SQL的情况下分析语句. 通过分析,我们就可以知道ORACLE是怎么样连接表,使用什么方式扫描表(索引扫描或全表扫描)以及使用到的索引名称. <BR><BR>你需要按照从里到外,从上到下的次序解读分析的结果. EXPLAIN PLAN分析的结果是用缩进的格式排列的, 最内部的操作将被最先解读, 如果两个操作处于同一层中,带有最小操作号的将被首先执行. <BR><BR>NESTED LOOP是少数不按照上述规则处理的操作, 正确的执行路径是检查对NESTED LOOP提供数据的操作,其中操作号最小的将被最先处理. <BR><BR><BR>译者按: <BR><BR>通过实践, 感到还是用SQLPLUS中的SET TRACE 功能比较方便. <BR><BR>举例: <BR><BR><BR>SQL&gt; list <BR><BR>1 SELECT * <BR><BR>2 FROM dept, emp <BR><BR>3* WHERE emp.deptno = dept.deptno <BR><BR>SQL&gt; set autotrace traceonly /*traceonly 可以不显示执行结果*/ <BR><BR>SQL&gt; / <BR><BR>14 rows selected. <BR><BR>Execution Plan <BR><BR>---------------------------------------------------------- <BR><BR>0 SELECT STATEMENT Optimizer=CHOOSE <BR><BR>1 0 NESTED LOOPS <BR><BR>2 1 TABLE ACCESS (FULL) OF 'EMP' <BR><BR>3 1 TABLE ACCESS (BY INDEX ROWID) OF 'DEPT' <BR><BR>4 3 INDEX (UNIQUE SCAN) OF 'PK_DEPT' (UNIQUE) <BR><BR><BR>Statistics <BR><BR>---------------------------------------------------------- <BR><BR>0 recursive calls <BR><BR>2 db block gets <BR><BR>30 consistent gets <BR><BR>0 physical reads <BR><BR>0 redo size <BR><BR>2598 bytes sent via SQL*Net to client <BR><BR>503 bytes received via SQL*Net from client <BR><BR>2 SQL*Net roundtrips to/from client <BR><BR>0 sorts (memory) <BR><BR>0 sorts (disk) <BR><BR>14 rows processed <BR><BR><BR>通过以上分析,可以得出实际的执行步骤是: <BR><BR>1. TABLE ACCESS (FULL) OF 'EMP' <BR><BR>2. INDEX (UNIQUE SCAN) OF 'PK_DEPT' (UNIQUE) <BR><BR>3. TABLE ACCESS (BY INDEX ROWID) OF 'DEPT' <BR><BR>4. NESTED LOOPS (JOINING 1 AND 3) <BR><BR><BR><BR>注: 目前许多第三方的工具如TOAD和ORACLE本身提供的工具如OMS的SQL Analyze都提供了极其方便的EXPLAIN PLAN工具.也许喜欢图形化界面的朋友们可以选用它们. </P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="/TrampEagle/archive/2006/02/25/32385.html"> 《上一页》</A>&nbsp; |&nbsp; <A HREF="/TrampEagle/archive/2006/02/25/32387.html">《下一页》</A><img src ="http://www.blogjava.net/TrampEagle/aggbug/32386.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-25 11:42 <a href="http://www.blogjava.net/TrampEagle/archive/2006/02/25/32386.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ORACLE SQL性能优化(六) </title><link>http://www.blogjava.net/TrampEagle/archive/2006/02/25/32385.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Sat, 25 Feb 2006 03:41:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/archive/2006/02/25/32385.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/32385.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/archive/2006/02/25/32385.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/32385.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/32385.html</trackback:ping><description><![CDATA[引自：<A href="http://www.dbonline.cn/source/oracle/20031218/oracle%20SQL%20performance%20tuning6.html">http://www.dbonline.cn/source/oracle/20031218/oracle%20SQL%20performance%20tuning6.html</A><BR><BR>
<TABLE cellSpacing=0 cellPadding=0>
<TBODY>
<TR>
<TD class=font14 align=middle>
<DIV class="style34 style35" align=center><STRONG>ORACLE SQL性能优化系列 (六) </STRONG></DIV></TD></TR>
<TR>
<TD class=font12 align=middle>
<DIV class=style36 align=center><BR>作者: black_snail</DIV></TD></TR>
<TR>
<TD class=font12></TD></TR>
<TR>
<TD class=font12 align=middle>
<TABLE cellSpacing=0 cellPadding=0>
<TBODY>
<TR>
<TD height=1893>
<P>&nbsp;</P>
<P class=style35>关键字 ORACLE PERFORMANCE TUNING SQL <BR><BR>出处 http://www.dbasupport.com <BR><BR><BR><BR>20. 用表连接替换EXISTS <BR><BR><BR>通常来说 , 采用表连接的方式比EXISTS更有效率 <BR><BR>SELECT ENAME <BR><BR>FROM EMP E <BR><BR>WHERE EXISTS (SELECT ‘X' <BR><BR>FROM DEPT <BR><BR>WHERE DEPT_NO = E.DEPT_NO <BR><BR>AND DEPT_CAT = ‘A'); <BR><BR><BR>(更高效) <BR><BR>SELECT ENAME <BR><BR>FROM DEPT D,EMP E <BR><BR>WHERE E.DEPT_NO = D.DEPT_NO <BR><BR>AND DEPT_CAT = ‘A' ; <BR><BR><BR>(译者按: 在RBO的情况下,前者的执行路径包括FILTER,后者使用NESTED LOOP) <BR><BR><BR><BR>21. 用EXISTS替换DISTINCT <BR><BR>当提交一个包含一对多表信息(比如部门表和雇员表)的查询时,避免在SELECT子句中使用DISTINCT. 一般可以考虑用EXIST替换 <BR><BR><BR>例如: <BR><BR>低效: <BR><BR>SELECT DISTINCT DEPT_NO,DEPT_NAME <BR><BR>FROM DEPT D,EMP E <BR><BR>WHERE D.DEPT_NO = E.DEPT_NO <BR><BR>高效: <BR><BR>SELECT DEPT_NO,DEPT_NAME <BR><BR>FROM DEPT D <BR><BR>WHERE EXISTS ( SELECT ‘X' <BR><BR>FROM EMP E <BR><BR>WHERE E.DEPT_NO = D.DEPT_NO); <BR><BR><BR>EXISTS 使查询更为迅速,因为RDBMS核心模块将在子查询的条件一旦满足后,立刻返回结果. <BR><BR><BR><BR>22. 识别'低效执行'的SQL语句 <BR><BR><BR>用下列SQL工具找出低效SQL: <BR><BR>SELECT EXECUTIONS , DISK_READS, BUFFER_GETS, <BR><BR>ROUND((BUFFER_GETS-DISK_READS)/BUFFER_GETS,2) Hit_radio, <BR><BR>ROUND(DISK_READS/EXECUTIONS,2) Reads_per_run, <BR><BR>SQL_TEXT <BR><BR>FROM V$SQLAREA <BR><BR>WHERE EXECUTIONS&gt;0 <BR><BR>AND BUFFER_GETS &gt; 0 <BR><BR>AND (BUFFER_GETS-DISK_READS)/BUFFER_GETS &lt; 0.8 <BR><BR>ORDER BY 4 DESC; <BR><BR><BR>(译者按: 虽然目前各种关于SQL优化的图形化工具层出不穷,但是写出自己的SQL工具来解决问题始终是一个最好的方法) <BR><BR><BR><BR>23. 使用TKPROF 工具来查询SQL性能状态 <BR><BR><BR>SQL trace 工具收集正在执行的SQL的性能状态数据并记录到一个跟踪文件中. 这个跟踪文件提供了许多有用的信息,例如解析次数.执行次数,CPU使用时间等.这些数据将可以用来优化你的系统. <BR><BR><BR>设置SQL TRACE在会话级别: 有效 <BR><BR><BR>ALTER SESSION SET SQL_TRACE TRUE <BR><BR><BR>设置SQL TRACE 在整个数据库有效仿, 你必须将SQL_TRACE参数在init.ora中设为TRUE, USER_DUMP_DEST参数说明了生成跟踪文件的目录 <BR><BR><BR>(译者按: 这一节中,作者并没有提到TKPROF的用法, 对SQL TRACE的用法也不够准确, 设置SQL TRACE首先要在init.ora中设定TIMED_STATISTICS, 这样才能得到那些重要的时间状态. 生成的trace文件是不可读的,所以要用TKPROF工具对其进行转换,TKPROF有许多执行参数. 大家可以参考ORACLE手册来了解具体的配置. ) <BR><BR>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <A HREF="/TrampEagle/archive/2006/02/25/32384.html">《上一页》</A>&nbsp; |&nbsp; <A HREF="/TrampEagle/archive/2006/02/25/32386.html">《下一页》</A></P></TD></TR></TBODY></TABLE><BR></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/TrampEagle/aggbug/32385.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-25 11:41 <a href="http://www.blogjava.net/TrampEagle/archive/2006/02/25/32385.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ORACLE SQL性能优化(五) </title><link>http://www.blogjava.net/TrampEagle/archive/2006/02/25/32384.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Sat, 25 Feb 2006 03:40:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/archive/2006/02/25/32384.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/32384.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/archive/2006/02/25/32384.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/32384.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/32384.html</trackback:ping><description><![CDATA[引自：<A href="http://www.dbonline.cn/source/oracle/20031218/oracle%20SQL%20performance%20tuning5.html">http://www.dbonline.cn/source/oracle/20031218/oracle%20SQL%20performance%20tuning5.html</A><BR><BR>
<TABLE cellSpacing=0 cellPadding=0>
<TBODY>
<TR>
<TD class=font14 align=middle>
<DIV class="style34 style35" align=center><STRONG>ORACLE SQL性能优化系列 (五) </STRONG></DIV></TD></TR>
<TR>
<TD class=font12 align=middle>
<DIV class=style36 align=center><BR>作者: black_snail</DIV></TD></TR>
<TR>
<TD class=font12></TD></TR>
<TR>
<TD class=font12 align=middle>
<TABLE cellSpacing=0 cellPadding=0>
<TBODY>
<TR>
<TD>
<P class=style35>&nbsp;</P>
<P class=style35>关键字 ORACLE PERFORMANCE SQL TUNING <BR><BR>出处 http://WWW.DBASUPPORT.COM <BR><BR><BR><BR>17. 使用表的别名(Alias) <BR><BR>当在SQL语句中连接多个表时, 请使用表的别名并把别名前缀于每个Column上.这样一来,就可以减少解析的时间并减少那些由Column歧义引起的语法错误. <BR><BR><BR>(译者注: Column歧义指的是由于SQL中不同的表具有相同的Column名,当SQL语句中出现这个Column时,SQL解析器无法判断这个Column的归属) <BR><BR><BR>18. 用EXISTS替代IN <BR><BR>在许多基于基础表的查询中,为了满足一个条件,往往需要对另一个表进行联接.在这种情况下, 使用EXISTS(或NOT EXISTS)通常将提高查询的效率. <BR><BR><BR>低效: <BR><BR>SELECT * <BR><BR>FROM EMP (基础表) <BR><BR>WHERE EMPNO &gt; 0 <BR><BR>AND DEPTNO IN (SELECT DEPTNO <BR><BR>FROM DEPT <BR><BR>WHERE LOC = ‘MELB') <BR><BR><BR>高效: <BR><BR>SELECT * <BR><BR>FROM EMP (基础表) <BR><BR>WHERE EMPNO &gt; 0 <BR><BR>AND EXISTS (SELECT ‘X' <BR><BR>FROM DEPT <BR><BR>WHERE DEPT.DEPTNO = EMP.DEPTNO <BR><BR>AND LOC = ‘MELB') <BR><BR><BR>(译者按: 相对来说,用NOT EXISTS替换NOT IN 将更显著地提高效率,下一节中将指出) <BR><BR><BR><BR>19. 用NOT EXISTS替代NOT IN <BR><BR>在子查询中,NOT IN子句将执行一个内部的排序和合并. 无论在哪种情况下,NOT IN都是最低效的 (因为它对子查询中的表执行了一个全表遍历). 为了避免使用NOT IN ,我们可以把它改写成外连接(Outer Joins)或NOT EXISTS. <BR><BR><BR>例如: <BR><BR>SELECT … <BR><BR>FROM EMP <BR><BR>WHERE DEPT_NO NOT IN (SELECT DEPT_NO <BR><BR>FROM DEPT <BR><BR>WHERE DEPT_CAT='A'); <BR><BR><BR>为了提高效率.改写为: <BR><BR><BR>(方法一: 高效) <BR><BR>SELECT …. <BR><BR>FROM EMP A,DEPT B <BR><BR>WHERE A.DEPT_NO = B.DEPT(+) <BR><BR>AND B.DEPT_NO IS NULL <BR><BR>AND B.DEPT_CAT(+) = ‘A' <BR><BR><BR><BR>(方法二: 最高效) <BR><BR>SELECT …. <BR><BR>FROM EMP E <BR><BR>WHERE NOT EXISTS (SELECT ‘X' <BR><BR>FROM DEPT D <BR><BR>WHERE D.DEPT_NO = E.DEPT_NO <BR><BR>AND DEPT_CAT = ‘A'); </P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <A HREF="/TrampEagle/archive/2006/02/25/32383.html">《上一页》</A>&nbsp; |&nbsp; <A HREF="/TrampEagle/archive/2006/02/25/32385.html">《下一页》</A><img src ="http://www.blogjava.net/TrampEagle/aggbug/32384.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-25 11:40 <a href="http://www.blogjava.net/TrampEagle/archive/2006/02/25/32384.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ORACLE SQL性能优化(四) </title><link>http://www.blogjava.net/TrampEagle/archive/2006/02/25/32383.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Sat, 25 Feb 2006 03:39:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/archive/2006/02/25/32383.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/32383.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/archive/2006/02/25/32383.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/32383.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/32383.html</trackback:ping><description><![CDATA[引自：<A href="http://www.dbonline.cn/source/oracle/20031218/oracle%20SQL%20performance%20tuning4.html">http://www.dbonline.cn/source/oracle/20031218/oracle%20SQL%20performance%20tuning4.html</A><BR><BR>
<TABLE cellSpacing=0 cellPadding=0>
<TBODY>
<TR>
<TD class=font14 align=middle>
<DIV class="style34 style35" align=center>ORACLE SQL性能优化系列 (四) </DIV></TD></TR>
<TR>
<TD class=font12 align=middle>
<DIV class=style36 align=center><BR>作者: black_snail</DIV></TD></TR>
<TR>
<TD class=font12></TD></TR>
<TR>
<TD class=font12 align=middle>
<TABLE cellSpacing=0 cellPadding=0>
<TBODY>
<TR>
<TD>
<P class=style37>&nbsp;</P>
<P class=style37>关键字 ORACLE SQL PERFORMANCE TUNING <BR><BR>出处 http://WWW.DBASUPPORT.COM <BR><BR><BR><BR>13. 计算记录条数 <BR><BR>和一般的观点相反, count(*) 比count(1)稍快 , 当然如果可以通过索引检索,对索引列的计数仍旧是最快的. 例如 COUNT(EMPNO) <BR><BR><BR>(译者按: 在CSDN论坛中,曾经对此有过相当热烈的讨论, 作者的观点并不十分准确,通过实际的测试,上述三种方法并没有显著的性能差别) <BR><BR><BR>14. 用Where子句替换HAVING子句 <BR><BR><BR>避免使用HAVING子句, HAVING 只会在检索出所有记录之后才对结果集进行过滤. 这个处理需要排序,总计等操作. 如果能通过WHERE子句限制记录的数目,那就能减少这方面的开销. <BR><BR><BR>例如: <BR><BR><BR>低效: <BR><BR>SELECT REGION，AVG(LOG_SIZE) <BR><BR>FROM LOCATION <BR><BR>GROUP BY REGION <BR><BR>HAVING REGION REGION != ‘SYDNEY' <BR><BR>AND REGION != ‘PERTH' <BR><BR><BR>高效 <BR><BR>SELECT REGION，AVG(LOG_SIZE) <BR><BR>FROM LOCATION <BR><BR>WHERE REGION REGION != ‘SYDNEY' <BR><BR>AND REGION != ‘PERTH' <BR><BR>GROUP BY REGION <BR><BR>(译者按: HAVING 中的条件一般用于对一些集合函数的比较,如COUNT() 等等. 除此而外,一般的条件应该写在WHERE子句中) <BR><BR><BR>15. 减少对表的查询 <BR><BR>在含有子查询的SQL语句中,要特别注意减少对表的查询. <BR><BR><BR>例如: <BR><BR>低效 <BR><BR>SELECT TAB_NAME <BR><BR>FROM TABLES <BR><BR>WHERE TAB_NAME = ( SELECT TAB_NAME <BR><BR>FROM TAB_COLUMNS <BR><BR>WHERE VERSION = 604) <BR><BR>AND　DB_VER= ( SELECT DB_VER <BR><BR>FROM TAB_COLUMNS <BR><BR>WHERE VERSION = 604) <BR><BR><BR>高效 <BR><BR>SELECT TAB_NAME <BR><BR>FROM TABLES <BR><BR>WHERE (TAB_NAME,DB_VER) <BR><BR>= ( SELECT TAB_NAME,DB_VER) <BR><BR>FROM TAB_COLUMNS <BR><BR>WHERE VERSION = 604) <BR><BR><BR>Update 多个Column 例子: <BR><BR>低效: <BR><BR>UPDATE EMP <BR><BR>SET EMP_CAT = (SELECT MAX(CATEGORY) FROM EMP_CATEGORIES), <BR><BR>SAL_RANGE = (SELECT MAX(SAL_RANGE) FROM EMP_CATEGORIES) <BR><BR>WHERE EMP_DEPT = 0020; <BR><BR><BR>高效: <BR><BR>UPDATE EMP <BR><BR>SET (EMP_CAT, SAL_RANGE) <BR><BR>= (SELECT MAX(CATEGORY) , MAX(SAL_RANGE) <BR><BR>FROM EMP_CATEGORIES) <BR><BR>WHERE EMP_DEPT = 0020; <BR><BR><BR><BR>16. 通过内部函数提高SQL效率. <BR><BR><BR>SELECT H.EMPNO,E.ENAME,H.HIST_TYPE,T.TYPE_DESC,COUNT(*) <BR><BR>FROM HISTORY_TYPE T,EMP E,EMP_HISTORY H <BR><BR>WHERE H.EMPNO = E.EMPNO <BR><BR>AND H.HIST_TYPE = T.HIST_TYPE <BR><BR>GROUP BY H.EMPNO,E.ENAME,H.HIST_TYPE,T.TYPE_DESC; <BR><BR><BR>通过调用下面的函数可以提高效率. <BR><BR>FUNCTION LOOKUP_HIST_TYPE(TYP IN NUMBER) RETURN VARCHAR2 <BR><BR>AS <BR><BR>TDESC VARCHAR2(30); <BR><BR>CURSOR C1 IS <BR><BR>SELECT TYPE_DESC <BR><BR>FROM HISTORY_TYPE <BR><BR>WHERE HIST_TYPE = TYP; <BR><BR>BEGIN <BR><BR>OPEN C1; <BR><BR>FETCH C1 INTO TDESC; <BR><BR>CLOSE C1; <BR><BR>RETURN (NVL(TDESC,'?')); <BR><BR>END; <BR><BR><BR>FUNCTION LOOKUP_EMP(EMP IN NUMBER) RETURN VARCHAR2 <BR><BR>AS <BR><BR>ENAME VARCHAR2(30); <BR><BR>CURSOR C1 IS <BR><BR>SELECT ENAME <BR><BR>FROM EMP <BR><BR>WHERE EMPNO=EMP; <BR><BR>BEGIN <BR><BR>OPEN C1; <BR><BR>FETCH C1 INTO ENAME; <BR><BR>CLOSE C1; <BR><BR>RETURN (NVL(ENAME,'?')); <BR><BR>END; <BR><BR><BR>SELECT H.EMPNO,LOOKUP_EMP(H.EMPNO), <BR><BR>H.HIST_TYPE,LOOKUP_HIST_TYPE(H.HIST_TYPE),COUNT(*) <BR><BR>FROM EMP_HISTORY H <BR><BR>GROUP BY H.EMPNO , H.HIST_TYPE; <BR><BR><BR>(译者按: 经常在论坛中看到如 '能不能用一个SQL写出….' 的贴子, 殊不知复杂的SQL往往牺牲了执行效率. 能够掌握上面的运用函数解决问题的方法在实际工作中是非常有意义的) <BR><BR><BR>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <A HREF="/TrampEagle/archive/2006/02/25/32382.html">《上一页》</A>&nbsp; |&nbsp; <A HREF="/TrampEagle/archive/2006/02/25/32384.html">《下一页》</A></P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/TrampEagle/aggbug/32383.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-25 11:39 <a href="http://www.blogjava.net/TrampEagle/archive/2006/02/25/32383.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>