﻿<?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-javaGrowing-随笔分类-JDBC</title><link>http://www.blogjava.net/juhongtao/category/6845.html</link><description /><language>zh-cn</language><lastBuildDate>Fri, 02 Mar 2007 03:34:03 GMT</lastBuildDate><pubDate>Fri, 02 Mar 2007 03:34:03 GMT</pubDate><ttl>60</ttl><item><title>JDBC高级应用三</title><link>http://www.blogjava.net/juhongtao/archive/2006/01/09/27258.html</link><dc:creator>javaGrowing</dc:creator><author>javaGrowing</author><pubDate>Mon, 09 Jan 2006 07:01:00 GMT</pubDate><guid>http://www.blogjava.net/juhongtao/archive/2006/01/09/27258.html</guid><wfw:comment>http://www.blogjava.net/juhongtao/comments/27258.html</wfw:comment><comments>http://www.blogjava.net/juhongtao/archive/2006/01/09/27258.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/juhongtao/comments/commentRss/27258.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/juhongtao/services/trackbacks/27258.html</trackback:ping><description><![CDATA[JDBC高级应用三<BR><BR>再谈JDBC连结<BR><BR>为什么要反复谈JDBC连结,因为所以JDBC的性能,最主要的就是JDBC连结,而SQL语句的<BR>优化,和JAVA编程无关,你的一个查询语句的效率,是你对于SQL语法的使用技巧,这一方<BR>面你就可请教DBA,而不是来看我这种程序设计者的文章.<BR><BR>我们已经知道,取得数据库连结,有几种层次的实现方法,一是直接得到物理连结,而是通<BR>过传统的连结池(没有多大区别),三是通过java的扩展包javax.sql.DataSource得到连结<BR>句柄,对于上面两种,没有什么可以多说的,对于DataSource,我们再深入一些.<BR>一般来说,DataSource是容器本身作为一个JDNI查找的对象返回出来的,也就是说要依赖<BR>容器进行配置,而如果一个100%的应用程序(Application),比如基于swing的App,我根本<BR>不需要运行容器,那我如何取得DataSource对象?这时可能要你自己写基于DataSource的<BR>连结池了(是不是有些太深入了?要做就做高手,SUN能做我们就能做).<BR>如果自己要实现DataSource,要清楚几个关系,即DataSource中返回的Connection是一个连<BR>结的句柄,它要和实际的物理连结关连,这些实际的物理连结就是PooledConnection,我们<BR>叫它池中的连结,可以通过实现ConnectionPoolDataSource,从中得到PooledConnection,<BR>这部分本来是厂商实现的,但这部份实现和传统的连结池没有什么大的区别,主要是从<BR>ConnectionPoolDataSource中得到PooledConnection的物理连结,但如何从PooledConnection<BR>中getConnection(),返回给用户.这部分实现就是DataSource实现的性能高低的关键,一般<BR>来说,我们可以先把一个物理连结PooledConnection和多个客户连结相关连来增加性能,也<BR>就是一个PooledConnection本身再作为一个工场的种子,通过一个PooledConnection再返回<BR>多个Connection,说白了就是多个Connection的请求通过一个PooledConnection传递给数据库.<BR>只要用户调用Connection的close()方法,就打断这个Connetion与PooledConnection的关联而<BR>让PooledConnection可以和新的Connection进行关联.<BR><BR>JDBC事务<BR><BR>JDBC1开始,就支持本地事务,所谓要地事务,就是在一个连结中的多个操作可以作为一个事务<BR>过程来提交.注意,只要你使用conn.setAutoCommit(false);方法就隐式地打开了一个事务.当<BR>事务被commit或abort时,隐含的是打开了一个新的事务.<BR>另外,当一次事务被commit或abort,PreparedSattement和CallableStatement绑定的结果集全<BR>部被关闭,而普通的Statement绑一的结果集将被维持.<BR>在处理多个操作时:<BR>conn.setAutoCommit(false);<BR>Statement&nbsp;st1&nbsp;=&nbsp;conn.createSatatement(sql1);<BR>Statement&nbsp;st2&nbsp;=&nbsp;conn.createSatatement(sql2);<BR>Statement&nbsp;st3&nbsp;=&nbsp;conn.createSatatement(sql3);<BR>Statement&nbsp;st4&nbsp;=&nbsp;conn.createSatatement(sql4);<BR>st1.executeXXXXX();<BR>st2.executeXXXXX();<BR>st3.executeXXXXX();<BR>st4.executeXXXXX();<BR>在这里,我们要么把四个操作一起回滚,或一起提交,但如果我们认为,1-3的操作是要求一致完<BR>成,而4的操作在一至三完成时再完成.在经前的版本中我们要把它们分开在两次事务中,但在<BR>JDBC3.0以后,我们可以利用conn.setSavepoint()&nbsp;来得到一个SavePoint来对同一事务中不同阶<BR>段进行断点保存.然后可以在任何断点上进行提交和回滚.同时我们还可以利用<BR>conn.setTransactionIsolation()来设置隔离级别.<BR>要注意的是,对事务的支持要看数据库的具体实现.如果数据库本身不支持事务,那么以上的操作<BR>都是无效的,可以从&nbsp;DatabaseMetaData中查询数据库对事务的支持情况.<BR><BR>毕竟,本地事务功能并不是很强,而如果不是编程人员对SQL语句传入错误,那么在一次连结中<BR>多个操作只完成部份的机率并不容易发生(当然有时还会发生的,要不本地事务就不会产生了).<BR>其实,JDBC事务最重要的是分布式事务,即同时操作不同的连结,可能是同物理库的不同空间,也<BR>可能是同一主机的不同数据库或不同主机的多个数据库.这就很难保证每个操作都是成功的,发<BR>生操作不一致的机会太多了,可以说如果不在事务中测试你就无法相信操作的一致性.所以分布<BR>式事务是JDBC的重要技术.<BR>在下一节我们重点介绍JDBC分布式事务. <img src ="http://www.blogjava.net/juhongtao/aggbug/27258.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/juhongtao/" target="_blank">javaGrowing</a> 2006-01-09 15:01 <a href="http://www.blogjava.net/juhongtao/archive/2006/01/09/27258.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JDBC高级应用二 </title><link>http://www.blogjava.net/juhongtao/archive/2006/01/09/27221.html</link><dc:creator>javaGrowing</dc:creator><author>javaGrowing</author><pubDate>Mon, 09 Jan 2006 02:08:00 GMT</pubDate><guid>http://www.blogjava.net/juhongtao/archive/2006/01/09/27221.html</guid><wfw:comment>http://www.blogjava.net/juhongtao/comments/27221.html</wfw:comment><comments>http://www.blogjava.net/juhongtao/archive/2006/01/09/27221.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/juhongtao/comments/commentRss/27221.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/juhongtao/services/trackbacks/27221.html</trackback:ping><description><![CDATA[JDBC高级应用(二)<BR>本来想继续谈JDBC的高级连结方式,事务模式.但发现关于大对象存储有很多人在问,所以<BR>先来插入一节关于大对象存储的内容,然后再接着原来的思路写下去.<BR><BR>JDBC的大对象存储听起来复杂,其实如果你明白了原理以后,就非常简单,网上有关这方面的<BR>教材很少,而SUN的文档中,我从1.2开始看到一在仍然是错误的,不知道写文档的人长脑子没<BR>有,就那几行代码你试试不就知道了,这么多次重抄下来还是错误的.<BR><BR><BR>大对象分类:一般来说,大对象分为:大的文本对象,比如一个很长的文本(请你要注意什么是<BR>文本文件,什么是二进制文件)文件,或者是你定义的一个长字符串,比如你定义了:<BR>String&nbsp;s&nbsp;=&nbsp;"我们要去吃饭了......................然后睡觉!";<BR>从吃饭到睡觉中间省略了实际的10000000000000字,虽然你不会真的定义这么称的String,但<BR>有时会从什么地方得到这样的String,要写到数据库中.<BR>另一种就是大的二进制对象,象执行文件,图象文件等,注意,word,excel,ppt这些"带格式"的文<BR>档都应该以二进制对象存储.<BR><BR>一般来说,数据库如果支持大对象存储,会有这几种类型的SQL数据类型:<BR>BLOB,CLOCB,NLOB,也有的数据数只有一种BLOB,基本上是这样的:BLOB用来存放二进制文件,而<BR>CLOB用来存放文本文件,NLOB是对多字节文本文件支持.假如你的文本文件是纯英文的,放在<BR>BLOB中当然可以,也就是说它是以byte格式存储的,而多字节是以CHAR格式存储的.<BR><BR>同样对于这几种类型的文档,有几种相对应的存取方式:<BR>setter:<BR>利用PreparedStatement的setXXX方法,<BR>setAsciiStream()方法用于写入一般的文本流.setBinaryStream()方法用于写入二进制流<BR>而setUnicodeStream()用于写好UNICODE编码的文本,与此相对应的ResultSet中三个getter方法<BR>用于取回:getAsciiStream(),getBinaryStream(),getBinaryStream().<BR>对于文件本身,要把它作为一个流,只要new&nbsp;InputStream(new&nbsp;FileInputStream("文件路径"))<BR>就可以了,但对于大的String对象,你不会写入文件再转换成输入流吧?<BR>new&nbsp;StringBufferInputStream(String&nbsp;s),记住了.<BR>JDBC2以后提供了java.sql.BLOB对象,我不建议大家使用它,一是很麻类,二是容易出错,要先插<BR>入一个空的BLOB对象,然后再填充它,实在没有必要,直接setXXX就行了,我试过,至少mysql,<BR>oracle,sql&nbsp;server是可以直接set的.<BR>好了,我们先看一个例子如何写入文件到数据库:<BR>数据结构:<BR>create&nbsp;table&nbsp;test(<BR>&nbsp;&nbsp;name&nbsp;varchar(200),<BR>&nbsp;&nbsp;content&nbsp;BLOB<BR>);<BR>File&nbsp;f&nbsp;=&nbsp;new&nbsp;File("a.exe");//先生成File对象是为了取得流的长度.FileInputStram可以直接<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//传入文件路径<BR>InputStream&nbsp;in&nbsp;=&nbsp;new&nbsp;InputStream(new&nbsp;FileInputStream(f));<BR>PreparedStatement&nbsp;ps&nbsp;=&nbsp;conn.prepareStatement("insert&nbsp;into&nbsp;test&nbsp;(?,?)");<BR>ps.setString(1,"a.exe");<BR>ps.setBinaryStream(2,in,(int)f.length());<BR>ps.executeUpdate();<BR>f的长度一定要做从long到int的转换,SUN的文档中好几版都没有改过来.就这么简单,当然,不同的<BR>数据库存本身要设置它允许的最大长度,MYSQL默认只能传1M的文件,要修改参数原能存更大的文件.<BR>如果要从数库中取得文件:<BR>PreparedStatement&nbsp;ps&nbsp;=&nbsp;conn.prepareStatement("select&nbsp;*&nbsp;from&nbsp;test&nbsp;where&nbsp;name=?");<BR>ps.setString(1,"a.exe");<BR>ResultSet&nbsp;rs&nbsp;=&nbsp;ps.executeQuery();<BR>if(rs.next()){<BR>&nbsp;InputStream&nbsp;in&nbsp;=&nbsp;rs.getBinaryStream("content");<BR>}<BR>得到in对象后,你可以进行任何处理,写向文件和写向页面只是out对象不同而已:<BR>写向文件:<BR>DateOutputStream&nbsp;out&nbsp;=&nbsp;new&nbsp;DateOutputStream(new&nbsp;FileOutputStream("b.exe"));<BR>写向页面:<BR>response.reset();<BR>response.setContType("类型");<BR>ServletOutputSreamt&nbsp;out&nbsp;=&nbsp;response.getOutputSream();<BR>得到out对象后,就可以输出了:<BR>byte[]&nbsp;buf&nbsp;=&nbsp;new&nbsp;byte[1024];<BR>int&nbsp;len&nbsp;=&nbsp;0;<BR>while((len&nbsp;=&nbsp;in.read(buf))&nbsp;&gt;0)<BR>&nbsp;&nbsp;out.write(buf,0,len);<BR>in.close();<BR>out.close();<BR>对于向页面输入,要设置什么样的ContType,要看你想如何输出,如果你想让对方下载,就设为<BR>"application/octet-stream",这样即使是文本,图象都会下载而不会在浏览器中打开.如果你要想<BR>在浏览器中打开,就要设置相应的类型,还要在容器的配置文件中设置支持这种文档类型的输出,但<BR>对于很多格式的文件,到底要输出什么类型,其实就是HTTP的MIME集,比如图片:image/gif,当然你如<BR>果你的文件扩展名(ext)不确定,你也不要用if(ext.equals("gif"))......这样来判断,我教你一个<BR>技巧,我之所以说是技巧,是我没有在别的地方发现有人用这种方法,对我来说我是绝对不会把别人的<BR>方法拿来说是我的技巧的:<BR>构造一个file类型的URL,我们知道URL目前JAVA可以支持HTTP,FTP,MAILTO,FILE,LDAP等,从FILE类型<BR>的URL就可以得到它的MIME:<BR><BR>URL&nbsp;u&nbsp;=&nbsp;new&nbsp;URL("file://a.exe");<BR>String&nbsp;mime&nbsp;=&nbsp;u.openConnection().getContentType();<BR>这样你就可以直接response.setContType(mime);而不用一个一个类型判断了.<BR>好了,大对象存储就说到这儿,不同的数据仍然和些特殊的规定,不在此一一列举了.<img src ="http://www.blogjava.net/juhongtao/aggbug/27221.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/juhongtao/" target="_blank">javaGrowing</a> 2006-01-09 10:08 <a href="http://www.blogjava.net/juhongtao/archive/2006/01/09/27221.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JDBC的高级应用一 </title><link>http://www.blogjava.net/juhongtao/archive/2006/01/09/27220.html</link><dc:creator>javaGrowing</dc:creator><author>javaGrowing</author><pubDate>Mon, 09 Jan 2006 02:07:00 GMT</pubDate><guid>http://www.blogjava.net/juhongtao/archive/2006/01/09/27220.html</guid><wfw:comment>http://www.blogjava.net/juhongtao/comments/27220.html</wfw:comment><comments>http://www.blogjava.net/juhongtao/archive/2006/01/09/27220.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/juhongtao/comments/commentRss/27220.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/juhongtao/services/trackbacks/27220.html</trackback:ping><description><![CDATA[JDBC高级应用&nbsp;一<BR><BR>关于数据库连结<BR><BR>我们所说有JDBC高级应用,并不是说它的技术含量很高(也许JAVA平台上不存在什么"技术含量"的说<BR>法,因为JAVA是给大家用的而不是给某些人用的).说它是高级应用,是因为它是对于JDBC基础应用来<BR>说的扩展,也就是可以优化你的应用性能,或方便于应用的实现.所以说它是一种高级应用而不叫高级<BR>技术.<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;JDBC中,java.sql包是基础的,也是核心的功能,javax.sql包则是高级的,扩展的功能.所以<BR>为了交流的方便,我们把它们区分为core&nbsp;API和optional&nbsp;API.<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;但是仍然然有一些core&nbsp;API中的功能,我把它归纳到高级应用中.象存储过程的调用,多结果集<BR>的处理,我之所以要把这些东西拿出来说明,是目前你在网上找不到任何一份详细的文档和例程,可以<BR>说有95%以上的开发人员都不知道真正如何处理这些工作.所以我会在回上海后详细写这一段的内容.<BR>现在我们还是来看看optional&nbsp;API给我们带来的好处:<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;我们已经了解,执行一个SQL语句,要经过如下几步:<BR>&nbsp;&nbsp;&nbsp;&nbsp;1.Connction<BR>&nbsp;&nbsp;&nbsp;&nbsp;2.Statement<BR>&nbsp;&nbsp;&nbsp;&nbsp;3.Statement.executeXXXXX();<BR>&nbsp;&nbsp;&nbsp;&nbsp;4.可选的对结果集的处理<BR>&nbsp;&nbsp;&nbsp;&nbsp;5.必要的Connction的关闭.(再次提醒如果你想成为中级水平以上的程序员,请你把关闭语<BR>&nbsp;&nbsp;&nbsp;&nbsp;句写在finally块中,在通过下面的介绍后我介绍一个方法可以用来验证你的程序是否有连结<BR>&nbsp;&nbsp;&nbsp;&nbsp;泄漏)<BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;这其中,生成Connction对象是最最重要的工作,也是最消耗资源的,因为连结对象要驱动底层<BR>的SOCKET,调用物理连结和数据库进行通信,所以生成,关闭,再生成这种连结对象就相当于我们在二十<BR>世纪八十年代(1980年以后出身的不了解吧?)喝易拉罐饮料一样.你买一瓶饮料是一块二角钱,你可知道<BR>那罐子(Connection)值一块零八分.而你喝下去的东西只值一角二分钱,这是我们那儿一个饮料厂的真实<BR>数据.<BR>&nbsp;&nbsp;&nbsp;&nbsp;在javax.sql包出来以前,我们只能买这样的饮料来喝,除非你不喝.也有一些人不服气自己生<BR>产饮料(poolman),可是消费者很快发现,它只是把原来单卖的易拉罐现在打包卖给了我们,因为它还是<BR>用原来的包装原料来生产的,poolman这种类型的连结池,其根本是从DriverManager中getConnection<BR>出来的.真正的效率如果不是你心理作用的话,也许比单个连结还要低!!!以及一些江湖好汉的"杰作",<BR>都无法跳出这个框框.我自己在那一段时间也曾醉心于研究这些"连结池",因为谁都可以把别人的原码<BR>读过后,再加上其他人的优点写出一个更好的来,可是,大家可以看到,我写出了好用的Upload,DownLoad,<BR>HtmlUtil,Encoder,Decoder等一系列工具.可是我没有写出成功的连结池.......<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;我们再来深入一步,为什么DriverManager生成的连结和基于它的连结池不能真正提高性能.<BR>DriverManager对象中,绝大多数的JDBC是封装了一个物理连结,也就是它抓住了一个和数据库通信的<BR>Socket,当你使用DriverManager.getConnection()时也就是有一个和数据库连结的Socket让你占用了.<BR>而且这个方法是同步的.大家知道这样的物理连结对于任何系统是有限制的,比如一个WEB服务器一般<BR>最大并发是150到250之间,数据库服务器也是这样的道理,你不仅要考虑你的程序不能用光连结,还要<BR>考虑不同Runtime中或其它应用程序也在同时和你一起使用这些物理连结,如果一台服务器上有JAVA&nbsp;<BR>WEB&nbsp;SERVER,还有一个C的应用程序也访问数据库,你不能那么无礼地要求人家C程序员他的程序必须<BR>等你的JAVA调用空闲才能访问数据库吧.所以物理连结是极其宝贵的.<BR>&nbsp;&nbsp;&nbsp;&nbsp;基于DriverManager.getConnection()的连结池只不过是预先生成这样的物理连结放在一个<BR>pool中,然后编号等你调用,它省略的是生成这样的连结的时间.注意你得到的连结在你没有释放之前,<BR>它无法处理别的工作,因为连结句柄在用户手中,另外这种连结池调用时是由程序调用者初始化的,<BR>每一次调用都必须有初始化工作,而调用者是否以优化的方法去运行它,完成还要看每个人的编程水<BR>平.另一方面,如果这种连结池是如果用于WEB容器管理,那简单是垃圾,因为它强迫使用静态变量来<BR>保持连结,容器根本无法做到访问控制.而且它不能在不同Runtime中被调用.<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;而javax.sql的实现采用了在用户连结和物理连结中间加一个缓冲的中间层,它虽然也只生<BR>成30个物理连结,但用户本身不能访问它,DataSource返回给用户的是一个JAVA抽象对象,客户程序把<BR>连结请求放回缓冲中由DataSource统一调度物理连结来处理,这样可以最大程序利用宝贵的物理连结<BR>也许现在的30个物理连结仍然不够负载,你仍然需要修改实际连结数,但我们知道,我们现在的这种连<BR>结方式已经不是DriverManager.getConnection()能比的了.也就是说,在同样多的物理连结下,<BR>DataSource可以给我们更多(是多得多的)的调用机会,其实,正常情况下一个从DriverManager中<BR>getConnection()出来物理连结的负载量只有百分之几,就是因为你的调用抓住了它的句柄而不能让<BR>它很好地工作.另外程序员只能返回它而不能关闭它,因为传统连结池中连结对象一旦由用户关闭,<BR>就要再次重新生成物理新的连结,所以用户只能释放,对于非连结池和连结池得到的连结对象,<BR>要用不同的代码编程,简单是一种痛苦.一旦没有注意,在处理完数据后不是释放而是关闭,这个错误<BR>到底是谁的过错?????????????<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;我上面说java.sql实现绝大多数是得到物理连结,也有例外,但不是那些以前的连结池,而是<BR>OSE,就是oracle的servlet环境,它是把servlet服务器实现在数据库的地址空间上,servlet服务去调<BR>用数据库根本没有通过传统的连结,因为数据是"敞开"的,这就象通过网络访问其它计算机文件和访问<BR>本地文件的区别.&nbsp;虽然它也提供标准的JDBC接口让你调用,但它底层根本不是用JDBC封装的.<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;DataSource的另外一个优点就是它完全实现数据库和应用程序的分离,如何配置服务器生成<BR>DataSource的引用和程序开发无关,你在程序开发中,只要通过JDNI查找DataSource的逻辑名称就行.<BR>而DriverManager.getConnection()中,你不得不把数据库驱动程序名,访问地址,用户,密码写在你的<BR>应用程序中,即使可以从配置文件中读取这些属性串,而不同的服务器配置文件的路径你都要修改.而<BR>DataSource提供了标准的配置.<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;说来说去,如何用DataSource来连结数据库?<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;非常简单:<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;DataSource&nbsp;ds&nbsp;=&nbsp;(DataSource)new&nbsp;InitialContext().lookup("jdbc/mydb");<BR>&nbsp;&nbsp;&nbsp;&nbsp;Connection&nbsp;conn&nbsp;=&nbsp;ds.getConnection();<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;当然我是为了说明它简单故意把它的异常给忽略了.实际应用中.你应该捕获它的异常.<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;如果你还不明白什么是JDNI,我劝你先找一些这方面的资料看看,这可以是网编程方面的<BR>基础协议啊.<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;关于DataSource&nbsp;ds&nbsp;=&nbsp;(DataSource)new&nbsp;InitialContext().lookup("jdbc/mydb");有几点<BR>需要说明的是,你只要配置好你的服务器(tomcat,resin,weblogic)这些服务器中都有一个例子,如果<BR>你不是很懂,你先把那个例子改动几个字就行了.用一段时间你就会慢慢理解它们代表什么了.<BR>然后你在容器环境下调用new&nbsp;InitialContext().lookup("jdbc/mydb")容器就会自动找到那个<BR>DataSource对象给你调用,这个过程对用户来说是透明的.<BR>&nbsp;&nbsp;&nbsp;&nbsp;那边那个聪明的朋友已经问了,因为DataSource的属性是已经配置好的放在容器中的,那我<BR>不在容器环境下,比如一个独立的application,我如何能取到DataSource呢?<BR>&nbsp;&nbsp;&nbsp;&nbsp;其实,如果你能知道new&nbsp;InitialContext()时,容器调用了哪些默认的配置,你就可以把<BR>这些配置参数手工加进去而不依赖容器环境了.好在InitialContext可以getEnvironment()&nbsp;,在生<BR>成这个对象后你可以get一下看看,把这些参数记下来,以后在没有这些参数的环境下put进去.<BR>&nbsp;&nbsp;&nbsp;&nbsp;这里多几句话,谈一下学习方法,我在国内主持几个论坛(不多,两三个),从没有问过别人什<BR>么问题,java技术又不是我发明的,不可能我什么都懂,一是问了好象有损于"高手"风范(哈哈,其实<BR>真正的高手还是要问别人的,只有我这种假高手才不会问别人).另一方面是我根本不必问别人,比如<BR>象application下,连结不同厂家的DataSource要put什么东西进去呢?一是去他们的网站看资料,虽然<BR>我的英语水平只有大家的10%,但我上英文网站的次数可能比你们多.二是看有没有什么共用的API能<BR>得到,好在可以getEnvironment(),但假如没有这个方法呢?这就要看你的学习态度了,有人会这论坛<BR>叫"高手球命",还有什么"急用,在线等待"什么的.而我,会把new&nbsp;InitialContext()反编译出来看看<BR>它调用了什么(孔子说,为了学习的目的,反编译是允许的,甚至说是伟大的,光明的,正确的思想,是有<BR>道德的,脱离了低级趣味的,有益于人民的行为!!!----孔子语录补集第123页第4行,1989年10月版)<BR>如果一个对象在构造时要求有参数,而它又有一个没有参数的重载的构造方法,你想想它肯定在没有<BR>参数的构造方法中调用了默认参数,你要做的就是把它们打印出来.有多少人是这样做的?<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;jdbc&nbsp;optional&nbsp;API的其它扩展功能:<BR>&nbsp;&nbsp;&nbsp;&nbsp;javax.sql不仅仅是在性能上的提高,而且它还支持分布式事务,在传统的连结过程中,我们<BR>可以在一个连结过程中,setAutoCommit()为fasle,然后通过rollback()或commit()那回滚和提交事<BR>务,这种在一个连结过程中的事务称为本地事务,但假如在一个事务中要对多个数据库操作,或多过<BR>Servlet参与操作,那就必须使用分布式事务.<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;关于JDBC的事务我会放在下面来介绍,一个值得庆贺的功能出来了,就是事务保存点已经<BR>JDBC3.0中实现,以前,如果我们把事务原子A,B,C放在一个事务中,如果A,B执行了,C失败,我们只能<BR>把A,B都回滚了,但现在我们可以先把A,B保存为一个点,然后以这个点为回滚或提交,这就象在用WORD<BR>编写文章时我们可以在不同的时候保存一个副本,而不会要么一字没有了,要么就是当前编辑的状态.<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;现在我们来优化我们在基础知识中实现的Bean,今天在家,没法上论坛,上次写的连结部分<BR>叫什么名字忘记了,现在我们就叫它PooledDB吧.<BR>&nbsp;&nbsp;&nbsp;&nbsp;当时我们已经把那个Bean分为三个部分,把生成连结部分独立出来了,而业务方法部份和扩<BR>展部分根本不要动它,这就是继承的好处:)<BR><BR><BR>package&nbsp;com.inmsg.beans;<BR><BR>import&nbsp;javax.naming.*;<BR>import&nbsp;javax.sql.*;<BR><BR>public&nbsp;class&nbsp;PooledDB&nbsp;{<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Connection&nbsp;con&nbsp;=&nbsp;null;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;String&nbsp;source&nbsp;=&nbsp;"";<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;PooledDB()&nbsp;throws&nbsp;Exception&nbsp;{//默认构造方法,如果构造时不加参数,连结jdbc/office<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;source&nbsp;=&nbsp;"java:comp/env/jdbc/office";<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Context&nbsp;ct&nbsp;=&nbsp;new&nbsp;InitialContext();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DataSource&nbsp;ds&nbsp;=&nbsp;(DataSource)&nbsp;ct.lookup(source);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;con&nbsp;=&nbsp;ds.getConnection();<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;//然后增加重载方法,用来连结其它的数据源&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;PooledDB(String&nbsp;source)&nbsp;throws&nbsp;Exception&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.source&nbsp;=&nbsp;source;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Context&nbsp;ct&nbsp;=&nbsp;new&nbsp;InitialContext();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DataSource&nbsp;ds&nbsp;=&nbsp;(DataSource)&nbsp;ct.lookup(source);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;con&nbsp;=&nbsp;ds.getConnection();<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;//注意一定要先把source赋给成员变量this.source,因为下面还有一个makeConnection()<BR>&nbsp;&nbsp;&nbsp;&nbsp;//辅助方法,如果不把source赋给this.source,则makeConnection()调用默认的source字符串<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;void&nbsp;makeConnection()&nbsp;throws&nbsp;Exception&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Context&nbsp;ct&nbsp;=&nbsp;new&nbsp;InitialContext();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DataSource&nbsp;ds&nbsp;=&nbsp;(DataSource)&nbsp;ct.lookup(source);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;con&nbsp;=&nbsp;ds.getConnection();<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;//现在我们把close()方法拿到父类来实现,这是经过综合考虑的,它是一个业务方法,无论是什么<BR>&nbsp;&nbsp;&nbsp;&nbsp;//方式取得连结,它本身不会修改,但为什么还封装到父类中呢?因为这样可以用一个独立的父类来<BR>&nbsp;&nbsp;&nbsp;&nbsp;//做连结测试,如果我只想试一下数据库能不能连结,我就不必再引用子类,直接用这个父类就行了<BR>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;close()&nbsp;throws&nbsp;Exception{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(con&nbsp;!=&nbsp;null&nbsp;&amp;&amp;&nbsp;!con.isClosed())&nbsp;con.close();<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;一般来说,构造方法尽量捕获异常处理而不要抛出异常,但作为Bean的实现,捕获异常调用者不容易<BR>看到异常信息,所以抛给调用者处理,另外这个类又要在应用程序中调用,又要考虑作为Bean调用,所以一定<BR>要有一个无参数的构造方法,否则不能作为javaBean调用.把异常抛出给调用者的另一目的,我在设计时是这<BR>样考虑的,就是强迫你一定在使用try{}catch(){}块,这样你就会想到再加一个finally{},再次提醒,一定要<BR>以下面的形式来调用你数据库连结的Bean或封装类:<BR>&nbsp;&nbsp;&nbsp;&nbsp;PooledDB&nbsp;pd&nbsp;=&nbsp;null;<BR>&nbsp;&nbsp;&nbsp;&nbsp;try{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pd&nbsp;=&nbsp;new&nbsp;PooledDB();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;....................<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;catch(Exception&nbsp;e){}<BR>&nbsp;&nbsp;&nbsp;&nbsp;finally{try{pd.close();}catch(Exception&nbsp;ex){}}<BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;如果要测试你的数据库连结是否有泄漏,请你把DataSource中最大连结数设为1,只用一个连结的情<BR>况下,如果你的程序中哪一处没有关闭连结,则下面的程序就不能再访问,然后从头到尾测试你的程序吧,一旦<BR>发现不能访问数据库了,就查看刚才访问的代码,这样所有程序测试后,就可以放心了,一般来说我是不用这么<BR>测试的,因为我在写数据库连结时是没有生成对象就写好close:<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;PooledDB&nbsp;pd&nbsp;=&nbsp;null;<BR>&nbsp;&nbsp;&nbsp;&nbsp;try{}<BR>&nbsp;&nbsp;&nbsp;&nbsp;catch(Exception&nbsp;e){}<BR>&nbsp;&nbsp;&nbsp;&nbsp;finally{try{pd.close();}catch(Exception&nbsp;ex){}}<BR>&nbsp;&nbsp;&nbsp;&nbsp;然后原在try{}的花括号中回车加上pd&nbsp;=&nbsp;PooledDB();和业务代码的&nbsp;:)当然,你们都比我聪明用不<BR>着这样死套也不会忘记close()的.<BR>&nbsp;&nbsp;&nbsp;&nbsp;不要太高兴,到目前为止你仍然还没得到一个最好的解决方案,以下我们还会对这个数据库连结的<BR>Bean(类)不断优化的..................................<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;好了,javax.sql的连结先说到这儿吧,今天是周六,出去玩一会了(广州街头上没有什么美女!) <img src ="http://www.blogjava.net/juhongtao/aggbug/27220.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/juhongtao/" target="_blank">javaGrowing</a> 2006-01-09 10:07 <a href="http://www.blogjava.net/juhongtao/archive/2006/01/09/27220.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JDBC基础</title><link>http://www.blogjava.net/juhongtao/archive/2006/01/09/27218.html</link><dc:creator>javaGrowing</dc:creator><author>javaGrowing</author><pubDate>Mon, 09 Jan 2006 02:04:00 GMT</pubDate><guid>http://www.blogjava.net/juhongtao/archive/2006/01/09/27218.html</guid><wfw:comment>http://www.blogjava.net/juhongtao/comments/27218.html</wfw:comment><comments>http://www.blogjava.net/juhongtao/archive/2006/01/09/27218.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/juhongtao/comments/commentRss/27218.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/juhongtao/services/trackbacks/27218.html</trackback:ping><description><![CDATA[<STRONG>JDBC基础(一)</STRONG> <BR><BR>本来不想写这部份入门级的内容,但既然栏目定为JDBC专栏,还是简单写一些吧.<BR>JDBC基础(一)<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;来,我们认识一下!<BR>&nbsp;&nbsp;&nbsp;&nbsp;JDBC,JAVA平台的DATABASE的连通性.白话一句,什么意思啊?<BR>&nbsp;&nbsp;&nbsp;&nbsp;就是JAVA平台上和数据库进行连结的"工具".<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;还是先一起来回顾一下接口吧:从下向上,接口是对"案例"的抽象,由一个案例抽象出一些规则.<BR>反过来,从上向下,被抽象出来的接口是对案例的一种承诺和约束.<BR>&nbsp;&nbsp;&nbsp;&nbsp;也就是说,只要你实现我规定的接口,你的类就已经具有了接口对外承诺的方法,只要"客户"会<BR>操作接口,不需要重新学习就会操作实现了该接口的新类!<BR>&nbsp;&nbsp;&nbsp;&nbsp;好了,用行话来说:<BR>&nbsp;&nbsp;&nbsp;&nbsp;1.通过接口可以实现不相关的类的相同行为.<BR>&nbsp;&nbsp;&nbsp;&nbsp;2.通过接口可以指明多个类需要实现的方法.<BR>&nbsp;&nbsp;&nbsp;&nbsp;3.通过接口可以了解对象的交互方法而不需要了解对象所对应的类蓝本.<BR>&nbsp;&nbsp;&nbsp;&nbsp;这几句话很明白吧?好象有一本什么模式的书把这段话用了30多页写出来,结果别人看了还不如<BR>我这几句话明白,不过我明白了为什么有些人要写书了.<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;搞懂了以上这东西,JDBC就好明白了.<BR>&nbsp;&nbsp;&nbsp;&nbsp;为了通用,JAVA中要求有一种机制,在操作不同厂商数据库时有相同的方法去操作,而不是每接<BR>触一种数据库就要学习新的方法.完成这种机制的"东西"就叫"JDBC"了.<BR>&nbsp;&nbsp;&nbsp;&nbsp;简单地分,JDBC有两部分组成,JDBC&nbsp;API和JDBC&nbsp;Driver&nbsp;Interface.<BR>&nbsp;&nbsp;&nbsp;&nbsp;JDBC&nbsp;API就是提供给"客户"(就是象你我这种菜鸟级程序员来用的,如果是高手都自己写JDBC了,<BR>哈哈)的一组独立于数据库的API,对任何数据库的操作,都可以用这组API来进行.那么要把这些通用的API<BR>翻译成特定数据库能懂的"指令",就要由JDBC&nbsp;Driver&nbsp;Interface来实现了,所以这部份是面向JDBC驱动程<BR>序开发商的编程接口,它会把我们通过JDBC&nbsp;API发给数据库的通用指令翻译给他们自己的数据库.<BR><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;还是通过实际操作来看看JDBC如何工作的吧.<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;因为JDBC&nbsp;API是通用接口,那么程序是如何知道我要连结的是哪种数据库呢?所以在和数据库连<BR>结时先要加载(或注册可用的Driver),其实就是JDBC签名.加载驱动程序和好多方法,最常用的就是先把驱<BR>动程序类溶解到内存中,作为"当前"驱动程序.注意"当前"是说内存中可以有多个驱动程序,但只有现在加<BR>载的这个作为首选连结的驱动程序.<BR>&nbsp;&nbsp;&nbsp;&nbsp;Class.forName("org.gjt.mm.mysql.Driver");<BR>&nbsp;&nbsp;&nbsp;&nbsp;Class.forName方法是先在内存中溶解签名为"org.gjt.mm.mysql.Driver"的Driver类,Driver类<BR>就会把相应的实现类对应到JDBC&nbsp;API的接口中.比如把org.gjt.mm.mysql.Connection的实例对象赋给<BR>java.sql.Connection接口句柄,以便"客户"能通过操作java.sql.Connection句柄来调用实际的<BR>org.gjt.mm.mysql.Connection中的方法.之于它们是如果映射的,这是厂商编程的,"客户"只要调用<BR>Class.forName("org.gjt.mm.mysql.Driver");方法就可以顺利地操作JDBC&nbsp;API了.<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;一个普通数据库的连结过程为:<BR>&nbsp;&nbsp;&nbsp;&nbsp;1.加载驱动程序.<BR>&nbsp;&nbsp;&nbsp;&nbsp;2.通过DriverManager到得一个与数据库连结的句柄.<BR>&nbsp;&nbsp;&nbsp;&nbsp;3.通过连结句柄绑定要执行的语句.<BR>&nbsp;&nbsp;&nbsp;&nbsp;4.接收执行结果.<BR>&nbsp;&nbsp;&nbsp;&nbsp;5.可选的对结果的处理.<BR>&nbsp;&nbsp;&nbsp;&nbsp;6.必要的关闭和数据库的连结. <BR><STRONG>JDBC基础(二)</STRONG> <BR><BR>因为是基础篇,所以还是对每一步骤简单说明一下吧:<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;前面说是,注册驱动程序有多方法,Class.forName();是一种显式地加载.当一个驱<BR>动程序类被Classloader装载后,在溶解的过程中,DriverManager会注册这个驱动类的实例.<BR>这个调用是自动发生的,也就是说DriverManager.registerDriver()方法被自动调用了,当然<BR>我们也可以直接调用DriverManager.registerDriver()来注册驱动程序,但是,以我的经验.<BR>MS的浏览中APPLET在调用这个方法时不能成功,也就是说MS在浏览器中内置的JVM对该方法的<BR>实现是无效的.<BR>&nbsp;&nbsp;&nbsp;&nbsp;另外我们还可以利用系统属性jdbc.drivers来加载多个驱动程序:<BR>System.setProperty("jdbc.drivers","driver1:driver2:.....:drivern");多个驱动程序之<BR>间用":"隔开,这样在连结时JDBC会按顺序搜索,直到找到第一个能成功连结指定的URL的驱动<BR>程序.<BR>&nbsp;&nbsp;&nbsp;&nbsp;在基础篇里我们先不介绍DataSource这些高级特性.<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;在成功注册驱动程序后,我们就可以用DriverManager的静态方法getConnection来得<BR>到和数据库连结的引用:<BR>&nbsp;&nbsp;&nbsp;&nbsp;Connection&nbsp;conn&nbsp;=&nbsp;DriverManager.getConnection(url);<BR>&nbsp;&nbsp;&nbsp;&nbsp;如果连结是成功的,则返回Connection对象conn,如果为null或抛出异常,则说明没有<BR>和数据库建立连结.<BR>&nbsp;&nbsp;&nbsp;&nbsp;对于getConnection()方法有三个重载的方法,一种是最简单的只给出数据源即:<BR>getConnection(url),另一种是同时给出一些数据源信息即getConnection(url,Properties),<BR>另外一种就是给出数据源,用户名和密码:getConnection(url,user,passwod),对于数据源信息.<BR>如果我们想在连结时给出更多的信息可以把这些信息压入到一个Properties,当然可以直接压<BR>入用户名密码,别外还可以压入指定字符集,编码方式或默认操作等一些其它信息.<BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;在得到一个连结后,也就是有了和数据库找交道的通道.我们就可以做我们想要的操<BR>作了.<BR>&nbsp;&nbsp;&nbsp;&nbsp;还是先来介绍一些一般性的操作:<BR>&nbsp;&nbsp;&nbsp;&nbsp;如果我们要对数据库中的表进行操作,要先缘故绑定一个语句:<BR>&nbsp;&nbsp;&nbsp;&nbsp;Statement&nbsp;stmt&nbsp;=&nbsp;conn.createStatement();<BR>&nbsp;&nbsp;&nbsp;&nbsp;然后利用这个语句来执行操作.根本操作目的,可以有两种结果返回,如果执行的查询<BR>操作,返回为结果集ResultSet,如果执行更新操作,则返回操作的记录数int.<BR>&nbsp;&nbsp;&nbsp;&nbsp;注意,SQL操作严格区分只有两个,一种就是读操作(查询操作),另一种就是写操作(更<BR>新操作),所以,create,insert,update,drop,delete等对数据有改写行为的操作都是更新操作.<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;ResultSet&nbsp;rs&nbsp;=&nbsp;stmt.executeQuery("select&nbsp;*&nbsp;from&nbsp;table&nbsp;where&nbsp;xxxxx");<BR>&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;x&nbsp;=&nbsp;stmt.executeUpdate("delete&nbsp;from&nbsp;table&nbsp;where&nbsp;......");<BR>&nbsp;&nbsp;&nbsp;&nbsp;如果你硬要用executeQuery执行一个更新操作是可以的,但不要把它赋给一个句柄,<BR>当然稍微有些经验的程序员是不会这么做的.<BR>&nbsp;&nbsp;&nbsp;&nbsp;至于对结果集的处理,我们放在下一节讨论,因为它是可操作的可选项,只有查询操作<BR>才返回结果集,对于一次操作过程的完成,一个非常必要的步骤是关闭数据库连结,在你没有了<BR>解更多的JDBC知识这前,你先把这一步骤作为JDBC操作中最最重要的一步,在以后的介绍中我会<BR>不断地提醒你去关闭数据库连结!!!!!!!!!!!<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;按上面介绍的步骤,一个完成的例子是这样的:(注意,为了按上面的步骤介绍,这个例<BR>子不是最好的)<BR>&nbsp;&nbsp;&nbsp;&nbsp;try{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Class.forName("org.gjt.mm.mysql.Driver");<BR>&nbsp;&nbsp;&nbsp;&nbsp;}catch(Exception&nbsp;e){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("没有成功加载驱动程序:"+e.toString());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;<BR>&nbsp;&nbsp;&nbsp;&nbsp;}//对于象我这样的经验,可以直接从e.toString()的简单的几个字判断出异常原因,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//如果你是一个新手应该选捕获它的子类,如何知道要捕获哪几个异常呢?一个简单<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//的方法就是先不加try{},直接Class.forName("org.gjt.mm.mysql.Driver");,编<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//译器就会告诉你要你捕获哪几个异常了,当然这是偷机取巧的方法,最好还是自己<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//去看JDK文档,它会告诉你每个方法有哪些异常要你捕获.<BR>&nbsp;&nbsp;&nbsp;&nbsp;Connection&nbsp;conn&nbsp;=&nbsp;null;<BR>&nbsp;&nbsp;&nbsp;&nbsp;try{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;conn&nbsp;=&nbsp;DriverManager.getConnection(<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"jdbc:mysql://host:3306/mysql",<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"user",<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"passwd");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Statement&nbsp;stmt&nbsp;=&nbsp;conn.createStatement();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ResultSet&nbsp;rs&nbsp;=&nbsp;stmt.executeQuery("select&nbsp;*&nbsp;from&nbsp;table");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//rs&nbsp;处理<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[rs.close();]<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[stmt.close();]<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;catch(Exception&nbsp;e){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("数据库操作出现异常:"+e.toString());<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;finally{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try{conn.close();}catch(Exception){}<BR>&nbsp;&nbsp;&nbsp;&nbsp;}//不管你以前是学习到的关于数据库流程是如何操作的,如果你相信我,从现在开始,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//请你一定要把数据库关闭的代码写到finally块中,切切! <BR><STRONG>JDBC基础(三)</STRONG> <BR><BR>关于Statement对象:<BR>&nbsp;&nbsp;&nbsp;&nbsp;前面说过,Statement对象是用来绑定要执行的操作的,在它上面有三种执行方法:<BR>即用来执行查询操作的executeQuery(),用来执行更新操作的executeUpdate()和用来执行<BR>动态的未知的操作的execute().<BR>&nbsp;&nbsp;&nbsp;&nbsp;JDBC在编译时并不对要执行的SQL语句检测,只是把它看着一个String,只有在驱动<BR>程序执行SQL语句时才知道正确与否.<BR>&nbsp;&nbsp;&nbsp;&nbsp;一个Statement对象同时只能有一个结果集在活动.这是宽容性的,就是说即使没有<BR>调用ResultSet的close()方法,只要打开第二个结果集就隐含着对上一个结果集的关闭.所以<BR>如果你想同时对多个结果集操作,就要创建多个Statement对象,如果不需要同时操作,那么可<BR>以在一个Statement对象上须序操作多个结果集.<BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;这里我不得不特别说明一下,很多人会用一个Statement进行嵌套查询,然后就来问<BR>我说为什么不能循环?道理上面已经说清楚了.我们来详细分析一下嵌套查询:<BR>&nbsp;&nbsp;&nbsp;&nbsp;Connection&nbsp;conn&nbsp;=&nbsp;null;<BR>&nbsp;&nbsp;&nbsp;&nbsp;Statement&nbsp;stmt&nbsp;=&nbsp;null;<BR>&nbsp;&nbsp;&nbsp;&nbsp;conn&nbsp;=&nbsp;.......;<BR>&nbsp;&nbsp;&nbsp;&nbsp;stmt&nbsp;=&nbsp;conm.createStatement(xxxxxx);<BR>&nbsp;&nbsp;&nbsp;&nbsp;ResultSet&nbsp;rs&nbsp;=&nbsp;stmt.executeQuery(sql1);<BR>&nbsp;&nbsp;&nbsp;&nbsp;while(rs.next()){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;str&nbsp;=&nbsp;rs.getString(xxxxx);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ResultSet&nbsp;rs1&nbsp;=&nbsp;stmt.executeQuery("select&nbsp;*&nbsp;from&nbsp;表&nbsp;where&nbsp;字段=str");<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>当stmt.executeQuery("select&nbsp;*&nbsp;from&nbsp;表&nbsp;where&nbsp;字段=str");赋给rs1时,这时隐含的操作<BR>是已经关闭了rs,你还能循环下去吗?<BR>所以如果要同时操作多个结果集一定要让它他绑定到不同的Statement对象上.好在一个connection<BR>对象可以创建任意多个Statement对象,而不需要你重新获取连结.<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;关于获取和设置Statement的选项:只要看看它的getXXX方法和setXXX方法就明白了,这儿<BR>作为基础知识只提一下以下几个:<BR>&nbsp;&nbsp;&nbsp;&nbsp;setQueryTimeout,设置一个SQL执行的超时限制.<BR>&nbsp;&nbsp;&nbsp;&nbsp;setMaxRows,设置结果集能容纳的行数.<BR>&nbsp;&nbsp;&nbsp;&nbsp;setEscapeProcessing,如果参数为true,则驱动程序在把SQL语句发给数据库前进行转义替<BR>换,否则让数据库自己处理,当然这些默认值都可以通过get方法查询.<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;Statement的两个子类:<BR>&nbsp;&nbsp;&nbsp;&nbsp;PreparedStatement:对于同一条语句的多次执行,Statement每次都要把SQL语句发送给数据<BR>库,这样做效率明显不高,而如果数据库支持预编译,PreparedStatement可以先把要执行的语句一次发<BR>给它,然后每次执行而不必发送相同的语句,效率当然提高,当然如果数据库不支持预编译,<BR>PreparedStatement会象Statement一样工作,只是效率不高而不需要用户工手干预.<BR>&nbsp;&nbsp;&nbsp;&nbsp;另外PreparedStatement还支持接收参数.在预编译后只要传输不同的参数就可以执行,大大<BR>提高了性能.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;PreparedStatement&nbsp;ps&nbsp;=&nbsp;conn.prepareStatement("select&nbsp;*&nbsp;from&nbsp;表&nbsp;where&nbsp;字段=?");<BR>&nbsp;&nbsp;&nbsp;&nbsp;ps.setString(1,参数);<BR>&nbsp;&nbsp;&nbsp;&nbsp;ResultSet&nbsp;rs&nbsp;=&nbsp;ps.executeQuery();<BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;CallableStatement:是PreparedStatement的子类,它只是用来执行存储过程的.<BR>&nbsp;&nbsp;&nbsp;&nbsp;CallableStatement&nbsp;sc&nbsp;=&nbsp;conn.prepareCall("{call&nbsp;query()}");<BR>&nbsp;&nbsp;&nbsp;&nbsp;ResultSet&nbsp;rs&nbsp;=&nbsp;cs.executeQuery();<BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;关于更高级的知识我们在JDBC高级应用中介绍. <BR><STRONG>JDBC基础(四)</STRONG> <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;作为基础知识的最后部分,我们来说一说结果集的处理,当然是说对一般结果集的处理.<BR>至于存储过程返回的多结果集,我们仍然放在高级应用中介绍.<BR>&nbsp;&nbsp;&nbsp;&nbsp;SQL语句如何执行的是查询操作,那就要返回一个ResultSet对象,要想把查询结果最后<BR>明白地显示给用户,必须对ResultSet进行处理.ResultSet返回的是一个表中符合条件的记录,对<BR>ResultSet的处理要逐行处理,而对于每一行的列的处理,则可以按任意顺序(注意,这只是JDBC规<BR>范的要求,有些JDBC实现时对于列的处理仍然要求用户按顺序处理,但这是极少数的).事实上,虽<BR>然你可以在处理列的时候可以按任意顺序,但如果你按从左到右的顺序则可以得到较高的性能.<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;这儿从底层来讲解一下ResultSet对象,在任何介绍JDBC的书上你是不会获得这样的知<BR>识的,因为那是数据库厂商的事.ResultSet对象实际维护的是一个二维指针,第一维是指向当前<BR>行,最初它指向的是结果集的第一行之前,所以如果要访问第一行,就要先next(),以后每一行都<BR>要先next()才能访问,然后第二维的指针指向列,只要当你去rs.getXXX(列)时,才通过<BR>Connection再去数据库把真实的数据取出来,否则没有什么机器能真的把要取的数据都放在内<BR>存中.<BR>&nbsp;&nbsp;&nbsp;&nbsp;所以,千万要记住,如果Connection已经关闭,那是不可能再从ResultSet中取到数据的.<BR>有很多人问我,我可不可以取到一个ResultSet把它写到Session中然后关闭Connection,这样就<BR>不要每次都连结了.我只能告诉你,你的想法非常好,但,是错误的!当然在javax.sql包中JDBC高<BR>级应用中有CacheRow和WebCacheRow可以把结果集缓存下来,但那和我们自己开一个数据结构把<BR>ResultSet的行集中所有值一次取出来保存起来没有什么两样.<BR>&nbsp;&nbsp;&nbsp;&nbsp;访问行中的列,可以按字段名或索引来访问.下面是一个简单的检索结果的程序:<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;ResultSet&nbsp;rs&nbsp;=&nbsp;stmt.executeQuery("select&nbsp;a1,a2,a3&nbsp;from&nbsp;table");<BR>&nbsp;&nbsp;&nbsp;&nbsp;while(rs.next()){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;i&nbsp;=&nbsp;rs.getInt(1);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;a&nbsp;=&nbsp;rs.getString("a2");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;..............<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;对于用来显示的结果集,用while来进行next()是最普通的,如果next()返回false,则<BR>说明已经没有可用的行了.但有时我们可能连一行都没有,而如果有记录又不知道是多少行,这时<BR>如果要对有记录和没有记录进行不同的处理,应该用以下流程进行判断:<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;if(rs.next()){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//因为已经先next()了,所经对记录应该用do{}while();来处理<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;do{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;i&nbsp;=&nbsp;rs.getInt(1);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;a&nbsp;=&nbsp;rs.getString("a2");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}while(rs.next());<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;esle{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("没有取得符合条件的记录!");<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;类型转换:<BR>&nbsp;&nbsp;&nbsp;&nbsp;ResultSet的getXXX方法将努力把结果集中的SQL数据类型转换为JAVA的数据类型,事实<BR>大多数类型是可以转换的,但仍然有不少糊弄是不能转换的,如你不能将一个SQL的float转换成<BR>JAVA的DATE,你无法将&nbsp;VARCHAR&nbsp;"我们"转换成JAVA的Int.<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;较大的值:<BR>&nbsp;&nbsp;&nbsp;&nbsp;对于大于Statement中getMaxFieldSize返回值的值,用普通的getBytes()或getString()<BR>是不能读取的,好在JAVA提供了读取输入浪的方法,对于大对象,我们可以通过rs.getXXXStream()<BR>来得到一个InputStream,XXX的类型包括Ascii,Binay,Unicode.根据你存储的字段类型来使用不<BR>同的流类型,一般来说,二进制文件用getBinayStream(),文本文件用getAsciiStyream(),对于<BR>Unicode字符的文本文件用getUnicodeStream(),相对应的数据库字段类型应该为:Blob,Clob和<BR>Nlob.<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;获取结果集的信息:<BR>&nbsp;&nbsp;&nbsp;&nbsp;大多数情况下编程人员对数据库结构是了解的,可以知道结果集中各列的情况,但有时并<BR>不知道结果集中有哪些列,是什么类型.这时可以通过getMetaData()来获取结果集的情况:<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;ResulSetMetaData&nbsp;rsmd&nbsp;=&nbsp;rs.getMetaData();<BR>&nbsp;&nbsp;&nbsp;&nbsp;rsmd.getColumnCount()返回列的个数.<BR>&nbsp;&nbsp;&nbsp;&nbsp;getColumnLabel(int)返回该int所对应的列的显示标题<BR>&nbsp;&nbsp;&nbsp;&nbsp;getColumnName(int)返回该int所对应的列的在数据库中的名称.<BR>&nbsp;&nbsp;&nbsp;&nbsp;getColumnType(int)返回该int所对应的列的在数据库中的数据类型.<BR>&nbsp;&nbsp;&nbsp;&nbsp;getColumnTypeName(int)返回该int所对应的列的数据类型在数据源中的名称.<BR>&nbsp;&nbsp;&nbsp;&nbsp;isReadOnly(int)返回该int所对应的列是否只读.<BR>&nbsp;&nbsp;&nbsp;&nbsp;isNullable(int)返回该int所对应的列是否可以为空<BR><img src ="http://www.blogjava.net/juhongtao/aggbug/27218.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/juhongtao/" target="_blank">javaGrowing</a> 2006-01-09 10:04 <a href="http://www.blogjava.net/juhongtao/archive/2006/01/09/27218.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>