﻿<?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-風向逆轉 - 就要爪哇-文章分类-DataBase</title><link>http://www.blogjava.net/iKingQu/category/8111.html</link><description>Java菜鸟升级中...</description><language>zh-cn</language><lastBuildDate>Tue, 27 Feb 2007 10:22:51 GMT</lastBuildDate><pubDate>Tue, 27 Feb 2007 10:22:51 GMT</pubDate><ttl>60</ttl><item><title>[收藏]数据库连接、设计以及备份技巧集锦</title><link>http://www.blogjava.net/iKingQu/articles/47124.html</link><dc:creator>風向逆轉 - 就要爪哇</dc:creator><author>風向逆轉 - 就要爪哇</author><pubDate>Fri, 19 May 2006 14:46:00 GMT</pubDate><guid>http://www.blogjava.net/iKingQu/articles/47124.html</guid><wfw:comment>http://www.blogjava.net/iKingQu/comments/47124.html</wfw:comment><comments>http://www.blogjava.net/iKingQu/articles/47124.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/iKingQu/comments/commentRss/47124.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/iKingQu/services/trackbacks/47124.html</trackback:ping><description><![CDATA[原文地址：<a href="http://java.ccidnet.com/art/297/20060326/489157_1.html">http://java.ccidnet.com/art/297/20060326/489157_1.html</a><img src ="http://www.blogjava.net/iKingQu/aggbug/47124.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/iKingQu/" target="_blank">風向逆轉 - 就要爪哇</a> 2006-05-19 22:46 <a href="http://www.blogjava.net/iKingQu/articles/47124.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[收藏]MySQL数据库优化</title><link>http://www.blogjava.net/iKingQu/articles/38162.html</link><dc:creator>風向逆轉 - 就要爪哇</dc:creator><author>風向逆轉 - 就要爪哇</author><pubDate>Wed, 29 Mar 2006 20:01:00 GMT</pubDate><guid>http://www.blogjava.net/iKingQu/articles/38162.html</guid><wfw:comment>http://www.blogjava.net/iKingQu/comments/38162.html</wfw:comment><comments>http://www.blogjava.net/iKingQu/articles/38162.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/iKingQu/comments/commentRss/38162.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/iKingQu/services/trackbacks/38162.html</trackback:ping><description><![CDATA[下决心开始学oracle了,用mysql已经有不短的时间了,今天写下这些算是对自己的一个mysql之旅的一个交代吧.以下仅仅是本人在使用mysql过程中的一点个人的体会,也许存在许多纰漏和错误,还请指正!! <br /><br /><br /><br />首先,为了使一个系统更快,最重要的部分就是基础设计,不过有些东西是现有情况下无法逾越的,比如说系统常见的瓶颈. <br /><br />我所能想到的: <br /><br />1:磁盘寻道能力,以高速硬盘(7200转/秒),理论上每秒寻道7200次.这是没有办法改变的,优化的方法是----用多个硬盘,或者把数据分散存储. <br /><br />2:硬盘的读写速度,这个速度非常的快(限于本人的知识所限,只知道在每秒几十甚至上百MB).这个更容易解决--可以从多个硬盘上并行读写. <br /><br />3:cpu.cpu处理内存中的数据,当有相对内存较小的表时,这是最常见的限制因素. <br /><br />4:内存的限制.当cpu需要超出适合cpu缓存的数据时,缓存的带宽就成了内存的一个瓶颈---不过现在内存大的惊人,一般不会出现这个问题. <br /><br />第二步: <br /><br />(本人使用的是学校网站的linux平台(Linux ADVX.Mandrakesoft.com 2.4.3-19mdk )) <br /><br />1:调节服务器参数 <br /><br />用shell&gt;mysqld-help这个命令声厂一张所有mysql选项和可配置变量的表.输出以下信息: <br /><br />possible variables for option--set-variable(-o) are: <br /><br />back_log current value:5 //要求mysql能有的连接数量.back_log指出在mysql暂停接受连接的时间内有多少个连接请求可以被存在堆栈中 <br /><br />connect_timeout current value:5 //mysql服务器在用bad handshake(不好翻译)应答前等待一个连接的时间 <br /><br />delayed_insert_timeout current value:200 //一个insert delayed在终止前等待insert的时间 <br /><br />delayed_insert_limit current value:50 //insert delayed处理器将检查是否有任何select语句未执行,如果有,继续前执行这些语句 <br /><br />delayed_queue_size current value:1000 //为insert delayed分配多大的队 <br /><br />flush_time current value:0 //如果被设置为非0,那么每个flush_time 时间,所有表都被关闭 <br /><br />interactive_timeout current value:28800 //服务器在关上它之前在洋交互连接上等待的时间 <br /><br />join_buffer_size current value:131072 //用与全部连接的缓冲区大小 <br /><br />key_buffer_size current value:1048540 //用语索引块的缓冲区的大小,增加它可以更好的处理索引 <br /><br />lower_case_table_names current value:0 // <br /><br />long_query_time current value:10 //如果一个查询所用时间大于此时间,slow_queried计数将增加 <br /><br />max_allowed_packet current value:1048576 //一个包的大小 <br /><br />max_connections current value:300 //允许同时连接的数量 <br /><br />max_connect_errors current value:10 //如果有多于该数量的中断连接,将阻止进一步的连接,可以用flush hosts来解决 <br /><br />max_delayed_threads current value:15 //可以启动的处理insert delayed的数量 <br /><br />max_heap_table_size current value:16777216 // <br /><br />max_join_size current value:4294967295 //允许读取的连接的数量 <br /><br />max_sort_length current value:1024 //在排序blob或者text时使用的字节数量 <br /><br />max_tmp_tables current value:32 //一个连接同时打开的临时表的数量 <br /><br />max_write_lock_count current value:4294967295 //指定一个值(通常很小)来启动mysqld,使得在一定数量的write锁定之后出现read锁定 <br /><br />net_buffer_length current value:16384 //通信缓冲区的大小--在查询时被重置为该大小 <br /><br />query_buffer_size current value:0 //查询时缓冲区大小 <br /><br />record_buffer current value:131072 //每个顺序扫描的连接为其扫描的每张表分配的缓冲区的大小 <br /><br />sort_buffer current value:2097116 //每个进行排序的连接分配的缓冲区的大小 <br /><br />table_cache current value:64 //为所有连接打开的表的数量 <br /><br />thread_concurrency current value:10 // <br /><br />tmp_table_size current value:1048576 //临时表的大小 <br /><br />thread_stack current value:131072 //每个线程的大小 <br /><br />wait_timeout current value:28800 //服务器在关闭它3之前的一个连接上等待的时间 <br /><br /><br /><br />根据自己的需要配置以上信息会对你帮助. <br /><br /><br /><br />第三: <br /><br />1:如果你在一个数据库中创建大量的表,那么执行打开,关闭，创建(表)的操作就会很慢. <br /><br />2:mysql使用内存 <br /><br />a: 关键字缓存区(key_buffer_size)由所有线程共享 <br /><br />b: 每个连接使用一些特定的线程空间.一个栈(默认为64k,变量thread_stack),一个连接缓冲区(变量net_buffer_length)和一个结果缓冲区(net_buffer_length).特定情况下,连接缓冲区和结果缓冲区被动态扩大到max_allowed_packet. <br /><br />c:所有线程共享一个基存储器 <br /><br />d:没有内存影射 <br /><br />e:每个做顺序扫描的请求分配一个读缓冲区(record_buffer) <br /><br />f:所有联结均有一遍完成并且大多数联结甚至可以不用一个临时表完成.最临时的表是基于内存的(heap)表 <br /><br />g:排序请求分配一个排序缓冲区和2个临时表 <br /><br />h:所有语法分析和计算都在一个本地存储器完成 <br /><br />i:每个索引文件只被打开一次,并且数据文件为每个并发运行的线程打开一次 <br /><br />j:对每个blob列的表，一个缓冲区动态的被扩大以便读入blob值 <br /><br />k:所有正在使用的表的表处理器被保存在一个缓冲器中并且作为一个fifo管理. <br /><br />l:一个mysqladmin flush-tables命令关闭所有不在使用的表并且在当前执行的线程结束时标记所有在使用的表准备关闭 <br /><br />3:mysql锁定表 <br /><br />mysql中所有锁定不会成为死锁. <br /><br />wirte锁定: <br /><br />mysql的锁定原理:a:如果表没有锁定,那么锁定;b否则,把锁定请求放入写锁定队列中 <br /><br />read锁定: <br /><br />mysql的锁定原理:a:如果表没有锁定,那么锁定;b否则,把锁定请求放入读锁定队列中 <br /><br /><br /><br />有时候会在一个表中进行很多的select,insert操作,可以在一个临时表中插入行并且偶尔用临时表的记录更新真正的表 <br /><br />a:用low_priority属性给一个特定的insert,update或者delete较低的优先级 <br /><br />b:max_write_lock_count指定一个值(通常很小)来启动mysqld,使得在一定数量的write锁定之后出现read锁定 <br /><br />c:通过使用set sql_low_priority_updates=1可以从一个特定的线程指定所有的更改应该由较低的优先级完成 <br /><br />d:用high_priority指定一个select <br /><br />e:如果使用insert....select....出现问题,使用myisam表------因为它支持因为它支持并发的select和insert <br /><br />4:最基本的优化是使数据在硬盘上占据的空间最小.如果索引做在最小的列上,那么索引也最小.实现方法: <br /><br />a:使用尽可能小的数据类型 <br /><br />b:如果可能，声明表列为NOT NULL. <br /><br />c:如果有可能使用变成的数据类型,如varchar(但是速度会受一定的影响) <br /><br />d:每个表应该有尽可能短的主索引 <br /><br />e:创建确实需要的索引 <br /><br />f:如果一个索引在头几个字符上有唯一的前缀,那么仅仅索引这个前缀----mysql支持在一个字符列的一部分上的索引 <br /><br />g:如果一个表经常被扫描,那么试图拆分它为更多的表 <br /><br /><br /><br /><br /><br />第四步 <br /><br />1:索引的使用,索引的重要性就不说了,功能也不说了,只说怎么做. <br /><br />首先要明确所有的mysql索引(primary,unique,index)在b树中有存储.索引主要用语: <br /><br />a:快速找到where指定条件的记录 <br /><br />b:执行联结时,从其他表检索行 <br /><br />c:对特定的索引列找出max()和min()值 <br /><br />d：如果排序或者分组在一个可用键的最前面加前缀，排序或分组一个表 <br /><br />e：一个查询可能被用来优化检索值，而不用访问数据文件．如果某些表的列是数字型并且正好是某个列的前缀，为了更快，值可以从索引树中取出 <br /><br />２：存储或者更新数据的查询速度 <br /><br />　grant的执行会稍稍的减低效率． <br /><br />　mysql的函数应该被高度的优化．可以用benchmark（loop_count,expression)来找出是否查询有问题 <br /><br />　select 的查询速度：如果想要让一个select．．．where．．．更快，我能想到的只有建立索引．可以在一个表上运行myisamchk－－analyze 来更好的优化查询．可以用myisamchk－－sort－index－－sort－records＝１来设置用一个索引排序一个索引和数据． <br /><br />３：mysql优化where子句 <br /><br />3.１：删除不必要的括号： <br /><br />　（（a AND b) AND c OR (((a AND b) AND (a AND d))))&gt;(a AND b AND c) OR (a AND b AND c AND d) <br /><br />3.2:使用常数 <br /><br />　（a&lt;b AND b=c) AND a=100 &gt; b&gt;5 AND b=c AND a=5 <br /><br />3.3:删除常数条件 <br /><br />（b&gt;=5 AND b=5) OR (b=6 AND 5=5) OR (b=100 AND 2=3) &gt; b=5 OR b=6 <br /><br />3.4:索引使用的常数表达式仅计算一次 <br /><br />3.5：在一个表中，没有一个where的count(*)直接从表中检索信息 <br /><br />3.6:所有常数的表在查询中在任何其他表之前读出 <br /><br />3.7:对外联结表最好联结组合是尝试了所有可能性找到的 <br /><br />3.8：如果有一个order　by字句和一个不同的group　by子句或者order　by或者group　by包含不是来自联结的第一个表的列，那么创建一个临时表 <br /><br />3.9:如果使用了sql_small_result，那么msyql使用在内存中的一个表 <br /><br />3.10:每个表的索引给查询并且使用跨越少于３０％的行的索引． <br /><br />3.11在每个记录输出前，跳过不匹配having子句的行 <br /><br /><br /><br />４：优化left　join <br /><br />在mysql中　a left join b按以下方式实现 <br /><br />a：表b依赖于表a　 <br /><br />b：表a依赖于所有用在left　join条件的表（除了b） <br /><br />c：所有left　join条件被移到where子句中 <br /><br />d：进行所有的联结优化，除了一个表总是在所有他依赖的表后读取．如果有一个循环依赖，那么将发生错误 <br /><br />e：进行所有的标准的where优化 <br /><br />f：如果在a中有一行匹配where子句，但是在b中没有任何匹配left　join条件，那么，在b中生成的所有设置为ＮＵＬＬ的一行 <br /><br />g：如果使用left　join来找出某些表中不存在的行并且在where部分有column_name IS NULL测试(column_name为NOT NULL列)．那么，mysql在它已经找到了匹配left　join条件的一行后，将停止在更多的行后寻找 <br /><br />５：优化limit <br /><br />a：如果用limit只选择一行，当mysql需要扫描整个表时，它的作用相当于索引 <br /><br />b：如果使用limit＃与order　by，mysql如果找到了第＃行，将结束排序，而不会排序正个表 <br /><br />c：当结合limit＃和distinct时，mysql如果找到了第＃行，将停止 <br /><br />d：只要mysql已经发送了第一个＃行到客户，mysql将放弃查询 <br /><br />e：limit 0一直会很快的返回一个空集合． <br /><br />f：临时表的大小使用limit＃计算需要多少空间来解决查询 <br /><br />６：优化insert <br /><br />插入一条记录的是由以下构成： <br /><br />a:连接（３） <br /><br />b:发送查询给服务器（２） <br /><br />c:分析查询（２） <br /><br />d:插入记录（１*记录大小） <br /><br />e：插入索引（１*索引） <br /><br />f：关闭（１） <br /><br />以上数字可以看成和总时间成比例 <br /><br />改善插入速度的一些方法： <br /><br />6.1：如果同时从一个连接插入许多行，使用多个值的insert，这比用多个语句要快 <br /><br />6.2：如果从不同连接插入很多行，使用insert　delayed语句速度更快 <br /><br />6.3: 用myisam，如果在表中没有删除的行，能在select：s正在运行的同时插入行 <br /><br />6.4: 当从一个文本文件装载一个表时，用load　data　infile．这个通常比insert快20 <br /><br />倍 <br /><br />6.5: 可以锁定表然后插入－－主要的速度差别是在所有insert语句完成后，索引缓冲区仅被存入到硬盘一次．一般与有不同的insert语句那样多次存入要快．如果能用一个单个语句插入所有的行，锁定就不需要．锁定也降低连接的整体时间．但是对某些线程最大等待时间将上升．例如： <br /><br />thread 1 does 1000 inserts <br /><br />thread 2,3 and 4 does 1 insert <br /><br />thread 5 does 1000 inserts <br /><br />如果不使用锁定，２，３，４将在１和５之前完成．如果使用锁定，２，３，４，将可能在１和５之后完成．但是整体时间应该快４０％．因为insert， update，delete操作在mysql中是很快的，通过为多于大约５次连续不断的插入或更新一行的东西加锁，将获得更好的整体性能．如果做很多一行的插入，可以做一个lock　tables，偶尔随后做一个unlock　tables（大约每１０００行）以允许另外的线程存取表．这仍然将导致获得好的性能．load　data　infile对装载数据仍然是很快的． <br /><br />为了对load　data　infile和insert得到一些更快的速度，扩大关键字缓冲区． <br /><br />７优化update的速度 <br /><br />它的速度依赖于被更新数据的大小和被更新索引的数量 <br /><br />使update更快的另一个方法是推迟修改，然后一行一行的做很多修改．如果锁定表，做一行一行的很多修改比一次做一个快 <br /><br />８优化delete速度 <br /><br />删除一个记录的时间与索引数量成正比．为了更快的删除记录，可以增加索引缓存的大小 <br /><br />从一个表删除所有行比删除这个表的大部分要快的多 <br /><br /><br /><br />第五步 <br /><br />１：选择一种表类型 <br /><br />1.1静态myisam <br /><br />这种格式是最简单且最安全的格式，它是磁盘格式中最快的．速度来自于数据能在磁盘上被找到的难易程度．当锁定有一个索引和静态格式的东西是，它很简单，只是行长度乘以数量．而且在扫描一张表时，每次用磁盘读取来读入常数个记录是很容易的．安全性来源于如果当写入一个静态myisam文件时导致计算机down 掉，myisamchk很容易指出每行在哪里开始和结束，因此，它通常能收回所有记录，除了部分被写入的记录．在mysql中所有索引总能被重建 <br /><br />1.2动态myisam <br /><br />这种格式每一行必须有一个头说明它有多长．当一个记录在更改期间变长时，它可以在多于一个位置上结束．能使用optimize　tablename或 myisamchk整理一张表．如果在同一个表中有像某些varchar或者blob列那样存取／改变的静态数据，将动态列移入另外一个表以避免碎片． <br /><br />1.2.1压缩myisam，用可选的myisampack工具生成 <br /><br />1.2.2内存 <br /><br />这种格式对小型／中型表很有用．对拷贝／创建一个常用的查找表到洋heap表有可能加快多个表联结，用同样数据可能要快好几倍时间． <br /><br />select tablename.a,tablename2.a from tablename,tablanem2,tablename3 where <br /><br />tablaneme.a=tablename2.a and tablename2.a=tablename3.a and tablename2.c!=0; <br /><br /><br /><br />为了加速它，可以用tablename2和tablename3的联结创建一个临时表，因为用相同列（tablename1.a）查找． <br /><br />CREATE  TEMPORARY  TABLE  test TYPE=HEAP <br /><br />SELECT <br /><br />tablename2.a as a2,tablename3.a as a3 <br /><br />FROM <br /><br />tablenam2,tablename3 <br /><br />WHERE <br /><br />tablename2.a=tablename3.a and c=0; <br /><br />SELECT tablename.a,test.a3 from tablename,test where tablename.a=test.a1; <br /><br />SELECT tablename.a,test,a3,from tablename,test where tablename.a=test.a1 and ....; <br /><br /><br /><br />1.3静态表的特点 <br /><br />1.3.1默认格式．用在表不包含varchar，blob，text列的时候 <br /><br />1.3.2所有的char，numeric和decimal列填充到列宽度 <br /><br />1.3.3非常快 <br /><br />1.3.4容易缓冲 <br /><br />1.3.5容易在down后重建，因为记录位于固定的位置 <br /><br />1.3.6不必被重新组织（用myisamchk），除非是一个巨量的记录被删除并且优化存储大小 <br /><br />1.3.7通常比动态表需要更多的存储空间 <br /><br /><br /><br />1.4动态表的特点 <br /><br />1.4.1如果表包含任何varchar，blob，text列，使用该格式 <br /><br />1.4.2所有字符串列是动态的 <br /><br />1.4.3每个记录前置一个位． <br /><br />1.4.4通常比定长表需要更多的磁盘空间 <br /><br />1.4.5每个记录仅仅使用所需要的空间，如果一个记录变的很大，它按需要被分成很多段，这导致了记录碎片 <br /><br />1.4.6如果用超过行长度的信息更新行，行被分段． <br /><br />1.4.7在系统down掉以后不好重建表，因为一个记录可以是多段 <br /><br />1.4.8对动态尺寸记录的期望行长度是３＋（number　of　columns＋７）／８＋(number <br /><br />of char columns)+packed size of numeric columns+length of strings +(number of <br /><br />NULL columns+7)/8 <br /><br />对每个连接有６个字节的惩罚．无论何时更改引起记录的变大，都有一个动态记录被连接．每个新连接至少有２０个字节，因此下一个变大将可能在同一个连接中．如果不是，将有另外一个连接．可以用myisamchk　－恶毒检查有多少连接．所有连接可以用myisamchk -r删除． <br /><br /><br /><br />1.5压缩表的特点 <br /><br />1.5.1一张用myisampack实用程序制作的只读表． <br /><br />1.5.2解压缩代码存在于所有mysql分发中，以便使没有myisampack的连接也能读取用myisampack压缩的表 <br /><br />1.5.3占据很小的磁盘空间 <br /><br />1.5.4每个记录被单独压缩．一个记录的头是一个定长的（１～～３个字节）这取决于表的最大记录．每列以不同的方式被压缩．一些常用的压缩类型是： <br /><br />　a:通常对每列有一张不同的哈夫曼表 <br /><br />　b:后缀空白压缩 <br /><br />　c:前缀空白压缩 <br /><br />d:用值０的数字使用１位存储 <br /><br />e:如果整数列的值有一个小范围，列使用最小的可能类型来存储．例如：如果所有的值在０到２５５之间，一个bigint可以作为一个tinyint存储 <br /><br />　g:如果列仅有可能值的一个小集合，列类型被转换到enum <br /><br />　h:列可以使用上面的压缩方法的组合 <br /><br />1.5.5能处理定长或动态长度的记录，去不能处理blob或者text列 <br /><br />1.5.6能用myisamchk解压缩 <br /><br />mysql能支持不同的索引类型，但一般的类型是isam，这是一个Ｂ树索引并且能粗略的为索引文件计算大小为(key_length+4)*0.67，在所有的键上的总和． <br /><br />字符串索引是空白压缩的。如果第一个索引是一个字符串，它可将压缩前缀如果字符串列有很多尾部空白或是一个总部能甬道全长的varchar列，空白压缩使索引文件更小．如果很多字符串有相同的前缀． <br /><br />1.6内存表的特点 <br /><br />mysql内部的heap表使用每偶溢出去的１００％动态哈希并且没有与删除有关的问题．只能通过使用在堆表中的一个索引来用等式存取东西（通常用＇＝＇操作符） <br /><br />堆表的缺点是： <br /><br />1.6.1想要同时使用的所有堆表需要足够的额外内存 <br /><br />1.6.2不能在索引的一个部分搜索 <br /><br />1.6.3不能按顺序搜索下一个条目（即，使用这个索引做一个order　by） <br /><br />1.6.4mysql不能算出在２个值之间大概有多少行．这被优化器使用是用来决定使用哪个索引的，但是在另一个方面甚至不需要磁盘寻道<img src ="http://www.blogjava.net/iKingQu/aggbug/38162.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/iKingQu/" target="_blank">風向逆轉 - 就要爪哇</a> 2006-03-30 04:01 <a href="http://www.blogjava.net/iKingQu/articles/38162.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[收藏]JDBC系列教程（六）---可调用语句</title><link>http://www.blogjava.net/iKingQu/articles/36764.html</link><dc:creator>風向逆轉 - 就要爪哇</dc:creator><author>風向逆轉 - 就要爪哇</author><pubDate>Tue, 21 Mar 2006 19:44:00 GMT</pubDate><guid>http://www.blogjava.net/iKingQu/articles/36764.html</guid><wfw:comment>http://www.blogjava.net/iKingQu/comments/36764.html</wfw:comment><comments>http://www.blogjava.net/iKingQu/articles/36764.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/iKingQu/comments/commentRss/36764.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/iKingQu/services/trackbacks/36764.html</trackback:ping><description><![CDATA[
		<p>
				<span class="Title">JDBC系列教程（六）---可调用语句 </span>
		</p>
		<p>CallableStatement </p>
		<p>本概述是从《JDBCTM Database Access from JavaTM: A Tutorial and Annotated Reference 》这本书中摘引来的。JavaSoft 目前正在准备这本书。这本书是一本教程，同时也是 JDBC 的重要参考手册，它将作为 Java 系列的组成部份在 1997 年春季由 Addison-Wesley 出版公司出版。  </p>
		<p>
				<br />7.1 概述 <br />CallableStatement 对象为所有的 DBMS 提供了一种以标准形式调用已储存过程的方法。已储存过程储存在数据库中。对已储存过程的调用是 CallableStatement 对象所含的内容。这种调用是用一种换码语法来写的，有两种形式：一种形式带结果参数，另一种形式不带结果参数（有关换码语法的信息，参见第 4 节“语句”）。结果参数是一种输出 (OUT) 参数，是已储存过程的返回值。两种形式都可带有数量可变的输入（IN 参数）、输出（OUT 参数）或输入和输出（INOUT 参数）的参数。问号将用作参数的占位符。 </p>
		<p>在 JDBC 中调用已储存过程的语法如下所示。注意，方括号表示其间的内容是可选项；方括号本身并不是语法的组成部份。 </p>
		<p>{call 过程名[(?, ?, ...)]} </p>
		<p>返回结果参数的过程的语法为：  </p>
		<p>{? = call 过程名[(?, ?, ...)]} </p>
		<p>不带参数的已储存过程的语法类似：  </p>
		<p>{call 过程名} </p>
		<p>通常，创建 CallableStatement 对象的人应当知道所用的 DBMS 是支持已储存过程的，并且知道这些过程都是些什么。然而，如果需要检查，多种 DatabaseMetaData 方法都可以提供这样的信息。例如，如果 DBMS 支持已储存过程的调用，则 supportsStoredProcedures 方法将返回 true，而 getProcedures 方法将返回对已储存过程的描述。 </p>
		<p>CallableStatement 继承 Statement 的方法（它们用于处理一般的 SQL 语句），还继承了 PreparedStatement 的方法（它们用于处理 IN 参数）。CallableStatement 中定义的所有方法都用于处理 OUT 参数或 INOUT 参数的输出部分：注册 OUT 参数的 JDBC 类型（一般 SQL 类型）、从这些参数中检索结果，或者检查所返回的值是否为 JDBC NULL。 </p>
		<p>
				<br />7.1.1 创建 CallableStatement 对象 <br />CallableStatement 对象是用 Connection 方法 prepareCall 创建的。下例创建 CallableStatement 的实例，其中含有对已储存过程 getTestData 调用。该过程有两个变量，但不含结果参数：  </p>
		<p>CallableStatement cstmt = con.prepareCall( <br />"{call getTestData(?, ?)}"); </p>
		<p>其中 ? 占位符为 IN、 OUT 还是 INOUT 参数，取决于已储存过程 getTestData。 </p>
		<p>
				<br />7.1.2 IN 和 OUT 参数 <br />将 IN 参数传给 CallableStatement 对象是通过 setXXX 方法完成的。该方法继承自 PreparedStatement。所传入参数的类型决定了所用的 setXXX 方法（例如，用 setFloat 来传入 float 值等）。 </p>
		<p>如果已储存过程返回 OUT 参数，则在执行 CallableStatement 对象以前必须先注册每个 OUT 参数的 JDBC 类型（这是必需的，因为某些 DBMS 要求 JDBC 类型）。注册 JDBC 类型是用 registerOutParameter 方法来完成的。语句执行完后，CallableStatement 的 getXXX 方法将取回参数值。正确的 getXXX 方法是为各参数所注册的 JDBC 类型所对应的 Java 类型（从 JDBC 类型到 Java 类型的标准映射见 8.6.1 节中的表）。换言之， registerOutParameter 使用的是 JDBC 类型（因此它与数据库返回的 JDBC 类型匹配），而 getXXX 将之转换为 Java 类型。 </p>
		<p>作为示例，下述代码先注册 OUT 参数，执行由 cstmt 所调用的已储存过程，然后检索在 OUT 参数中返回的值。方法 getByte 从第一个 OUT 参数中取出一个 Java 字节，而 getBigDecimal 从第二个 OUT 参数中取出一个 BigDecimal 对象（小数点后面带三位数）：  </p>
		<p>CallableStatement cstmt = con.prepareCall( <br />"{call getTestData(?, ?)}"); <br />cstmt.registerOutParameter(1, java.sql.Types.TINYINT); <br />cstmt.registerOutParameter(2, java.sql.Types.DECIMAL, 3); <br />cstmt.executeQuery(); <br />byte x = cstmt.getByte(1); <br />java.math.BigDecimal n = cstmt.getBigDecimal(2, 3); </p>
		<p>CallableStatement 与 ResultSet 不同，它不提供用增量方式检索大 OUT 值的特殊机制。 </p>
		<p>
				<br />7.1.3 INOUT 参数 <br />既支持输入又接受输出的参数（INOUT 参数）除了调用 registerOutParameter 方法外，还要求调用适当的 setXXX 方法（该方法是从 PreparedStatement 继承来的）。setXXX 方法将参数值设置为输入参数，而 registerOutParameter 方法将它的 JDBC 类型注册为输出参数。setXXX 方法提供一个 Java 值，而驱动程序先把这个值转换为 JDBC 值，然后将它送到数据库中。 </p>
		<p>这种 IN 值的 JDBC 类型和提供给 registerOutParameter 方法的 JDBC 类型应该相同。然后，要检索输出值，就要用对应的 getXXX 方法。例如，Java 类型为 byte 的参数应该使用方法 setByte 来赋输入值。应该给 registerOutParameter 提供类型为 TINYINT 的 JDBC 类型，同时应使用 getByte 来检索输出值 （第 8 节“JDBC 和 Java 类型之间的映射”将给出详细信息和类型映射表）。 </p>
		<p>下例假设有一个已储存过程 reviseTotal，其唯一参数是 INOUT 参数。方法 setByte 把此参数设为 25，驱动程序将把它作为 JDBC TINYINT 类型送到数据库中。接着，registerOutParameter 将该参数注册为 JDBC TINYINT。执行完该已储存过程后，将返回一个新的 JDBC TINYINT 值。方法 getByte 将把这个新值作为 Java byte 类型检索。 </p>
		<p>CallableStatement cstmt = con.prepareCall( <br />"{call reviseTotal(?)}"); <br />cstmt.setByte(1, 25); <br />cstmt.registerOutParameter(1, java.sql.Types.TINYINT); <br />cstmt.executeUpdate(); <br />byte x = cstmt.getByte(1); </p>
		<p>7.1.4 先检索结果，再检索 OUT 参数 <br />由于某些 DBMS 的限制，为了实现最大的可移植性，建议先检索由执行 CallableStatement 对象所产生的结果，然后再用 CallableStatement.getXXX 方法来检索 OUT 参数。 </p>
		<p>如果 CallableStatement 对象返回多个 ResultSet 对象（通过调用 execute 方法），在检索 OUT 参数前应先检索所有的结果。这种情况下，为确保对所有的结果都进行了访问，必须对 Statement 方法 getResultSet、getUpdateCount 和 getMoreResults 进行调用，直到不再有结果为止。 </p>
		<p>检索完所有的结果后，就可用 CallableStatement.getXXX 方法来检索 OUT 参数中的值。 </p>
		<p>
				<br />7.1.5 检索作为 OUT 参数的 NULL 值 <br />返回到 OUT 参数中的值可能会是 JDBC NULL。当出现这种情形时，将对 JDBC NULL 值进行转换以使 getXXX 方法所返回的值为 null、0 或 false，这取决于 getXXX 方法类型。对于 ResultSet 对象，要知道 0 或 false 是否源于 JDBC NULL 的唯一方法，是用方法 wasNull 进行检测。如果 getXXX 方法读取的最后一个值是 JDBC NULL，则该方法返回 true，否则返回 flase。第 5 节“ResultSet”将给出详细信息。   </p>
		<br />
		<br />
		<p id="TBPingURL">Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=630452</p>
<img src ="http://www.blogjava.net/iKingQu/aggbug/36764.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/iKingQu/" target="_blank">風向逆轉 - 就要爪哇</a> 2006-03-22 03:44 <a href="http://www.blogjava.net/iKingQu/articles/36764.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[收藏]JDBC系列教程（五）---准备语句</title><link>http://www.blogjava.net/iKingQu/articles/36762.html</link><dc:creator>風向逆轉 - 就要爪哇</dc:creator><author>風向逆轉 - 就要爪哇</author><pubDate>Tue, 21 Mar 2006 19:32:00 GMT</pubDate><guid>http://www.blogjava.net/iKingQu/articles/36762.html</guid><wfw:comment>http://www.blogjava.net/iKingQu/comments/36762.html</wfw:comment><comments>http://www.blogjava.net/iKingQu/articles/36762.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/iKingQu/comments/commentRss/36762.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/iKingQu/services/trackbacks/36762.html</trackback:ping><description><![CDATA[
		<div class="postText">
				<p>
						<span class="Title">JDBC系列教程（五）---准备语句</span>
				</p>
				<p>PreparedStatement </p>
				<p>本概述是从《JDBCTM Database Access from JavaTM: A Tutorial and Annotated Reference 》这本书中摘引来的。JavaSoft 目前正在准备这本书。这是一本教程，同时也是 JDBC 的重要参考手册，它将作为 Java 系列的组成部份在 1997 年春季由 Addison-Wesley 出版公司出版。  </p>
				<p>
						<br />6.1 概述 <br />该 PreparedStatement 接口继承 Statement，并与之在两方面有所不同：  </p>
				<p>
						<br />PreparedStatement 实例包含已编译的 SQL 语句。这就是使语句“准备好”。  <br />包含于 PreparedStatement 对象中的 SQL 语句可具有一个或多个 IN 参数。IN 参数的值在 SQL 语句创建时未被指定。相反的，该语句为每个 IN 参数保留一个问号（“？”）作为占位符。每个问号的值必须在该语句执行之前，通过适当的 setXXX 方法来提供。  </p>
				<p>由于 PreparedStatement 对象已预编译过，所以其执行速度要快于 Statement 对象。因此，多次执行的 SQL 语句经常创建为 PreparedStatement 对象，以提高效率。 </p>
				<p>作为 Statement 的子类，PreparedStatement 继承了 Statement 的所有功能。另外它还添加了一整套方法，用于设置发送给数据库以取代 IN 参数占位符的值。同时，三种方法 execute、 executeQuery 和 executeUpdate 已被更改以使之不再需要参数。这些方法的 Statement 形式（接受 SQL 语句参数的形式）不应该用于 PreparedStatement 对象。 </p>
				<p>
						<br />6.1.1 创建 PreparedStatement 对象 <br />以下的代码段（其中 con 是 Connection 对象）创建包含带两个 IN 参数占位符的 SQL 语句的 PreparedStatement 对象：  </p>
				<p>PreparedStatement pstmt = con.prepareStatement( <br />"UPDATE table4 SET m = ? WHERE x = ?"); </p>
				<p>pstmt 对象包含语句 "UPDATE table4 SET m = ? WHERE x = ?"，它已发送给 DBMS，并为执行作好了准备。 </p>
				<p>
						<br />6.1.2 传递 IN 参数 <br />在执行 PreparedStatement 对象之前，必须设置每个 ? 参数的值。这可通过调用 setXXX 方法来完成，其中 XXX 是与该参数相应的类型。例如，如果参数具有 Java 类型 long，则使用的方法就是 setLong。setXXX 方法的第一个参数是要设置的参数的序数位置，第二个参数是设置给该参数的值。例如，以下代码将第一个参数设为 123456789，第二个参数设为 100000000：  </p>
				<p>pstmt.setLong(1, 123456789); <br />pstmt.setLong(2, 100000000); </p>
				<p>一旦设置了给定语句的参数值，就可用它多次执行该语句，直到调用 clearParameters 方法清除它为止。 </p>
				<p>在连接的缺省模式下（启用自动提交），当语句完成时将自动提交或还原该语句。 </p>
				<p>如果基本数据库和驱动程序在语句提交之后仍保持这些语句的打开状态，则同一个 PreparedStatement 可执行多次。如果这一点不成立，那么试图通过使用 PreparedStatement 对象代替 Statement 对象来提高性能是没有意义的。 </p>
				<p>利用 pstmt（前面创建的 PreparedStatement 对象），以下代码例示了如何设置两个参数占位符的值并执行 pstmt 10 次。如上所述，为做到这一点，数据库不能关闭 pstmt。在该示例中，第一个参数被设置为 "Hi"并保持为常数。在 for 循环中，每次都将第二个参数设置为不同的值：从 0 开始，到 9 结束。 </p>
				<p>pstmt.setString(1, "Hi"); <br />for (int i = 0; i &lt; 10; i++) { <br />pstmt.setInt(2, i); <br />int rowCount = pstmt.executeUpdate(); <br />} </p>
				<p>6.1.3 IN 参数中数据类型的一致性 <br />setXXX 方法中的 XXX 是 Java 类型。它是一种隐含的 JDBC 类型（一般 SQL 类型），因为驱动程序将把 Java 类型映射为相应的 JDBC 类型（遵循该 JDBC Guide中§8.6.2 “映射 Java 和 JDBC 类型”表中所指定的映射），并将该 JDBC 类型发送给数据库。例如，以下代码段将 PreparedStatement 对象 pstmt 的第二个参数设置为 44，Java 类型为 short：  </p>
				<p>pstmt.setShort(2, 44); </p>
				<p>驱动程序将 44 作为 JDBC SMALLINT 发送给数据库，它是 Java short 类型的标准映射。 </p>
				<p>程序员的责任是确保将每个 IN 参数的 Java 类型映射为与数据库所需的 JDBC 数据类型兼容的 JDBC 类型。不妨考虑数据库需要 JDBC SMALLINT 的情况。如果使用方法 setByte ，则驱动程序将 JDBC TINYINT 发送给数据库。这是可行的，因为许多数据库可从一种相关的类型转换为另一种类型，并且通常 TINYINT 可用于 SMALLINT 适用的任何地方。然而，对于要适用于尽可能多的数据库的应用程序，最好使用与数据库所需的确切的 JDBC 类型相应的 Java 类型。如果所需的 JDBC 类型是 SMALLINT，则使用 setShort 代替 setByte 将使应用程序的可移植性更好。 </p>
				<p>
						<br />6.1.4 使用 setObject <br />程序员可使用 setObject 方法显式地将输入参数转换为特定的 JDBC 类型。该方法可以接受第三个参数，用来指定目标 JDBC 类型。将 Java Object 发送给数据库之前，驱动程序将把它转换为指定的 JDBC 类型。 </p>
				<p>如果没有指定 JDBC 类型，驱动程序就会将 Java Object 映射到其缺省的 JDBC 类型（参见第 8.6.4 节中的表格），然后将它发送到数据库。这与常规的 setXXX 方法类似；在这两种情况下，驱动程序在将值发送到数据库之前，会将该值的 Java 类型映射为适当的 JDBC 类型。二者的差别在于 setXXX 方法使用从 Java 类型到 JDBC 类型的标准映射（参见第 8.6.2 节中的表格），而 setObject 方法使用从 Java Object 类型到 JDBC 类型的映射（参见第 8.6.4 节中的表格）。 </p>
				<p>方法 setObject 允许接受所有 Java 对象的能力使应用程序更为通用，并可在运行时接受参数的输入。这种情况下，应用程序在编译时并不清楚输入类型。通过使用 setObject，应用程序可接受所有 Java 对象类型作为输入，并将其转换为数据库所需的 JDBC 类型。第 8.6.5 节中的表格显示了 setObject 可执行的所有可能转换。 </p>
				<p>
						<br />6.1.5 将 JDBC NULL 作为 IN 参数发送 <br />setNull 方法允许程序员将 JDBC NULL 值作为 IN 参数发送给数据库。但要注意，仍然必须指定参数的 JDBC 类型。 </p>
				<p>当把 Java null 值传递给 setXXX 方法时（如果它接受 Java 对象作为参数），也将同样把 JDBC NULL 发送到数据库。但仅当指定 JDBC 类型时，方法 setObject 才能接受 null 值。 </p>
				<p>
						<br />6.1.6 发送大的 IN 参数 <br />setBytes 和 setString 方法能够发送无限量的数据。但是，有时程序员更喜欢用较小的块传递大型的数据。这可通过将 IN 参数设置为 Java 输入流来完成。当语句执行时，JDBC 驱动程序将重复调用该输入流，读取其内容并将它们当作实际参数数据传输。 </p>
				<p>JDBC 提供了三种将 IN 参数设置为输入流的方法：setBinaryStream 用于含有未说明字节的流， setAsciiStream 用于含有 ASCII 字符的流，而 setUnicodeStream 用于含有 Unicode 字符的流。因为必须指定流的总长度，所以这些方法所采用的参数比其它的 setXXX 方法要多一个。这很有必要，因为一些数据库在发送数据之前需要知道其总的传送大小。 </p>
				<p>以下代码例示了使用流作为 IN 参数来发送文件内容：  </p>
				<p>java.io.File file = new java.io.File("/tmp/data"); <br />int fileLength = file.length(); <br />java.io.InputStream fin = new java.io.FileInputStream(file); <br />java.sql.PreparedStatement pstmt = con.prepareStatement( <br />"UPDATE Table5 SET stuff = ? WHERE index = 4"); <br />pstmt.setBinaryStream (1, fin, fileLength); <br />pstmt.executeUpdate(); </p>
				<p>当语句执行时，将反复调用输入流 fin 以传递其数据。 </p>
				<br />
				<br />
				<p id="TBPingURL">Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=630450</p>
		</div>
<img src ="http://www.blogjava.net/iKingQu/aggbug/36762.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/iKingQu/" target="_blank">風向逆轉 - 就要爪哇</a> 2006-03-22 03:32 <a href="http://www.blogjava.net/iKingQu/articles/36762.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[收藏]JDBC系列教程（四）---结果设置</title><link>http://www.blogjava.net/iKingQu/articles/36761.html</link><dc:creator>風向逆轉 - 就要爪哇</dc:creator><author>風向逆轉 - 就要爪哇</author><pubDate>Tue, 21 Mar 2006 19:31:00 GMT</pubDate><guid>http://www.blogjava.net/iKingQu/articles/36761.html</guid><wfw:comment>http://www.blogjava.net/iKingQu/comments/36761.html</wfw:comment><comments>http://www.blogjava.net/iKingQu/articles/36761.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/iKingQu/comments/commentRss/36761.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/iKingQu/services/trackbacks/36761.html</trackback:ping><description><![CDATA[
		<div class="postText">
				<p>
						<span class="Title">JDBC系列教程（四）---结果设置 </span>
				</p>
				<p>ResultSet <br />本概述是从《JDBCTM Database Access from JavaTM: A Tutorial and Annotated Reference 》这本书中摘引来的。JavaSoft 目前正在准备这本书。这是一本教程，同时也是 JDBC 的重要参考手册，它将作为 Java 系列的组成部份在 1997 年春季由 Addison-Wesley 出版公司出版。  </p>
				<p>5.1 概述 <br />ResultSet 包含符合 SQL 语句中条件的所有行，并且它通过一套 get 方法（这些 get 方法可以访问当前行中的不同列）提供了对这些行中数据的访问。ResultSet.next 方法用于移动到 ResultSet 中的下一行，使下一行成为当前行。 </p>
				<p>结果集一般是一个表，其中有查询所返回的列标题及相应的值。例如，如果查询为 SELECT a, b, c FROM Table1，则结果集将具有如下形式：  </p>
				<p>
						<br />a b c <br />-------- --------- -------- <br />12345 Cupertino CA <br />83472 Redmond WA <br />83492 Boston MA </p>
				<p>
						<br />下面的代码段是执行 SQL 语句的示例。该 SQL 语句将返回行集合，其中列 1 为 int，列 2 为 String，而列 3 则为字节数组：  </p>
				<p>
						<br />java.sql.Statement stmt = conn.createStatement(); <br />ResultSet r = stmt.executeQuery("SELECT a, b, c FROM Table1"); <br />while (r.next()) <br />{ <br />// 打印当前行的值。 <br />int i = r.getInt("a"); <br />String s = r.getString("b"); <br />float f = r.getFloat("c"); <br />System.out.println("ROW = " + i + " " + s + " " + f); <br />} </p>
				<p>5.1.1 行和光标 <br />ResultSet 维护指向其当前数据行的光标。每调用一次 next 方法，光标向下移动一行。最初它位于第一行之前，因此第一次调用 next 将把光标置于第一行上，使它成为当前行。随着每次调用 next 导致光标向下移动一行，按照从上至下的次序获取 ResultSet 行。 </p>
				<p>在 ResultSet 对象或其父辈 Statement 对象关闭之前，光标一直保持有效。 </p>
				<p>在 SQL 中，结果表的光标是有名字的。如果数据库允许定位更新或定位删除，则需要将光标的名字作为参数提供给更新或删除命令。可通过调用方法 getCursorName 获得光标名。 </p>
				<p>注意：不是所有的 DBMS 都支持定位更新和删除。可使用 DatabaseMetaData.supportsPositionedDelete 和 supportsPositionedUpdate 方法来检查特定连接是否支持这些操作。当支持这些操作时，DBMS/驱动程序必须确保适当锁定选定行，以使定位更新不会导致更新异常或其它并发问题。 </p>
				<p>
						<br />5.1.2 列 <br />方法 getXXX 提供了获取当前行中某列值的途径。在每一行内，可按任何次序获取列值。但为了保证可移植性，应该从左至右获取列值，并且一次性地读取列值。 </p>
				<p>列名或列号可用于标识要从中获取数据的列。例如，如果 ResultSet 对象 rs 的第二列名为“title”，并将值存储为字符串，则下列任一代码将获取存储在该列中的值：  </p>
				<p>String s = rs.getString("title"); <br />String s = rs.getString(2); </p>
				<p>注意列是从左至右编号的，并且从列 1 开始。同时，用作 getXXX 方法的输入的列名不区分大小写。 </p>
				<p>提供使用列名这个选项的目的是为了让在查询中指定列名的用户可使用相同的名字作为 getXXX 方法的参数。另一方面，如果 select 语句未指定列名（例如在“select * from table1”中或列是导出的时），则应该使用列号。这些情况下，用户将无法确切知道列名。 </p>
				<p>有些情况下，SQL 查询返回的结果集中可能有多个列具有相同的名字。如果列名用作 getXXX 方法的参数，则 getXXX 将返回第一个匹配列名的值。因而，如果多个列具有相同的名字，则需要使用列索引来确保检索了正确的列值。这时，使用列号效率要稍微高一些。 </p>
				<p>关于 ResultSet 中列的信息，可通过调用方法 ResultSet.getMetaData 得到。返回的 ResultSetMetaData 对象将给出其 ResultSet 对象各列的编号、类型和属性。 </p>
				<p>如果列名已知，但不知其索引，则可用方法 findColumn 得到其列号。 </p>
				<p>
						<br />5.1.3 数据类型和转换 <br />对于 getXXX 方法，JDBC 驱动程序试图将基本数据转换成指定 Java 类型，然后返回适合的 Java 值。例如，如果 getXXX 方法为 getString，而基本数据库中数据类型为 VARCHAR，则 JDBC 驱动程序将把 VARCHAR 转换成 Java String。getString 的返回值将为 Java String 对象。 </p>
				<p>下表显示了允许用 getXXX 获取的 JDBC 类型及推荐用它获取的 JDBC 类型（通用 SQL 类型）。小写的 x 表示允许 getXXX 方法获取该数据类型；大写的 X 表示对该数据类型推荐使用 getXXX 方法。例如，除了 getBytes 和 getBinaryStream 之外的任何 getXXX 方法都可用来获取 LONGVARCHAR 值，但是推荐根据返回的数据类型使用 getAsciiStream 或 getUnicodeStream 方法。方法 getObject 将任何数据类型返回为 Java Object。当基本数据类型是特定于数据库的抽象类型或当通用应用程序需要接受任何数据类型时，它是非常有用的。 </p>
				<p>可使用 ResultSet.getXXX 方法获取常见的 JDBC 数据类型。 </p>
				<p>“x”表示该 getXXX 方法可合法地用于获取给定 JDBC 类型。 </p>
				<p>“X”表示推荐使用该 getXXX 方法来获取给定 JDBC 类型。 </p>
				<p>　 T <br />I <br />N <br />Y <br />I <br />N <br />T S <br />M <br />A <br />L <br />L <br />I <br />N <br />T I <br />N <br />T <br />E <br />G <br />E <br />R B <br />I <br />G </p>
				<p>N <br />T R <br />E <br />A <br />L F <br />L <br />O <br />A <br />T D <br />O <br />U <br />B <br />L <br />E D <br />E <br />C <br />I <br />M <br />A <br />L N <br />U <br />M <br />E <br />R <br />I <br />C B <br />I <br />T C <br />H <br />A <br />R V <br />A <br />R <br />C <br />H <br />A <br />R <br />L <br />O <br />N <br />G <br />V <br />A <br />R <br />C <br />H <br />A <br />R B <br />I <br />N <br />A <br />R <br />Y V <br />A <br />R <br />B <br />I <br />N <br />A <br />R <br />Y L <br />O <br />N <br />G <br />V <br />A <br />R <br />B <br />I <br />N <br />A <br />R <br />Y D <br />A <br />T <br />E T <br />I <br />M <br />E T <br />I <br />M <br />E <br />S <br />T <br />A <br />M <br />P  <br />getByte X x x x x x x x x x x x x 　 　 　 　 　 　  <br />getShort x X x x x x x x x x x x x 　 　 　 　 　 　  <br />getInt x x X x x x x x x x x x x 　 　 　 　 　 　  <br />getLong x x x X x x x x x x x x x 　 　 　 　 　 　  <br />getFloat x x x x X x x x x x x x x 　 　 　 　 　 　  <br />getDouble x x x x x X X x x x x x x 　 　 　 　 　 　  <br />getBigDecimal x x x x x x x X X x x x x 　 　 　 　 　 　  <br />getBoolean x x x x x x x x x X x x x 　 　 　 　 　 　  <br />getString x x x x x x x x x x X X x x x x x x x  <br />getBytes 　 　 　 　 　 　 　 　 　 　 　 　 　 X X x 　 　 　  <br />getDate 　 　 　 　 　 　 　 　 　 　 x x x 　 　 　 X 　 x  <br />getTime 　 　 　 　 　 　 　 　 　 　 x x x 　 　 　 　 X x  <br />getTimestamp 　 　 　 　 　 　 　 　 　 　 x x x 　 　 　 x 　 X  <br />getAsciiStream 　 　 　 　 　 　 　 　 　 　 x x X x x x 　 　 　  <br />getUnicodeStream 　 　 　 　 　 　 　 　 　 　 x x X x x x 　 　 　  <br />getBinaryStream 　 　 　 　 　 　 　 　 　 　 　 　 　 x x X 　 　 　  <br />getObject x x x x x x x x x x x x x x x x x x x  </p>
				<p>
						<br />5.1.4 对非常大的行值使用流 <br />ResultSet 可以获取任意大的 LONGVARBINARY 或 LONGVARCHAR 数据。方法 getBytes 和 getString 将数据返回为大的块（最大为 Statement.getMaxFieldSize 的返回值）。但是，以较小的固定块获取非常大的数据可能会更方便，而这可通过让 ResultSet 类返回 java.io.Input 流来完成。从该流中可分块读取数据。注意：必须立即访问这些流，因为在下一次对 ResultSet 调用 getXXX 时它们将自动关闭（这是由于基本实现对大块数据访问有限制）。  </p>
				<p>JDBC API 具有三个获取流的方法，分别具有不同的返回值：  </p>
				<p>
						<br />getBinaryStream 返回只提供数据库原字节而不进行任何转换的流。 </p>
				<p>
						<br />getAsciiStream 返回提供单字节 ASCII 字符的流。 </p>
				<p>
						<br />getUnicodeStream 返回提供双字节 Unicode 字符的流。 </p>
				<p>
						<br />注意：它不同于 Java 流，后者返回无类型字节并可（例如）通用于 ASCII 和 Unicode 字符。 </p>
				<p>下列代码演示了 getAsciiStream 的用法：  </p>
				<p>java.sql.Statement stmt = con.createStatement(); <br />ResultSet r = stmt.executeQuery("SELECT x FROM Table2"); <br />// 现在以 4K 块大小获取列 1 结果： <br />byte buff = new byte[4096]; <br />while (r.next()) { <br />Java.io.InputStream fin = r.getAsciiStream(1); <br />for (;;) { <br />int size = fin.read(buff); <br />if (size == -1) { // 到达流末尾 <br />break; <br />} <br />// 将新填充的缓冲区发送到 ASCII 输出流： <br />output.write(buff, 0, size); <br />} <br />} </p>
				<p>5.1.5 NULL 结果值 <br />要确定给定结果值是否是 JDBC NULL，必须先读取该列，然后使用 ResultSet.wasNull 方法检查该次读取是否返回 JDBC NULL。 </p>
				<p>当使用 ResultSet.getXXX 方法读取 JDBC NULL 时，方法 wasNull 将返回下列值之一：  </p>
				<p>
						<br />Java null 值：对于返回 Java 对象的 getXXX 方法（例如 getString、getBigDecimal、getBytes、getDate、getTime、getTimestamp、getAsciiStream、getUnicodeStream、getBinaryStream、getObject 等）。 </p>
				<p>
						<br />零值：对于 getByte、getShort、getInt、getLong、getFloat 和 getDouble。 </p>
				<p>
						<br />false 值：对于 getBoolean。 </p>
				<p>
						<br />5.1.6 可选结果集或多结果集 <br />通常使用 executeQuery（它返回单个 ResultSet）或 executeUpdate（它可用于任何数据库修改语句，并返回更新行数）可执行 SQL 语句。但有些情况下，应用程序在执行语句之前不知道该语句是否返回结果集。此外，有些已存储过程可能返回几个不同的结果集和/或更新计数。 </p>
				<p>为了适应这些情况，JDBC 提供了一种机制，允许应用程序执行语句，然后处理由结果集和更新计数组成的任意集合。这种机制的原理是首先调用一个完全通用的 execute 方法，然后调用另外三个方法，getResultSet、getUpdateCount 和 getMoreResults。这些方法允许应用程序一次一个地研究语句结果，并确定给定结果是 ResultSet 还是更新计数。 </p>
				<p>用户不必关闭 ResultSet；当产生它的 Statement 关闭、重新执行或用于从多结果序列中获取下一个结果时，该 ResultSet 将被 Statement 自动关闭。  </p>
				<br />
				<br />
				<p id="TBPingURL">Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=630447</p>
		</div>
<img src ="http://www.blogjava.net/iKingQu/aggbug/36761.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/iKingQu/" target="_blank">風向逆轉 - 就要爪哇</a> 2006-03-22 03:31 <a href="http://www.blogjava.net/iKingQu/articles/36761.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[收藏]JDBC系列教程（三）---语句</title><link>http://www.blogjava.net/iKingQu/articles/36760.html</link><dc:creator>風向逆轉 - 就要爪哇</dc:creator><author>風向逆轉 - 就要爪哇</author><pubDate>Tue, 21 Mar 2006 19:30:00 GMT</pubDate><guid>http://www.blogjava.net/iKingQu/articles/36760.html</guid><wfw:comment>http://www.blogjava.net/iKingQu/comments/36760.html</wfw:comment><comments>http://www.blogjava.net/iKingQu/articles/36760.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/iKingQu/comments/commentRss/36760.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/iKingQu/services/trackbacks/36760.html</trackback:ping><description><![CDATA[
		<div class="postText">
				<p>
						<span class="Title">JDBC系列教程（三）---语句 </span>
				</p>
				<p>Statement <br />本概述是从《JDBCTM Database Access from JavaTM: A Tutorial and Annotated Reference 》这本书中摘引来的。JavaSoft 目前正在准备这本书。这是一本教程，同时也是 JDBC 的重要参考手册，它将作为 Java 系列的组成部份在 1997 年春季由 Addison-Wesley 出版公司出版。  </p>
				<p>4.1 概述 <br />Statement 对象用于将 SQL 语句发送到数据库中。实际上有三种 Statement 对象，它们都作为在给定连接上执行 SQL 语句的包容器：Statement、PreparedStatement（它从 Statement 继承而来）和 CallableStatement（它从 PreparedStatement 继承而来）。它们都专用于发送特定类型的 SQL 语句： Statement 对象用于执行不带参数的简单 SQL 语句；PreparedStatement 对象用于执行带或不带 IN 参数的预编译 SQL 语句；CallableStatement 对象用于执行对数据库已存储过程的调用。 </p>
				<p>Statement 接口提供了执行语句和获取结果的基本方法。PreparedStatement 接口添加了处理 IN 参数的方法；而 CallableStatement 添加了处理 OUT 参数的方法。 </p>
				<p>
						<br />4.1.1 创建 Statement 对象 <br />建立了到特定数据库的连接之后，就可用该连接发送 SQL 语句。Statement 对象用 Connection 的方法 createStatement 创建，如下列代码段中所示：  </p>
				<p>Connection con = DriverManager.getConnection(url, "sunny", ""); <br />Statement stmt = con.createStatement(); </p>
				<p>为了执行 Statement 对象，被发送到数据库的 SQL 语句将被作为参数提供给 Statement 的方法：  </p>
				<p>ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM Table2"); </p>
				<p>4.1.2 使用 Statement 对象执行语句 <br />Statement 接口提供了三种执行 SQL 语句的方法：executeQuery、executeUpdate 和 execute。使用哪一个方法由 SQL 语句所产生的内容决定。 </p>
				<p>方法 executeQuery 用于产生单个结果集的语句，例如 SELECT 语句。 </p>
				<p>方法 executeUpdate 用于执行 INSERT、UPDATE 或 DELETE 语句以及 SQL DDL（数据定义语言）语句，例如 CREATE TABLE 和 DROP TABLE。INSERT、UPDATE 或 DELETE 语句的效果是修改表中零行或多行中的一列或多列。executeUpdate 的返回值是一个整数，指示受影响的行数（即更新计数）。对于 CREATE TABLE 或 DROP TABLE 等不操作行的语句，executeUpdate 的返回值总为零。 </p>
				<p>方法 execute 用于执行返回多个结果集、多个更新计数或二者组合的语句。因为多数程序员不会需要该高级功能，所以本概述后面将在单独一节中对其进行介绍。 </p>
				<p>执行语句的所有方法都将关闭所调用的 Statement 对象的当前打开结果集（如果存在）。这意味着在重新执行 Statement 对象之前，需要完成对当前 ResultSet 对象的处理。 </p>
				<p>应注意，继承了 Statement 接口中所有方法的 PreparedStatement 接口都有自己的 executeQuery、executeUpdate 和 execute 方法。Statement 对象本身不包含 SQL 语句，因而必须给 Statement.execute 方法提供 SQL 语句作为参数。PreparedStatement 对象并不将 SQL 语句作为参数提供给这些方法，因为它们已经包含预编译 SQL 语句。CallableStatement 对象继承这些方法的 PreparedStatement 形式。对于这些方法的 PreparedStatement 或 CallableStatement 版本，使用查询参数将抛出 SQLException。 </p>
				<p>
						<br />4.1.3 语句完成 <br />当连接处于自动提交模式时，其中所执行的语句在完成时将自动提交或还原。语句在已执行且所有结果返回时，即认为已完成。对于返回一个结果集的 executeQuery 方法，在检索完 ResultSet 对象的所有行时该语句完成。对于方法 executeUpdate，当它执行时语句即完成。但在少数调用方法 execute 的情况中，在检索所有结果集或它生成的更新计数之后语句才完成。 </p>
				<p>有些 DBMS 将已存储过程中的每条语句视为独立的语句；而另外一些则将整个过程视为一个复合语句。在启用自动提交时，这种差别就变得非常重要，因为它影响什么时候调用 commit 方法。在前一种情况中，每条语句单独提交；在后一种情况中，所有语句同时提交。 </p>
				<p>
						<br />4.1.4 关闭 Statement 对象 <br />Statement 对象将由 Java 垃圾收集程序自动关闭。而作为一种好的编程风格，应在不需要 Statement 对象时显式地关闭它们。这将立即释放 DBMS 资源，有助于避免潜在的内存问题。 </p>
				<p>
						<br />4.1.5 Statement 对象中的 SQL 转义语法  <br />Statement 可包含使用 SQL 转义语法的 SQL 语句。转义语法告诉驱动程序其中的代码应该以不同方式处理。驱动程序将扫描任何转义语法，并将它转换成特定数据库可理解的代码。这使得转义语法与 DBMS 无关，并允许程序员使用在没有转义语法时不可用的功能。 </p>
				<p>转义子句由花括号和关键字界定：  </p>
				<p>{keyword . . . parameters . . . } </p>
				<p>该关键字指示转义子句的类型，如下所示。 </p>
				<p>
						<br />escape 表示 LIKE 转义字符  </p>
				<p>
						<br />字符“%”和“_”类似于 SQL LIKE 子句中的通配符（“%”匹配零个或多个字符，而“_”则匹配一个字符）。为了正确解释它们，应在其前面加上反斜杠（“\”），它是字符串中的特殊转义字符。在查询末尾包括如下语法即可指定用作转义字符的字符：  </p>
				<p>{escape 'escape-character'} </p>
				<p>
						<br />例如，下列查询使用反斜杠字符作为转义字符，查找以下划线开头的标识符名：  </p>
				<p>stmt.executeQuery("SELECT name FROM Identifiers <br />WHERE Id LIKE `\_%' {escape `\'}; </p>
				<p>
						<br />fn 表示标量函数 </p>
				<p>
						<br />几乎所有 DBMS 都具有标量值的数值、字符串、时间、日期、系统和转换函数。要使用这些函数，可使用如下转义语法：关键字 fn 后跟所需的函数名及其参数。例如，下列代码调用函数 concat 将两个参数连接在一起：  </p>
				<p>{fn concat("Hot", "Java")}; </p>
				<p>
						<br />可用下列语法获得当前数据库用户名：  </p>
				<p>{fn user()}; </p>
				<p>
						<br />标量函数可能由语法稍有不同的 DBMS 支持，而它们可能不被所有驱动程序支持。各种 DatabaseMetaData 方法将列出所支持的函数。例如，方法 getNumericFunctions 返回用逗号分隔的数值函数列表，而方法 getStringFunctions 将返回字符串函数，等等。 </p>
				<p>驱动程序将转义函数调用映射为相应的语法，或直接实现该函数。 </p>
				<p>
						<br />d、t 和 ts 表示日期和时间文字 </p>
				<p>
						<br />DBMS 用于日期、时间和时间标记文字的语法各不相同。JDBC 使用转义子句支持这些文字的语法的 ISO 标准格式。驱动程序必须将转义子句转换成 DBMS 表示。 </p>
				<p>例如，可用下列语法在 JDBC SQL 语句中指定日期：  </p>
				<p>{d `yyyy-mm-dd'} </p>
				<p>
						<br />在该语法中，yyyy 为年代，mm 为月份，而 dd 则为日期。驱动程序将用等价的特定于 DBMS 的表示替换这个转义子句。例如，如果 '28- FEB-99' 符合基本数据库的格式，则驱动程序将用它替换 {d 1999-02-28}。 </p>
				<p>对于 TIME 和 TIMESTAMP 也有类似的转义子句：  </p>
				<p>{t `hh:mm:ss'} <br />{ts `yyyy-mm-dd hh:mm:ss.f . . .'} </p>
				<p>
						<br />TIMESTAMP 中的小数点后的秒（.f . . .）部分可忽略。 </p>
				<p>
						<br />call 或 ? = call 表示已存储过程 </p>
				<p>
						<br />如果数据库支持已存储过程，则可从 JDBC 中调用它们，语法为：  </p>
				<p>{call procedure_name[(?, ?, . . .)]} </p>
				<p>
						<br />或（其中过程返回结果参数）：  </p>
				<p>{? = call procedure_name[(?, ?, . . .)]} </p>
				<p>
						<br />方括号指示其中的内容是可选的。它们不是语法的必要部分。 </p>
				<p>输入参数可以为文字或参数。有关详细信息，参见 JDBC 指南中第 7 节，“CallableStatement”。 </p>
				<p>可通过调用方法 DatabaseMetaData.supportsStoredProcedures 检查数据库是否支持已存储过程。 </p>
				<p>
						<br />oj 表示外部连接 </p>
				<p>
						<br />外部连接的语法为  </p>
				<p>{oj outer-join} </p>
				<p>
						<br />其中 outer-join 形式为  </p>
				<p>table LEFT OUTER JOIN {table / outer-join} ON search-condition </p>
				<p>
						<br />外部连接属于高级功能。有关它们的解释可参见 SQL 语法。JDBC 提供了三种 DatabaseMetaData 方法用于确定驱动程序支持哪些外部连接类型：supportsOuterJoins、supportsFullOuterJoins 和 supportsLimitedOuterJoins。 </p>
				<p>
						<br />方法 Statement.setEscapeProcessing 可打开或关闭转义处理；缺省状态为打开。当性能极为重要时，程序员可能想关闭它以减少处理时间。但通常它将出于打开状态。应注意： setEscapeProcessing 不适用于 PreparedStatement 对象，因为在调用该语句前它就可能已被发送到数据库。有关预编译的信息，参见 PreparedStatement。 </p>
				<p>
						<br />4.1.6 使用方法 execute <br />execute 方法应该仅在语句能返回多个 ResultSet 对象、多个更新计数或 ResultSet 对象与更新计数的组合时使用。当执行某个已存储过程或动态执行未知 SQL 字符串（即应用程序程序员在编译时未知）时，有可能出现多个结果的情况，尽管这种情况很少见。例如，用户可能执行一个已存储过程（使用 CallableStatement 对象 - 参见第 135 页的 CallableStatement），并且该已存储过程可执行更新，然后执行选择，再进行更新，再进行选择，等等。通常使用已存储过程的人应知道它所返回的内容。 </p>
				<p>因为方法 execute 处理非常规情况，所以获取其结果需要一些特殊处理并不足为怪。例如，假定已知某个过程返回两个结果集，则在使用方法 execute 执行该过程后，必须调用方法 getResultSet 获得第一个结果集，然后调用适当的 getXXX 方法获取其中的值。要获得第二个结果集，需要先调用 getMoreResults 方法，然后再调用 getResultSet 方法。如果已知某个过程返回两个更新计数，则首先调用方法 getUpdateCount，然后调用 getMoreResults，并再次调用 getUpdateCount。 </p>
				<p>对于不知道返回内容，则情况更为复杂。如果结果是 ResultSet 对象，则方法 execute 返回 true；如果结果是 Java int，则返回 false。如果返回 int，则意味着结果是更新计数或执行的语句是 DDL 命令。在调用方法 execute 之后要做的第一件事情是调用 getResultSet 或 getUpdateCount。调用方法 getResultSet 可以获得两个或多个 ResultSet 对象中第一个对象；或调用方法 getUpdateCount 可以获得两个或多个更新计数中第一个更新计数的内容。 </p>
				<p>当 SQL 语句的结果不是结果集时，则方法 getResultSet 将返回 null。这可能意味着结果是一个更新计数或没有其它结果。在这种情况下，判断 null 真正含义的唯一方法是调用方法 getUpdateCount，它将返回一个整数。这个整数为调用语句所影响的行数；如果为 -1 则表示结果是结果集或没有结果。如果方法 getResultSet 已返回 null（表示结果不是 ResultSet 对象），则返回值 -1 表示没有其它结果。也就是说，当下列条件为真时表示没有结果（或没有其它结果）：  </p>
				<p>((stmt.getResultSet() == null) &amp;&amp; (stmt.getUpdateCount() == -1)) </p>
				<p>如果已经调用方法 getResultSet 并处理了它返回的 ResultSet 对象，则有必要调用方法 getMoreResults 以确定是否有其它结果集或更新计数。如果 getMoreResults 返回 true，则需要再次调用 getResultSet 来检索下一个结果集。如上所述，如果 getResultSet 返回 null，则需要调用 getUpdateCount 来检查 null 是表示结果为更新计数还是表示没有其它结果。 </p>
				<p>当 getMoreResults 返回 false 时，它表示该 SQL 语句返回一个更新计数或没有其它结果。因此需要调用方法 getUpdateCount 来检查它是哪一种情况。在这种情况下，当下列条件为真时表示没有其它结果：  </p>
				<p>((stmt.getMoreResults() == false) &amp;&amp; (stmt.getUpdateCount() == -1)) </p>
				<p>下面的代码演示了一种方法用来确认已访问调用方法 execute 所产生的全部结果集和更新计数：  </p>
				<p>
						<br />stmt.execute(queryStringWithUnknownResults); <br />while (true) { <br />int rowCount = stmt.getUpdateCount(); <br />if (rowCount &gt; 0) { // 它是更新计数 <br />System.out.println("Rows changed = " + count); <br />stmt.getMoreResults(); <br />continue; <br />} <br />if (rowCount == 0) { // DDL 命令或 0 个更新 <br />System.out.println(" No rows changed or statement was DDL <br />command"); <br />stmt.getMoreResults(); <br />continue; <br />} </p>
				<p>// 执行到这里，证明有一个结果集 <br />// 或没有其它结果 </p>
				<p>ResultSet rs = stmt.getResultSet; <br />if (rs != null) { <br />. . . // 使用元数据获得关于结果集列的信息 <br />while (rs.next()) { <br />. . . // 处理结果 <br />stmt.getMoreResults(); <br />continue; <br />} <br />break; // 没有其它结果 </p>
				<br />
				<br />
				<p id="TBPingURL">Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=630445</p>
		</div>
<img src ="http://www.blogjava.net/iKingQu/aggbug/36760.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/iKingQu/" target="_blank">風向逆轉 - 就要爪哇</a> 2006-03-22 03:30 <a href="http://www.blogjava.net/iKingQu/articles/36760.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[收藏]JDBC系列教程（二）---驱动设置</title><link>http://www.blogjava.net/iKingQu/articles/36759.html</link><dc:creator>風向逆轉 - 就要爪哇</dc:creator><author>風向逆轉 - 就要爪哇</author><pubDate>Tue, 21 Mar 2006 19:28:00 GMT</pubDate><guid>http://www.blogjava.net/iKingQu/articles/36759.html</guid><wfw:comment>http://www.blogjava.net/iKingQu/comments/36759.html</wfw:comment><comments>http://www.blogjava.net/iKingQu/articles/36759.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/iKingQu/comments/commentRss/36759.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/iKingQu/services/trackbacks/36759.html</trackback:ping><description><![CDATA[
		<div class="postText">
				<p>
						<span class="Title">JDBC系列教程（二）---驱动设置 </span>
				</p>
				<p>DriverManager <br />本概述摘自《JDBCTM Database Access from JavaTM: A Tutorial and Annotated Reference》，目前正由 JavaSoft 继续编写。这本书既是 JDBC 的教程，也是一本权威性参考手册，将作为 Java 系列的一部分在 1997 年春季由 Addison-Wesley 出版公司出版。  </p>
				<p>3.1 概述 <br />DriverManager 类是 JDBC 的管理层，作用于用户和驱动程序之间。它跟踪可用的驱动程序，并在数据库和相应驱动程序之间建立连接。另外，DriverManager 类也处理诸如驱动程序登录时间限制及登录和跟踪消息的显示等事务。 </p>
				<p>对于简单的应用程序，一般程序员需要在此类中直接使用的唯一方法是 DriverManager.getConnection。正如名称所示，该方法将建立与数据库的连接。JDBC 允许用户调用 DriverManager 的方法 getDriver、getDrivers 和 registerDriver 及 Driver 的方法 connect。但多数情况下，让 DriverManager 类管理建立连接的细节为上策。 </p>
				<p>
						<br />3.1.1 跟踪可用驱动程序 <br />DriverManager 类包含一列 Driver 类，它们已通过调用方法 DriverManager.registerDriver 对自己进行了注册。所有 Driver 类都必须包含有一个静态部分。它创建该类的实例，然后在加载该实例时 DriverManager 类进行注册。这样，用户正常情况下将不会直接调用 DriverManager.registerDriver；而是在加载驱动程序时由驱动程序自动调用。加载 Driver 类，然后自动在 DriverManager 中注册的方式有两种：  </p>
				<p>
						<br />通过调用方法 Class.forName。这将显式地加载驱动程序类。由于这与外部设置无关，因此推荐使用这种加载驱动程序的方法。以下代码加载类 acme.db.Driver：  <br />Class.forName("acme.db.Driver"); </p>
				<p>如果将 acme.db.Driver 编写为加载时创建实例，并调用以该实例为参数的 DriverManager.registerDriver（本该如此），则它在 DriverManager 的驱动程序列表中，并可用于创建连接。 </p>
				<p>
						<br />通过将驱动程序添加到 java.lang.System 的属性 jdbc.drivers 中。这是一个由 DriverManager 类加载的驱动程序类名的列表，由冒号分隔：初始化 DriverManager 类时，它搜索系统属性 jdbc.drivers，如果用户已输入了一个或多个驱动程序，则 DriverManager 类将试图加载它们。以下代码说明程序员如何在 ~/.hotjava/properties 中输入三个驱动程序类（启动时，HotJava 将把它加载到系统属性列表中）：  <br />jdbc.drivers=foo.bah.Driver:wombat.sql.Driver:bad.test.ourDriver; </p>
				<p>对 DriverManager 方法的第一次调用将自动加载这些驱动程序类。 </p>
				<p>注意：加载驱动程序的第二种方法需要持久的预设环境。如果对这一点不能保证，则调用方法 Class.forName 显式地加载每个驱动程序就显得更为安全。这也是引入特定驱动程序的方法，因为一旦 DriverManager 类被初始化，它将不再检查 jdbc.drivers 属性列表。 </p>
				<p>在以上两种情况中，新加载的 Driver 类都要通过调用 DriverManager.registerDriver 类进行自我注册。如上所述，加载类时将自动执行这一过程。 </p>
				<p>由于安全方面的原因，JDBC 管理层将跟踪哪个类加载器提供哪个驱动程序。这样，当 DriverManager 类打开连接时，它仅使用本地文件系统或与发出连接请求的代码相同的类加载器提供的驱动程序。 </p>
				<p>
						<br />3.1.2 建立连接 <br />加载 Driver 类并在 DriverManager 类中注册后，它们即可用来与数据库建立连接。当调用 DriverManager.getConnection 方法发出连接请求时，DriverManager 将检查每个驱动程序，查看它是否可以建立连接。 </p>
				<p>有时可能有多个 JDBC 驱动程序可以与给定的 URL 连接。例如，与给定远程数据库连接时，可以使用 JDBC-ODBC 桥驱动程序、JDBC 到通用网络协议驱动程序或数据库厂商提供的驱动程序。在这种情况下，测试驱动程序的顺序至关重要，因为 DriverManager 将使用它所找到的第一个可以成功连接到给定 URL 的驱动程序。 </p>
				<p>首先 DriverManager 试图按注册的顺序使用每个驱动程序（jdbc.drivers 中列出的驱动程序总是先注册）。它将跳过代码不可信任的驱动程序，除非加载它们的源与试图打开连接的代码的源相同。 </p>
				<p>它通过轮流在每个驱动程序上调用方法 Driver.connect，并向它们传递用户开始传递给方法 DriverManager.getConnection 的 URL 来对驱动程序进行测试，然后连接第一个认出该 URL 的驱动程序。 </p>
				<p>这种方法初看起来效率不高，但由于不可能同时加载数十个驱动程序，因此每次连接实际只需几个过程调用和字符串比较。 </p>
				<p>以下代码是通常情况下用驱动程序（例如 JDBC-ODBC 桥驱动程序）建立连接所需所有步骤的示例：  </p>
				<p>Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); //加载驱动程序 <br />String url = "jdbc:odbc:fred"; <br />DriverManager.getConnection(url, "userID", "passwd"); </p>
				<br />
				<br />
				<p id="TBPingURL">Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=630442</p>
		</div>
<img src ="http://www.blogjava.net/iKingQu/aggbug/36759.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/iKingQu/" target="_blank">風向逆轉 - 就要爪哇</a> 2006-03-22 03:28 <a href="http://www.blogjava.net/iKingQu/articles/36759.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[收藏]JDBC系列教程（一）---连接</title><link>http://www.blogjava.net/iKingQu/articles/36758.html</link><dc:creator>風向逆轉 - 就要爪哇</dc:creator><author>風向逆轉 - 就要爪哇</author><pubDate>Tue, 21 Mar 2006 19:27:00 GMT</pubDate><guid>http://www.blogjava.net/iKingQu/articles/36758.html</guid><wfw:comment>http://www.blogjava.net/iKingQu/comments/36758.html</wfw:comment><comments>http://www.blogjava.net/iKingQu/articles/36758.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/iKingQu/comments/commentRss/36758.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/iKingQu/services/trackbacks/36758.html</trackback:ping><description><![CDATA[
		<div class="postText">
				<p>
						<span class="Title">JDBC系列教程（一）---连接</span>
				</p>
				<p>连接 <br />本概述是从《JDBCTM Database Access from JavaTM: A Tutorial and Annotated Reference 》这本书中摘引来的。JavaSoft 目前正在准备这本书。这本书是一本教程，同时也是 JDBC 的重要参考手册，它将作为 Java 系列的组成部份在 1997 年春季由 Addison-Wesley 出版公司出版。  </p>
				<p>
						<br />2.1 概述 <br />Connection 对象代表与数据库的连接。连接过程包括所执行的 SQL 语句和在该连接上所返回的结果。一个应用程序可与单个数据库有一个或多个连接，或者可与许多数据库有连接。 </p>
				<p>
						<br />2.1.1 打开连接 <br />与数据库建立连接的标准方法是调用 DriverManager.getConnection 方法。该方法接受含有某个 URL 的字符串。DriverManager 类（即所谓的 JDBC 管理层）将尝试找到可与那个 URL 所代表的数据库进行连接的驱动程序。DriverManager 类存有已注册的 Driver 类的清单。当调用方法 getConnection 时，它将检查清单中的每个驱动程序，直到找到可与 URL 中指定的数据库进行连接的驱动程序为止。Driver 的方法 connect 使用这个 URL 来建立实际的连接。 </p>
				<p>用户可绕过 JDBC 管理层直接调用 Driver 方法。这在以下特殊情况下将很有用：当两个驱动器可同时连接到数据库中，而用户需要明确地选用其中特定的驱动器。但一般情况下，让 DriverManager 类处理打开连接这种事将更为简单。 </p>
				<p>下述代码显示如何打开一个与位于 URL "jdbc:odbc:wombat" 的数据库的连接。所用的用户标识符为 "oboy" ，口令为 "12Java"：  </p>
				<p>String url = "jdbc:odbc:wombat"; <br />Connection con = DriverManager.getConnection(url, "oboy", "12Java"); </p>
				<p>2.1.2 一般用法的 URL <br />由于 URL 常引起混淆，我们将先对一般 URL 作简单说明，然后再讨论 JDBC URL。 </p>
				<p>URL（统一资源定位符）提供在 Internet 上定位资源所需的信息。可将它想象为一个地址。 </p>
				<p>URL 的第一部份指定了访问信息所用的协议，后面总是跟着冒号。常用的协议有 "ftp"（代表“文件传输协议”）和 "http" （代表“超文本传输协议”）。如果协议是 "file"，表示资源是在某个本地文件系统上而非在 Internet 上（下例用于表示我们所描述的部分；它并非 URL 的组成部分）。 </p>
				<p>
						<a href="ftp://javasoft.com/docs/JDK-1_apidocs.zip">ftp://javasoft.com/docs/JDK-1_apidocs.zip</a>
						<br />
						<a href="http://java.sun.com/products/jdk/CurrentRelease">http://java.sun.com/products/jdk/CurrentRelease</a>
						<br />file:/home/haroldw/docs/books/tutorial/summary.html </p>
				<p>URL 的其余部份（冒号后面的）给出了数据资源所处位置的有关信息。如果协议是 file，则 URL 的其余部份是文件的路径。对于 ftp 和 http 协议，URL 的其余部份标识了主机并可选地给出某个更详尽的地址路径。例如，以下是 JavaSoft 主页的 URL。该 URL 只标识了主机：  </p>
				<p>
						<a href="http://java.sun.com/">http://java.sun.com</a>
				</p>
				<p>从该主页开始浏览，就可以进到许多其它的网页中，其中之一就是 JDBC 主页。JDBC 主页的 URL 更为具体，它看起来类似：  </p>
				<p>
						<a href="http://java.sun.com/products/jdbc">http://java.sun.com/products/jdbc</a>
				</p>
				<p>2.1.3 JDBC URL  <br />JDBC URL 提供了一种标识数据库的方法，可以使相应的驱动程序能识别该数据库并与之建立连接。实际上，驱动程序编程员将决定用什么 JDBC URL 来标识特定的驱动程序。用户不必关心如何来形成 JDBC URL；他们只须使用与所用的驱动程序一起提供的 URL 即可。JDBC 的作用是提供某些约定，驱动程序编程员在构造他们的 JDBC URL 时应该遵循这些约定。 </p>
				<p>由于 JDBC URL 要与各种不同的驱动程序一起使用，因此这些约定应非常灵活。首先，它们应允许不同的驱动程序使用不同的方案来命名数据库。例如， odbc 子协议允许（但并不是要求） URL 含有属性值。 </p>
				<p>第二，JDBC URL 应允许驱动程序编程员将一切所需的信息编入其中。这样就可以让要与给定数据库对话的 applet 打开数据库连接，而无须要求用户去做任何系统管理工作。 </p>
				<p>第三， JDBC URL 应允许某种程度的间接性。也就是说，JDBC URL 可指向逻辑主机或数据库名，而这种逻辑主机或数据库名将由网络命名系统动态地转换为实际的名称。这可以使系统管理员不必将特定主机声明为 JDBC 名称的一部份。网络命名服务（例如 DNS、 NIS 和 DCE ）有多种,而对于使用哪种命名服务并无限制。 </p>
				<p>JDBC URL 的标准语法如下所示。它由三部分组成，各部分间用冒号分隔：  </p>
				<p>jdbc:&lt; 子协议 &gt;:&lt; 子名称 &gt; </p>
				<p>JDBC URL 的三个部分可分解如下：  </p>
				<p>
						<br />jdbc ─ 协议。JDBC URL 中的协议总是 jdbc。 </p>
				<p>
						<br />&lt;子协议&gt; ─ 驱动程序名或数据库连接机制（这种机制可由一个或多个驱动程序支持）的名称。子协议名的典型示例是 "odbc"，该名称是为用于指定 ODBC 风格的数据资源名称的 URL 专门保留的。例如，为了通过 JDBC-ODBC 桥来访问某个数据库，可以用如下所示的 URL：  <br />jdbc:odbc:fred </p>
				<p>本例中，子协议为 "odbc"，子名称 "fred" 是本地 <br />ODBC 数据资源。 </p>
				<p>如果要用网络命名服务（这样 JDBC URL 中的数据库名称不必是实际名称），则命名服务可以作为子协议。例如，可用如下所示的 URL ：  </p>
				<p>jdbc:dcenaming:accounts-payable </p>
				<p>本例中，该 URL 指定了本地 DCE 命名服务应该将 <br />数据库名称 "accounts-payable" 解析为更为具体的 <br />可用于连接真实数据库的名称。 </p>
				<p>
						<br />&lt;子名称&gt; ─ 一种标识数据库的方法。子名称可以依不同的子协议而变化。它还可以有子名称的子名称（含有驱动程序编程员所选的任何内部语法）。使用子名称的目的是为定位数据库提供足够的信息。前例中，因为 ODBC 将提供其余部份的信息，因此用 "fred" 就已足够。然而，位于远程服务器上的数据库需要更多的信息。例如，如果数据库是通过 Internet 来访问的，则在 JDBC URL 中应将网络地址作为子名称的一部份包括进去，且必须遵循如下所示的标准 URL 命名约定：  <br />//主机名:端口/子协议 </p>
				<p>假设 "dbnet" 是个用于将某个主机连接到 Internet 上的协议，则 JDBC URL 类似： </p>
				<p>jdbc:dbnet://wombat:356/fred  </p>
				<p>2.1.4 "odbc" 子协议 <br />子协议 odbc 是一种特殊情况。它是为用于指定 ODBC 风格的数据资源名称的 URL 而保留的，并具有下列特性：允许在子名称（数据资源名称）后面指定任意多个属性值。odbc 子协议的完整语法为：  </p>
				<p>
						<br />jdbc:odbc:&lt; 数据资源名称 &gt;[;&lt; 属性名 &gt;=&lt; 属性值 &gt;]* </p>
				<p>因此，以下都是合法的 jdbc:odbc 名称：  </p>
				<p>jdbc:odbc:qeor7 <br />jdbc:odbc:wombat <br />jdbc:odbc:wombat;CacheSize=20;ExtensionCase=LOWER <br />jdbc:odbc:qeora;UID=kgh;PWD=fooey </p>
				<p>2.1.5 注册子协议 <br />驱动程序编程员可保留某个名称以将之用作 JDBC URL 的子协议名。当 DriverManager 类将此名称加到已注册的驱动程序清单中时，为之保留该名称的驱动程序应能识别该名称并与它所标识的数据库建立连接。例如，odbc 是为 JDBC- ODBC 桥而保留的。示例之二，假设有个 Miracle 公司，它可能会将 "miracle" 注册为连接到其 Miracle DBMS 上的 JDBC 驱动程序的子协议，从而使其他人都无法使用这个名称。 </p>
				<p>JavaSoft 目前作为非正式代理负责注册 JDBC 子协议名称。要注册某个子协议名称，请发送电子邮件到下述地址：  </p>
				<p>
						<a href="mailto:jdbc@wombat.eng.sun.com">jdbc@wombat.eng.sun.com</a>
				</p>
				<p>2.1.6 发送 SQL 语句 <br />连接一旦建立，就可用来向它所涉及的数据库传送 SQL 语句。JDBC 对可被发送的 SQL 语句类型不加任何限制。这就提供了很大的灵活性，即允许使用特定的数据库语句或甚至于非 SQL 语句。然而，它要求用户自己负责确保所涉及的数据库可以处理所发送的 SQL 语句，否则将自食其果。例如，如果某个应用程序试图向不支持储存程序的 DBMS 发送储存程序调用，就会失败并将抛出异常。JDBC 要求驱动程序应至少能提供 ANSI SQL-2 Entry Level 功能才可算是符合 JDBC 标准TM 的。这意味着用户至少可信赖这一标准级别的功能。 </p>
				<p>JDBC 提供了三个类，用于向数据库发送 SQL 语句。Connection 接口中的三个方法可用于创建这些类的实例。下面列出这些类及其创建方法：  </p>
				<p>
						<br />Statement ─ 由方法 createStatement 所创建。Statement 对象用于发送简单的 SQL 语句。  <br />PreparedStatement ─ 由方法 prepareStatement 所创建。PreparedStatement 对象用于发送带有一个或多个输入参数（ IN 参数）的 SQL 语句。PreparedStatement 拥有一组方法，用于设置 IN 参数的值。执行语句时，这些 IN 参数将被送到数据库中。PreparedStatement 的实例扩展了 Statement ，因此它们都包括了 Statement 的方法。PreparedStatement 对象有可能比 Statement 对象的效率更高，因为它已被预编译过并存放在那以供将来使用。  <br />CallableStatement ─ 由方法 prepareCall 所创建。CallableStatement 对象用于执行 SQL 储存程序 ─ 一组可通过名称来调用（就象函数的调用那样）的 SQL 语句。CallableStatement 对象从 PreparedStatement 中继承了用于处理 IN 参数的方法，而且还增加了用于处理 OUT 参数和 INOUT 参数的方法。  </p>
				<p>以下所列提供的方法可以快速决定应用哪个 Connection 方法来创建不同类型的 SQL 语句：  </p>
				<p>
						<br />createStatement 方法用于： </p>
				<p>
						<br />简单的 SQL 语句（不带参数）  </p>
				<p>
						<br />prepareStatement 方法用于：  </p>
				<p>
						<br />带一个或多个 IN 参数的 SQL 语句  </p>
				<p>
						<br />经常被执行的简单 SQL 语句 </p>
				<p>
						<br />prepareCall 方法用于：  </p>
				<p>
						<br />调用已储存过程 </p>
				<p>
						<br />2.1.7 事务 <br />事务由一个或多个这样的语句组成：这些语句已被执行、完成并被提交或还原。当调用方法 commit 或 rollback 时，当前事务即告就结束，另一个事务随即开始。 </p>
				<p>缺省情况下，新连接将处于自动提交模式。也就是说，当执行完语句后，将自动对那个语句调用 commit 方法。这种情况下，由于每个语句都是被单独提交的，因此一个事务只由一个语句组成。如果禁用自动提交模式，事务将要等到 commit 或 rollback 方法被显式调用时才结束，因此它将包括上一次调用 commit 或 rollback 方法以来所有执行过的语句。对于第二种情况，事务中的所有语句将作为组来提交或还原。 </p>
				<p>方法 commit 使 SQL 语句对数据库所做的任何更改成为永久性的，它还将释放事务持有的全部锁。而方法 rollback 将弃去那些更改。 </p>
				<p>有时用户在另一个更改生效前不想让此更改生效。这可通过禁用自动提交并将两个更新组合在一个事务中来达到。如果两个更新都是成功的，则调用 commit 方法，从而使两个更新结果成为永久性的；如果其中之一或两个更新都失败了，则调用 rollback 方法，以将值恢复为进行更新之前的值。 </p>
				<p>大多数 JDBC 驱动程序都支持事务。事实上，符合 JDBC 的驱动程序必须支持事务。DatabaseMetaData 给出的信息描述 DBMS 所提供的事务支持水平。 </p>
				<p>
						<br />2.1.8 事务隔离级别 <br />如果 DBMS 支持事务处理，它必须有某种途径来管理两个事务同时对一个数据库进行操作时可能发生的冲突。用户可指定事务隔离级别，以指明 DBMS 应该花多大精力来解决潜在冲突。例如，当事务更改了某个值而第二个事务却在该更改被提交或还原前读取该值时该怎么办？ 假设第一个事务被还原后，第二个事务所读取的更改值将是无效的，那么是否可允许这种冲突？ JDBC 用户可用以下代码来指示 DBMS 允许在值被提交前读取该值（“dirty 读取”），其中 con 是当前连接：  </p>
				<p>con.setTransactionIsolation(TRANSACTION_READ_UNCOMMITTED); </p>
				<p>事务隔离级别越高，为避免冲突所花的精力也就越多。Connection 接口定义了五级，其中最低级别指定了根本就不支持事务，而最高级别则指定当事务在对某个数据库进行操作时，任何其它事务不得对那个事务正在读取的数据进行任何更改。通常，隔离级别越高，应用程序执行的速度也就越慢（由于用于锁定的资源耗费增加了，而用户间的并发操作减少了）。在决定采用什么隔离级别时，开发人员必须在性能需求和数据一致性需求之间进行权衡。当然，实际所能支持的级别取决于所涉及的 DBMS 的功能。 </p>
				<p>当创建 Connection 对象时，其事务隔离级别取决于驱动程序，但通常是所涉及的数据库的缺省值。用户可通过调用 setIsolationLevel 方法来更改事务隔离级别。新的级别将在该连接过程的剩余时间内生效。要想只改变一个事务的事务隔离级别，必须在该事务开始前进行设置，并在该事务结束后进行复位。我们不提倡在事务的中途对事务隔离级别进行更改，因为这将立即触发 commit 方法的调用，使在此之前所作的任何更改变成永久性的。  </p>
				<br />
				<br />
				<p id="TBPingURL">Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=630439</p>
		</div>
<img src ="http://www.blogjava.net/iKingQu/aggbug/36758.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/iKingQu/" target="_blank">風向逆轉 - 就要爪哇</a> 2006-03-22 03:27 <a href="http://www.blogjava.net/iKingQu/articles/36758.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[收藏]Java程序员的存储过程</title><link>http://www.blogjava.net/iKingQu/articles/36757.html</link><dc:creator>風向逆轉 - 就要爪哇</dc:creator><author>風向逆轉 - 就要爪哇</author><pubDate>Tue, 21 Mar 2006 19:26:00 GMT</pubDate><guid>http://www.blogjava.net/iKingQu/articles/36757.html</guid><wfw:comment>http://www.blogjava.net/iKingQu/comments/36757.html</wfw:comment><comments>http://www.blogjava.net/iKingQu/articles/36757.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/iKingQu/comments/commentRss/36757.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/iKingQu/services/trackbacks/36757.html</trackback:ping><description><![CDATA[
		<div class="postText">
				<p>
						<span class="Title">Java程序员的存储过程 <br /></span>--------------------------------------------------------------------------------</p>
				<p>原文：<a href="http://www.onjava.com/pub/a/onjava/2003/08/13/stored_procedures.html">http://www.onjava.com/pub/a/onjava/2003/08/13/stored_procedures.html</a><br />by Nic Ferrier </p>
				<p>本文阐述了怎么使用DBMS存储过程。我阐述了使用存储过程的基本的和高级特性，比如返回ResultSet。本文假设你对DBMS和JDBC已经非常熟悉，也假设你能够毫无障碍地阅读其它语言写成的代码（即不是Java的语言），但是，并不要求你有任何存储过程的编程经历。 <br />存储过程是指保存在数据库并在数据库端执行的程序。你可以使用特殊的语法在Java类中调用存储过程。在调用时，存储过程的名称及指定的参数通过JDBC连接发送给DBMS，执行存储过程并通过连接（如果有）返回结果。 <br />使用存储过程拥有和使用基于EJB或CORBA这样的应用服务器一样的好处。区别是存储过程可以从很多流行的DBMS中免费使用，而应用服务器大都非常昂贵。这并不只是许可证费用的问题。使用应用服务器所需要花费的管理、编写代码的费用，以及客户程序所增加的复杂性，都可以通过DBMS中的存储过程所整个地替代。 <br />你可以使用Java，Python，Perl或C编写存储过程，但是通常使用你的DBMS所指定的特定语言。Oracle使用PL/SQL，PostgreSQL使用pl/pgsql，DB2使用Procedural SQL。这些语言都非常相似。在它们之间移植存储过程并不比在Sun的EJB规范不同实现版本之间移植Session Bean困难。并且，存储过程是为嵌入SQL所设计，这使得它们比Java或C等语言更加友好地方式表达数据库的机制。 <br />因为存储过程运行在DBMS自身，这可以帮助减少应用程序中的等待时间。不是在Java代码中执行4个或5个SQL语句，而只需要在服务器端执行1个存储过程。网络上的数据往返次数的减少可以戏剧性地优化性能。 </p>
				<p>使用存储过程 </p>
				<p>简单的老的JDBC通过CallableStatement类支持存储过程的调用。该类实际上是PreparedStatement的一个子类。假设我们有一个poets数据库。数据库中有一个设置诗人逝世年龄的存储过程。下面是对老酒鬼Dylan Thomas（old soak Dylan Thomas，不指定是否有关典故、文化，请批评指正。译注）进行调用的详细代码： </p>
				<p>
						<font color="#003366">try<br />{<br />    int age = 39;<br />    String poetName = "dylan thomas";<br />    CallableStatement proc =<br />        connection.prepareCall("{ call set_death_age(?, ?) }");<br />    proc.setString(1, poetName);<br />    proc.setInt(2, age);<br />    cs.execute();<br />}<br />catch (SQLException e)<br />{<br />    // ....<br />}</font>
				</p>
				<p>传给prepareCall方法的字串是存储过程调用的书写规范。它指定了存储过程的名称，？代表了你需要指定的参数。 <br />和JDBC集成是存储过程的一个很大的便利：为了从应用中调用存储过程，不需要存根（stub）类或者配置文件，除了你的DBMS的JDBC驱动程序外什么也不需要。 <br />当这段代码执行时，数据库的存储过程就被调用。我们没有去获取结果，因为该存储过程并不返回结果。执行成功或失败将通过例外得知。失败可能意味着调用存储过程时的失败（比如提供的一个参数的类型不正确），或者一个应用程序的失败（比如抛出一个例外指示在poets数据库中并不存在“Dylan Thomas”） </p>
				<p>结合SQL操作与存储过程 </p>
				<p>映射Java对象到SQL表中的行相当简单，但是通常需要执行几个SQL语句；可能是一个SELECT查找ID，然后一个INSERT插入指定ID的数据。在高度规格化（符合更高的范式，译注）的数据库模式中，可能需要多个表的更新，因此需要更多的语句。Java代码会很快地膨胀，每一个语句的网络开销也迅速增加。 <br />将这些SQL语句转移到一个存储过程中将大大简化代码，仅涉及一次网络调用。所有关联的SQL操作都可以在数据库内部发生。并且，存储过程语言，例如PL/SQL，允许使用SQL语法，这比Java代码更加自然。下面是我们早期的存储过程，使用Oracle的PL/SQL语言编写： </p>
				<p>
						<font color="#003366">create procedure set_death_age(poet VARCHAR2, poet_age NUMBER)<br />    poet_id NUMBER;<br />begin<br />  SELECT id INTO poet_id FROM poets WHERE name = poet;<br />  INSERT INTO deaths (mort_id, age) VALUES (poet_id, poet_age);<br />end set_death_age;</font>
				</p>
				<p>很独特？不。我打赌你一定期待看到一个poets表上的UPDATE。这也暗示了使用存储过程实现是多么容易的一件事情。set_death_age几乎可以肯定是一个很烂的实现。我们应该在poets表中添加一列来存储逝世年龄。Java代码中并不关心数据库模式是怎么实现的，因为它仅调用存储过程。我们以后可以改变数据库模式以提高性能，但是我们不必修改我们代码。 <br />下面是调用上面存储过程的Java代码： </p>
				<p>
						<font color="#003366">public static void setDeathAge(Poet dyingBard, int age)<br />    throws SQLException<br />{<br />   Connection con = null;<br />   CallableStatement proc = null;<br /><br />   try<br />   {<br />      con  = connectionPool.getConnection();<br />      proc = con.prepareCall("{ call set_death_age(?, ?) }");<br />      proc.setString(1, dyingBard.getName());<br />      proc.setInt(2, age);<br />      proc.execute();<br />   }<br />   finally<br />   {<br />      try<br />      {<br />         proc.close();<br />      }<br />      catch (SQLException e) {}<br />      con.close();<br />   }<br />}</font>
				</p>
				<p>为了确保可维护性，建议使用像这儿这样的static方法。这也使得调用存储过程的代码集中在一个简单的模版代码中。如果你用到许多存储过程，就会发现仅需要拷贝、粘贴就可以创建新的方法。因为代码的模版化，甚至也可以通过脚本自动生产调用存储过程的代码。 </p>
				<p>Functions </p>
				<p>存储过程可以有返回值，所以CallableStatement类有类似getResultSet这样的方法来获取返回值。当存储过程返回一个值时，你必须使用registerOutParameter方法告诉JDBC驱动器该值的SQL类型是什么。你也必须调整存储过程调用来指示该过程返回一个值。 <br />下面接着上面的例子。这次我们查询Dylan Thomas逝世时的年龄。这次的存储过程使用PostgreSQL的pl/pgsql： </p>
				<p>
						<font color="#003366">create function snuffed_it_when (VARCHAR) returns integer '<br />declare<br />    poet_id NUMBER;<br />    poet_age NUMBER;<br />begin<br />    -- first get the id associated with the poet.<br />    SELECT id INTO poet_id FROM poets WHERE name = $1;<br />    -- get and return the age.<br />    SELECT age INTO poet_age FROM deaths WHERE mort_id = poet_id;<br />    return age;<br />end;<br />' language 'pl/pgsql';</font>
				</p>
				<p>另外，注意pl/pgsql参数名通过Unix和DOS脚本的$n语法引用。同时，也注意嵌入的注释，这是和Java代码相比的另一个优越性。在Java中写这样的注释当然是可以的，但是看起来很凌乱，并且和SQL语句脱节，必须嵌入到Java String中。 <br />下面是调用这个存储过程的Java代码： </p>
				<p>
						<font color="#003366">connection.setAutoCommit(false);<br />CallableStatement proc =<br />    connection.prepareCall("{ ? = call snuffed_it_when(?) }");<br />proc.registerOutParameter(1, Types.INTEGER);<br />proc.setString(2, poetName);<br />cs.execute();<br />int age = proc.getInt(2);</font>
				</p>
				<p>如果指定了错误的返回值类型会怎样？那么，当调用存储过程时将抛出一个RuntimeException，正如你在ResultSet操作中使用了一个错误的类型所碰到的一样。 </p>
				<p>复杂的返回值 </p>
				<p>关于存储过程的知识，很多人好像就熟悉我们所讨论的这些。如果这是存储过程的全部功能，那么存储过程就不是其它远程执行机制的替换方案了。存储过程的功能比这强大得多。 <br />当你执行一个SQL查询时，DBMS创建一个叫做cursor（游标）的数据库对象，用于在返回结果中迭代每一行。ResultSet是当前时间点的游标的一个表示。这就是为什么没有缓存或者特定数据库的支持，你只能在ResultSet中向前移动。 <br />某些DBMS允许从存储过程中返回游标的一个引用。JDBC并不支持这个功能，但是Oracle、PostgreSQL和DB2的JDBC驱动器都支持在ResultSet上打开到游标的指针（pointer）。 <br />设想列出所有没有活到退休年龄的诗人，下面是完成这个功能的存储过程，返回一个打开的游标，同样也使用PostgreSQL的pl/pgsql语言： </p>
				<p>
						<font color="#003366">create procedure list_early_deaths () return refcursor as '<br />declare<br />    toesup refcursor;<br />begin<br />    open toesup for<br />        SELECT poets.name, deaths.age<br />        FROM poets, deaths<br />        -- all entries in deaths are for poets.<br />        -- but the table might become generic.<br />        WHERE poets.id = deaths.mort_id<br />            AND deaths.age &lt; 60;<br />    return toesup;<br />end;<br />' language 'plpgsql';</font>
				</p>
				<p>下面是调用该存储过程的Java方法，将结果输出到PrintWriter： <br />PrintWriter: </p>
				<p>
						<font color="#003366">static void sendEarlyDeaths(PrintWriter out)<br />{<br />    Connection con = null;<br />    CallableStatement toesUp = null;<br />    try<br />    {<br />        con = ConnectionPool.getConnection();<br /><br />        // PostgreSQL needs a transaction to do this...<br />        con.setAutoCommit(false);<br /><br />        // Setup the call.<br />        CallableStatement toesUp<br />            = connection.prepareCall("{ ? = call list_early_deaths () }");<br />        toesUp.registerOutParameter(1, Types.OTHER);<br />        getResults.execute();<br /><br />        ResultSet rs = (ResultSet) getResults.getObject(1);<br />        while (rs.next())<br />        {<br />            String name = rs.getString(1);<br />            int age = rs.getInt(2);<br />            out.println(name + " was " + age + " years old.");<br />        }<br />        rs.close();<br />    }<br />    catch (SQLException e)<br />    {<br />        // We should protect these calls.<br />        toesUp.close();<br />        con.close();<br />    }<br />}</font>
				</p>
				<p>因为JDBC并不直接支持从存储过程中返回游标，我们使用Types.OTHER来指示存储过程的返回类型，然后调用getObject()方法并对返回值进行强制类型转换。 <br />这个调用存储过程的Java方法是mapping的一个好例子。Mapping是对一个集上的操作进行抽象的方法。不是在这个过程上返回一个集，我们可以把操作传送进去执行。本例中，操作就是把ResultSet打印到一个输出流。这是一个值得举例的很常用的例子，下面是调用同一个存储过程的另外一个方法实现： </p>
				<p>
						<font color="#003366">public class ProcessPoetDeaths<br />{<br />    public abstract void sendDeath(String name, int age);<br />}<br /><br />static void mapEarlyDeaths(ProcessPoetDeaths mapper)<br />{<br />    Connection con = null;<br />    CallableStatement toesUp = null;<br />    try<br />    {<br />        con = ConnectionPool.getConnection();<br />        con.setAutoCommit(false);<br /><br />        CallableStatement toesUp<br />            = connection.prepareCall("{ ? = call list_early_deaths () }");<br />        toesUp.registerOutParameter(1, Types.OTHER);<br />        getResults.execute();<br /><br />        ResultSet rs = (ResultSet) getResults.getObject(1);<br />        while (rs.next())<br />        {<br />            String name = rs.getString(1);<br />            int age = rs.getInt(2);<br />            mapper.sendDeath(name, age);<br />        }<br />        rs.close();<br />    }<br />    catch (SQLException e)<br />    {<br />        // We should protect these calls.<br />        toesUp.close();<br />        con.close();<br />    }<br />}</font>
				</p>
				<p>这允许在ResultSet数据上执行任意的处理，而不需要改变或者复制获取ResultSet的方法： </p>
				<p>
						<font color="#003366">static void sendEarlyDeaths(final PrintWriter out)<br />{<br />    ProcessPoetDeaths myMapper = new ProcessPoetDeaths()<br />    {<br />        public void sendDeath(String name, int age)<br />        {<br />            out.println(name + " was " + age + " years old.");<br />        }<br />    };<br />    mapEarlyDeaths(myMapper);<br />}</font>
				</p>
				<p>这个方法使用ProcessPoetDeaths的一个匿名实例调用mapEarlyDeaths。该实例拥有sendDeath方法的一个实现，和我们上面的例子一样的方式把结果写入到输出流。当然，这个技巧并不是存储过程特有的，但是和存储过程中返回的ResultSet结合使用，是一个非常强大的工具。 </p>
				<p>结论 </p>
				<p>存储过程可以帮助你在代码中分离逻辑，这基本上总是有益的。这个分离的好处有： <br />&amp;#8226; 快速创建应用，使用和应用一起改变和改善的数据库模式。  <br />&amp;#8226; 数据库模式可以在以后改变而不影响Java对象，当我们完成应用后，可以重新设计更好的模式。 <br />&amp;#8226; 存储过程通过更好的SQL嵌入使得复杂的SQL更容易理解。 <br />&amp;#8226; 编写存储过程比在Java中编写嵌入的SQL拥有更好的工具－－大部分编辑器都提供语法高亮！ <br />&amp;#8226; 存储过程可以在任何SQL命令行中测试，这使得调试更加容易。 </p>
				<p>并不是所有的数据库都支持存储过程，但是存在许多很棒的实现，包括免费/开源的和非免费的，所以移植并不是一个问题。Oracle、PostgreSQL和DB2都有类似的存储过程语言，并且有在线的社区很好地支持。 <br />存储过程工具很多，有像TOAD或TORA这样的编辑器、调试器和IDE，提供了编写、维护PL/SQL或pl/pgsql的强大的环境。 <br />存储过程确实增加了你的代码的开销，但是它们和大多数的应用服务器相比，开销小得多。如果你的代码复杂到需要使用DBMS，我建议整个采用存储过程的方式。 </p>
				<p>资源 </p>
				<p>&amp;#8226; JDBC specification  <br />&amp;#8226; PostgreSQL  <br />&amp;#8226; Oracle Corporation's Oracle database server  <br />&amp;#8226; IBM's DB2 database server  </p>
				<p>作者简介：Nic Ferrier 是Web应用方面的独立软件顾问。  </p>
				<br />
				<br />
				<p id="TBPingURL">Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=629847</p>
		</div>
<img src ="http://www.blogjava.net/iKingQu/aggbug/36757.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/iKingQu/" target="_blank">風向逆轉 - 就要爪哇</a> 2006-03-22 03:26 <a href="http://www.blogjava.net/iKingQu/articles/36757.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[收藏]谈谈JDBC接口技术</title><link>http://www.blogjava.net/iKingQu/articles/36756.html</link><dc:creator>風向逆轉 - 就要爪哇</dc:creator><author>風向逆轉 - 就要爪哇</author><pubDate>Tue, 21 Mar 2006 19:24:00 GMT</pubDate><guid>http://www.blogjava.net/iKingQu/articles/36756.html</guid><wfw:comment>http://www.blogjava.net/iKingQu/comments/36756.html</wfw:comment><comments>http://www.blogjava.net/iKingQu/articles/36756.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/iKingQu/comments/commentRss/36756.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/iKingQu/services/trackbacks/36756.html</trackback:ping><description><![CDATA[
		<div class="postText">
				<p>
						<span class="Title">谈谈JDBC接口技术</span>
						<br />--------------------------------------------------------------------------------<br /> <br />   JDBC是一种可用于执行SQL语句的JavaAPI（ApplicationProgrammingInterface应用程序设计接口）。它由一些Java语言编写的类和界面组成。JDBC为数据库应用开发人员、数据库前台工具开发人员提供了一种标准的应用程序设计接口，使开发人员可以用纯Java语言编写完整的数据库应用程序。  </p>
				<p>    一、ODBC到JDBC的发展历程  </p>
				<p>    说到JDBC，很容易让人联想到另一个十分熟悉的字眼“ODBC”。它们之间有没有联系呢？如果有，那么它们之间又是怎样的关系呢？  </p>
				<p>    ODBC是OpenDatabaseConnectivity的英文简写。它是一种用来在相关或不相关的数据库管理系统（DBMS）中存取数据的，用C语言实现的，标准应用程序数据接口。通过ODBCAPI，应用程序可以存取保存在多种不同数据库管理系统（DBMS）中的数据，而不论每个DBMS使用了何种数据存储格式和编程接口。  </p>
				<p>    1．ODBC的结构模型  <br />    ODBC的结构包括四个主要部分：应用程序接口、驱动器管理器、数据库驱动器和数据源。  <br />    应用程序接口：屏蔽不同的ODBC数据库驱动器之间函数调用的差别，为用户提供统一的SQL编程接口。  <br />    驱动器管理器：为应用程序装载数据库驱动器。  <br />    数据库驱动器：实现ODBC的函数调用，提供对特定数据源的SQL请求。如果需要，数据库驱动器将修改应用程序的请求，使得请求符合相关的DBMS所支持的文法。  <br />    数据源：由用户想要存取的数据以及与它相关的操作系统、DBMS和用于访问DBMS的网络平台组成。  </p>
				<p>    虽然ODBC驱动器管理器的主要目的是加载数据库驱动器，以便ODBC函数调用，但是数据库驱动器本身也执行ODBC函数调用，并与数据库相互配合。因此当应用系统发出调用与数据源进行连接时，数据库驱动器能管理通信协议。当建立起与数据源的连接时，数据库驱动器便能处理应用系统向DBMS发出的请求，对分析或发自数据源的设计进行必要的翻译，并将结果返回给应用系统。  </p>
				<p>    2．JDBC的诞生  </p>
				<p>    自从Java语言于1995年5月正式公布以来，Java风靡全球。出现大量的用java语言编写的程序，其中也包括数据库应用程序。由于没有一个Java语言的API，编程人员不得不在Java程序中加入C语言的ODBC函数调用。这就使很多Java的优秀特性无法充分发挥，比如平台无关性、面向对象特性等。随着越来越多的编程人员对Java语言的日益喜爱，越来越多的公司在Java程序开发上投入的精力日益增加，对java语言接口的访问数据库的API的要求越来越强烈。也由于ODBC的有其不足之处，比如它并不容易使用，没有面向对象的特性等等，SUN公司决定开发一Java语言为接口的数据库应用程序开发接口。在JDK1．x版本中，JDBC只是一个可选部件，到了JDK1．1公布时，SQL类包（也就是JDBCAPI）就成为Java语言的标准部件。  </p>
				<p>    二、JDBC技术概述  </p>
				<p>    JDBC是一种可用于执行SQL语句的JavaAPI（ApplicationProgrammingInterface，应用程序设计接口）。它由一些Java语言写的类、界面组成。JDBC给数据库应用开发人员、数据库前台工具开发人员提供了一种标准的应用程序设计接口，使开发人员可以用纯Java语言编写完整的数据库应用程序。  </p>
				<p>    通过使用JDBC，开发人员可以很方便地将SQL语句传送给几乎任何一种数据库。也就是说，开发人员可以不必写一个程序访问Sybase，写另一个程序访问Oracle，再写一个程序访问Microsoft的SQLServer。用JDBC写的程序能够自动地将SQL语句传送给相应的数据库管理系统（DBMS）。不但如此，使用Java编写的应用程序可以在任何支持Java的平台上运行，不必在不同的平台上编写不同的应用。Java和JDBC的结合可以让开发人员在开发数据库应用时真正实现“WriteOnce，RunEverywhere！”  </p>
				<p>    Java具有健壮、安全、易用等特性，而且支持自动网上下载，本质上是一种很好的数据库应用的编程语言。它所需要的是Java应用如何同各种各样的数据库连接，JDBC正是实现这种连接的关键。  </p>
				<p>    JDBC扩展了Java的能力，如使用Java和JDBCAPI就可以公布一个Web页，页中带有能访问远端数据库的Ap?plet。或者企业可以通过JDBC让全部的职工（他们可以使用不同的操作系统，如Windwos，Machintosh和UNIX）在In?tranet上连接到几个全球数据库上，而这几个全球数据库可以是不相同的。随着越来越多的程序开发人员使用Java语言，对Java访问数据库易操作性的需求越来越强烈。  </p>
				<p>    MIS管理人员喜欢Java和JDBC，因为这样可以更容易经济地公布信息。各种已经安装在数据库中的事务处理都将继续正常运行，甚至这些事务处理是存储在不同的数据库管理系统中；而对新的数据库应用来说，开发时间将缩短，安装和版本升级将大大简化。程序员可以编写或改写一个程序，然后将它放在服务器上，而每个用户都可以访问服务器得到最新的版本。对于信息服务行业，Java和JDBC提供了一种很好的向外界用户更新信息的方法。  </p>
				<p>    1．JDBC的任务  </p>
				<p>    简单地说，JDBC能完成下列三件事：  <br />    1）同一个数据库建立连接；  <br />    2）向数据库发送SQL语句；  <br />    3）处理数据库返回的结果。  </p>
				<p>    2．JDBC一种底层的API  </p>
				<p>    JDBC是一种底层API，这意味着它将直接调用SQL命令。JDBC完全胜任这个任务，而且比其他数据库互联更加容易实现。同时它也是构造高层API和数据库开发工具的基础。高层API和数据库开发工具应该是用户界面更加友好，使用更加方便，更易于理解的。但所有这样的API将最终被翻译为象JDBC这样的底层API。目前两种基于JDBC的高层API正处在开发阶段。  </p>
				<p>    1）SQL语言嵌入Java的预处理器。虽然DBMS已经实现了SQL查询，但JDBC要求SQL语句被当作字符串参数传送给Java程序。而嵌入式SQL预处理器允许程序员将SQL语句混用：Java变量可以在SQL语句中使用，来接收或提供数值。然后SQL的预处理器将把这种Java／SQL混用的程序翻译成带有JDBCAPI的Java程序。  </p>
				<p>    2）实现从关系数据库到Java类的直接映射。Javasoft和其他公司已经宣布要实现这一技术。在这种“对象／关系”映射中，表的每一行都将变成这类的一个实例，每一列的值对应实例的一个属性。程序员可以直接操作Java的对象；而存取所需要的SQL调用将在内部直接产生。还可以实现更加复杂的映射，比如多张表的行在一个Java的类中实现。  </p>
				<p>    随着大家对JDBC兴趣的不断浓厚，越来越多的开发人员已经开始利用JDBC为基础的工具进行开发。这使开发工作变得容易。同时，程序员也正在开发对最终用户来说访问数据库更加容易的应用程序。  </p>
				<p>    3．JDBC和ODBC及其他API的比较  </p>
				<p>    到目前为止，微软的ODBC可能是用得最广泛的访问关系数据库的API。它提供了连接几乎任何一种平台、任何一种数据库的能力。那么，为什么不直接从Java中直接使用ODBC呢？  </p>
				<p>    回答是可以从Java中使用ODBC，但最好在JDBC的协助下，用JDBC－ODBC桥接器实现。那么，为什么需要JDBC呢？要回答这个问题，有这么几个方面：  </p>
				<p>    1）ODBC并不适合在Java中直接使用。ODBC是一个C语言实现的API，从Java程序调用本地的C程序会带来一系列类似安全性、完整性、健壮性的缺点。  </p>
				<p>    2）其次，完全精确地实现从C代码ODBC到JavaAPI写的ODBC的翻译也并不令人满意。比如，Java没有指针，而ODBC中大量地使用了指针，包括极易出错的空指针“void＊”。因此，对Java程序员来说，把JDBC设想成将ODBC转换成面向对象的API是很自然的。  </p>
				<p>    3）ODBC并不容易学习，它将简单特性和复杂特性混杂在一起，甚至对非常简单的查询都有复杂的选项。而JDBC刚好相反，它保持了简单事物的简单性，但又允许复杂的特性。  </p>
				<p>    4）JDBC这样的JavaAPI对于纯Java方案来说是必须的。当使用ODBC时，人们必须在每一台客户机上安装ODBC驱动器和驱动管理器。如果JDBC驱动器是完全用Java语言实现的话，那么JDBC的代码就可以自动的下载和安装，并保证其安全性，而且，这将适应任何Java平台，从网络计算机NC到大型主机Mainframe。  </p>
				<p>    总而言之，JDBCAPI是能体现SQL最基本抽象概念的、最直接的Java接口。它建构在ODBC的基础上，因此，熟悉ODBC的程序员将发现学习JDBC非常容易。JDBC保持了ODBC的基本设计特征。实际上，这两种接口都是基于X／OPENSQL的调用级接口（CLI）。它们的最大的不同是JDBC是基于Java的风格和优点，并强化了Java的风格和优点。  </p>
				<p>    最近，微软又推出了除了ODBC以外的新的API，如RDO，ADO和OLEDB。这些API事实上在很多方面上同JDBC一样朝着相同的方向努力，也就是努力成为一个面向对象的，基于ODBC的类接口。然而，这些接口目前并不能代替ODBC，尤其在ODBC驱动器已经在市场完全形成的时候，更重要的是它们只是ODBC的“漂亮的包装”。  </p>
				<p>    4．JDBC两层模型和三层模型  </p>
				<p>    JDBC支持两层模型，也支持三层模型访问数据库。 两层模型中，一个java Appple或者一个JA－va应用直接同数据库连接。这就需要能直接被访问的数据库进行连接的JDBC驱动器。用户的SQL语句被传送给数据库，而这些语句执行的结果将被传回给用户。数据库可以在同一机器上，也可以另一机器上通过网络进行连接。这被称为“Client/Server”结构，用户的计算机作为Client,运行数据库的计算机作为Server。这个网络可是intranet，比如连接全体雇员的企业内部网，当然也可以是internet。  </p>
				<p>    在三层模型中，命令将被发送到服务的“中间层”，而“中间层”将SQL语句发送到数据库。数据库处理SQL语句并将结果返回“中间层”，然后“中间层”将它们 返回用户。MIS管理员将发现三层模型很有吸引力，因为“中间层”可以进行对访问的控制并协同数据库的更新，另一个优势就是如果有一个“中间层”用户就可以使用一个易用的高层的API，这个API可以由“中间层”进行转换，转换成底层的调用。而且，在许多情况下，三层模型可以提供更好的性能。  </p>
				<p>    到目前为止，“中间层”通常还是用C或C++实现，以保证其高性能。但随着优化编译器的引入，将java的字节码转换成高效的机器码，用java来实现“中间层”将越来越实际。而JDBC是允许从一个java“中间层”访问数据库的关键。 </p>
				<br />
				<br />
				<p id="TBPingURL">Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=629840</p>
		</div>
<img src ="http://www.blogjava.net/iKingQu/aggbug/36756.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/iKingQu/" target="_blank">風向逆轉 - 就要爪哇</a> 2006-03-22 03:24 <a href="http://www.blogjava.net/iKingQu/articles/36756.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[收藏]JDBC 4.0规范之目标</title><link>http://www.blogjava.net/iKingQu/articles/36755.html</link><dc:creator>風向逆轉 - 就要爪哇</dc:creator><author>風向逆轉 - 就要爪哇</author><pubDate>Tue, 21 Mar 2006 19:23:00 GMT</pubDate><guid>http://www.blogjava.net/iKingQu/articles/36755.html</guid><wfw:comment>http://www.blogjava.net/iKingQu/comments/36755.html</wfw:comment><comments>http://www.blogjava.net/iKingQu/articles/36755.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/iKingQu/comments/commentRss/36755.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/iKingQu/services/trackbacks/36755.html</trackback:ping><description><![CDATA[
		<div class="postText">
				<p>
						<span class="Title">JDBC 4.0规范之目标</span>
						<br />--------------------------------------------------------------------------------<br /> <br />　　历史</p>
				<p>　　JDBC API是一种成熟的技术，最早发布是1997年1月。在最初的版本中，JDBC API着重提供一个对SQL数据库的基本调用级接口。之后，JDBC 2.1规范和2.0可选包规范拓宽了API的范围。包括支持更高级应用和管理使用JDBC API来增强其应用的应用服务所需的各项特征。</p>
				<p>　　JDBC 3.0规范以填补较小范围内的功能缺失为目标。对于JDBC 4.0，我们的目标有两个：提高所有开发者在JAVA平台使用SQL开发的易用性。第二，提供企业级特性的JDBC工具集和API来管理JDBC资源。 <br />　　目标概述</p>
				<p>　　下面列出了一般的JDBC API和JDBC 4.0 API的目标和设计原理：</p>
				<p>　　1.适合J2EE和J2SE平台</p>
				<p>　　JDBC API是JAVA平台的重要技术。JDBC 4.0 API应遵循JAVA 2 企业版和JAVA 2 标准版平台的总体方向。另外，最近开发的JAVA 5.0平台已经展示出一系列新的特性和语言的改进，并在本规范中广泛使用。</p>
				<p>　　2.兼容SQL:2003</p>
				<p>　　JDBC API提供用JAVA编程语言编写标准SQL来对应用进行程序级访问能力。JDBC 3.0致力于确保其可以支持可广泛支持工业的具有SQL:99特征的子集。对于JDBC 4.0也一样，支持SQL:2003是本规范的一个主要组成部分。我们期望在不久的将来可以实现。</p>
				<p>　　3.巩固以前的规范</p>
				<p>　　本文档把4个以前的JDBC规范组织成一个单一的JDBC API规范。</p>
				<p>　　4.提供中立于开发商的一般访问特性</p>
				<p>　　JDBC API致力于提供支持针对不同开发商应用的高带宽的一般访问特征。其目标是提供与原生应用可以达到的同级别的访问特性。然而，本API必须足够通用和灵活以适应大范围的实施。</p>
				<p>　　5.关注于SQL</p>
				<p>　　JDBC API一直关注于用JAVA编程语言访问相关数据。这个目标曾在JDBC 3.0 API规范中说明，在本规范中仍是一个主要原则。提供API和工具来改进开发难度，并继续集中于在JAVA平台开发基于SQL的软件的需要。与以前的规范相似，本规范也不阻止与其它技术进行交互，如XML，CORBA和非关系型数据。</p>
				<p>　　6.提供基础数据和更高级别的API</p>
				<p>　　JDBC API提供标准API访各种数据源或旧系统。实施的差异使通过JDBC API抽象透明化。这使其成为对想开发可移动工具和应用的工具开发商来说，一个有价值的目标平台。</p>
				<p>　　由于它是一个用JAVA编程语言对SQL的“调用”级接口，所以JDBC API也适用于更高级别应用的底层，如EJB 3.0容器管理的持久性，SQLJ和JDBC的RowSet实现。</p>
				<p>　　7.保持简单</p>
				<p>　　JDBC API意欲成为一种使用简单、直接的接口。在之上可以构建更多复杂的实体。这个目标通过定义大量紧凑、单一目的方法来代替少数带有控制标识参数的复杂、多目的的方法来实现。</p>
				<p>　　8.增强可靠性、可用行和可测性</p>
				<p>　　可靠性、可用行和可测性是J2EE和J2SE平台的主题，也是未来JAVA平台的主题。JDBC 4.0 API严格按照以上目标进行。它扩展支持了一些领域，包括资源管理、对逻辑连接预备声明的复用和错误处理。</p>
				<p>　　9.支持对已有应用和驱动的向后兼容</p>
				<p>　　使用已有JDBC技术的驱动和应用必须能够在支持JDBC 4.0 API的JAVA虚拟机上继续工作。那些只使用更早版本中定义的JDBC API（不包括在JDBC 2.0中已废除的）的应用，应该不需要修改就可以继续运行。已有的应用应该可以直接迁移到JDBC 4.0技术。</p>
				<p>　　10.与JDBC RowSet工具紧密联系</p>
				<p>　　J2SE 5.0包含一个标准JDBC RowSet工具（在《JDBC RowSet工具集》中说明（JSR-114））。本规范会提供一个工具集包括工具类级别和元数据语言级别的工具。它允许开发者轻易的把使用JDBC技术的应用迁移到JDBC RowSet模型。该模型可以断开数据源访问连接，另外能够管理来自于XML驻留点的关系数据存储。</p>
				<p>　　11.允许对连接器的向前兼容</p>
				<p>　　连接器构架定义了一个标准方法来对资源适配器进行打包和布署。它允许一个J2EE容器整合它与外部资源的连接、处理和安全管理。JDBC 4.0 API提供JDBC驱动到连接器架构的迁移路径。对那些产品中使用JDBC技术的开发商来说，应可以转向对连接器API的实现。希望这些实现会重新包装已有数据源的实现。这样他们可以对连接器框架进行复用。 </p>
				<p>　　12.清晰的列明需求</p>
				<p>　　遵从JDBC要求的需求，要明确和易于识别。JDBC 4.0规范和API文档（Javadoc）会明晰什么特性是需要的，什么特性是可选的。</p>
				<br />
				<br />
				<p id="TBPingURL">Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=629584</p>
		</div>
<img src ="http://www.blogjava.net/iKingQu/aggbug/36755.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/iKingQu/" target="_blank">風向逆轉 - 就要爪哇</a> 2006-03-22 03:23 <a href="http://www.blogjava.net/iKingQu/articles/36755.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[收藏]JDBC连接Oracle数据库常见问题及解决方法</title><link>http://www.blogjava.net/iKingQu/articles/36519.html</link><dc:creator>風向逆轉 - 就要爪哇</dc:creator><author>風向逆轉 - 就要爪哇</author><pubDate>Mon, 20 Mar 2006 16:27:00 GMT</pubDate><guid>http://www.blogjava.net/iKingQu/articles/36519.html</guid><wfw:comment>http://www.blogjava.net/iKingQu/comments/36519.html</wfw:comment><comments>http://www.blogjava.net/iKingQu/articles/36519.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/iKingQu/comments/commentRss/36519.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/iKingQu/services/trackbacks/36519.html</trackback:ping><description><![CDATA[
		<ol>
				<li>Jbuilder正确连接 oracle 9i需要注意的几个问题 
<ul><li>oracle8以上的应该都使用classes12.jar文件作为jdbc驱动； 
</li><li>正确设置windows的classpath和jbuilder中的enterprise setup 、configure libraries，将casses12.jar路径正确添加到上述需要设置的地方；<strong></strong></li><li>进入database pilot，在database pilot中，file---new 在driver列表中如果oracle的驱动是红色的，就说明你的oralce在jb中加载失败；<strong></strong></li><li>若③没有错，则新建一个url：jdbc:oracle:thin:@（yourhostname）:1521:（your sid）<strong></strong></li><li>连接数据库的Username/password </li></ul><p><strong>注:</strong>大部分站友的jbuilder连接oracle的问题都是由于没有正确配置classpath等引起的。</p></li>
				<li>使用非XA方式连接ORACLE数据库 
<p>在windows下配置Oracle_XA时要注意两点：</p><p>#ORACLE 10<br />Oracle_XA;xaosw;%ORACLE_HOME% dbms oraxa10.lib%ORACLE_HOME%precompibmsvcorasql10.lib</p><p>#oracle 9<br />Oracle_XA;xaosw;%ORACLE_HOME% dbms oraxa9.lib%ORACLE_HOME%precompibmsvcorasql9.lib</p><p>Unix下<br />Oracle_XA:xaosw:....</p><p>第一：在windows 下 ORACLE_XA 和xaosw后面的是分号";"，不是冒号":"<br />第二：上面的这些LIB写在一行上，中间用空格分开，如果没有在系统的环境变量中设置ORACLE_HOME，就写绝对路径。</p></li>
				<li>JDBC操作ORACLE数据库时出现‘java.sql.SQLException:IO异常，不在流模式下’ 
<ol><li>用OracleStatment，，不要用java.sql.Statment 
</li><li>如果对已有连接进行setAutoCommit失败，则关闭该连接并重新建立一个连接 
</li><li>到ORACLE站点下载一个最新的JDBC Driver，如果操作LOB类型，用ORACLE自带的接口和类 </li></ol></li>
				<li>weblogic连接oracle问题：The Network Adapter could not establish the connection 
<p>可能是服务器的监听停掉了，是数据库的问题，与应用无关；应该先检查一下oracle是否正常，用sql*plus连接一下数据库，看能否正常连接；</p></li>
				<li>Weblogic中使用Oracle连接池及Oracle备份的注意事项 
<p>使用HP-UNIX，Weblogic 8.1,Oracle 9.2.0.5<br />配置了一个普通的连接池,驱动程序采用oracle的Oracle’s Driver(Thin) version 9.0.1, 9.2.0</p><p>错误情况：<br />结果使用数据库连接池时报错，说没有连接池资源了。实际上数据库的连接池完全空闲，并且测试也是对的，Oracle也是正常可以连接、使用的。</p><p>问题根源：<br />通过层层排错，发现原来后台在使用Oracle的exp备份一个只有同义词的用户，导致exp进程僵死。<br />杀死exp、重启Oracle等无法解决问题，最终重启UNIX，禁止备份只有同义词的用户，问题解决。</p><p>总结：<br />应该是Oracle9的exp BUG导致连接池问题，不要使用exp倒出同义词</p></li>
				<li>连接Oracle时抛出如下异常：java.sql.SQLException: Io exception:The Network Adapter could not establish connection一种产生原因 
<p>Oracle Database Connection (from oracle.com)<br />PROBLEM <br />You are attempting to connect to an Oracle instance using JDBC and you are receiving the following error.  <br />   java.sql.SQLException: Io exception: <br />     The Network Adapter could not establish connection<br />   SQLException: SQLState (null) vendor code (17002) <br />Any or all of the following conditions may also apply:<br />1) You are able to establish a SQL*Plus connection from the same <br />   client to the same Oracle instance.<br />2) You are able to establish a JDBC OCI connection, but not a Thin<br />   connection from the same client to the same Oracle instance.<br />3) The same JDBC application is able to connect from a different<br />   client to the same Oracle instance.<br />4) The same behavior applies whether the initial JDBC connection<br />   string specifies a hostname or an IP address.<br />REDISCOVERY  <br />To verify whether you are hitting this problem, verify whether the Oracle instance is configured for Multithreaded Server (MTS). If the Oracle instance is not configured for MTS, you are probably encountering a different problem. Otherwise, continue. Try forcing the JDBC connection to use a dedicated server instead of a shared server. This can be accomplished in several ways. For JDBC OCI or Thin, this can be done by reconfiguring the server for dedicated connections only. This approach, however, may not be feasible in many cases. In such cases, the following options apply: For JDBC OCI:   <br />1) Add the (SERVER=DEDICATED) property to the TNS connect string<br />   stored in the tnsnames.ora file on the client.<br />2) Set the user_dedicated_server=ON in sqlnet.ora on the client.<br />For JDBC Thin: <br />You must specify a full name-value pair connect string (the same as it might appear in the tnsnames.ora file) instead of the short JDBC Thin syntax. For example, instead of <br />"jdbc:oracle:thin::port:sid"<br />you would need to use a string of the form<br />"jdbc:oracle:thin:@(DESCRIPTION="                    +<br />                       "(ADDRESS_LIST="              + <br />                           "(ADDRESS=(PROTOCOL=TCP)" + <br />                                    "(HOST=host)"    +                                      =<br />                                    "(PORT=port)"    +<br />                           ")"                       +<br />                       ")"                           +<br />                       "(CONNECT_DATA="              +<br />                           "(SERVICE_NAME=sid)"      +<br />                           "(SERVER=DEDICATED)"      +<br />                       ")"                           +<br />                     ")"<br />If the connection works fine after having made these changes, it is very likely that this is the problem you are encountering. In this case, one last test will help to verify this fact. <br />Log into the remote host on which the Oracle instance is running and execute the appropriate command to determine what the server 'thinks' its hostname is (i.e. the name that was configured when the server was installed and configured). For example, on a Unix host the 'hostname' command can be used for this purpose. <br />Using the name displayed (e.g. by the hostname command), exactly as it appeared (i.e. if the output from the hostname command had the domain name included, then include it), return to the client which was unable to connect and try pinging the server. <br />NOTE: It is critical that you attempt to ping the server using EXACTLY the same hostname you got from the server. <br />If you are unable to ping the server via this hostname, then you almost certainly hitting this problem. If not, this may be a new issue, but at least you will have found a workaround (i.e. use a dedicated connection). <br />EXPLANATION  <br />To understand why this problem occurs, one must first understand the differences in how the listener handles connections to shared servers versus dedicated servers. <br />When connecting to a dedicated server, the client connects to the listener (via hostname or IP address). The listener then spawns a dedicated server process and hands off the socket used to accept the client connection to that server. The client and server then start communicating via the endpoints established by the initial connection. NOTE: There is only one connection in this case. When connecting to a shared server, the initial client connection to the listener is the same. However, with MTS, there is no need to spawn a new server process; a pool of shared processes already exists. Also, clients do not communicate directly with the server processes in MTS; rather, they communicate with a dispatcher. <br />For this reason, when setting up an MTS connection, the listener sends a redirect message back to the client asking the client to close the connection to the listener and connect to a dispatcher. The information in this message includes the hostname and a port number for the appropriate dispatcher. The redirect message will ALWAYS specify a hostname, even if the client initially provided an IP address. <br />If, for any reason, the hostname provided to the listener (e.g. by the 'hostname' or another command) doesn't agree with the hostname by which the server is known on the client, the connection fails. <br />On the other hand, if "(SERVER=DEDICATED)" already appears in the TNS connect string in tnsnames.ora or if "use_dedicated_server=ON" already appears in the sqlnet.ora file, you may find that SQL*Plus and/or JDBC OCI work fine, while JDBC Thin fails. <br />SOLUTION  <br />Obviously, one solution is to use dedicated servers. However, this may not always be feasible. <br />The key is to make sure the hostname on both the client and server agree. This can be accomplished by reconfiguring either the client or the server, but there are things to be aware of in both cases. <br />If the server is configured to return a different hostname, then it is possible that other clients which used to work will now fail. <br />In some cases, it may not be feasible to reconfigure the client. For example, if the server version of the hostname does not include the domain, you would need to remove the domain portion of the hostname on the client; but, if the client needs to connect to more than one server with the same base name in different domains, this may not be possible, as the hostname may be ambiguous. <br />REFERENCES  <br />bug:1269734 java.sql.SQLException: Io exception: The Network Adapter could not be found.</p></li>
				<li>连接ORACLE数据库报错：javax.naming.NameNotFoundException: Unable to resolve oracThin. Resolved: '' Unresolved:'oracThin' ; remaining name ''<strong> </strong><p>问题描述：配置完JDBC后，打开页面的时候，报出如下错误信息：<br />javax.naming.NameNotFoundException: Unable to resolve oracThin. Resolved: '' Unresolved:'oracThin' ; remaining name ''<br />JDBC配置如下：<br />Connection Pools(连接池)<br />Name:OracThin<br />URL:jdbc:oracle:thin.0.0.1:LYSIMIS<br />Driver Classname:oracle.jdbc.driver.OracleDriver<br />Properties:<br />user=system<br />password=manager<br />dll=ocijdbc8<br />protocol=thin<br />数据源配置如下：<br />Name:OracThin<br />JNDI Name:OracThin<br />Pool Name:OracThin<br />当程序执行到这一步时出错。<br />ctx = new InitialContext();<br />ds = (javax.sql.DataSource)ctx.lookup ("OracThin");<strong> </strong></p><p>问题解决后汉字是乱码<strong> </strong></p><p>错误产生原因及解决办法：</p><ul><li>URL:jdbc:oracle:thin:.0.0.1:1521:LYSIMI，thin后面加:,127.0.0.1后面加端口号 
</li><li>注意名字大小写. 
</li><li>target 到server上 
</li><li>再看看pool是否起来了，没起来的话,重起weblogic 
</li><li>乱码问题（Java是基于Unicode）： 
<ul><li>在JSP 文件中加入&lt;%@ page contentType="text/html; charset=GBK" %&gt; 
</li><li>在weblogic.xml文件的&lt;jsp-descriptor&gt;部分加入<br />&lt;jsp-descriptor&gt;<br />&lt;jsp-param&gt;<br />&lt;param-name&gt;compilerSupportsEncoding&lt;/param-name&gt;<br />&lt;param-value&gt;true&lt;/param-value&gt;<br />&lt;/jsp-param&gt;<br />&lt;jsp-param&gt;<br />&lt;param-name&gt;encoding&lt;/param-name&gt;<br />&lt;param-value&gt;GBK&lt;/param-value&gt;<br />&lt;/jsp-param&gt;<br />&lt;/jsp-descriptor&gt; 
</li><li></li></ul></li></ul></li>
				<li>oracle XA的疑惑 
<p>问题描述：Oracle_XA;xaosw;D:oracleora92 dbmsXAORAXA9.lib C:msvcoraSQL9.lib中<br />xaosw是什么意思</p><p>解答：可以参考ORACLE的XA部分的文档。</p><p><a href="http://www-rohan.sdsu.edu/doc/oracle/server803/A54642_01/ch_xa.htm" target="_blank">http://www-rohan.sdsu.edu/doc/oracle/server803/A54642_01/ch_xa.htm</a></p></li>
				<li>oracle与weblogic自动启动与停止 
<p>问题描述：每次重新启动服务器时oracle数据库若没有关闭，则必须先关闭后在重新启动redhat advance server，oracle才能够正常运行</p><p>原因及解决办法参见：</p><p><a href="http://dev2dev.bea.com.cn/bbs/thread.jspa?forumID=81&amp;threadID=8839&amp;messageID=43184#43184">http://dev2dev.bea.com.cn/bbs/thread.jspa?forumID=81&amp;threadID=8839&amp;messageID=43184#43184</a></p></li>
		</ol>
		<br />
		<br />
		<p id="TBPingURL">Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=579829</p>
<img src ="http://www.blogjava.net/iKingQu/aggbug/36519.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/iKingQu/" target="_blank">風向逆轉 - 就要爪哇</a> 2006-03-21 00:27 <a href="http://www.blogjava.net/iKingQu/articles/36519.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[收藏]JDBC数据库驱动下载网址大全 (好东西转来分享)</title><link>http://www.blogjava.net/iKingQu/articles/36505.html</link><dc:creator>風向逆轉 - 就要爪哇</dc:creator><author>風向逆轉 - 就要爪哇</author><pubDate>Mon, 20 Mar 2006 15:57:00 GMT</pubDate><guid>http://www.blogjava.net/iKingQu/articles/36505.html</guid><wfw:comment>http://www.blogjava.net/iKingQu/comments/36505.html</wfw:comment><comments>http://www.blogjava.net/iKingQu/articles/36505.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/iKingQu/comments/commentRss/36505.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/iKingQu/services/trackbacks/36505.html</trackback:ping><description><![CDATA[
		<p>链接:<a href="http://blog.csdn.net/baggio785/archive/2006/02/07/593802.aspx">http://blog.csdn.net/baggio785/archive/2006/02/07/593802.aspx</a></p>
<img src ="http://www.blogjava.net/iKingQu/aggbug/36505.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/iKingQu/" target="_blank">風向逆轉 - 就要爪哇</a> 2006-03-20 23:57 <a href="http://www.blogjava.net/iKingQu/articles/36505.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[收藏]Oracle内存分配与调整 </title><link>http://www.blogjava.net/iKingQu/articles/36492.html</link><dc:creator>風向逆轉 - 就要爪哇</dc:creator><author>風向逆轉 - 就要爪哇</author><pubDate>Mon, 20 Mar 2006 15:38:00 GMT</pubDate><guid>http://www.blogjava.net/iKingQu/articles/36492.html</guid><wfw:comment>http://www.blogjava.net/iKingQu/comments/36492.html</wfw:comment><comments>http://www.blogjava.net/iKingQu/articles/36492.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/iKingQu/comments/commentRss/36492.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/iKingQu/services/trackbacks/36492.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: l       														前言																																																																								对于				oracle				的内存的管理，截止到				9iR2				，都是相当重要的...&nbsp;&nbsp;<a href='http://www.blogjava.net/iKingQu/articles/36492.html'>阅读全文</a><img src ="http://www.blogjava.net/iKingQu/aggbug/36492.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/iKingQu/" target="_blank">風向逆轉 - 就要爪哇</a> 2006-03-20 23:38 <a href="http://www.blogjava.net/iKingQu/articles/36492.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[收藏]Oracle基本数据类型存储格式浅析（一）—（五）</title><link>http://www.blogjava.net/iKingQu/articles/36491.html</link><dc:creator>風向逆轉 - 就要爪哇</dc:creator><author>風向逆轉 - 就要爪哇</author><pubDate>Mon, 20 Mar 2006 15:37:00 GMT</pubDate><guid>http://www.blogjava.net/iKingQu/articles/36491.html</guid><wfw:comment>http://www.blogjava.net/iKingQu/comments/36491.html</wfw:comment><comments>http://www.blogjava.net/iKingQu/articles/36491.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/iKingQu/comments/commentRss/36491.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/iKingQu/services/trackbacks/36491.html</trackback:ping><description><![CDATA[
		<font face="Courier New">Oracle基本数据类型存储格式浅析（一）——字符类型<br />发表人:yangtingkun | 发表时间: 2004年十二月09日, 23:47<br />前一阵看完文档，对oracle的基本数据类型的存储格式有了一些了解，最近有做了一些测试进行了验证。<br />打算整理总结一下，这一篇主要说明字符类型的存储格式。主要包括char、varchar2和long等几种类型。<br /> <br />SQL&gt; create table test_char (char_col char(10), varchar_col varchar2(10), long_col long);<br />表已创建。<br />SQL&gt; insert into test_char values ('abc', '123', ',fd');<br />已创建 1 行。<br />SQL&gt; commit;<br />提交完成。<br />SQL&gt; select rowid from test_char;<br />ROWID<br />------------------<br />AAAB3LAAFAAAAAgAAA<br />根据rowid的定义规则，第7～9位是表示的是数据文件，F表示5，而10～15位表示的是在这个数据文件中的第几个BLOCK，g表示32。（rowid编码相当于64进制。用A~Z a~z 0~9 + /共64个字符表示。A表示0，B表示1，……，a表示26，……，0表示52，……，+表示62，/表示63。）<br />我们根据计算的结果去dump这个block。<br />SQL&gt; ALTER SYSTEM DUMP DATAFILE 5 BLOCK 32;<br />系统已更改。<br />打开产生的trace文件：<br />data_block_dump,data header at 0x3421064<br />===============<br />tsiz: 0x1f98<br />hsiz: 0x14<br />pbl: 0x03421064<br />bdba: 0x01400020<br />     76543210<br />flag=--------<br />ntab=1<br />nrow=1<br />frre=-1<br />fsbo=0x14<br />fseo=0x1f82<br />avsp=0x1f6e<br />tosp=0x1f6e<br />0xe:pti[0] nrow=1 offs=0<br />0x12:pri[0] offs=0x1f82<br />block_row_dump:<br />tab 0, row 0, @0x1f82<br />tl: 22 fb: --H-FL-- lb: 0x1  cc: 3<br />col  0: [10]  61 62 63 20 20 20 20 20 20 20<br />col  1: [ 3]  31 32 33<br />col  2: [ 3]  2c 66 64<br />end_of_block_dump<br />End dump data blocks tsn: 5 file#: 5 minblk 32 maxblk 32<br />观察dump出来的结果，可以发现以下几点：<br />1.对于每个字段，除了保存字段的值以外，还会保存当前字段中数据的长度。而且，oracle显然没有把字段的长度定义或类型定义保存在block中，这些信息保存在oracle的数据字典里面。<br />2. 根据dump的结果，可以清楚的看到，字符类型在数据库中是以ascii格式存储的。<br />SQL&gt; select chr(to_number('61', 'xx')) from dual;<br />CH<br />--<br />a<br />3.char类型为定长格式，存储的时候会在字符串后面填补空格，而varchar2和long类型都是变长的。<br />SQL&gt; SELECT DUMP(CHAR_COL, 16) D_CHAR FROM TEST_CHAR;<br />D_CHAR<br />-------------------------------------------------------------<br />Typ=96 Len=10: 61,62,63,20,20,20,20,20,20,20<br />SQL&gt; SELECT DUMP(VARCHAR_COL, 16) D_VARCHAR2 FROM TEST_CHAR;<br />D_VARCHAR2<br />-------------------------------------------------------------<br />Typ=1 Len=3: 31,32,33<br />SQL&gt; SELECT DUMP(LONG_COL, 16) D_VARCHAR2 FROM TEST_CHAR;<br />SELECT DUMP(LONG_COL, 16) D_VARCHAR2 FROM TEST_CHAR<br />            *<br />ERROR 位于第 1 行:<br />ORA-00997: 非法使用 LONG 数据类型<br />由于DUMP不支持LONG类型，因此我们使用了alter system dump block的方式，通过比较两种方式得到的结果，发现DUMP()函数不但方便，结果清晰，而且指出了进行DUMP的数据类型，在以后的例子中，除非必要的情况，否则都会采用DUMP()函数的方式进行说明。<br />下面看一下插入中文的情况，首先看一下数据库的字符集<br />SQL&gt; select name, value$ from sys.props$ where name like '%CHARACTERSET%';<br />NAME                           VALUE$<br />------------------------------ ------------------------------<br />NLS_CHARACTERSET               ZHS16GBK<br />NLS_NCHAR_CHARACTERSET         AL16UTF16<br />SQL&gt; insert into test_char values ('定长', '变长', null);<br />已创建 1 行。<br />SQL&gt; SELECT DUMP(CHAR_COL, 16) D_CHAR FROM TEST_CHAR;<br />D_CHAR<br />----------------------------------------------------------------<br />Typ=96 Len=10: 61,62,63,20,20,20,20,20,20,20<br />Typ=96 Len=10: b6,a8,b3,a4,20,20,20,20,20,20<br />SQL&gt; SELECT DUMP(VARCHAR_COL, 16) D_VARCHAR2 FROM TEST_CHAR;<br />D_VARCHAR2<br />----------------------------------------------------------------<br />Typ=1 Len=3: 31,32,33<br />Typ=1 Len=4: b1,e4,b3,a4<br />根据dump结果，可以清楚的看出，普通英文字符和标点用一个字节表示，而中文字符或中文标点需要两个字节来表示。<br />下面，对比一下nchar和nvarchar2与char、varchar2类型有什么不同。<br />SQL&gt; create table test_nchar (nchar_col nchar(10), nvarchar_col nvarchar2(10));<br />表已创建。<br />SQL&gt; insert into test_nchar values ('nchar定长', 'nvarchar变长');<br />已创建 1 行。<br />从这里已经可以看出一些不同了，如果按照刚才中文的计算方法，'nvarchar变长'的长度是8+2*2=12已经超过了数据类型定义的大小，可是为什么插入成功了？<br />还是dump一下看看结果吧。<br />SQL&gt; select dump(nchar_col, 16) from test_nchar;<br />DUMP(NCHAR_COL,16)<br />--------------------------------------------------------------<br />Typ=96 Len=20: 0,6e,0,63,0,68,0,61,0,72,5b,9a,95,7f,0,20,0,20,0,20<br />SQL&gt; select dump(nvarchar_col, 16) from test_nchar;<br />DUMP(NVARCHAR_COL,16)<br />--------------------------------------------------------------<br />Typ=1 Len=20: 0,6e,0,76,0,61,0,72,0,63,0,68,0,61,0,72,53,d8,95,7f<br />这下就明白了，虽然仍然是采用ascii码存储，但是nchar使用的AL16UTF16字符集，编码长度变为2个字节。这样中文使用两个字节，对于可以用一个字节就表示的英文字符，采用了高位补0的方式凑足2位，这样，对于采用AL16UTF16字符集的nchar类型，无论中文还是英文都用2位字符表示。因此'nvarchar变长'的长度是10，并没有超过数据类型的限制。<br />==============================================================<br />Oracle基本数据类型存储格式浅析（二）——数字类型<br />发表人:yangtingkun | 发表时间: 2004年十二月14日, 22:57<br />这篇文章主要描述NUMBER类型的数据和如何在数据库中存储的。<br />Oracle的NUMBER类型最多由三个部分构成，这三个部分分别是最高位表示位、数据部分、符号位。其中负数包含符号位，正数不会包括符号位。另外，数值0比较特殊，它只包含一个数值最高位表示位80，没有数据部分。<br />正数的最高位表示位大于80，负数的最高位表示位小于80。其中一个正数的最高位是个位的话，则最高位表示位为C1，百位、万位依次为C2、C3，百分位、万分为依次为C0、BF。一个负数的最高位为个位的话，最高位表示位为3E，百位、万位依次为3D、3C，百分位、万分位依次为3F、40。<br />数据部分每一位都表示2位数。这个两位数可能是从0到99，如果是数据本身是正数，则分别用二进制的1到64表示，如果数据本身是负数，则使用二进制65到2表示。<br />符号位用66表示。<br />上面的这些是我通过DUMP结果总结出来的，对于上面提到的这些关系常数，Oracle之所以这样选择是有道理的，我们后面根据例子也可以推导出来，而且会进一步说明为什么会采用这种方式表示。这里列出的意思是使大家先对NUMBER类型数据有一个大概的了解。<br />下面我们通过一个例子详细说明：<br /> <br />SQL&gt; CREATE TABLE TEST_NUMBER (NUMBER_COL NUMBER);<br />表已创建。<br />SQL&gt; INSERT INTO TEST_NUMBER VALUES (0);<br />已创建 1 行。<br />SQL&gt; INSERT INTO TEST_NUMBER VALUES (1);<br />已创建 1 行。<br />SQL&gt; INSERT INTO TEST_NUMBER VALUES (2);<br />已创建 1 行。<br />SQL&gt; INSERT INTO TEST_NUMBER VALUES (25);<br />已创建 1 行。<br />SQL&gt; INSERT INTO TEST_NUMBER VALUES (123);<br />已创建 1 行。<br />SQL&gt; INSERT INTO TEST_NUMBER VALUES (4100);<br />已创建 1 行。<br />SQL&gt; INSERT INTO TEST_NUMBER VALUES (132004078);<br />已创建 1 行。<br />SQL&gt; INSERT INTO TEST_NUMBER VALUES (2.01);<br />已创建 1 行。<br />SQL&gt; INSERT INTO TEST_NUMBER VALUES (0.3);<br />已创建 1 行。<br />SQL&gt; INSERT INTO TEST_NUMBER VALUES (0.00000125);<br />已创建 1 行。<br />SQL&gt; INSERT INTO TEST_NUMBER VALUES (115.200003);<br />已创建 1 行。<br />SQL&gt; INSERT INTO TEST_NUMBER VALUES (-1);<br />已创建 1 行。<br />SQL&gt; INSERT INTO TEST_NUMBER VALUES (-5);<br />已创建 1 行。<br />SQL&gt; INSERT INTO TEST_NUMBER VALUES (-20032);<br />已创建 1 行。<br />SQL&gt; INSERT INTO TEST_NUMBER VALUES (-234.432);<br />已创建 1 行。<br />SQL&gt; COMMIT;<br />提交完成。<br />SQL&gt; COL D_NUMBER FORMAT A50<br />SQL&gt; SELECT NUMBER_COL, DUMP(NUMBER_COL, 16) D_NUMBER FROM TEST_NUMBER;<br />NUMBER_COL D_NUMBER<br />---------- --------------------------------------------------<br />         0 Typ=2 Len=1: 80<br />         1 Typ=2 Len=2: c1,2<br />         2 Typ=2 Len=2: c1,3<br />        25 Typ=2 Len=2: c1,1a<br />       123 Typ=2 Len=3: c2,2,18<br />      4100 Typ=2 Len=2: c2,2a<br /> 132004078 Typ=2 Len=6: c5,2,21,1,29,4f<br />      2.01 Typ=2 Len=3: c1,3,2<br />        .3 Typ=2 Len=2: c0,1f<br /> .00000125 Typ=2 Len=3: be,2,1a<br />115.200003 Typ=2 Len=6: c2,2,10,15,1,4<br />        -1 Typ=2 Len=3: 3e,64,66<br />        -5 Typ=2 Len=3: 3e,60,66<br />    -20032 Typ=2 Len=5: 3c,63,65,45,66<br />  -234.432 Typ=2 Len=6: 3d,63,43,3a,51,66<br />已选择15行。<br /> <br />    下面根据例子得到的结果，对每行进行说明。首先说明两点基本的。DUMP函数返回的TYPE＝2表示DUMP的数据类型是NUMBER，LENGTH＝N表示数值在数据库中存储的长度是N。<br />1.DUMP(0)的结果是0x80，在前面已经提到，0只有高位表示位，没有数据位。由于0的特殊，既不属于正数，也不属于负数，因此使用高位表示位用80表示就足够了，不会和其它数据冲突，Oracle出于节省空间的考虑将后面数据部分省掉了。但是为什么Oracle选择0x80表示0呢？我们知道正数和负数互为相反数，每个正数都有一个对应的负数。因此如果我们要使用编码表示数值，则表示正数和负数的编码应该各占一半，这样才能保证使Oracle表示数据范围是合理的。而0x80的二进制编码是1000 0000，正好是一个字节编码最大值的一半，因此，Oracle选择0x80来表示0，是十分有道理的。<br />2.DUMP(1)的结果是0xc102，0xc1表示了最高位个位，0x2表示数值是1。首先，Oracle为什么用C1表示个位呢？其实，道理和刚才的差不多。采用科学计数法，任何一个实数S都可以描述为A.B×10n，A表示整数部分，B表示小数部分，而N表示10的指数部分。当S大于1时，N大于等于0，S小于1时，N小于0。也就是说，采用指数的方式表示，N大于0和N小于0的情况各占一半左右时，Oracle所表示的范围最广。因此，Oracle选择了C1表示个位是最高位的情况。<br />SQL&gt; SELECT TO_CHAR(ROUND(TO_NUMBER('81', 'XXX') + (TO_NUMBER('FF', 'XXX') - TO_NUMBER('81', 'XXX') + 1)/2), 'XX') FROM DUAL; <br />TO_<br />---<br /> C1<br /> <br />为什么ORACLE使用0x2表示1，而不直接使用0x1表示1呢？Oracle每个字节表示2位数，因此对于这个2位数，出现的可能是0～99共100种可能，问题出在0这里。Oracle底层是用C语言实现的，我们知道二进制0在C语言中用作字符串终结符，Oracle为了避免这个问题，因此使用了0x1表示0，并依次类推，使用0x64表示99。<br />3.DUMP(2)的结果是0xc103。<br />4.DUMP(25)的结果是0xc11a。前面提到，数据部分是以2位为最小单位保存的。因此对于25来说，最高位表示位仍然是个位，个位上的值是25，根据上面推出的规则，25在存储为0xc11a。<br />SQL&gt; SELECT TO_CHAR(25 + 1, 'xx') FROM DUAL; <br />TO_<br />---<br /> 1a<br /> <br />5.DUMP(123)的结果是0xc20218。由于123最高为是百位，所以最高位表示位为0xc2，百位上是1，用0x02表示，个位上是23，用0x18表示。<br />6.DUMP(4100)的结果是0xc22a。<br />注意一点，如果数字最后数位上如果是0，Oracle出于节省空间的考虑不会存储。比如：4100只保存百位上的41，12000000只保存百位位上的12，512000只保存万位上的51和百位上的20。<br />7.DUMP(132004078)的结果是0xc5022101294f。最高位是亿位，因此用0xC5表示，亿位上是1用0x02表示，百位位上是32用0x21表示，万位上是0用0x01表示，百位上是40用0x29表示，个位上78用0x4F表示。<br />注意：中间数位上的0不能省略。<br />8.DUMP(2.01)的结果是0xc10302。最高位是个位用0xC1表示，个位上是2用0x03表示，百分位上是1用0x02表示。<br />注意：个位下面一位是百分位不是十分位。<br />9.DUMP(0.3)的结果是0xc01f。最高位是百分位，使用0xC0表示，百分位上是30用0x1F表示。<br />10.DUMP(0.00000125)的结果是0xbe021a。最高位是百万分位，用0xBE表示，最高位上的1用0x02表示，25用0x1a表示。<br />11.DUMP(115.200003)的结果是0xc20210150104。<br />12.DUMP(-1)的结果是0x3e6466。最高位个位，用0x3E表示，64表示个位上是1，66是符号位，表示这个数是负数。<br />负数和正数互为相反数，负数的最高位表示位和它对应的相反数的最高位相加的值是FF。1的最高位表示位是C1，-1的最高位表示位是3E。负数中1用64表示。负数中的数值和它相反数的数据相加是0x66，也就是符号位。正数1用0x02表示，负数1用0x64表示，二者相加是0x66。负数多个一个标识位，用0x66表示。由于正数的表示范围是0x01到0x64，负数的表示范围是0x65到0x02。因此，不会在表示数字时出现的0x66表示。<br />13.DUMP(-5)的结果是0x3e6066。0x3e表示最高位是个位，0x60表示个位上是5，0x66是符号标识位。0x3E加0xC1是0xFF。0x60加0x06的结果是0x66。<br />14.DUMP(-20032)的结果是0x3c63654566。最高位是万位，正数的万位是0xC3，因此负数的万位是0x3C。万位上是2，正数用0x03表示，负数为0x63，百位上是0，正数用0x01表示，负数使用0x65表示，个位上是32，正数用0x21表示，负数使用0x45表示。0x66是负数表示位。<br />15.DUMP(-234.432)的结果是0x3d63433a5166。<br /> <br />根据Oracle的存储特性，还可以推出Oracle的number类型的取值范围。<br />Oracle的concept上是这样描述的：<br />The following numbers can be stored in a NUMBER column:<br />Positive numbers in the range 1 x 10-130 to 9.99...9 x 10125 with up to 38 significant digits.<br />Negative numbers from -1 x 10-130 to 9.99...99 x 10125 with up to 38 significant digits.<br />Zero.<br />下面来推导出取值范围。<br />来看符号位，0xC1表示个位。<br />SQL&gt; select to_number('ff', 'xxx') - to_number('c1', 'xxx') from dual;<br />TO_NUMBER('FF','XXX')-TO_NUMBER('C1','XXX')<br />-------------------------------------------<br />                                         62<br />由于Oracle是两位、两位存储的，因此最高位相当于62×2=124，而且最高位上最大值是99，因此正数的最大值为9.999……×10125。<br />SQL&gt; select to_number('c1', 'xxx') - to_number('80', 'xxx') from dual;<br />TO_NUMBER('C1','XXX')-TO_NUMBER('80','XXX')<br />-------------------------------------------<br />                                         65<br />最高位相当于65×2=130，因此正数的最小值为1×10-130。<br />负数和正数在各使用了一半的编码，因此具有相同的极值范围。<br />==============================================================<br />Oracle基本数据类型存储格式浅析（三）——日期类型（一）<br />发表人:yangtingkun | 发表时间: 2004年十二月15日, 14:00<br />这篇文章描述DATE类型的数据在Oracle中是以何种格式存放的。 <br />下面通过一个例子进行说明。 <br />  <br />SQL&gt; create table test_date (date_col date);<br />表已创建。<br />SQL&gt; insert into test_date values (to_date('2000-1-1 0:0:0', 'yyyy-mm-dd hh24:mi:ss'));<br />已创建 1 行。<br />SQL&gt; insert into test_date values (to_date('1-1-1 0:0:0', 'yyyy-mm-dd hh24:mi:ss'));<br />已创建 1 行。<br />SQL&gt; insert into test_date values (to_date('-1-1-1 0:0:0', 'syyyy-mm-dd hh24:mi:ss'));<br />已创建 1 行。<br />SQL&gt; insert into test_date values (to_date('-101-1-1 0:0:0', 'syyyy-mm-dd hh24:mi:ss'));<br />已创建 1 行。<br />SQL&gt; insert into test_date values (to_date('-4712-1-1 0:0:0', 'syyyy-mm-dd hh24:mi:ss'));<br />已创建 1 行。<br />SQL&gt; insert into test_date values (to_date('9999-12-31 23:59:59', 'syyyy-mm-dd hh24:mi:ss'));<br />已创建 1 行。<br />SQL&gt; insert into test_date values (sysdate);<br />已创建 1 行。<br />SQL&gt; insert into test_date values (to_date('-4713-1-1 0:0:0', 'syyyy-mm-dd hh24:mi:ss'));<br />insert into test_date values (to_date('-4713-1-1 0:0:0', 'syyyy-mm-dd hh24:mi:ss'))<br />                                      *<br />ERROR 位于第 1 行:<br />ORA-01841: （全）年度值必须介于 -4713 和 +9999 之间，且不为 0<br />SQL&gt; insert into test_date values (to_date('0000-1-1 0:0:0', 'yyyy-mm-dd hh24:mi:ss'));<br />insert into test_date values (to_date('0000-1-1 0:0:0', 'yyyy-mm-dd hh24:mi:ss'))<br />                                      *<br />ERROR 位于第 1 行:<br />ORA-01841: （全）年度值必须介于 -4713 和 +9999 之间，且不为 0<br />SQL&gt; col dump_date format a80<br />SQL&gt; select to_char(date_col, 'syyyy-mm-dd hh24:mi:ss'), dump(date_col) dump_date from test_date;<br />TO_CHAR(DATE_COL,'SY DUMP_DATE<br />-------------------- ---------------------------------------<br /> 2000-01-01 00:00:00 Typ=12 Len=7: 120,100,1,1,1,1,1<br /> 0001-01-01 00:00:00 Typ=12 Len=7: 100,101,1,1,1,1,1<br />-0001-01-01 00:00:00 Typ=12 Len=7: 100,99,1,1,1,1,1<br />-0101-01-01 00:00:00 Typ=12 Len=7: 99,99,1,1,1,1,1<br />-4712-01-01 00:00:00 Typ=12 Len=7: 53,88,1,1,1,1,1<br /> 9999-12-31 23:59:59 Typ=12 Len=7: 199,199,12,31,24,60,60<br /> 2004-12-15 13:56:19 Typ=12 Len=7: 120,104,12,15,14,57,20<br />已选择7行。<br /> <br />通过最后两条语句已经可以看出Oracle的DATE类型的取值范围是公元前4712年1月1日至公元9999年12月31日。而且根据日期的特定，要不然是公元1年，要不然是公元前1年，不会出现0年的情况。<br />日期类型长度是7，7个字节分别表示世纪、年、月、日、时、分和秒。<br />由于不会出现0的情况，月和日都是按照原值存储的，月的范围是1～12，日的范围是1～31。<br />由于时、分、秒都会出现0的情况，因此存储时采用原值加1的方式。0时保存为1，13时保存为14，23时保存为24。分和秒的情况与小时类似。小时的范围是0～23，在数据库中以1～24保存。分和秒的范围都是0～59，在数据库中以1～60保存。<br />年和世纪的情况相对比较复杂，可分为公元前和公元后两种情况。由于最小的世纪的值是-47（公元前4712年），最大值是99（公元9999年）。为了避免负数的产生，oracle把世纪加100保存在数据库中。公元2000年，世纪保存为120，公元9999年，世纪保存为199，公元前101年，世纪保存为99（100+(-1)），公元前4712年，世纪保存为53（100+(-47)）。<br />注意，对于公元前1年，虽然已经是公元前了，但是表示世纪的前两位的值仍然是0，因此，这时的保存的世纪的值仍然是100。世纪的范围是-47～99，保存的值是53～199。<br />年的保存与世纪的保存方式类似，也把年的值加上100进行保存。对于公元2000年，年保持为100，公元1年保存为101，公元2004年保存为104，公元9999年保存为199，公元前1年，保存为99（100+(-1)），公元前101年，保存为99（100+(-1)），公元前4712年保存为88（100+(-12)）。对于公元前的年，保存的值总是小于等于100，对于公元后的年，保存的值总是大于等于100。年的范围是0～99，保存的值是1～199。<br />注意：一般的世纪，都包含了100年，而对于0世纪，由于包含公元前和公元后两部分且不包含0年，因此包含了198年。<br />==============================================================<br />Oracle基本数据类型存储格式浅析（三）——日期类型（二）<br />发表人:yangtingkun | 发表时间: 2004年十二月16日, 18:03<br />这篇文章描述TIMESTAMP类型的数据在Oracle中是以何种格式存放的。<br />下面通过一个例子进行说明。<br /> <br />SQL&gt; create table test_time (col_time timestamp);<br />表已创建。<br />SQL&gt; insert into test_time values (to_timestamp('0001-1-1 0:0:0.0', 'syyyy-mm-dd hh24:mi:ss.ff'));<br />已创建 1 行。<br />SQL&gt; insert into test_time values (to_timestamp('2000-1-1 0:0:0.0', 'syyyy-mm-dd hh24:mi:ss.ff'));<br />已创建 1 行。<br />SQL&gt; insert into test_time values (to_timestamp('9999-12-31 23:59:59.999999', 'syyyy-mm-dd hh24:mi:ss.ff'));<br />已创建 1 行。<br />SQL&gt; insert into test_time values (to_timestamp('-0001-1-1 0:0:0.0', 'syyyy-mm-dd hh24:mi:ss.ff'));<br />已创建 1 行。<br />SQL&gt; insert into test_time values (to_timestamp('-0100-3-4 13:2:3.234015', 'syyyy-mm-dd hh24:mi:ss.ff'));<br />已创建 1 行。<br />SQL&gt; insert into test_time values (systimestamp);<br />已创建 1 行。<br />SQL&gt; insert into test_time values (to_timestamp('2000-1-1 0:0:0.123456789', 'syyyy-mm-dd hh24:mi:ss.ff9'));<br />已创建 1 行。<br />SQL&gt; commit;<br />提交完成。<br />SQL&gt; select to_char(col_time, 'syyyy-mm-dd hh24:mi:ss.ff9') time, dump(col_time) dump_time<br />  2  from test_time;<br />TIME                           DUMP_TIME<br />------------------------------ ----------------------------------------------------<br /> 0001-01-01 00:00:00.000000000 Typ=180 Len=7: 100,101,1,1,1,1,1<br /> 2000-01-01 00:00:00.000000000 Typ=180 Len=7: 120,100,1,1,1,1,1<br /> 9999-12-31 23:59:59.999999000 Typ=180 Len=11: 199,199,12,31,24,60,60,59,154,198,24<br />-0001-01-01 00:00:00.000000000 Typ=180 Len=7: 100,99,1,1,1,1,1<br />-0100-03-04 13:02:03.234015000 Typ=180 Len=11: 99,100,3,4,14,3,4,13,242,201,24<br /> 2004-12-15 16:14:52.738000000 Typ=180 Len=11: 120,104,12,15,17,15,53,43,252,252,128<br /> 2000-01-01 00:00:00.123457000 Typ=180 Len=11: 120,100,1,1,1,1,1,7,91,205,232<br />已选择7行。<br />与DATE类型对比可以发现，对于TIMESTAMP类型，如果不包含微秒信息或者微秒值为0，那么存储结果和DATE完全相同。当微秒值为0时，Oracle为了节省空间，不会保存微秒信息。<br />如果毫秒值不为0，Oracle把微秒值当作一个9位数的数字来保存。<br />比如999999000，保存为59,154,198,24。234015000保存为13,242,201,24。<br />SQL&gt; select to_char(999999000, 'xxxxxxxxxx') from dual;<br />TO_CHAR(999<br />-----------<br />   3b9ac618<br />SQL&gt; select to_number('3b', 'xxx') one, to_number('9a', 'xxx') two, <br />  2  to_number('c6', 'xxx') three, to_number('18', 'xxx') four from dual;<br />       ONE        TWO      THREE       FOUR<br />---------- ---------- ---------- ----------<br />        59        154        198         24<br />SQL&gt; select to_char(234015000, 'xxxxxxxx') from dual;<br />TO_CHAR(2<br />---------<br />  df2c918<br />SQL&gt; select to_number('d', 'xxx') one, to_number('f2', 'xxx') two, <br />  2  to_number('c9', 'xxx') three, to_number('18', 'xxx') four from dual;<br />       ONE        TWO      THREE       FOUR<br />---------- ---------- ---------- ----------<br />        13        242        201         24<br /> <br />另外，注意一点，不指定精度的情况下，TIMESTAMP默认取6位。长度超过6位，会四舍五入到6位。如果希望保存9位的TIMESTAMP，必须明确指定精度。<br />SQL&gt; alter table test_time modify (col_time timestamp(9));<br />表已更改。<br />SQL&gt; insert into test_time values (to_timestamp('2000-1-1 0:0:0.123456789', 'syyyy-mm-dd hh24:mi:ss.ff9'));<br />已创建 1 行。<br />SQL&gt; select to_char(col_time, 'syyyy-mm-dd hh24:mi:ss.ff9') time, dump(col_time) dump_time <br />  2  from test_time;<br />TIME                           DUMP_TIME<br />------------------------------ ---------------------------------------------------<br /> 0001-01-01 00:00:00.000000000 Typ=180 Len=7: 100,101,1,1,1,1,1<br /> 2000-01-01 00:00:00.000000000 Typ=180 Len=7: 120,100,1,1,1,1,1<br /> 9999-12-31 23:59:59.999999000 Typ=180 Len=11: 199,199,12,31,24,60,60,59,154,198,24<br />-0001-01-01 00:00:00.000000000 Typ=180 Len=7: 100,99,1,1,1,1,1<br />-0100-03-04 13:02:03.234015000 Typ=180 Len=11: 99,100,3,4,14,3,4,13,242,201,24<br /> 2004-12-15 16:14:52.738000000 Typ=180 Len=11: 120,104,12,15,17,15,53,43,252,252,128<br /> 2000-01-01 00:00:00.123457000 Typ=180 Len=11: 120,100,1,1,1,1,1,7,91,205,232<br /> 2000-01-01 00:00:00.123456789 Typ=180 Len=11: 120,100,1,1,1,1,1,7,91,205,21<br />已选择8行。<br />============================================================== <br />Oracle基本数据类型存储格式浅析（三）——日期类型（三）<br />发表人:yangtingkun | 发表时间: 2004年十二月17日, 17:00<br />如果直接在SQL语句中对SYSDATE或由TO_DATE函数生成日期进行DUMP操作，会发现得到的结果与DUMP数据库中保存的日期的结果不一样。<br /> <br />SQL&gt; truncate table test_date;<br />表已截掉。<br />SQL&gt; insert into test_date values (to_date('2004-12-17 16:42:42', 'syyyy-mm-dd hh24:mi:ss'));<br />已创建 1 行。<br />SQL&gt; col dump_date format a65<br />SQL&gt; select to_char(date_col, 'syyyy-mm-dd hh24:mi:ss') dat, dump(date_col) dump_date from test_date;<br />DAT                  DUMP_DATE<br />-------------------- ---------------------------------------------------------<br /> 2004-12-17 16:42:42 Typ=12 Len=7: 120,104,12,17,17,43,43<br />SQL&gt; select to_char(to_date('2004-12-17 16:42:42', 'syyyy-mm-dd hh24:mi:ss'), 'syyyy-mm-dd hh24:mi:ss') dat, <br />  2  dump(to_date('2004-12-17 16:42:42', 'syyyy-mm-dd hh24:mi:ss')) dump_date from dual;<br />DAT                  DUMP_DATE<br />-------------------- ---------------------------------------------------------<br /> 2004-12-17 16:42:42 Typ=13 Len=8: 212,7,12,17,16,42,42,0<br />存储在数据库中的DATE类型是12，而直接在SQL中使用的DATE类型是13。而且二者的长度以及表示方式都不相同。这两种类型的不同指出主要体现在两点：一：时、分、秒的表示不同；二、世纪和年的表示不同。<br />SQL中使用DATE的时分秒没有采用加1存储方式，而且原值存储。<br />SQL中使用DATE没有采用世纪、年的方式保持，而是采用了按数值保存的方式。第一位表示低位，第二位表示高位。低位表示最大的值是255。如上面的例子中，212+7×256=2004。<br />SQL&gt; select to_char(to_date('-2004-12-17 16:42:42', 'syyyy-mm-dd hh24:mi:ss'), 'syyyy-mm-dd hh24:mi:ss') dat, <br />  2  dump(to_date('-2004-12-17 16:42:42', 'syyyy-mm-dd hh24:mi:ss')) dump_date from dual;<br />DAT                  DUMP_DATE<br />-------------------- ---------------------------------------------------<br />-2004-12-17 16:42:42 Typ=13 Len=8: 44,248,12,17,16,42,42,0<br />SQL&gt; select dump(to_date('-1-1-1', 'syyyy-mm-dd')) from dual;<br />DUMP(TO_DATE('-1-1-1','SYYYY-MM-D<br />---------------------------------<br />Typ=13 Len=8: 255,255,1,1,0,0,0,0<br /> <br />对于公元前的日期，Oracle从255，255开始保存。公元前的年的保存的值和对应的公元后的年的值相加的和是256，255。如上例中的公元2004年和公元前2004年的值相加：212+44=256，7+248=255。<br />SQL中DATE类型最后还包括一个0，似乎目前没有使用。<br />==============================================================<br />Oracle基本数据类型存储格式浅析（三）——日期类型（四）<br />发表人:yangtingkun | 发表时间: 2005年一月12日, 02:26<br />本文对TIMESTAMP WITH LOCAL TIME ZONE和TIMESTAMP WITH TIME ZONE类型的存储格式进行简单的说明。<br /> <br />SQL&gt; CREATE TABLE TEST_TIMESTAMP(TIME1 TIMESTAMP(9), TIME2 TIMESTAMP(6) WITH LOCAL TIME ZONE, <br />  2  TIME3 TIMESTAMP(4) WITH TIME ZONE); <br />表已创建。 <br />SQL&gt; INSERT INTO TEST_TIMESTAMP VALUES (SYSTIMESTAMP, SYSTIMESTAMP, SYSTIMESTAMP); <br />已创建 1 行。 <br />SQL&gt; SELECT * FROM TEST_TIMESTAMP; <br />TIME1<br />----------------------------------------------------<br />TIME2<br />----------------------------------------------------<br />TIME3<br />----------------------------------------------------<br />11-1月 -05 11.08.15.027000000 下午<br />11-1月 -05 11.08.15.027000 下午<br />11-1月 -05 11.08.15.0270 下午 +08:00 <br />SQL&gt; SELECT DUMP(TIME1, 16), DUMP(TIME2, 16), DUMP(TIME3, 16) FROM TEST_TIMESTAMP; <br />DUMP(TIME1,16)<br />-------------------------------------------------------------<br />DUMP(TIME2,16)<br />-------------------------------------------------------------<br />DUMP(TIME3,16)<br />-------------------------------------------------------------<br />Typ=180 Len=11: 78,69,1,b,18,9,10,1,9b,fc,c0<br />Typ=231 Len=11: 78,69,1,b,18,9,10,1,9b,fc,c0<br />Typ=181 Len=13: 78,69,1,b,10,9,10,1,9b,fc,c0,1c,3c <br />可以发现，如果客户端和数据库中的时区是一致的，那么TIMESTAMP和TIMESTAMP WITH LOCAL TIME ZONE存储的数据是完全一样的。 <br />TIMESTAMP WITH TIME ZONE则略有不同，它保存的是0时区的时间，和所处的时区信息。 <br />修改客户端主机的时区，由东8区（+8区）改为0时区。 <br />SQL&gt; INSERT INTO TEST_TIMESTAMP VALUES (SYSTIMESTAMP, SYSTIMESTAMP, SYSTIMESTAMP); <br />已创建 1 行。 <br />修改客户端主机的时区，改为西5区（-5时区）。 <br />SQL&gt; INSERT INTO TEST_TIMESTAMP VALUES (SYSTIMESTAMP, SYSTIMESTAMP, SYSTIMESTAMP); <br />已创建 1 行。 <br />修改客户端主机的时区，改为西12区（-12时区）。 <br />SQL&gt; INSERT INTO TEST_TIMESTAMP VALUES (SYSTIMESTAMP, SYSTIMESTAMP, SYSTIMESTAMP); <br />已创建 1 行。 <br />修改客户端主机的时区，改为东13区（+13时区）。 <br />SQL&gt; INSERT INTO TEST_TIMESTAMP VALUES (SYSTIMESTAMP, SYSTIMESTAMP, SYSTIMESTAMP); <br />已创建 1 行。 <br />修改客户端主机的时区，改为西3.5区（-3.5时区）。 <br />SQL&gt; INSERT INTO TEST_TIMESTAMP VALUES (SYSTIMESTAMP, SYSTIMESTAMP, SYSTIMESTAMP); <br />已创建 1 行。 <br />修改客户端主机的时区，改为东9.5区（+9.5时区）。 <br />SQL&gt; INSERT INTO TEST_TIMESTAMP VALUES (SYSTIMESTAMP, SYSTIMESTAMP, SYSTIMESTAMP); <br />已创建 1 行。 <br />SQL&gt; COMMIT; <br />提交完成。 <br />修改客户端主机的时区，改回东8区（+8时区）。 <br />SQL&gt; SELECT * FROM TEST_TIMESTAMP; <br />TIME1<br />-----------------------------------------------<br />TIME2<br />-----------------------------------------------<br />TIME3<br />-----------------------------------------------<br />11-1月 -05 11.08.15.027000000 下午<br />11-1月 -05 11.08.15.027000 下午<br />11-1月 -05 11.08.15.0270 下午 +08:00 <br />11-1月 -05 03.11.43.746000000 下午<br />11-1月 -05 11.11.43.746000 下午<br />11-1月 -05 03.11.43.7460 下午 +00:00 <br />11-1月 -05 10.14.08.987000000 上午<br />11-1月 -05 11.14.08.987000 下午<br />11-1月 -05 10.14.08.9870 上午 -05:00 <br />11-1月 -05 03.15.01.732000000 上午<br />11-1月 -05 11.15.01.732000 下午<br />11-1月 -05 03.15.01.7320 上午 -12:00 <br />12-1月 -05 04.20.21.522000000 上午<br />11-1月 -05 11.20.21.522000 下午<br />12-1月 -05 04.20.21.5220 上午 +13:00 <br />11-1月 -05 02.15.16.567000000 下午<br />12-1月 -05 01.45.16.567000 上午<br />11-1月 -05 02.15.16.5670 下午 -03:30 <br />12-1月 -05 03.16.54.992000000 上午<br />12-1月 -05 01.46.54.992000 上午<br />12-1月 -05 03.16.54.9920 上午 +09:30 <br />已选择7行。 <br />SQL&gt; SELECT DUMP(TIME1, 16), DUMP(TIME2, 16), DUMP(TIME3, 16) FROM TEST_TIMESTAMP; <br />DUMP(TIME1,16)<br />-------------------------------------------------------------<br />DUMP(TIME2,16)<br />-------------------------------------------------------------<br />DUMP(TIME3,16)<br />-------------------------------------------------------------<br />Typ=180 Len=11: 78,69,1,b,18,9,10,1,9b,fc,c0<br />Typ=231 Len=11: 78,69,1,b,18,9,10,1,9b,fc,c0<br />Typ=181 Len=13: 78,69,1,b,10,9,10,1,9b,fc,c0,1c,3c <br />Typ=180 Len=11: 78,69,1,b,10,c,2c,2c,77,e,80<br />Typ=231 Len=11: 78,69,1,b,18,c,2c,2c,77,e,80<br />Typ=181 Len=13: 78,69,1,b,10,c,2c,2c,77,e,80,14,3c <br />Typ=180 Len=11: 78,69,1,b,b,f,9,3a,d4,6c,c0<br />Typ=231 Len=11: 78,69,1,b,18,f,9,3a,d4,6c,c0<br />Typ=181 Len=13: 78,69,1,b,10,f,9,3a,d4,6c,c0,f,3c <br />Typ=180 Len=11: 78,69,1,b,4,10,2,2b,a1,6f,0<br />Typ=231 Len=11: 78,69,1,b,18,10,2,2b,a1,6f,0<br />Typ=181 Len=13: 78,69,1,b,10,10,2,2b,a1,6f,0,8,3c <br />Typ=180 Len=11: 78,69,1,c,5,15,16,1f,1d,16,80<br />Typ=231 Len=11: 78,69,1,b,18,15,16,1f,1d,16,80<br />Typ=181 Len=13: 78,69,1,b,10,15,16,1f,1d,16,80,21,3c <br />Typ=180 Len=11: 78,69,1,b,f,10,11,21,cb,bb,c0<br />Typ=231 Len=11: 78,69,1,c,2,2e,11,21,cb,bb,c0<br />Typ=181 Len=13: 78,69,1,b,12,2e,11,21,cb,bb,c0,11,1e <br />Typ=180 Len=11: 78,69,1,c,4,11,37,3b,20,b8,0<br />Typ=231 Len=11: 78,69,1,c,2,2f,37,3b,20,b8,0<br />Typ=181 Len=13: 78,69,1,b,12,2f,37,3b,20,b8,0,1d,5a <br />  <br />SQL&gt; SELECT TO_NUMBER('1C', 'XXX'), TO_NUMBER('3C', 'XXX') FROM DUAL; <br />TO_NUMBER('1C','XXX') TO_NUMBER('3C','XXX')<br />--------------------- ---------------------<br />                   28                    60 <br />SQL&gt; SELECT TO_NUMBER('14', 'XXX'), TO_NUMBER('3C', 'XXX'), TO_NUMBER('143C', 'XXXXXXX') FROM DUAL; <br />TO_NUMBER('14','XXX') TO_NUMBER('3C','XXX')<br />--------------------- ---------------------<br />                   20                    60 <br />SQL&gt; SELECT TO_NUMBER('3C', 'XXX') , TO_NUMBER('1E', 'XXX'), TO_NUMBER('5A', 'XXX') FROM DUAL; <br />TO_NUMBER('3C','XXX') TO_NUMBER('1E','XXX') TO_NUMBER('5A','XXX')<br />--------------------- --------------------- -------------------<br />                   60                    30                  90 <br />可以看出，修改时区会导致系统TIMESTAMP时间发生变化，但是对于TIMESTAMP WITH LOCAL TIME ZONE类型，总是将系统的时间转化到数据库服务器上时区的时间进行存储。 <br />TIMESTAMP WITH TIME ZONE保存的是当前时间转化到0时区的对应的时间，并通过最后两位来保存时区信息。 <br />第一位表示时区的小时部分。0时区用0x14表示。东n区在这个基础上加n，西n区在这个基础上减n。我们所处的东8区表示为0x1C。西5区表示为0xF。 <br />第二位表示时区的分钟部分。标准是0x3C，即60分钟。对于东时区的半区，在这个基础上加上30分钟，如果是西时区，则减去30分钟。<br />==============================================================<br />Oracle基本数据类型存储格式浅析（四）——ROWID类型（一）<br />发表人:yangtingkun | 发表时间: 2004年十二月21日, 19:14<br />Oracle的ROWID用来唯一标识表中的一条记录，是这条数据在数据库中存放的物理地址。<br />Oracle的ROWID分为两种：物理ROWID和逻辑ROWID。索引组织表使用逻辑ROWID，其他类型的表使用物理ROWID。其中物理ROWID在Oracle的8版本中进行了扩展，Oracle7及以下版本使用约束ROWID，Oracle8及以上版本使用扩展ROWID。本文描述物理扩展ROWID，由于约束ROWID仅仅是为了兼容早期版本，因此不做讨论。<br />SQL&gt; create table test_rowid (id number, row_id rowid);<br />表已创建。<br />SQL&gt; insert into test_rowid values (1, null);<br />已创建 1 行。<br />SQL&gt; update test_rowid set row_id = rowid where id = 1;<br />已更新 1 行。<br />SQL&gt; commit;<br />提交完成。<br />SQL&gt; select rowid, row_id from test_rowid;<br />ROWID              ROW_ID<br />------------------ ------------------<br />AAABnRAAGAAAACWAAA AAABnRAAGAAAACWAAA<br />Oracle的物理扩展ROWID有18位，每位采用64位编码，分别用A~Z、a~z、0~9、+、/共64个字符表示。A表示0，B表示1，……Z表示25，a表示26，……z表示51，0表示52，……，9表示61，+表示62，/表示63。<br />ROWID具体划分可以分为4部分。<br />1.OOOOOO：前6位表示DATA OBJECT NUMBER，将起转化位数字后匹配DBA_OBJECTS中的DATA_OBJECT_ID，可以确定表信息。<br />如上面例子中的DATA OBJECT NUMBER是AAABnR，转化位数字是1×64×64 ＋39×64 ＋ 17。<br />SQL&gt; select owner, object_name from dba_objects <br />  2  where data_object_id = 1*64*64 + 39*64 + 17;<br />OWNER                          OBJECT_NAME<br />------------------------------ -----------------------------<br />YANGTK                         TEST_ROWID<br />2.FFF：第7到9位表示相对表空间的数据文件号。<br />上面的例子中是AAG，表示数据文件6。<br />SQL&gt; select file_name, tablespace_name from dba_data_files where relative_fno = 6;<br />FILE_NAME                                     TABLESPACE_NAME<br />--------------------------------------------- ---------------<br />E:ORACLEORADATATESTYANGTK01.DBF           YANGTK<br />3.BBBBBB：第10到15位表示这条记录在数据文件中的第几个BLOCK中。<br />上面的例子是AAAACW，转化位数字是2×64＋22，表示这条记录在数据文件中的第150个BLOCK。<br />4.RRR：最后3位表示这条记录是BLOCK中的第几条记录。<br />上面的例子是AAA，表示第0条记录（总是从0开始计数）。<br />SQL&gt; alter system dump datafile 6 block 150;<br />系统已更改。<br />SQL&gt; select row_id, dump(row_id, 16) dump_rowid from test_rowid;<br />ROW_ID             DUMP_ROWID<br />------------------ -------------------------------------------------<br />AAABnRAAGAAAACWAAA Typ=69 Len=10: 0,0,19,d1,1,80,0,96,0,0<br /> <br />找到对应的dump文件，可以发现类型的信息<br />*** 2004-12-21 17:58:26.000<br />*** SESSION ID:(13.91) 2004-12-21 17:58:26.000<br />Start dump data blocks tsn: 6 file#: 6 minblk 150 maxblk 150<br />buffer tsn: 6 rdba: 0x01800096 (6/150)<br />scn: 0x0000.2e389c16 seq: 0x01 flg: 0x06 tail: 0x9c160601<br />frmt: 0x02 chkval: 0xc97d type: 0x06=trans data<br />Block header dump:  0x01800096<br /> Object id on Block? Y<br /> seg/obj: 0x19d1  csc: 0x00.2e389c0f  itc: 2  flg: O  typ: 1 - DATA<br />     fsl: 0  fnx: 0x0 ver: 0x01<br /> <br /> Itl           Xid                  Uba         Flag  Lck        Scn/Fsc<br />0x01   0x0003.009.00000057  0x0080004b.0042.56  --U-    1  fsc 0x0000.2e389c16<br />0x02   0x0000.000.00000000  0x00000000.0000.00  ----    0  fsc 0x0000.00000000<br /> <br />data_block_dump,data header at 0x651105c<br />===============<br />tsiz: 0x3fa0<br />hsiz: 0x14<br />pbl: 0x0651105c<br />bdba: 0x01800096<br />     76543210<br />flag=--------<br />ntab=1<br />nrow=1<br />frre=-1<br />fsbo=0x14<br />fseo=0x3f89<br />avsp=0x3f7b<br />tosp=0x3f7b<br />0xe:pti[0] nrow=1 offs=0<br />0x12:pri[0] offs=0x3f89<br />block_row_dump:<br />tab 0, row 0, @0x3f89<br />tl: 17 fb: --H-FL-- lb: 0x1  cc: 2<br />col  0: [ 2]  c1 02<br />col  1: [10]  00 00 19 d1 01 80 00 96 00 00<br />end_of_block_dump<br />End dump data blocks tsn: 6 file#: 6 minblk 150 maxblk 150<br /> <br />有时需要查看表的DUMP信息，但是很难准确定位表中数据开始于哪个BLOCK，根据ROWID中包含的信息就可以方便的找到起始BLOCK。<br />下面简单描述一下ROWID类型是如何存储的。<br />SQL&gt; select row_id, dump(row_id, 16) dump_rowid from test_rowid;<br />ROW_ID             DUMP_ROWID<br />------------------ -------------------------------------------------<br />AAABnRAAGAAAACWAAA Typ=69 Len=10: 0,0,19,d1,1,80,0,96,0,0<br />前4位表示ROWID的前6位，也就是DATA_OBJECT_ID信息。数据以数值的格式保存。<br />SQL&gt; select to_number('19d1', 'xxxxxx') from dual;<br />TO_NUMBER('19D1','XXXXXX')<br />--------------------------<br />                      6609<br />SQL&gt; select 1*64*64 + 39*64 + 17 from dual;<br />1*64*64+39*64+17<br />----------------<br />            6609<br />这里存在一个问题，根据ROWID的取值范围，OBJECT_DATA_ID最大的值是64的6次方，而根据DUMP，oracle只用了4位保存，因此取值范围是256的4次方。<br />SQL&gt; set numwid 12<br />SQL&gt; select power(64, 6), power(256, 4), power(64, 6)/power(256, 4) from dual;<br /> POWER(64,6) POWER(256,4) POWER(64,6)/POWER(256,4)<br />------------ ------------ ------------------------<br /> 68719476736   4294967296                       16<br />可见，OBJECT_DATA_ID的最大值是4294967296，当超过这个值时会出现重复的情况。（当然，现实中不大可能）。<br />后面4位比较特殊，是数据文件号和BLOCK数的“和”值构成。<br />数据文件的数值乘64后保存在5、6位上。<br />SQL&gt; select to_number('0180', 'xxxx') from dual;<br />TO_NUMBER('0180','XXXX')<br />------------------------<br />                     384<br />SQL&gt; select 6*64 from dual;<br />        6*64<br />------------<br />         384<br />同时，6位BLOCK的值，也保存在这4位上，并与数据文件转存结果相加。仍然是以数字格式存放。<br />SQL&gt; select to_number('96', 'xxx') from dual;<br />TO_NUMBER('96','XXX')<br />---------------------<br />                  150<br />SQL&gt; select 2*64 + 22 from dual;<br />   2*64+22<br />----------<br />       150<br />由于采用两位保存数据文件的值，且最小单位是64，因此，ROWID中可以保存的数据文件数是1024，超过1024会造成ROWID的重复。<br />SQL&gt; select 256*256/64 from dual;<br />256*256/64<br />----------<br />      1024<br />由于BLOCK的值和数据文件共用这4位，因此BLOCK的第3位最大值应小于64，这样才能保证ROWID的不重复。因此BLOCK值的最大值应该是4194304。<br />SQL&gt; select 64*256*256 from dual;<br />64*256*256<br />----------<br />   4194304<br />最后两位保存BLOCK中记录的值。这个值的最大值是65536。<br />SQL&gt; select 256*256 from dual;<br />   256*256<br />----------<br />     65536<br /> <br />下面看一个例子，Oracle是如何将相对文件号和BLOCK号“共享”第5、6字节的。<br />SQL&gt; select blocks from user_segments where segment_name = 'TEST1';<br />    BLOCKS<br />----------<br />     86016<br />SQL&gt; select max(rowid), dump(max(rowid)) dump_rowid from test1;<br />MAX(ROWID)         DUMP_ROWID<br />------------------ -------------------------------------------<br />AAABy+AAJAAAU5EAAM Typ=69 Len=10: 0,0,28,190,2,65,78,68,0,12<br />SQL&gt; select dbms_rowid.rowid_relative_fno('AAABy+AAJAAAU5EAAM') fno, <br />  2  dbms_rowid.rowid_block_number('AAABy+AAJAAAU5EAAM') block_num from dual;<br />       FNO  BLOCK_NUM<br />---------- ----------<br />         9      85572<br />SQL&gt; select 9*64, 2*256+65 from dual;<br />      9*64   2*256+65<br />---------- ----------<br />       576        577<br />SQL&gt; select 1*256*256 + 78*256 + 68 from dual;<br />1*256*256+78*256+68<br />-------------------<br />              85572<br />可以看到，5、6为的值除以64得到的商是相对文件号，余数是BLOCK号的高位，乘以65536后加上低两位才是BLOCK号。<br />==============================================================<br />Oracle基本数据类型存储格式浅析（四）——ROWID类型（二）<br />发表人:yangtingkun | 发表时间: 2004年十二月22日, 23:52<br />Oracle的文档上没有介绍逻辑ROWID的编码规则，而且通过DUMP的结果也很难反推出编码规则。因此，本文只简单讨论一下逻辑ROWID的存储。<br />下面来看例子。<br />SQL&gt; create table test_index (id number primary key, name varchar2(20)) organization index; <br />表已创建。 <br />SQL&gt; insert into test_index values (1, 'a'); <br />已创建 1 行。 <br />SQL&gt; commit; <br />提交完成。 <br />SQL&gt; col dump_rowid format a60<br />SQL&gt; select rowid, dump(rowid) dump_rowid from test_index; <br />ROWID                       DUMP_ROWID<br />--------------------------- ----------------------------------------<br />*BAFAB4wCwQL+               Typ=208 Len=10: 2,4,1,64,7,140,2,193,2,254 <br />逻辑ROWID的DUMP结果前两位都是2和4，最后一位都是254，（我还没有发现其他的情况），由于逻辑ROWID和主键的值有关，所以长度是不定的，因此应该是用来表示开始和结束的。 <br />第3、4位和物理ROWID一样，表示的是相对表空间的数据文件号乘以64的值。 <br />第5、6位表示这条记录在数据文件的第几个BLOCK中。 <br />从第7位开始到DUMP结果的倒数第二位，表示主键的值。首先是主键中第一个字段的长度，这里是2，然后是主键的值，由于是NUMBER类型，因此193，2表示数值1。如果是多个字段组成的主键，第一个字段之后是第二个字段的长度，然后是第二个字段的值……。 <br />SQL&gt; select (1*256 + 64)/64 from dual; <br />(1*256+64)/64<br />-------------<br />            5 <br />SQL&gt; select 7*256 + 140 from dual; <br /> 7*256+140<br />----------<br />      1932 <br />SQL&gt; alter system dump datafile 5 block 1932; <br />系统已更改。 <br />找到相应的dump文件，可以发现刚才插入的记录。 <br />Dump file f:oracleadmintest4udumptest4_ora_3828.trc<br />Thu Dec 23 00:17:53 2004<br />ORACLE V9.2.0.4.0 - Production vsnsta=0<br />vsnsql=12 vsnxtr=3<br />Windows 2000 Version 5.1 Service Pack 1, CPU type 586<br />Oracle9i Enterprise Edition Release 9.2.0.4.0 - Production<br />With the Partitioning, Oracle Label Security, OLAP and Oracle Data Mining options<br />JServer Release 9.2.0.4.0 - Production<br />Windows 2000 Version 5.1 Service Pack 1, CPU type 586<br />Instance name: test4 <br />Redo thread mounted by this instance: 1 <br />Oracle process number: 9 <br />Windows thread id: 3828, image: ORACLE.EXE <br />*** 2004-12-23 00:17:53.361<br />*** SESSION ID:(8.82) 2004-12-23 00:17:53.301<br />Start dump data blocks tsn: 5 file#: 5 minblk 1932 maxblk 1932<br />buffer tsn: 5 rdba: 0x0140078c (5/1932)<br />scn: 0x0000.00e9f122 seq: 0x01 flg: 0x02 tail: 0xf1220601<br />frmt: 0x02 chkval: 0x0000 type: 0x06=trans data<br />Block header dump:  0x0140078c<br /> Object id on Block? Y<br /> seg/obj: 0x1e48  csc: 0x00.e9f113  itc: 2  flg: E  typ: 2 - INDEX<br />     brn: 0  bdba: 0x1400789 ver: 0x01<br />     inc: 0  exflg: 0<br /> <br /> Itl           Xid                  Uba         Flag  Lck        Scn/Fsc<br />0x01   0x0000.000.00000000  0x00000000.0000.00  ----    0  fsc 0x0000.00000000<br />0x02   0x0005.008.000000e7  0x00800226.005c.24  --U-    1  fsc 0x0000.00e9f122<br /> <br />Leaf block dump<br />===============<br />header address 71963236=0x44a1264<br />kdxcolev 0<br />KDXCOLEV Flags = - - -<br />kdxcolok 0<br />kdxcoopc 0x90: opcode=0: iot flags=I-- is converted=Y<br />kdxconco 1<br />kdxcosdc 0<br />kdxconro 1<br />kdxcofbo 38=0x26<br />kdxcofeo 8026=0x1f5a<br />kdxcoavs 7988<br />kdxlespl 0<br />kdxlende 0<br />kdxlenxt 0=0x0<br />kdxleprv 0=0x0<br />kdxledsz 0<br />kdxlebksz 8036<br />row#0[8026] flag: K----, lock: 2<br />col 0; len 2; (2):  c1 02<br />tl: 5 fb: --H-FL-- lb: 0x0  cc: 1<br />col  0: [ 1]<br />Dump of memory from 0x044A31C7 to 0x044A31C8<br />44A31C0          61010100                        [...a]        <br />----- end of leaf block dump -----<br />End dump data blocks tsn: 5 file#: 5 minblk 1932 maxblk 1932 <br />可以看到，根据DUMP结果的3、4、5、6位可以定位记录的物理位置。 <br />需要注意的是，索引组织表以主键的顺序存储数据，因此插入、更新和删除数据都可能造成一条记录的物理位置发生变化，这时通过ROWID中的DATAFILE和BLOCK的信息可能就无法正确定位到记录的物理位置。当根据逻辑ROWID访问索引组织表时，首先会根据DATAFILE和BLOCK信息去找到相应的BLOCK，检查数据是否在这个BLOCK中，如果不在，就通过逻辑ROWID中的主键信息去通过索引扫描，找到这条记录。这就是Oracle文档在提到的physical guess。 <br />下面看一个由字符串和日期组成联合主键的例子。 <br />SQL&gt; create table test_index2 (id char(4), time date, <br />  2  constraint pk_test_index2 primary key (id, time)) organization index; <br />表已创建。 <br />SQL&gt; insert into test_index2 values ('1', sysdate); <br />已创建 1 行。 <br />SQL&gt; col dump_rowid format a75<br />SQL&gt; select rowid, dump(rowid) dump_rowid from test_index2; <br />ROWID                        DUMP_ROWID<br />---------------------------- ------------------------------------------------------------------<br />*BAFAB5QEMSAgIAd4aAwXASMT/g  Typ=208 Len=20: 2,4,1,64,7,148,4,49,32,32,32,7,120,104,12,23,1,35,19,254 <br />可以看出，第7位是字段id的长度4，然后是字符串1和三个空格的ASCII码，这是字符串的存储格式，后面跟着的7是字段time长度，后面七位是日期的存储格式。在逻辑ROWID中，数值、字符和日期类型的存储格式都和它们本身的存储格式一致，这里不在赘述。 <br />一般情况下，使用一位来表示长度，但是如果长度超过了127（16进制DUMP的结果是7F），则长度开始用两位表示。第一位以8开头，这个8只是标识位，表明长度字段现在由两位来表示。例如长度128表示位8080，而支持的最大值3800表示为8ED8。<br />==============================================================<br />Oracle基本数据类型存储格式浅析（五）——RAW类型<br />发表人:yangtingkun | 发表时间: 2004年十二月23日, 15:20<br />和其他数据类型相比，RAW类型的存储显得直观多了，它和SELECT时数据展示的值完全一样。（SELECT时是按照16进制展示的）<br /> <br />SQL&gt; create table test_raw (id number, raw_date raw(10));<br />表已创建。<br />SQL&gt; insert into test_raw values (1, hextoraw('ff'));<br />已创建 1 行。<br />SQL&gt; drop table test_raw;<br />表已丢弃。<br />SQL&gt; create table test_raw (raw_col raw(10));<br />表已创建。<br />SQL&gt; insert into test_raw values (hextoraw('ff'));<br />已创建 1 行。<br />SQL&gt; insert into test_raw values (hextoraw('0'));<br />已创建 1 行。<br />SQL&gt; insert into test_raw values (hextoraw('23fc'));<br />已创建 1 行。<br />SQL&gt; insert into test_raw values (hextoraw('fffffffffff'));<br />已创建 1 行。<br />SQL&gt; insert into test_raw values (hextoraw('ffffffffffffffffffff'));<br />已创建 1 行。<br />SQL&gt; insert into test_raw values (utl_raw.cast_to_raw('051'));<br />已创建 1 行。<br />SQL&gt; select raw_col, dump(raw_col, 16) dump_raw from test_raw;<br />RAW_COL              DUMP_RAW<br />-------------------- -----------------------------------------------<br />FF                   Typ=23 Len=1: ff<br />00                   Typ=23 Len=1: 0<br />23FC                 Typ=23 Len=2: 23,fc<br />0FFFFFFFFFFF         Typ=23 Len=6: f,ff,ff,ff,ff,ff<br />FFFFFFFFFFFFFFFFFFFF Typ=23 Len=10: ff,ff,ff,ff,ff,ff,ff,ff,ff,ff<br />303531               Typ=23 Len=3: 30,35,31<br />已选择6行。<br />RAW类型的存储很简单，对比字段的查询结果和DUMP的结果就一目了然了。<br />需要注意的是，两种转化为RAW的函数之间的差别。当使用HEXTORAW时，会把字符串中数据当作16进制数。而使用UTL_RAW.CAST_TO_RAW时，直接把字符串中每个字符的ASCII码存放到RAW类型的字段中。<br />SQL&gt; insert into test_raw values ('gg');<br />insert into test_raw values ('gg')<br />                             *<br />ERROR 位于第 1 行:<br />ORA-01465: 无效的十六进制数字<br />SQL&gt; insert into test_raw values (hextoraw('gg'));<br />insert into test_raw values (hextoraw('gg'))<br />                                      *<br />ERROR 位于第 1 行:<br />ORA-01465: 无效的十六进制数字<br />SQL&gt; insert into test_raw values (utl_raw.cast_to_raw('gg'));<br />已创建 1 行。<br />SQL&gt; select raw_col, dump(raw_col, 16) dump_raw from test_raw;<br />RAW_COL              DUMP_RAW<br />-------------------- ----------------------------------------------<br />FF                   Typ=23 Len=1: ff<br />00                   Typ=23 Len=1: 0<br />23FC                 Typ=23 Len=2: 23,fc<br />6767                 Typ=23 Len=2: 67,67<br />0FFFFFFFFFFF         Typ=23 Len=6: f,ff,ff,ff,ff,ff<br />FFFFFFFFFFFFFFFFFFFF Typ=23 Len=10: ff,ff,ff,ff,ff,ff,ff,ff,ff,ff<br />303531               Typ=23 Len=3: 30,35,31<br />已选择7行。<br /><br /></font>
		<p id="TBPingURL">Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=607570</p>
<img src ="http://www.blogjava.net/iKingQu/aggbug/36491.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/iKingQu/" target="_blank">風向逆轉 - 就要爪哇</a> 2006-03-20 23:37 <a href="http://www.blogjava.net/iKingQu/articles/36491.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[收藏]Windows环境下的Oracle服务</title><link>http://www.blogjava.net/iKingQu/articles/36490.html</link><dc:creator>風向逆轉 - 就要爪哇</dc:creator><author>風向逆轉 - 就要爪哇</author><pubDate>Mon, 20 Mar 2006 15:35:00 GMT</pubDate><guid>http://www.blogjava.net/iKingQu/articles/36490.html</guid><wfw:comment>http://www.blogjava.net/iKingQu/comments/36490.html</wfw:comment><comments>http://www.blogjava.net/iKingQu/articles/36490.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/iKingQu/comments/commentRss/36490.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/iKingQu/services/trackbacks/36490.html</trackback:ping><description><![CDATA[
		<span id="ArticleContent1_ArticleContent1_lblContent">注：SID - 数据库标识<br />    HOME_NAME - Oracle Home名称，如OraHome92、OraHome81<br />（1）OracleServiceSID                        <br />数据库服务，这个服务会自动地启动和停止数据库。如果安装了一个数据库，它的缺省启动类型为自动。服务进程为ORACLE.EXE，参数文件initSID.ora，日志文件SIDALRT.log，控制台SVRMGRL.EXE、SQLPLUS.EXE。<br /><br />（2）OracleHOME_NAMETNSListener<br />监听器服务，服务只有在数据库需要远程访问时才需要（无论是通过另外一台主机还是在本地通过 SQL*Net 网络协议都属于远程访问），不用这个服务就可以访问本地数据库，它的缺省启动类型为自动。服务进程为TNSLSNR.EXE，参数文件Listener.ora，日志文件listener.log，控制台LSNRCTL.EXE，默认端口1521、1526。<br /><br />（3）OracleHOME_NAMEAgent<br />OEM代理服务，接收和响应来自OEM控制台的任务和事件请求，只有使用OEM管理数据库时才需要，它的缺省启动类型为自动。服务进程为DBSNMP.EXE，参数文件snmp_rw.ora，日志文件nmi.log，控制台LSNRCTL.EXE，默认端口1748。<br /><br />（4）OracleHOME_NAMEClientCache        <br />名字缓存服务，服务缓存用于连接远程数据库的Oracle Names 数据。它的缺省启动类型是手动。然而，除非有一台Oracle Names 服务器，否则没有必要运行这个服务。服务进程为ONRSD.EXE，参数文件NAMES.ORA，日志文件ONRSD.LOG，控制台NAMESCTL.EXE。<br /><br />（5）OracleHOME_NAMECMAdmin                        <br />连接管理服务，是构建Connection Manager服务器所用，只有服务器作为Connection Manager才需要，它的缺省启动类型是手动。服务进程为CMADMIN.EXE，参数文件CMAN.ORA，日志文件CMADM_PID.TRC，控制台CMCTL.EXE，默认端口1830。<br /><br />（6）OracleHOME_NAMECMan                        <br />连接网关服务，是构建Connection Manager服务器所用，只有服务器作为Connection Manager才需要，它的缺省启动类型是手动。服务进程为CMGW.EXE，参数文件CMAN.ORA，日志文件CMAN_PID.TRC，控制台CMCTL.EXE，默认端口1630。<br /><br />（7）OracleHOME_NAMEDataGatherer<br />性能包数据采集服务，除非使用Oracle Capacity Planner 和 Oracle Performance Manager，否则不需要启动，它的缺省启动类型是手动。服务进程为VPPDC.EXE，日志文件alert_dg.log，控制台vppcntl.exe。<br /><br />（8）OracleHOME_NAMEHTTPServer<br />Oracle提供的WEB服务器，一般情况下我们只用它来访问Oracle Apache 目录下的Web 页面，比如说JSP 或者modplsql 页面。除非你使用它作为你的HTTP服务，否则不需要启动（若启动它会接管IIS的服务），它的缺省启动类型是手动。服务进程为APACHE.EXE，参数文件httpd.conf，默认端口80。<br /><br />（9）OracleHOME_NAMEPagingServer<br />通过一个使用调制解调器的数字传呼机或者电子邮件发出警告（没试过），它的缺省启动类型是手动。服务进程PAGNTSRV.EXE，日志文件paging.log。<br /><br />（10）OracleHOME_NAMENames<br />Oracle Names服务，只有服务器作为Names Server才需要，它的缺省启动类型是手动。服务进程NAMES.EXE，参数文件NAMES.ORA，日志文件NAMES.LOG，控制台NAMESCTL.EXE，默认端口1575。<br /><br />（11）OracleSNMPPeerMasterAgent<br />SNMP服务代理，用于支持SNMP的网管软件对服务器的管理，除非你使用网管工具监控数据库的情况，否则不需要启动，它的缺省启动类型是手动。服务进程为AGNTSVC.EXE，参数文件MASTER.CFG，默认端口161。<br /><br />（12）OracleSNMPPeerEncapsulater<br />SNMP协议封装服务，用于SNMP协议转换，除非你使用一个不兼容的SNMP代理服务，否则不需要启动，它的缺省启动类型是手动。服务进程为ENCSVC.EXE，参数文件ENCAPS.CFG，默认端口1161。<br /><br />（13）OracleHOME_NAMEManagementServer<br />OEM管理服务，使用OEM时需要，它的缺省启动类型是手动。服务进程为OMSNTSVR.EXE，日志文件oms.nohup。<br /></span>
		<br />
		<div style="FONT-SIZE: 14px; LINE-HEIGHT: 25px">
				<strong>作者Blog：</strong>
				<a id="ArticleContent1_ArticleContent1_AuthorBlogLink" href="http://blog.csdn.net/lm20224/" target="_blank">http://blog.csdn.net/lm20224/</a>
		</div>
<img src ="http://www.blogjava.net/iKingQu/aggbug/36490.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/iKingQu/" target="_blank">風向逆轉 - 就要爪哇</a> 2006-03-20 23:35 <a href="http://www.blogjava.net/iKingQu/articles/36490.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[收藏]数据库连接池Java实现小结</title><link>http://www.blogjava.net/iKingQu/articles/36482.html</link><dc:creator>風向逆轉 - 就要爪哇</dc:creator><author>風向逆轉 - 就要爪哇</author><pubDate>Mon, 20 Mar 2006 15:24:00 GMT</pubDate><guid>http://www.blogjava.net/iKingQu/articles/36482.html</guid><wfw:comment>http://www.blogjava.net/iKingQu/comments/36482.html</wfw:comment><comments>http://www.blogjava.net/iKingQu/articles/36482.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/iKingQu/comments/commentRss/36482.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/iKingQu/services/trackbacks/36482.html</trackback:ping><description><![CDATA[  因为工作需要要使用到连接池，所以拜读了互联网上众多前辈的文章，学了不少经验，这里想做一个小结，加上自己的想法和在一起，希望能给大家一些帮助。 
<h3>目的：</h3><ul><li>消除数据库频繁连接带来的开销和瓶颈。</li></ul><h3>解决方案：</h3><ul><li>不过多的限制用户的使用，既不能太多的要求用户按规定的方法得到和使用数据库连</li></ul><ul><li>尽量保持用户的习惯</li></ul><p>目前的很多方法都是要求用户只能按规定方法使用连接，不能使用直接关闭数据连接的方法。解决办法就是使用代理类，来中间解决。可以参考<a href="http://www-900.ibm.com/developerWorks/cn/java/l-connpoolproxy/index.shtml"><font color="#000000" size="2">http://www-900.ibm.com/developerWorks/cn/java/l-connpoolproxy/index.shtml</font></a></p><ul><li>能维护连接的正常状态</li></ul><p>因为针对数据库连接创建的资源，如果不能及时的释放，就会影响下一次数据连接的使用。例如在sql 2k中，一个连接不同创建多条Statement否则操作时会有数据连接占线的异常，所以必须在归还连接以后释放这些资源。</p><p></p><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>
   <a href="file://判/"><font face="Arial" color="#000000">//判</font></a>断是使用了createStatement语句<br />   if (CREATESTATE.equals(method.getName()))<br />   {<br />    obj = method.invoke(conn, args);<br />    statRef = (Statement)obj;//记录语句<br />    return obj;<br />   }
</code></pre></td></tr></tbody></table><p></p><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>   <a href="file://判/"><font face="Arial" color="#000000">//判</font></a>断是否调用了close的方法，如果调用close方法则把连接置为无用状态<br />   if(CLOSE.equals(method.getName()))<br />   {<br />    <a href="file://设/"><font face="Arial" color="#000000">//设</font></a>置不使用标志<br />    setIsFree(false);<br />    <a href="file://检/"><font face="Arial" color="#000000">//检</font></a>查是否有后续工作，清除该连接无用资源<br />    if (statRef != null)<br />     statRef.close();<br />    if (prestatRef != null)<br />     prestatRef.close();<br />    return null;<br />   }<br /></code></pre></td></tr></tbody></table><ul><li>正确保护类不被违例使用</li></ul><p>一个考虑就是不能让用户随便使用代理类，而只能自己使用，一个就是用内部私有类，一个就是使用只有指定类才能调用的标志。我的实现就是采用后者。</p><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code> /**<br />  * 创建连接的工厂，只能让工厂调用<br />  * @param factory 要调用工厂，并且一定被正确初始化<br />  * @param param 连接参数<br />  * @return 连接<br />  */<br /> static public _Connection getConnection(ConnectionFactory factory, ConnectionParam param)<br /> {<br />  if (factory.isCreate())//判断是否正确初始化的工厂<br />  {<br />   _Connection _conn = new _Connection(param);<br />   return _conn;<br />  }<br />  else<br />   return null;<br /> }<br /></code></pre></td></tr></tbody></table><ul><li>提供良好的用户接口，简单实用</li></ul><p>使用静态方法创建工厂，然后来得到连接，使用完全和普通的Connection方法一样，没有限制。同时为了方便，设置了连接参数类和工厂参数类。</p><ul><li><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code> ConnectionParam param = new ConnectionParam(driver,url,user,password);<br />  ConnectionFactory cf = null;//new ConnectionFactory(param, new FactoryParam());<br />  try{<br />   cf = new ConnectionFactory(param,new FactoryParam());<br />   Connection conn1 = cf.getFreeConnection();<br />   Connection conn2 = cf.getFreeConnection();<br />   Connection conn3 = cf.getFreeConnection();<br />   Statement stmt = conn1.createStatement();<br />   ResultSet rs = stmt.executeQuery("select * from requests");<br />   if (rs.next())<br />   {<br />    System.out.println("conn1 y");  <br />   }<br />   else<br />   {<br />    System.out.println("conn1 n");  <br />   } <br />   stmt.close();<br />   conn1.close();  </code></pre></td></tr></tbody></table></li><li>为了实现连接池的正常运作，使用了单态模</li></ul><p></p><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code> /**<br />  * 使用指定的参数创建一个连接池<br />  */<br /> public ConnectionFactory(ConnectionParam param, FactoryParam fparam)<br />  throws SQLException <br /> {<br />  <a href="file://不/"><font face="Arial" color="#000000">//不</font></a>允许参数为空<br />  if ((param == null)||(fparam == null))<br />   throw new SQLException("ConnectionParam和FactoryParam不能为空");<br />  if (m_instance == null)<br />  {<br />   synchronized(ConnectionFactory.class){<br />    if (m_instance == null)<br />    {<br />     <a href="file://new/"><font face="Arial" color="#000000">//new</font></a> instance<br />     <a href="file://参/"><font face="Arial" color="#000000">//参</font></a>数定制<br />     m_instance = new ConnectionFactory();<br />     m_instance.connparam = param;<br />     m_instance.MaxConnectionCount = fparam.getMaxConn();<br />     m_instance.MinConnectionCount = fparam.getMinConn();<br />     m_instance.ManageType = fparam.getType();<br />     m_instance.isflag = true;<br />     <a href="file://初/"><font face="Arial" color="#000000">//初</font></a>始化，创建MinConnectionCount个连接<br />     System.out.println("connection factory 创建！");<br />     try{<br />      for (int i=0; i &lt; m_instance.MinConnectionCount; i++)<br />      {<br />       _Connection _conn = _Connection.getConnection(m_instance, m_instance.connparam);<br />       if (_conn == null) continue;<br />       System.out.println("connection创建");<br />       m_instance.FreeConnectionPool.add(_conn);//加入空闲连接池<br />       m_instance.current_conn_count ++;<br />       <a href="file://标/"><font face="Arial" color="#000000">//标</font></a>志是否支持事务<br />       m_instance.supportTransaction = _conn.isSupportTransaction();    <br />      }<br />     }<br />     catch(Exception e)<br />     {<br />      e.printStackTrace();<br />     }<br />     <a href="file://根/"><font face="Arial" color="#000000">//根</font></a>据策略判断是否需要查询<br />     if (m_instance.ManageType != 0)<br />     {<br />      Thread t = new Thread(new FactoryMangeThread(m_instance));<br />      t.start();<br />     } <br />    }<br />   }<br />  }<br /> }<br /> </code></pre></td></tr></tbody></table><ul><li>连接池的管理</li></ul><p>对于连接池的管理，我是设想使用静态管理和动态管理两种策略，设置了最大限制，和恒定的连接数。使用了2个池，一个空闲池，一个使用池。静态就是使用的时候发现空闲连接不够再去检查。动态就是使用了一个线程定时检查。</p><p></p><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code> //根据策略判断是否需要查询<br />     if (m_instance.ManageType != 0)<br />     {<br />      Thread t = new Thread(new FactoryMangeThread(m_instance));<br />      t.start();<br />     }</code></pre><pre><code> //连接池调度线程<br />public class FactoryMangeThread implements Runnable {<br /> ConnectionFactory cf = null;<br /> long delay = 1000;<br /> public FactoryMangeThread(ConnectionFactory obj)<br /> {<br />  cf = obj;<br /> }<br /> /* (non-Javadoc)<br />  * @see java.lang.Runnable#run()<br />  */<br /> public void run() {<br />  while(true){<br />   try{<br />    Thread.sleep(delay);<br />   }<br />   catch(InterruptedException e){}<br />   System.out.println("eeeee");<br />   <a href="file://判/"><font face="Arial" color="#000000">//判</font></a>断是否已经关闭了工厂，那就退出监听<br />   if (cf.isCreate())<br />    cf.schedule();<br />   else<br />    System.exit(1);<br />  }<br /> }<br />}</code></pre></td></tr></tbody></table><p>最后给出完整的源代码：</p><p></p><hr /><p></p><p>_Connectio.java</p><p></p><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre>package scut.ailab.connectionpool;</pre><pre>import java.lang.reflect.*;<br />import java.sql.*;</pre><pre>/**<br /> * @author youyongming<br /> * 定义数据库连接的代理类<br /> */<br />public class _Connection implements InvocationHandler {<br /> <a href="file://定/"><font face="Arial" color="#000000">//定</font></a>义连接<br /> private Connection conn = null;<br /> <a href="file://定/"><font face="Arial" color="#000000">//定</font></a>义监控连接创建的语句<br /> private Statement statRef = null;<br /> private PreparedStatement prestatRef = null;<br /> <a href="file://是/"><font face="Arial" color="#000000">//是</font></a>否支持事务标志<br /> private boolean supportTransaction = false;<br /> <a href="file://数/"><font face="Arial" color="#000000">//数</font></a>据库的忙状态<br /> private boolean isFree = false;<br /> <a href="file://最/"><font face="Arial" color="#000000">//最</font></a>后一次访问时间<br /> long lastAccessTime = 0;<br /> <a href="file://定/"><font face="Arial" color="#000000">//定</font></a>义要接管的函数的名字<br /> String CREATESTATE = "createStatement";<br /> String CLOSE = "close";<br /> String PREPARESTATEMENT = "prepareStatement";<br /> String COMMIT = "commit";<br /> String ROLLBACK = "rollback";</pre><pre> /**<br />  * 构造函数，采用私有，防止被直接创建<br />  * @param param 连接参数<br />  */<br /> private _Connection(ConnectionParam param) {<br />  <a href="file://记/"><font face="Arial" color="#000000">//记</font></a>录日至<br />  <br />  try{<br />   <a href="file://创/"><font face="Arial" color="#000000">//创</font></a>建连接<br />   Class.forName(param.getDriver()).newInstance();<br />   conn = DriverManager.getConnection(param.getUrl(),param.getUser(), param.getPassword());   <br />   DatabaseMetaData dm = null;<br />   dm = conn.getMetaData();<br />   <a href="file://判/"><font face="Arial" color="#000000">//判</font></a>断是否支持事务<br />   supportTransaction = dm.supportsTransactions();<br />  }<br />  catch(Exception e)<br />  {<br />   e.printStackTrace();<br />  }<br /> }</pre><pre><br /> /* (non-Javadoc)<br />  * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])<br />  */<br /> public Object invoke(Object proxy, Method method, Object[] args)<br />  throws Throwable {<br />   Object obj = null;<br />   <a href="file://判/"><font face="Arial" color="#000000">//判</font></a>断是否调用了close的方法，如果调用close方法则把连接置为无用状态<br />   if(CLOSE.equals(method.getName()))<br />   {<br />    <a href="file://设/"><font face="Arial" color="#000000">//设</font></a>置不使用标志<br />    setIsFree(false);<br />    <a href="file://检/"><font face="Arial" color="#000000">//检</font></a>查是否有后续工作，清除该连接无用资源<br />    if (statRef != null)<br />     statRef.close();<br />    if (prestatRef != null)<br />     prestatRef.close();<br />    return null;<br />   }<br />   <a href="file://判/"><font face="Arial" color="#000000">//判</font></a>断是使用了createStatement语句<br />   if (CREATESTATE.equals(method.getName()))<br />   {<br />    obj = method.invoke(conn, args);<br />    statRef = (Statement)obj;//记录语句<br />    return obj;<br />   }<br />   <a href="file://判/"><font face="Arial" color="#000000">//判</font></a>断是使用了prepareStatement语句<br />   if (PREPARESTATEMENT.equals(method.getName()))<br />   {<br />    obj = method.invoke(conn, args);<br />    prestatRef = (PreparedStatement)obj;<br />    return obj;<br />   }<br />   <a href="file://如/"><font face="Arial" color="#000000">//如</font></a>果不支持事务，就不执行该事物的代码<br />   if ((COMMIT.equals(method.getName())||ROLLBACK.equals(method.getName())) &amp;&amp; (!isSupportTransaction()))<br />    return null;   <br />   obj = method.invoke(conn, args); <br />   <a href="file://设/"><font face="Arial" color="#000000">//设</font></a>置最后一次访问时间，以便及时清除超时的连接<br />   lastAccessTime = System.currentTimeMillis();<br />   return obj;<br /> }</pre><pre> /**<br />  * 创建连接的工厂，只能让工厂调用<br />  * @param factory 要调用工厂，并且一定被正确初始化<br />  * @param param 连接参数<br />  * @return 连接<br />  */<br /> static public _Connection getConnection(ConnectionFactory factory, ConnectionParam param)<br /> {<br />  if (factory.isCreate())//判断是否正确初始化的工厂<br />  {<br />   _Connection _conn = new _Connection(param);<br />   return _conn;<br />  }<br />  else<br />   return null;<br /> }<br /> <br /> public Connection getFreeConnection() {<br />  <a href="file://返/"><font face="Arial" color="#000000">//返</font></a>回数据库连接conn的接管类，以便截住close方法<br />  Connection conn2 = (Connection)Proxy.newProxyInstance(<br />   conn.getClass().getClassLoader(),<br />   conn.getClass().getInterfaces(),this);<br />  return conn2;<br /> }</pre><pre> /**<br />  * 该方法真正的关闭了数据库的连接<br />  * @throws SQLException<br />  */<br /> void close() throws SQLException{<br />  <a href="file://由/"><font face="Arial" color="#000000">//由</font></a>于类属性conn是没有被接管的连接，因此一旦调用close方法后就直接关闭连接<br />  conn.close();<br /> }<br />   <br /> public void setIsFree(boolean value)<br /> {<br />  isFree = value;<br /> }<br /> <br /> public boolean isFree() {<br />  return isFree;<br /> } <br /> /**<br />  * 判断是否支持事务<br />  * @return boolean<br />  */<br /> public boolean isSupportTransaction() {<br />  return supportTransaction;<br /> } <br />}<br /></pre></td></tr></tbody></table><p></p><hr />
ConnectionFactory.java 
<p></p><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre> package scut.ailab.connectionpool;</pre><pre>/**<br /> * @author youyongming<br /> *<br /> */<br />import java.util.LinkedHashSet;<br />import java.sql.*;<br />import java.util.Iterator;</pre><pre>public class ConnectionFactory {<br /> private static ConnectionFactory m_instance = null;<br /> <a href="file://在/"><font face="Arial" color="#000000">//在</font></a>使用的连接池<br /> private LinkedHashSet ConnectionPool = null;<br /> <a href="file://空/"><font face="Arial" color="#000000">//空</font></a>闲连接池<br /> private LinkedHashSet FreeConnectionPool = null;<br /> <a href="file://最/"><font face="Arial" color="#000000">//最</font></a>大连接数<br /> private int MaxConnectionCount = 4;<br /> <a href="file://最/"><font face="Arial" color="#000000">//最</font></a>小连接数<br /> private int MinConnectionCount = 2;<br /> <a href="file://当/"><font face="Arial" color="#000000">//当</font></a>前连接数<br /> private int current_conn_count = 0;<br /> <a href="file://连/"><font face="Arial" color="#000000">//连</font></a>接参数<br /> private ConnectionParam connparam = null;<br /> <a href="file://是/"><font face="Arial" color="#000000">//是</font></a>否创建工厂的标志<br /> private boolean isflag = false;<br /> <a href="file://是/"><font face="Arial" color="#000000">//是</font></a>否支持事务<br /> private boolean supportTransaction = false;<br /> <a href="file://定/"><font face="Arial" color="#000000">//定</font></a>义管理策略<br /> private int ManageType = 0;</pre><pre> private ConnectionFactory() {<br />  ConnectionPool = new LinkedHashSet();<br />  FreeConnectionPool = new LinkedHashSet();<br /> }<br /> <br /> /**<br />  * 使用指定的参数创建一个连接池<br />  */<br /> public ConnectionFactory(ConnectionParam param, FactoryParam fparam)<br />  throws SQLException <br /> {<br />  <a href="file://不/"><font face="Arial" color="#000000">//不</font></a>允许参数为空<br />  if ((param == null)||(fparam == null))<br />   throw new SQLException("ConnectionParam和FactoryParam不能为空");<br />  if (m_instance == null)<br />  {<br />   synchronized(ConnectionFactory.class){<br />    if (m_instance == null)<br />    {<br />     <a href="file://new/"><font face="Arial" color="#000000">//new</font></a> instance<br />     <a href="file://参/"><font face="Arial" color="#000000">//参</font></a>数定制<br />     m_instance = new ConnectionFactory();<br />     m_instance.connparam = param;<br />     m_instance.MaxConnectionCount = fparam.getMaxConn();<br />     m_instance.MinConnectionCount = fparam.getMinConn();<br />     m_instance.ManageType = fparam.getType();<br />     m_instance.isflag = true;<br />     <a href="file://初/"><font face="Arial" color="#000000">//初</font></a>始化，创建MinConnectionCount个连接<br />     System.out.println("connection factory 创建！");<br />     try{<br />      for (int i=0; i &lt; m_instance.MinConnectionCount; i++)<br />      {<br />       _Connection _conn = _Connection.getConnection(m_instance, m_instance.connparam);<br />       if (_conn == null) continue;<br />       System.out.println("connection创建");<br />       m_instance.FreeConnectionPool.add(_conn);//加入空闲连接池<br />       m_instance.current_conn_count ++;<br />       <a href="file://标/"><font face="Arial" color="#000000">//标</font></a>志是否支持事务<br />       m_instance.supportTransaction = _conn.isSupportTransaction();    <br />      }<br />     }<br />     catch(Exception e)<br />     {<br />      e.printStackTrace();<br />     }<br />     <a href="file://根/"><font face="Arial" color="#000000">//根</font></a>据策略判断是否需要查询<br />     if (m_instance.ManageType != 0)<br />     {<br />      Thread t = new Thread(new FactoryMangeThread(m_instance));<br />      t.start();<br />     } <br />    }<br />   }<br />  }<br /> }<br /> <br /> /**<br />  * 标志工厂是否已经创建<br />  * @return boolean<br />  */ <br /> public boolean isCreate()<br /> {<br />  return m_instance.isflag;<br /> }<br /> <br /> /**<br />  * 从连接池中取一个空闲的连接<br />  * @return Connection<br />  * @throws SQLException<br />  */<br /> public synchronized Connection getFreeConnection() <br />  throws SQLException<br /> {<br />  Connection conn = null;<br />  <a href="file://获/"><font face="Arial" color="#000000">//获</font></a>取空闲连接<br />  Iterator iter = m_instance.FreeConnectionPool.iterator();<br />  while(iter.hasNext()){<br />   _Connection _conn = (_Connection)iter.next();<br />   <a href="file://找/"><font face="Arial" color="#000000">//找</font></a>到未用连接<br />   if(!_conn.isFree()){<br />    conn = _conn.getFreeConnection();<br />    _conn.setIsFree(true);<br />    <a href="file://移/"><font face="Arial" color="#000000">//移</font></a>出空闲区<br />    m_instance.FreeConnectionPool.remove(_conn);<br />    <a href="file://加/"><font face="Arial" color="#000000">//加</font></a>入连接池 <br />    m_instance.ConnectionPool.add(_conn);   <br />    break;<br />   }<br />  }<br />  <a href="file://检/"><font face="Arial" color="#000000">//检</font></a>查空闲池是否为空<br />  if (m_instance.FreeConnectionPool.isEmpty())<br />  {<br />   <a href="file://再/"><font face="Arial" color="#000000">//再</font></a>检查是否能够分配<br />   if (m_instance.current_conn_count &lt; m_instance.MaxConnectionCount)<br />   {<br />   <a href="file://新/"><font face="Arial" color="#000000">//新</font></a>建连接到空闲连接池<br />    int newcount = 0 ;<br />    <a href="file://取/"><font face="Arial" color="#000000">//取</font></a>得要建立的数目<br />    if (m_instance.MaxConnectionCount - m_instance.current_conn_count &gt;=m_instance.MinConnectionCount)<br />    {<br />     newcount = m_instance.MinConnectionCount;<br />    }<br />    else<br />    {<br />     newcount = m_instance.MaxConnectionCount - m_instance.current_conn_count;<br />    }<br />    /<a href="file://创/"><font face="Arial" color="#000000">/创</font></a>建连接<br />    for (int i=0;i &lt;newcount; i++)<br />    {<br />     _Connection _conn = _Connection.getConnection(m_instance, m_instance.connparam);<br />     m_instance.FreeConnectionPool.add(_conn);<br />     m_instance.current_conn_count ++;<br />    }<br />   }<br />   else<br />   {//如果不能新建，检查是否有已经归还的连接<br />    iter = m_instance.ConnectionPool.iterator();<br />    while(iter.hasNext()){<br />     _Connection _conn = (_Connection)iter.next();<br />     if(!_conn.isFree()){<br />      conn = _conn.getFreeConnection();<br />      _conn.setIsFree(false);<br />      m_instance.ConnectionPool.remove(_conn); <br />      m_instance.FreeConnectionPool.add(_conn);   <br />      break;<br />     }<br />    }    <br />   }<br />  }//if (FreeConnectionPool.isEmpty())<br /> <a href="file://再/"><font face="Arial" color="#000000">//再</font></a>次检查是否能分配连接<br />  if(conn == null){<br />   iter = m_instance.FreeConnectionPool.iterator();<br />   while(iter.hasNext()){<br />    _Connection _conn = (_Connection)iter.next();<br />    if(!_conn.isFree()){<br />     conn = _conn.getFreeConnection();<br />     _conn.setIsFree(true);<br />     m_instance.FreeConnectionPool.remove(_conn); <br />     m_instance.ConnectionPool.add(_conn);   <br />     break;<br />    }<br />   }<br />   if(conn == null)//如果不能则说明无连接可用<br />    throw new SQLException("没有可用的数据库连接");<br />  }<br />  System.out.println("get connection");<br />  return conn;<br /> }<br /> <br /> /**<br />  * 关闭该连接池中的所有数据库连接<br />  * @throws SQLException<br />  */<br /> public synchronized void close() throws SQLException<br /> {<br />  this.isflag = false;<br />  SQLException excp = null;<br />  <a href="file://关/"><font face="Arial" color="#000000">//关</font></a>闭空闲池<br />  Iterator iter = m_instance.FreeConnectionPool.iterator();<br />  while(iter.hasNext()){<br />   try{<br />    ((_Connection)iter.next()).close();<br />    System.out.println("close connection:free");<br />    m_instance.current_conn_count --;<br />   }catch(Exception e){<br />    if(e instanceof SQLException)<br />     excp = (SQLException)e;<br />   }<br />  }<br />  <a href="file://关/"><font face="Arial" color="#000000">//关</font></a>闭在使用的连接池<br />  iter = m_instance.ConnectionPool.iterator();<br />  while(iter.hasNext()){<br />   try{<br />    ((_Connection)iter.next()).close();<br />    System.out.println("close connection:inused");<br />    m_instance.current_conn_count --;<br />   }catch(Exception e){<br />    if(e instanceof SQLException)<br />     excp = (SQLException)e;<br />   }<br />  }  <br />  if(excp != null)<br />   throw excp;<br /> } <br /> <br /> /**<br />  * 返回是否支持事务<br />  * @return boolean<br />  */<br /> public boolean isSupportTransaction() {<br />  return m_instance.supportTransaction;<br /> }  <br /> /**<br />  * 连接池调度管理<br />  *<br />  */<br /> public void schedule()<br /> {<br />  Connection conn = null;<br />  <a href="file://再/"><font face="Arial" color="#000000">//再</font></a>检查是否能够分配<br />  Iterator iter = null;<br />  <a href="file://检/"><font face="Arial" color="#000000">//检</font></a>查是否有已经归还的连接<br />  {<br />   iter = m_instance.ConnectionPool.iterator();<br />   while(iter.hasNext()){<br />    _Connection _conn = (_Connection)iter.next();<br />    if(!_conn.isFree()){<br />     conn = _conn.getFreeConnection();<br />     _conn.setIsFree(false);<br />     m_instance.ConnectionPool.remove(_conn); <br />     m_instance.FreeConnectionPool.add(_conn);   <br />     break;<br />    }<br />   }    <br />  }<br />  if (m_instance.current_conn_count &lt; m_instance.MaxConnectionCount)<br />  {<br />   <a href="file://新/"><font face="Arial" color="#000000">//新</font></a>建连接到空闲连接池<br />   int newcount = 0 ;<br />   <a href="file://取/"><font face="Arial" color="#000000">//取</font></a>得要建立的数目<br />   if (m_instance.MaxConnectionCount - m_instance.current_conn_count &gt;=m_instance.MinConnectionCount)<br />   {<br />    newcount = m_instance.MinConnectionCount;<br />   }<br />   else<br />   {<br />    newcount = m_instance.MaxConnectionCount - m_instance.current_conn_count;<br />   }<br />   <a href="file://创/"><font face="Arial" color="#000000">//创</font></a>建连接<br />   for (int i=0;i &lt;newcount; i++)<br />   {<br />    _Connection _conn = _Connection.getConnection(m_instance, m_instance.connparam);<br />    m_instance.FreeConnectionPool.add(_conn);<br />    m_instance.current_conn_count ++;<br />   }<br />  }<br /> }<br />}</pre></td></tr></tbody></table><p></p><hr />
ConnectionParam.java 
<p></p><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre> package scut.ailab.connectionpool;</pre><pre>import java.io.Serializable;</pre><pre>/**<br /> * @author youyongming<br /> * 实现数据库连接的参数类<br /> */<br />public class ConnectionParam implements Serializable {<br /> private String driver;    <a href="file://数/"><font face="Arial" color="#000000">//数</font></a>据库驱动程序<br /> private String url;   <a href="file://数/"><font face="Arial" color="#000000">//数</font></a>据连接的URL<br /> private String user;    <a href="file://数/"><font face="Arial" color="#000000">//数</font></a>据库用户名<br /> private String password;   <a href="file://数/"><font face="Arial" color="#000000">//数</font></a>据库密码<br /> <br /> /**<br />  * 唯一的构造函数，需要指定连接的四个必要参数<br />  * @param driver 数据驱动<br />  * @param url  数据库连接url<br />  * @param user  用户名<br />  * @param password 密码<br />  */<br /> public ConnectionParam(String driver,String url,String user,String password)<br /> {<br />  this.driver = driver;<br />  this.url = url;<br />  this.user = user;<br />  this.password = password;<br /> }</pre><pre> public String getDriver() {<br />  return driver;<br /> }</pre><pre> public String getPassword() {<br />  return password;<br /> }</pre><pre> public String getUrl() {<br />  return url;<br /> }</pre><pre> public String getUser() {<br />  return user;<br /> }</pre><pre> public void setDriver(String driver) {<br />  this.driver = driver;<br /> }</pre><pre> public void setPassword(String password) {<br />  this.password = password;<br /> }</pre><pre> public void setUrl(String url) {<br />  this.url = url;<br /> }</pre><pre> public void setUser(String user) {<br />  this.user = user;<br /> }</pre><pre> /**<br />  * @see java.lang.Object#clone()<br />  */<br /> public Object clone(){<br />  ConnectionParam param = new ConnectionParam(driver,url,user,password);<br />  return param;<br /> }</pre><pre> /**<br />  * @see java.lang.Object#equals(java.lang.Object)<br />  */<br /> public boolean equals(Object obj) {<br />  if(obj instanceof ConnectionParam){<br />   ConnectionParam param = (ConnectionParam)obj;<br />   return ((driver.compareToIgnoreCase(param.getDriver()) == 0)&amp;&amp;<br />   (url.compareToIgnoreCase(param.getUrl()) == 0)&amp;&amp;<br />   (user.compareToIgnoreCase(param.getUser()) == 0)&amp;&amp;<br />   (password.compareToIgnoreCase(param.getPassword()) == 0));<br />  }<br />  return false;<br /> }<br />}<br /></pre></td></tr></tbody></table><p></p><hr />
FactoryMangeThread.java 
<p></p><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre> /*<br /> * Created on 2003-5-13<br /> *<br /> * To change the template for this generated file go to<br /> * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments<br /> */<br />package scut.ailab.connectionpool;</pre><pre>/**<br /> * @author youyongming<br /> *<br /> */<br /><a href="file://连/"><font face="Arial" color="#000000">//连</font></a>接池调度线程<br />public class FactoryMangeThread implements Runnable {<br /> ConnectionFactory cf = null;<br /> long delay = 1000;<br /> public FactoryMangeThread(ConnectionFactory obj)<br /> {<br />  cf = obj;<br /> }<br /> /* (non-Javadoc)<br />  * @see java.lang.Runnable#run()<br />  */<br /> public void run() {<br />  while(true){<br />   try{<br />    Thread.sleep(delay);<br />   }<br />   catch(InterruptedException e){}<br />   System.out.println("eeeee");<br />   <a href="file://判/"><font face="Arial" color="#000000">//判</font></a>断是否已经关闭了工厂，那就退出监听<br />   if (cf.isCreate())<br />    cf.schedule();<br />   else<br />    System.exit(1);<br />  }<br /> }<br />}<br /></pre></td></tr></tbody></table><p></p><hr />
FactoryParam.java 
<p></p><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre> /*<br /> * Created on 2003-5-13<br /> *<br /> * To change the template for this generated file go to<br /> * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments<br /> */<br />package scut.ailab.connectionpool;</pre><pre>/**<br /> * @author youyongming<br /> *<br /> */<br /><a href="file://连/"><font face="Arial" color="#000000">//连</font></a>接池工厂参数<br />public class FactoryParam {<br /> <a href="file://最/"><font face="Arial" color="#000000">//最</font></a>大连接数<br /> private int MaxConnectionCount = 4;<br /> <a href="file://最/"><font face="Arial" color="#000000">//最</font></a>小连接数<br /> private int MinConnectionCount = 2; <br /> <a href="file://回/"><font face="Arial" color="#000000">//回</font></a>收策略<br /> private int ManageType = 0;<br /> <br /> public FactoryParam() {<br /> }<br /> <br /> /**<br />  * 构造连接池工厂参数的对象<br />  * @param max 最大连接数<br />  * @param min 最小连接数<br />  * @param type 管理策略<br />  */<br /> public FactoryParam(int max, int min, int type)<br /> {<br />  this.ManageType = type;<br />  this.MaxConnectionCount = max;<br />  this.MinConnectionCount = min;<br /> }<br /> <br /> /**<br />  * 设置最大的连接数<br />  * @param value<br />  */<br /> public void setMaxConn(int value)<br /> {<br />  this.MaxConnectionCount = value;<br /> }<br /> /**<br />  * 获取最大连接数<br />  * @return<br />  */<br /> public int getMaxConn()<br /> {<br />  return this.MaxConnectionCount;<br /> }<br /> /**<br />  * 设置最小连接数<br />  * @param value<br />  */<br /> public void setMinConn(int value)<br /> {<br />  this.MinConnectionCount = value;<br /> }<br /> /**<br />  * 获取最小连接数<br />  * @return<br />  */<br /> public int getMinConn()<br /> {<br />  return this.MinConnectionCount;<br /> }<br /> public int getType()<br /> {<br />  return this.ManageType;<br /> }<br />}</pre></td></tr></tbody></table><p></p><hr />
testmypool.java 
<p></p><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre> /*<br /> * Created on 2003-5-13<br /> *<br /> * To change the template for this generated file go to<br /> * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments<br /> */<br />package scut.ailab.connectionpool;</pre><pre>/**<br /> * @author youyongming<br /> *<br /> */<br />import java.sql.*;</pre><pre>public class testmypool {</pre><pre> public void test1()<br /> {<br />  String user = "DevTeam";<br />  String password = "DevTeam";<br />  String driver = "sun.jdbc.odbc.JdbcOdbcDriver";<br />  String url = "jdbc:odbc:gfqh2";<br />  ConnectionParam param = new ConnectionParam(driver,url,user,password);<br />  ConnectionFactory cf = null;//new ConnectionFactory(param, new FactoryParam());<br />  try{<br />   cf = new ConnectionFactory(param,new FactoryParam());<br />   Connection conn1 = cf.getFreeConnection();<br />   Connection conn2 = cf.getFreeConnection();<br />   Connection conn3 = cf.getFreeConnection();<br />   Statement stmt = conn1.createStatement();<br />   ResultSet rs = stmt.executeQuery("select * from requests");<br />   if (rs.next())<br />   {<br />    System.out.println("conn1 y");  <br />   }<br />   else<br />   {<br />    System.out.println("conn1 n");  <br />   } <br />   stmt.close();<br />   conn1.close();  <br />   Connection conn4 = cf.getFreeConnection();<br />   Connection conn5 = cf.getFreeConnection();<br />   stmt = conn5.createStatement();<br />   rs = stmt.executeQuery("select * from requests");<br />   if (rs.next())<br />   {<br />    System.out.println("conn5 y");  <br />   }<br />   else<br />   {<br />    System.out.println("conn5 n");  <br />   } <br />   conn2.close();<br />   conn3.close();<br />   conn4.close();<br />   conn5.close();<br />   }<br />  catch(Exception e)<br />  {<br />   e.printStackTrace();<br />  }<br />  finally{<br />   try{<br />    cf.close();<br />   }<br />   catch(Exception e)<br />   {<br />    e.printStackTrace();<br />   }<br />  } <br /> }<br /> public static void main(String[] args) {<br />  String user = "DevTeam";<br />  String password = "DevTeam";<br />  String driver = "sun.jdbc.odbc.JdbcOdbcDriver";<br />  String url = "jdbc:odbc:gfqh2";<br />  ConnectionParam param = new ConnectionParam(driver,url,user,password);<br />  ConnectionFactory cf = null;//new ConnectionFactory(param,new FactoryParam());<br />  try{<br />   cf = new ConnectionFactory(param,new FactoryParam());<br />   ConnectionFactory cf1= new ConnectionFactory(param,new FactoryParam());<br />   Connection conn1 = null;<br />   long time = System.currentTimeMillis();<br />   for (int i=0; i &lt;10;i++)<br />   {<br />    conn1 = cf.getFreeConnection();<br />    Statement stmt = conn1.createStatement();<br />    ResultSet rs = stmt.executeQuery("select * from requests");<br />    if (rs.next())<br />    {<br />     System.out.println("conn1 y");  <br />    }<br />    else<br />    {<br />     System.out.println("conn1 n");  <br />    } <br />    conn1.close();  <br />   }<br />   System.out.println("pool:" + (System.currentTimeMillis()-time));<br />   time = System.currentTimeMillis();<br />   Class.forName(param.getDriver()).newInstance();<br />   for (int i=0; i &lt;10;i++)<br />   {<br />    conn1 = DriverManager.getConnection(param.getUrl(),param.getUser(), param.getPassword());   <br />    Statement stmt = conn1.createStatement();<br />    ResultSet rs = stmt.executeQuery("select * from requests");<br />    if (rs.next())<br />    {<br />     System.out.println("conn1 y");  <br />    }<br />    else<br />    {<br />     System.out.println("conn1 n");  <br />    } <br />    conn1.close();  <br />   }   <br />   System.out.println("no pool:" + (System.currentTimeMillis()-time));<br />  }<br />  catch(Exception e)<br />  {<br />   e.printStackTrace();<br />  }<br />  finally{<br />   try{<br />    cf.close();<br />   }<br />   catch(Exception e)<br />   {<br />    e.printStackTrace();<br />   }<br />  }<br /> }<br />}<br /></pre></td></tr></tbody></table><br /><br />原文链接:http://www.jaron.cn/chs_db/16/2003-09/20030916110224-101181.html<img src ="http://www.blogjava.net/iKingQu/aggbug/36482.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/iKingQu/" target="_blank">風向逆轉 - 就要爪哇</a> 2006-03-20 23:24 <a href="http://www.blogjava.net/iKingQu/articles/36482.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[收藏]Tomcat5配置Mysql JDBC数据库连接池 </title><link>http://www.blogjava.net/iKingQu/articles/36473.html</link><dc:creator>風向逆轉 - 就要爪哇</dc:creator><author>風向逆轉 - 就要爪哇</author><pubDate>Mon, 20 Mar 2006 15:14:00 GMT</pubDate><guid>http://www.blogjava.net/iKingQu/articles/36473.html</guid><wfw:comment>http://www.blogjava.net/iKingQu/comments/36473.html</wfw:comment><comments>http://www.blogjava.net/iKingQu/articles/36473.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/iKingQu/comments/commentRss/36473.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/iKingQu/services/trackbacks/36473.html</trackback:ping><description><![CDATA[
		<div class="postText">如果只是对MySql感兴趣可以照抄这篇短文，如果想配置其他数据库类型的连接池，也可以做简单修改参数即可使用。 <br /><br /><br />1、 安装Tomcat<br /><br />参考Tomcat for window 的安装向导，基本直接安装即可，注意：安装时会提示输入管理用户名和密码，这是以后会用到的用户名和密码，切记。<br /><br /><br />2、 安装MySql<br /><br />默认安装即可。<br /><br /><br />3、 使用Tomcat的Web管理应用配置数据源<br /><br />启动Tomcat服务器，打开浏览器，输入<a href="http://localhost:8080/admin/">http://localhost:8080/admin/</a>（其中localhost可能是一台机器的IP或是服务器名称），进入管理界面的登陆页面，这时候请输入原来安装时要求输入的用户名和密码，登陆到管理界面，<br /><br /><br />选择Resources－Data sources进入配置数据源界面，选择Data Source Actions －&gt;选择Create New Data Source,进入配置详细信息界面，内容如下：<br /><br />JNDI Name: jdbc/mysql<br />Data Source URL: jdbc:mysql://192.168.0.16/SUBRDB <br />JDBC Driver Class: org.gjt.mm.mysql.Driver <br />User Name: root<br />Password: ********<br />Max. Active Connections: 4<br />Max. Idle Connections: 2<br />Max. Wait for Connection: 500<br />Validation Query: <br /><br /><br />要求输入的JNDI Name等信息，其中除了JDBC DriverClass之外，其他的可以根据你的需要填写。比如Data Source URL的内容可能是：jdbc:mysql:// IP或是名称/DataBaseName，其中DataBaseName是你的数据库名称，IP是你的数据库的所在的服务器的IP或是名称。最后点击Save－&gt;Commit Change.这样你的数据源的基本资料配置一半了。<br /><br /><br />4、 web.xml和%TOMCAT_HOME%\conf\Catalina\localhost下对应你的引用的配置文件修改<br /><br />通过文件夹导航到%TOMCAT_HOME%\conf，打开web.xml,在&lt;/web-app&gt;的前面添加以下内容：<br /><br />&lt;resource-ref&gt;<br /><br />&lt;description&gt;DB Connection&lt;/description&gt;<br /><br />&lt;res-ref-name&gt;jdbc/mysql&lt;/res-ref-name&gt;<br /><br />&lt;res-type&gt;javax.sql.DataSource&lt;/res-type&gt;<br /><br />&lt;res-auth&gt;Container&lt;/res-auth&gt;<br /><br />&lt;/resource-ref&gt;<br /><br /><br />注意res-ref-name填写的内容要与在上文提到的JNDI Name名称一致。 <br /><br />通过文件夹导航到%TOMCAT_HOME%\conf\Catalina\localhost下，找到你的web应用对应的.xml文件，如 ROOT.xml，并在此文件的下添入代码：<br /><br />&lt;ResourceLink name="jdbc/mysql" global="jdbc/mysql" type="javax.sql.DataSourcer"/&gt;<br /><br />到这里，配置工作就基本完成了。<br /><br /><br />5、 其他注意事项<br /><br />别忘了JDBC驱动程序mysql-connector-java-3.0.9-stable-bin.jar一定要放置到Tomcat的对应目录,你的JDBC驱动可能版比笔者高，不过只要能与所使用的MySql对应就可以了，因为我发现版本太低的JDBC驱动不能支持4.0.*版本的MySQL数据库，建议放置在%TOMCAT_HOME%\common\lib和应用的WEB-INF\lib下。两者有什么不同呢？其实一看就明白了，common\li是所有的应用都可以使用的库文件位置。<br /><br />重启你的Tomcat服务。<br /><br /><br /><br />6、 编写测试代码<br /><br />在应用的目录下建立一个Test.jsp文件，代码如下：<br /><br />&lt;!doctype html public "-//w3c//dtd html 4.0 transitional//en" <br /><br />"http://www.w3.org/TR/REC-html40/strict.dtd"&gt;<br /><br />&lt;%@ page import="java.sql.*"%&gt;<br /><br />&lt;%@ page import="javax.sql.*"%&gt;<br /><br />&lt;%@ page import="javax.naming.*"%&gt;<br /><br />&lt;%@ page session="false" %&gt;<br /><br />&lt;html&gt;<br /><br />&lt;head&gt;<br /><br />&lt;meta http-equiv="Content-Type" content="text/html; charset=gb2312"&gt;<br /><br />&lt;title&gt;&lt;/title&gt;<br /><br />&lt;% <br /><br />out.print("我的测试开始");<br /><br />DataSource ds = null;<br /><br />try{<br /><br />InitialContext ctx=new InitialContext();<br /><br />ds=(DataSource)ctx.lookup("java:comp/env/jdbc/mysql");<br /><br />Connection conn = ds.getConnection();<br /><br />Statement stmt = conn.createStatement();<br /><br />//提示：users必须是数据库已有的表，<br /><br />//这里的数据库前文提及的Data Source URL配置里包含的数据库。<br /><br />String strSql = " select * from users";<br /><br />ResultSet rs = stmt.executeQuery(strSql);<br /><br />while(rs.next()){<br /><br />out.print(rs.getString(1)); <br /><br />}<br /><br />out.print("我的测试结束");<br /><br />}<br /><br />catch(Exception ex){<br /><br />out.print(“出现例外，信息是:”+ex.getMessage());<br /><br />ex.printStackTrace();<br /><br />}<br /><br />%&gt;<br /><br />&lt;/head&gt;<br /><br />&lt;body&gt;<br /><br />&lt;/body&gt;<br /><br />&lt;/html&gt;<br /><br /><br />运行结果：<br /><br />我的测试开始12345678我的测试结束，因为我的rs.getString(1) 在数据库就是存放12345678<br /><br /><br /><p id="TBPingURL">Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=613371</p></div>
<img src ="http://www.blogjava.net/iKingQu/aggbug/36473.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/iKingQu/" target="_blank">風向逆轉 - 就要爪哇</a> 2006-03-20 23:14 <a href="http://www.blogjava.net/iKingQu/articles/36473.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[收藏]一个关于优化SQL的文章</title><link>http://www.blogjava.net/iKingQu/articles/36467.html</link><dc:creator>風向逆轉 - 就要爪哇</dc:creator><author>風向逆轉 - 就要爪哇</author><pubDate>Mon, 20 Mar 2006 15:03:00 GMT</pubDate><guid>http://www.blogjava.net/iKingQu/articles/36467.html</guid><wfw:comment>http://www.blogjava.net/iKingQu/comments/36467.html</wfw:comment><comments>http://www.blogjava.net/iKingQu/articles/36467.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/iKingQu/comments/commentRss/36467.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/iKingQu/services/trackbacks/36467.html</trackback:ping><description><![CDATA[
		<div class="postText">大家都在讨论关于数据库优化方面的东东，刚好参与开发了一个数据仓库方面的项目，以下的一点东西算是数据库优化方面的学习+实战的一些心得体会了，拿出来大家共享。欢迎批评指正阿！ <br /><br />SQL语句： <br />是对数据库(数据)进行操作的惟一途径； <br />消耗了70%~90%的数据库资源；独立于程序设计逻辑，相对于对程序源代码的优化，对SQL语句的优化在时间成本和风险上的代价都很低； <br />可以有不同的写法；易学，难精通。 <br /><br />SQL优化： <br />固定的SQL书写习惯，相同的查询尽量保持相同，存储过程的效率较高。 <br />应该编写与其格式一致的语句，包括字母的大小写、标点符号、换行的位置等都要一致 <br /><br />ORACLE优化器： <br />在任何可能的时候都会对表达式进行评估，并且把特定的语法结构转换成等价的结构，这么做的原因是 <br />要么结果表达式能够比源表达式具有更快的速度 <br />要么源表达式只是结果表达式的一个等价语义结构 <br />不同的SQL结构有时具有同样的操作（例如：= ANY (subquery) and IN (subquery)），ORACLE会把他们映射到一个单一的语义结构。 <br /><br />1 常量优化： <br />常量的计算是在语句被优化时一次性完成，而不是在每次执行时。下面是检索月薪大于2000的的表达式： <br />sal &gt; 24000/12 <br />sal &gt; 2000 <br />sal*12 &gt; 24000 <br />如果SQL语句包括第一种情况，优化器会简单地把它转变成第二种。 <br />优化器不会简化跨越比较符的表达式，例如第三条语句，鉴于此，应尽量写用常量跟字段比较检索的表达式，而不要将字段置于表达式当中。否则没有办法优化，比如如果sal上有索引，第一和第二就可以使用，第三就难以使用。 <br /><br />2 操作符优化： <br />优化器把使用LIKE操作符和一个没有通配符的表达式组成的检索表达式转换为一个“=”操作符表达式。 <br />例如：优化器会把表达式ename LIKE 'SMITH'转换为ename = 'SMITH' <br />优化器只能转换涉及到可变长数据类型的表达式，前一个例子中，如果ENAME字段的类型是CHAR(10)， 那么优化器将不做任何转换。 <br />一般来讲LIKE比较难以优化。 <br /><br />其中： <br />~~ IN 操作符优化： <br />优化器把使用IN比较符的检索表达式替换为等价的使用“=”和“OR”操作符的检索表达式。 <br />例如，优化器会把表达式ename IN ('SMITH','KING','JONES')替换为 <br />ename = 'SMITH' OR ename = 'KING' OR ename = 'JONES‘ <br /><br />~~ ANY和SOME 操作符优化: <br />优化器将跟随值列表的ANY和SOME检索条件用等价的同等操作符和“OR”组成的表达式替换。 <br />例如，优化器将如下所示的第一条语句用第二条语句替换： <br />sal &gt; ANY (:first_sal, :second_sal) <br />sal &gt; :first_sal OR sal &gt; :second_sal <br />优化器将跟随子查询的ANY和SOME检索条件转换成由“EXISTS”和一个相应的子查询组成的检索表达式。 <br />例如，优化器将如下所示的第一条语句用第二条语句替换： <br />x &gt; ANY (SELECT sal FROM emp WHERE job = 'ANALYST') <br />EXISTS (SELECT sal FROM emp WHERE job = 'ANALYST' AND x &gt; sal) <br /><br />~~ ALL操作符优化: <br />优化器将跟随值列表的ALL操作符用等价的“=”和“AND”组成的表达式替换。例如： <br />sal &gt; ALL (:first_sal, :second_sal)表达式会被替换为： <br />sal &gt; :first_sal AND sal &gt; :second_sal <br />对于跟随子查询的ALL表达式，优化器用ANY和另外一个合适的比较符组成的表达式替换。例如 <br />x &gt; ALL (SELECT sal FROM emp WHERE deptno = 10) 替换为： <br />NOT (x &lt;= ANY (SELECT sal FROM emp WHERE deptno = 10)) <br />接下来优化器会把第二个表达式适用ANY表达式的转换规则转换为下面的表达式： <br />NOT EXISTS (SELECT sal FROM emp WHERE deptno = 10 AND x &lt;= sal) <br /><br />~~ BETWEEN 操作符优化: <br />优化器总是用“&gt;=”和“&lt;=”比较符来等价的代替BETWEEN操作符。 <br />例如：优化器会把表达式sal BETWEEN 2000 AND 3000用sal &gt;= 2000 AND sal &lt;= 3000来代替。 <br /><br />~~ NOT 操作符优化: <br />优化器总是试图简化检索条件以消除“NOT”逻辑操作符的影响，这将涉及到“NOT”操作符的消除以及代以相应的比较运算符。 <br />例如，优化器将下面的第一条语句用第二条语句代替： <br />NOT deptno = (SELECT deptno FROM emp WHERE ename = 'TAYLOR') <br />deptno &lt;&gt; (SELECT deptno FROM emp WHERE ename = 'TAYLOR') <br />通常情况下一个含有NOT操作符的语句有很多不同的写法，优化器的转换原则是使“NOT”操作符后边的子句尽可能的简单，即使可能会使结果表达式包含了更多的“NOT”操作符。 <br />例如，优化器将如下所示的第一条语句用第二条语句代替： <br />NOT (sal &lt; 1000 OR comm IS NULL) <br />NOT sal &lt; 1000 AND comm IS NOT NULL sal &gt;= 1000 AND comm IS NOT NULL <br /><br />如何编写高效的SQL: <br />当然要考虑sql常量的优化和操作符的优化啦，另外，还需要： <br /><br />1 合理的索引设计： <br />例：表record有620000行，试看在不同的索引下，下面几个SQL的运行情况： <br />语句A <br />SELECT count(*) FROM record <br />WHERE date &gt;'19991201' and date &lt; '19991214‘ and amount &gt;2000 <br /><br />语句B <br />SELECT count(*) FROM record <br />WHERE date &gt;'19990901' and place IN ('BJ','SH') <br /><br />语句C <br />SELECT date,sum(amount) FROM record <br />group by date <br />1 在date上建有一个非聚集索引 <br />A：(25秒) <br />B：(27秒) <br />C：(55秒) <br />分析： <br />date上有大量的重复值，在非聚集索引下，数据在物理上随机存放在数据页上，在范围查找时，必须执行一次表扫描才能找到这一范围内的全部行。 <br />2 在date上的一个聚集索引 <br />A：（14秒） <br />B：（14秒） <br />C：（28秒） <br />分析： <br />在聚集索引下，数据在物理上按顺序在数据页上，重复值也排列在一起，因而在范围查找时，可以先找到这个范围的起末点，且只在这个范围内扫描数据页，避免了大范围扫描，提高了查询速度。 <br />3 在place，date，amount上的组合索引 <br />A：（26秒） <br />C：（27秒） <br />B：（&lt; 1秒） <br />分析： <br />这是一个不很合理的组合索引，因为它的前导列是place，第一和第二条SQL没有引用place，因此也没有利用上索引；第三个SQL使用了place，且引用的所有列都包含在组合索引中，形成了索引覆盖，所以它的速度是非常快的。 <br />4 在date，place，amount上的组合索引 <br />A： (&lt; 1秒) <br />B：（&lt; 1秒） <br />C：（11秒） <br />分析： <br />这是一个合理的组合索引。它将date作为前导列，使每个SQL都可以利用索引，并且在第一和第三个SQL中形成了索引覆盖，因而性能达到了最优。 <br /><br />总结1 <br />缺省情况下建立的索引是非聚集索引，但有时它并不是最佳的；合理的索引设计要建立在对各种查询的分析和预测上。一般来说： <br />有大量重复值、且经常有范围查询（between, &gt;,&lt; ，&gt;=,&lt; =）和order by、group by发生的列，考虑建立聚集索引； <br />经 常同时存取多列，且每列都含有重复值可考虑建立组合索引；在条件表达式中经常用到的不同值较多的列上建立检索，在不同值少的列上不要建立索引。比如在雇员 表的“性别”列上只有“男”与“女”两个不同值，因此就无必要建立索引。如果建立索引不但不会提高查询效率，反而会严重降低更新速度。 <br />组合索引要尽量使关键查询形成索引覆盖，其前导列一定是使用最频繁的列。 <br /><br />2 避免使用不兼容的数据类型： <br />例如float和INt、char和varchar、bINary和varbINary是不兼容的。数据类型的不兼容可能使优化器无法执行一些本来可以进行的优化操作。例如: <br />SELECT name FROM employee WHERE salary ＞ 60000 <br />在这条语句中,如salary字段是money型的,则优化器很难对其进行优化,因为60000是个整型数。我们应当在编程时将整型转化成为钱币型,而不要等到运行时转化。 <br /><br />3 IS NULL 与IS NOT NULL： <br />不 能用null作索引，任何包含null值的列都将不会被包含在索引中。即使索引有多列这样的情况下，只要这些列中有一列含有null，该列就会从索引中排 除。也就是说如果某列存在空值，即使对该列建索引也不会提高性能。任何在WHERE子句中使用is null或is not null的语句优化器是不允 许使用索引的。 <br /><br />4 IN和EXISTS： <br />EXISTS要远比IN的效率高。里面关系到full table scan和range scan。几乎将所有的IN操作符子查询改写为使用EXISTS的子查询。 <br />例子： <br />语句1 <br />SELECT dname, deptno FROM dept <br />WHERE deptno NOT IN <br />(SELECT deptno FROM emp); <br />语句2 <br />SELECT dname, deptno FROM dept <br />WHERE NOT EXISTS <br />(SELECT deptno FROM emp <br />WHERE dept.deptno = emp.deptno); <br />明显的，2要比1的执行性能好很多 <br />因为1中对emp进行了full table scan,这是很浪费时间的操作。而且1中没有用到emp的INdex， <br />因为没有WHERE子句。而2中的语句对emp进行的是range scan。 <br /><br />5 IN、OR子句常会使用工作表，使索引失效： <br />如果不产生大量重复值，可以考虑把子句拆开。拆开的子句中应该包含索引。 <br /><br />6 避免或简化排序： <br />应当简化或避免对大型表进行重复的排序。当能够利用索引自动以适当的次序产生输出时，优化器就避免了排序的步骤。以下是一些影响因素： <br />索引中不包括一个或几个待排序的列； <br />group by或order by子句中列的次序与索引的次序不一样； <br />排序的列来自不同的表。 <br />为了避免不必要的排序，就要正确地增建索引，合理地合并数据库表（尽管有时可能影响表的规范化，但相对于效率的提高是值得的）。如果排序不可避免，那么应当试图简化它，如缩小排序的列的范围等。 <br /><br />7 消除对大型表行数据的顺序存取： <br />在 嵌套查询中，对表的顺序存取对查询效率可能产生致命的影响。比如采用顺序存取策略，一个嵌套3层的查询，如果每层都查询1000行，那么这个查询就要查询 10亿行数据。避免这种情况的主要方法就是对连接的列进行索引。例如，两个表：学生表（学号、姓名、年龄??）和选课表（学号、课程号、成绩）。如果两个 表要做连接，就要在“学号”这个连接字段上建立索引。 <br />还可以使用并集来避免顺序存取。尽管在所有的检查列上都有索引，但某些形式的WHERE子句强迫优化器使用顺序存取。下面的查询将强迫对orders表执行顺序操作： <br />SELECT ＊ FROM orders WHERE (customer_num=104 AND order_num&gt;1001) OR order_num=1008 <br />虽然在customer_num和order_num上建有索引，但是在上面的语句中优化器还是使用顺序存取路径扫描整个表。因为这个语句要检索的是分离的行的集合，所以应该改为如下语句： <br />SELECT ＊ FROM orders WHERE customer_num=104 AND order_num&gt;1001 <br />UNION <br />SELECT ＊ FROM orders WHERE order_num=1008 <br />这样就能利用索引路径处理查询。 <br /><br />8 避免相关子查询： <br />一个列的标签同时在主查询和WHERE子句中的查询中出现，那么很可能当主查询中的列值改变之后，子查询必须重新查询一次。查询嵌套层次越多，效率越低，因此应当尽量避免子查询。如果子查询不可避免，那么要在子查询中过滤掉尽可能多的行。 <br /><br />9 避免困难的正规表达式： <br />MATCHES和LIKE关键字支持通配符匹配，技术上叫正规表达式。但这种匹配特别耗费时间。例如：SELECT ＊ FROM customer WHERE zipcode LIKE “98_ _ _” <br />即使在zipcode字段上建立了索引，在这种情况下也还是采用顺序扫描的方式。如果把语句改为SELECT ＊ FROM customer WHERE zipcode &gt;“98000”，在执行查询时就会利用索引来查询，显然会大大提高速度。 <br />另外，还要避免非开始的子串。例如语句：SELECT ＊ FROM customer WHERE zipcode[2，3] &gt;“80”，在WHERE子句中采用了非开始子串，因而这个语句也不会使用索引。 <br /><br />10 不充份的连接条件： <br />例：表card有7896行，在card_no上有一个非聚集索引，表account有191122行，在account_no上有一个非聚集索引，试看在不同的表连接条件下，两个SQL的执行情况： <br />SELECT sum(a.amount) FROM account a,card b WHERE a.card_no = b.card_no <br />（20秒） <br />将SQL改为： <br />SELECT sum(a.amount) FROM account a,card b WHERE a.card_no = b.card_no and a.account_no=b.account_no <br />（&lt; 1秒） <br />分析： <br />在第一个连接条件下，最佳查询方案是将account作外层表，card作内层表，利用card上的索引，其I/O次数可由以下公式估算为： <br />外层表account上的22541页+（外层表account的191122行*内层表card上对应外层表第一行所要查找的3页）=595907次I/O <br />在第二个连接条件下，最佳查询方案是将card作外层表，account作内层表，利用account上的索引，其I/O次数可由以下公式估算为： <br />外层表card上的1944页+（外层表card的7896行*内层表account上对应外层表每一行所要查找的4页）= 33528次I/O <br />可见，只有充份的连接条件，真正的最佳方案才会被执行。 <br />多表操作在被实际执行前，查询优化器会根据连接条件，列出几组可能的连接方案并从中找出系统开销最小的最佳方案。连接条件要充份考虑带有索引的表、行数多的表；内外表的选择可由公式：外层表中的匹配行数*内层表中每一次查找的次数确定，乘积最小为最佳方案。 <br />不可优化的WHERE子句 <br />例1 <br />下列SQL条件语句中的列都建有恰当的索引，但执行速度却非常慢： <br />SELECT * FROM record WHERE substrINg(card_no,1,4)='5378' <br />(13秒) <br />SELECT * FROM record WHERE amount/30&lt; 1000 <br />（11秒） <br />SELECT * FROM record WHERE convert(char(10),date,112)='19991201' <br />（10秒） <br />分析： <br />WHERE子句中对列的任何操作结果都是在SQL运行时逐列计算得到的，因此它不得不进行表搜索，而没有使用该列上面的索引；如果这些结果在查询编译时就能得到，那么就可以被SQL优化器优化，使用索引，避免表搜索，因此将SQL重写成下面这样： <br />SELECT * FROM record WHERE card_no like '5378%' <br />（&lt; 1秒） <br />SELECT * FROM record WHERE amount&lt; 1000*30 <br />（&lt; 1秒） <br />SELECT * FROM record WHERE date= '1999/12/01' <br />（&lt; 1秒） <br /><br />11 存储过程中，采用临时表优化查询： <br />例 <br />1．从parven表中按vendor_num的次序读数据： <br />SELECT part_num，vendor_num，price FROM parven ORDER BY vendor_num <br />INTO temp pv_by_vn <br />这个语句顺序读parven（50页），写一个临时表（50页），并排序。假定排序的开销为200页，总共是300页。 <br />2．把临时表和vendor表连接，把结果输出到一个临时表，并按part_num排序： <br />SELECT pv_by_vn，＊ vendor.vendor_num FROM pv_by_vn，vendor <br />WHERE pv_by_vn.vendor_num=vendor.vendor_num <br />ORDER BY pv_by_vn.part_num <br />INTO TMP pvvn_by_pn <br />DROP TABLE pv_by_vn <br />这 个查询读取pv_by_vn(50页)，它通过索引存取vendor表1.5万次，但由于按vendor_num次序排列，实际上只是通过索引顺序地读 vendor表（40＋2=42页），输出的表每页约95行，共160页。写并存取这些页引发5＊160=800次的读写，索引共读写892页。 <br />3．把输出和part连接得到最后的结果： <br />SELECT pvvn_by_pn.＊，part.part_desc FROM pvvn_by_pn，part <br />WHERE pvvn_by_pn.part_num=part.part_num <br />DROP TABLE pvvn_by_pn <br />这样，查询顺序地读pvvn_by_pn(160页)，通过索引读part表1.5万次，由于建有索引，所以实际上进行1772次磁盘读写，优化比例为30∶1。 <br /><br />好了，搞定。 <br />其实sql的优化，各种数据库之间都是互通的。<br /><br /><p id="TBPingURL">Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=629369</p></div>
<img src ="http://www.blogjava.net/iKingQu/aggbug/36467.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/iKingQu/" target="_blank">風向逆轉 - 就要爪哇</a> 2006-03-20 23:03 <a href="http://www.blogjava.net/iKingQu/articles/36467.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[收藏]Transact SQL 常用语句以及函数 </title><link>http://www.blogjava.net/iKingQu/articles/36463.html</link><dc:creator>風向逆轉 - 就要爪哇</dc:creator><author>風向逆轉 - 就要爪哇</author><pubDate>Mon, 20 Mar 2006 14:53:00 GMT</pubDate><guid>http://www.blogjava.net/iKingQu/articles/36463.html</guid><wfw:comment>http://www.blogjava.net/iKingQu/comments/36463.html</wfw:comment><comments>http://www.blogjava.net/iKingQu/articles/36463.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/iKingQu/comments/commentRss/36463.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/iKingQu/services/trackbacks/36463.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Transact SQL  						语 句 功 能 						========================================================================																						　　						--						数据操作 																												...&nbsp;&nbsp;<a href='http://www.blogjava.net/iKingQu/articles/36463.html'>阅读全文</a><img src ="http://www.blogjava.net/iKingQu/aggbug/36463.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/iKingQu/" target="_blank">風向逆轉 - 就要爪哇</a> 2006-03-20 22:53 <a href="http://www.blogjava.net/iKingQu/articles/36463.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[收藏]SQL优化</title><link>http://www.blogjava.net/iKingQu/articles/36423.html</link><dc:creator>風向逆轉 - 就要爪哇</dc:creator><author>風向逆轉 - 就要爪哇</author><pubDate>Mon, 20 Mar 2006 12:53:00 GMT</pubDate><guid>http://www.blogjava.net/iKingQu/articles/36423.html</guid><wfw:comment>http://www.blogjava.net/iKingQu/comments/36423.html</wfw:comment><comments>http://www.blogjava.net/iKingQu/articles/36423.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/iKingQu/comments/commentRss/36423.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/iKingQu/services/trackbacks/36423.html</trackback:ping><description><![CDATA[最近做项目发现很多SQL没有优化: 现在总结几种优化方式.<br /> 首先先了解一个SQL语句的执行过程分3步: 语法分析(parase)与编译,执行,取数据. <br />1： 在语法分析与编译时:oracle 使用哈希函数为SQL语句在库缓存中分配一个SQL区,<br />首先检查语句是否存在,若在,则查询数据库字典、检查必须的权限。<br />若无，需要语法分析与编译。所以SQL语句存在与内存中，将减少分析，编译时间。<br />SQL语句的分析与编译占整个语句运行过程的60%的时间，SQL优化的目标就是减少分析与编译的时间，共享代码。 
<p>查询SQL语句分析与编译的时间：<br />select * from v$sysstat <br />where name in ('parse time cpu','parse time elapsed','parse count (hard)')</p><p>一个SQL语句的响应时间(elapsed time )应该是服务时间+等待时间.<br />服务时间= CPU执行时间.<br />等待时间 可以从v$system_event<br />select total_waits, total_timeouts, time_waited, average_wait ,event <br />from v$system_event <br />where event='latch free'<br />所以解析一个SQL语句的平均等待时间是"等待时间/parse count" 这个值接近0<br />通过数据字典v$sqlare,可以查询到频繁被分析与编译的SQL语句.应该减少SQL语句的分析与编译的次数.</p><p>2: 将常用的实体驻留内存.<br />为了减少分析与编译时间,可以将常用的的实体如: 存储过程,包等,尽可能驻留在内存区域.<br /> 1)预留内存空间. sql&gt; show parameter shared_pool_reserved_size<br />       2)将频繁使用的实体驻留在内存中. 在使用DBMS_SHARED_POOL程序包前,必须首先运行系统提供的程序包: dbmspool.sql 和prvtpool.plb<br /> 在加载这两个程序包后,自动生成所需的包. <br />      加载: sql&gt; @/u01/app/oracle/product/8.17/rdbms/admin/dbmspool.sql<br />           sql&gt; @/u01/app/oracle/product/8.17/rdbms/admin/prvtpool.sql<br />      包DBMS_SHARED_POOL包含以下存储过程.<br />      dbms_shared_pool.keep 用于将实体保存内存. dbms_shared_pool.keep(object in varchar2,[type in char default p]);<br />                             object 表示参数名, type 表示被驻留内存的实体类型;P 表示存储过程,C表示光标,R表示触发器,默认P<br />        <br />      dbms_shared_pool.unkeep 用于取消被设置进入内存的实体. dbms_shared_pool.unkeep(object in varchar2,[type in char default p]);<br />                             object 表示参数名, type 表示被驻留内存的实体类型;P 表示存储过程,C表示光标,R表示触发器,默认P<br />      <br />      dbms_shared_pool.size(minsize in number)</p><p>      select name ,type ,source_size+code_size+parsed_size+error_size "total bytes" <br />      from dba_object_size <br />      where owner='SCOTT'</p><p>3: 创建索引. <br />   select index_name,table_owner, table_name, tablespace_name from all_indexes<br />   <br />   select user_indexes.TABLE_NAME, user_indexes.INDEX_NAME,uniqueness, column_name<br />   from user_ind_columns ,user_indexes<br />   where user_ind_columns.INDEX_NAME=user_indexes.INDEX_NAME<br />   and user_ind_columns.TABLE_NAME=user_indexes.TABLE_NAME<br />   order by user_indexes.TABLE_TYPE,user_indexes.TABLE_NAME,user_indexes.INDEX_NAME,user_ind_columns.COLUMN_POSITION</p><p>4: 创建聚簇(cluster): 是一组存储在一起的有共同列或经常一起使用的表,被聚簇的两个表只有一个数据段.聚簇表在存储时,在物理层将子表合并到父表中,这样就少了表的连接时间.</p><p>5: 创建哈希索引.<br /> <br />6: SQL优化器: 基于成本的优化器CBO(cose_based)和基于规则RBO(rule_based)<br />   sql&gt; show parameter OPTIMIZER_MODE<br />   可以修改参数文件: initSID.ora,增加: optimizer_Mode={CHOOSE| RULE| FIRST_ROWS|ALL_ROWS}<br />  all_rows , first_rows(n)基于成本; rule 基于规则,choose基于规则、成本。<br />  /*+ ordered*/ <br />  /*+ rule */<br />  /*+ first_rows(50) */</p><p>  /*+ordered star*/ <br />  写发：  <br />  alter system flush shared_pool;<br />select /*+ rule */ aa from visit <br /><br />原文链接:http://www.blogjava.net/yanmin/archive/2006/03/20/36332.html</p><img src ="http://www.blogjava.net/iKingQu/aggbug/36423.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/iKingQu/" target="_blank">風向逆轉 - 就要爪哇</a> 2006-03-20 20:53 <a href="http://www.blogjava.net/iKingQu/articles/36423.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[收藏]Tomcat5配置Mysql/Oracle JDBC数据库连接池</title><link>http://www.blogjava.net/iKingQu/articles/35334.html</link><dc:creator>風向逆轉 - 就要爪哇</dc:creator><author>風向逆轉 - 就要爪哇</author><pubDate>Tue, 14 Mar 2006 18:32:00 GMT</pubDate><guid>http://www.blogjava.net/iKingQu/articles/35334.html</guid><wfw:comment>http://www.blogjava.net/iKingQu/comments/35334.html</wfw:comment><comments>http://www.blogjava.net/iKingQu/articles/35334.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/iKingQu/comments/commentRss/35334.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/iKingQu/services/trackbacks/35334.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Tomcat5配置Mysql/Oracle JDBC数据库连接池&nbsp; 1、&nbsp; 安装Tomcat&nbsp;在安装了jdk后，参考Tomcat for window 的安装向导，基本直接安装即可，注意：安装时会提示输入管理用户名和密码，这是以后会用到的用户名和密码，切记。&nbsp;&nbsp;&nbsp;2、&nbsp; 安装MySql/Oracl...&nbsp;&nbsp;<a href='http://www.blogjava.net/iKingQu/articles/35334.html'>阅读全文</a><img src ="http://www.blogjava.net/iKingQu/aggbug/35334.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/iKingQu/" target="_blank">風向逆轉 - 就要爪哇</a> 2006-03-15 02:32 <a href="http://www.blogjava.net/iKingQu/articles/35334.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[收藏]HSQLDB介绍</title><link>http://www.blogjava.net/iKingQu/articles/35156.html</link><dc:creator>風向逆轉 - 就要爪哇</dc:creator><author>風向逆轉 - 就要爪哇</author><pubDate>Mon, 13 Mar 2006 18:51:00 GMT</pubDate><guid>http://www.blogjava.net/iKingQu/articles/35156.html</guid><wfw:comment>http://www.blogjava.net/iKingQu/comments/35156.html</wfw:comment><comments>http://www.blogjava.net/iKingQu/articles/35156.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/iKingQu/comments/commentRss/35156.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/iKingQu/services/trackbacks/35156.html</trackback:ping><description><![CDATA[<SPAN class=postbody><SPAN style="FONT-WEIGHT: bold">by jini</SPAN> <BR><BR>目前稳定版本 1.7.1 <BR>下载处 <A href="http://hsqldb.sourceforge.net/" target=_blank>http://hsqldb.sourceforge.net/</A> <BR><BR>(1)简介 <BR>hsqldb 是由 Tomas Muller 的 Hypersonic SQL 後续开发出来的专案, hsql 已经停止研发了, hypersonic db 是纯 java 所开发的资料库, 可以透过 jdbc driver 来存取, 支援 ANSI-92 标准的 SQL 语法, 而且他占的空间很小, 大约只有 160K, 拥有快速的资料库引擎, 也提供了一些工具, 例如 web-server, 记忆体查询, 及一些管理工具. <BR>他是属於 BSD 的 license, 可以自由下载, 并且可以安装使用在商业产品之上. <BR><BR>(2)安装 <BR>当你下载了hsqldb_1_7_1.zip, 将他解压缩到一个目录, 我将称之为 %HSQLDB_HOME% , 你可以看到 hsqldb 下面有这些目录 <BR>- bin <BR>- build <BR>- data <BR>- demo <BR>- doc <BR>- lib　　- hsqldb.jar <BR>　　　　 - servlet.jar <BR>- src <BR><BR>最重要的就是 hsqldb.jar, 将他放到工作目录下的 lib 中. <BR><BR>(3)使用 <BR>在命令列模式到工作目录 <BR>cmd&gt;java -cp lib/hsqldb.jar org.hsqldb.util.DatabaseManager <BR>选择 HSQL Database Engine Standalone ( 档案型资料库 ) <BR>Driver : org.hsqldb.jdbcDriver <BR>URL : jdbc:hsqldb:test <BR>sa 登入, 如果该资料库 ( test ) 不存在, 他就会建立 <BR>test.properties 及 test.script <BR><BR>可以直接在 fundad.script 加入 SQL 标准语法, <BR>当程式起来的时候, hsqldb 会自动汇入 script 中的资料到记忆体之中 <BR>语法与 T-SQL 相当, 可参考 <A href="http://hsqldb.sourceforge.net/doc/hsqlSyntax.html" target=_blank>http://hsqldb.sourceforge.net/doc/hsqlSyntax.html</A> <BR><BR>(4)结论 <BR>如果你是小型简单的资料库运用, 可以采用这个 opensource <BR>尤其在 embedded application 的应用, <BR>但是大型资料例如上百万笔, 除非你的 RAM 很大很大, <BR>否则会导致速度过慢, <BR>我尝试在我的电脑 P4-1.8G 512MB 安装 1百万笔资料 ( 每笔 4 栏位 ) <BR>就会 out of memory <BR>如果强制 java -mx1024m -ms1024m 也要花上超过一小时.... @@" <BR>不过, 他有个很好用的地方, 就是 bundle 在 Applet 之中 <BR>有兴趣的人可以参考他的范例. <BR><BR>附带一提 <BR><BR>hibernate 中可以采用 HSQLDialect 去连结，HSQLDB还有支持分页的sql语句</SPAN><SPAN class=postbody></SPAN><SPAN class=gensmall><BR><BR><BR>PS: 1、&nbsp;在hibernate中，使用hsql中，如果设置主键类型用Idenetity，那么java的field用Integer类型，不能用Long。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2、目前HSQLDB版本已经升级到1.8.0.2</SPAN><img src ="http://www.blogjava.net/iKingQu/aggbug/35156.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/iKingQu/" target="_blank">風向逆轉 - 就要爪哇</a> 2006-03-14 02:51 <a href="http://www.blogjava.net/iKingQu/articles/35156.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[收藏]Oracle性能调优原则</title><link>http://www.blogjava.net/iKingQu/articles/33738.html</link><dc:creator>風向逆轉 - 就要爪哇</dc:creator><author>風向逆轉 - 就要爪哇</author><pubDate>Sun, 05 Mar 2006 11:23:00 GMT</pubDate><guid>http://www.blogjava.net/iKingQu/articles/33738.html</guid><wfw:comment>http://www.blogjava.net/iKingQu/comments/33738.html</wfw:comment><comments>http://www.blogjava.net/iKingQu/articles/33738.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/iKingQu/comments/commentRss/33738.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/iKingQu/services/trackbacks/33738.html</trackback:ping><description><![CDATA[<P>Oracle性能调优原则 　&nbsp;&nbsp; <BR>&nbsp;<BR>任何事情都有它的源头，要解决问题，也得从源头开始，影响ORACLE性能的源头非常多，主要包括如下方面:<BR>　　数据库的硬件配置:CPU、内存、网络条件</P>
<P>　　1. CPU:在任何机器中CPU的数据处理能力往往是衡量计算机性能的一个标志，并且ORACLE是一个提供并行能力的数据库系统，在CPU方面的要求就更高了，如果运行队列数目超过了CPU处理的数目，性能就会下降，我们要解决的问题就是要适当增加CPU的数量了，当然我们还可以将需要许多资源的进程KILL掉;</P>
<P>　　2. 内存:衡量机器性能的另外一个指标就是内存的多少了，在ORACLE中内存和我们在建数据库中的交换区进行数据的交换，读数据时，磁盘I/O必须等待物理I/O操作完成，在出现ORACLE的内存瓶颈时，我们第一个要考虑的是增加内存，由于I/O的响应时间是影响ORACLE性能的主要参数，我将在这方面进行详细的讲解</P>
<P>　　3. 网络条件:NET*SQL负责数据在网络上的来往，大量的SQL会令网络速度变慢。比如10M的网卡和100的网卡就对NET*SQL有非常明显的影响，还有交换机、集线器等等网络设备的性能对网络的影响很明显，建议在任何网络中不要试图用3个集线器来将网段互联。</P>
<P>　　OS参数的设置</P>
<P>　　下表给出了OS的参数设置及说明，DBA可以根据实际需要对这些参数进行设置</P>
<P>　　内核参数名</P>
<P>　　说明</P>
<P>　　bufpages</P>
<P>　　对buffer空间不按静态分配，采用动态分配，使bufpages值随nbuf一起对buffer空间进行动态分配。</P>
<P>　　create_fastlinks</P>
<P>　　对HFS文件系统允许快速符号链接</P>
<P>　　dbc_max_pct</P>
<P>　　加大最大动态buffer空间所占物理内存的百分比，以满足应用系统的读写命中率的需要。</P>
<P>　　dbc_min_pct</P>
<P>　　设置最小动态buffer空间所占物理内存的百分比</P>
<P>　　desfree</P>
<P>　　提高开始交换操作的最低空闲内存下限，保障系统的稳定性，防止出现不可预见的系统崩溃(Crash)。</P>
<P>　　fs_async</P>
<P>　　允许进行磁盘异步操作，提高CPU和磁盘的利用率</P>
<P>　　lotsfree</P>
<P>　　提高系统解除换页操作的空闲内存的上限值，保证应用程序有足够的可用内存空间。</P>
<P>　　maxdsiz</P>
<P>　　针对系统数据量大的特点，加大最大数据段的大小，保证应用的需要。(32位)</P>
<P>　　maxdsiz_64bit</P>
<P>　　maximum process data segment size for 64_bit</P>
<P>　　Maxssiz</P>
<P>　　加大最大堆栈段的大小。(32_bit)</P>
<P>　　maxssiz_64bit</P>
<P>　　加大最大堆栈段的大小。(64_bit)</P>
<P>　　Maxtsiz</P>
<P>　　提高最大代码段大小，满足应用要求</P>
<P>　　maxtsiz_64bit</P>
<P>　　原值过大，应调小</P>
<P>　　Minfree</P>
<P>　　提高停止交换操作的自由内存的上限</P>
<P>　　Shmem</P>
<P>　　允许进行内存共享，以提高内存的利用率</P>
<P>　　Shmmax</P>
<P>　　设置最大共享内存段的大小，完全满足目前的需要</P>
<P>　　Timeslice</P>
<P>　　由于系统的瓶颈主要反映在磁盘I/O上，因此　降低时间片的大小，一方面可避免因磁盘I/O不畅造成CPU的等待，从而提高了CPU的综合利用率。另一方面减少了进程的阻塞量。</P>
<P>　　unlockable_mem</P>
<P>　　提高了不可锁内存的大小，使可用于换页和交换的内存空间扩大,用以满足系统对内存管理的要求。</P>
<P>　　用户SQL质量</P>
<P>　　以上讲的都是硬件方面的东西，在条件有限的条件下，我们可以调整应用程序的SQL质量:</P>
<P>　　1. 不要进行全表扫描(Full Table Scan):全表扫描导致大量的I/O</P>
<P>　　2. 尽量建好和使用好索引:建索引也是有讲究的，在建索引时，也不是索引越多越好，当一个表的索引达到4个以上时，ORACLE的性能可能还是改善不了，因为OLTP系统每表超过5个索引即会降低性能，而且在一个sql 中， Oracle 从不能使用超过 5个索引;当我们用到GROUP BY和ORDER BY时,ORACLE就会自动对数据进行排序,而ORACLE在INIT.ORA中决定了sort_area_size区的大小,当排序不能在我们给定的排序区完成时,ORACLE就会在磁盘中进行排序,也就是我们讲的临时表空间中排序, 过多的磁盘排序将会令 free buffer waits 的值变高,而这个区间并不只是用于排序的,对于开发人员我提出如下忠告:</P>
<P>　　1)、select,update,delete 语句中的子查询应当有规律地查找少于20%的表行.如果一个语句查找的行数超过总行数的20%,它将不能通过使用索引获得性能上的提高.</P>
<P>　　2)、索引可能产生碎片,因为记录从表中删除时,相应也从表的索引中删除.表释放的空间可以再用,而索引释放的空间却不能再用.频繁进行删除操作的被索引的表,应当阶段性地重建索引,以避免在索引中造成空间碎片,影响性能.在许可的条件下,也可以阶段性地truncate表,truncate命令删除表中所有记录,也删除索引碎片.</P>
<P>　　3)、在使用索引时一定要按索引对应字段的顺序进行引用。</P>
<P>　　4)、用(+)比用NOT IN更有效率。</P>
<P>　　降低ORACLE的竞争:</P>
<P>　　先讲几个ORACLE的几个参数，这几个参数关系到ORACLE的竞争:</P>
<P>　　1)、freelists 和 freelist 组:他们负责ORACLE的处理表和索引的空间管理;</P>
<P>　　2)、pctfree 及 pctused:该参数决定了freelists 和 freelist 组的行为，pctfree 和pctused 参数的唯一目的就是为了控制块如何在 freelists 中进出</P>
<P>　　设置好pctfree 及 pctused对块在freelists的移走和读取很重要。</P>
<P>　　其他参数的设置</P>
<P>　　1)、包括SGA区(系统全局区):系统全局区(SGA)是一个分配给Oracle 的包含一个 Oracle 实例的数据库的控制信息内存段。</P>
<P>　　主要包括数据库高速缓存(the database buffer cache)，</P>
<P>　　重演日志缓存(the redo log buffer)，</P>
<P>　　共享池(the shared pool)，</P>
<P>　　数据字典缓存(the data dictionary cache)以及其它各方面的信息</P>
<P>　　2)、db_block_buffers(数据高速缓冲区)访问过的数据都放在这一片内存区域，该参数越大，Oracle在内存中找到相同数据的可能性就越大，也即加快了查询速度。</P>
<P>　　3)、share_pool_size (SQL共享缓冲池):该参数是库高速缓存和数据字典的高速缓存。</P>
<P>　　4)、Log_buffer (重演日志缓冲区)</P>
<P>　　5)、sort_area_size(排序区)</P>
<P>　　6)、processes (同时连接的进程数)</P>
<P>　　7)、db_block_size (数据库块大小):Oracle默认块为2KB，太小了，因为如果我们有一个8KB的数据，则2KB块的数据库要读4次盘，才能读完，而8KB块的数据库只要1次就读完了，大大减少了I/O操作。数据库安装完成后，就不能再改变db_block_size的值了，只能重新建立数据库并且建库时，要选择手工安装数据库。</P>
<P>　　8)、open_links (同时打开的链接数)</P>
<P>　　9)、dml_locks</P>
<P>　　10)、open_cursors (打开光标数)</P>
<P>　　11)、dbwr_io_slaves (后台写进程数)<BR></P><img src ="http://www.blogjava.net/iKingQu/aggbug/33738.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/iKingQu/" target="_blank">風向逆轉 - 就要爪哇</a> 2006-03-05 19:23 <a href="http://www.blogjava.net/iKingQu/articles/33738.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[收藏]JDBC入门系列专题讲座</title><link>http://www.blogjava.net/iKingQu/articles/33721.html</link><dc:creator>風向逆轉 - 就要爪哇</dc:creator><author>風向逆轉 - 就要爪哇</author><pubDate>Sun, 05 Mar 2006 07:52:00 GMT</pubDate><guid>http://www.blogjava.net/iKingQu/articles/33721.html</guid><wfw:comment>http://www.blogjava.net/iKingQu/comments/33721.html</wfw:comment><comments>http://www.blogjava.net/iKingQu/articles/33721.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/iKingQu/comments/commentRss/33721.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/iKingQu/services/trackbacks/33721.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 1. 				介绍														 												　　许多开发者和用户都在寻找				Java				程序中访问数据库的便捷方法。由于				Java				是一个健壮，安全，易于使用的，易于理解且可以从网络中自动				download 				，所以它成为开发数据库应用的一种良好的语言基础。					...&nbsp;&nbsp;<a href='http://www.blogjava.net/iKingQu/articles/33721.html'>阅读全文</a><img src ="http://www.blogjava.net/iKingQu/aggbug/33721.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/iKingQu/" target="_blank">風向逆轉 - 就要爪哇</a> 2006-03-05 15:52 <a href="http://www.blogjava.net/iKingQu/articles/33721.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[收藏]JDBC连接各种数据库经验技巧集萃</title><link>http://www.blogjava.net/iKingQu/articles/33709.html</link><dc:creator>風向逆轉 - 就要爪哇</dc:creator><author>風向逆轉 - 就要爪哇</author><pubDate>Sun, 05 Mar 2006 07:14:00 GMT</pubDate><guid>http://www.blogjava.net/iKingQu/articles/33709.html</guid><wfw:comment>http://www.blogjava.net/iKingQu/comments/33709.html</wfw:comment><comments>http://www.blogjava.net/iKingQu/articles/33709.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/iKingQu/comments/commentRss/33709.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/iKingQu/services/trackbacks/33709.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: JDBC														连接各种数据库经验技巧集萃																																																Java				数据库连接（				JDBC				）由一组用				Java 				编程语言编写的类和接口组成。				JDBC				为工具				/	...&nbsp;&nbsp;<a href='http://www.blogjava.net/iKingQu/articles/33709.html'>阅读全文</a><img src ="http://www.blogjava.net/iKingQu/aggbug/33709.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/iKingQu/" target="_blank">風向逆轉 - 就要爪哇</a> 2006-03-05 15:14 <a href="http://www.blogjava.net/iKingQu/articles/33709.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>