konhon

忘掉過去,展望未來。找回自我,超越自我。
逃避不一定躲的过, 面对不一定最难过, 孤单不一定不快乐, 得到不一定能长久, 失去不一定不再拥有, 可能因为某个理由而伤心难过, 但我却能找个理由让自己快乐.

Google

BlogJava 首页 新随笔 联系 聚合 管理
  203 Posts :: 0 Stories :: 61 Comments :: 0 Trackbacks

#

     摘要: 以下问题是这一期间回答waterlily 关于java的一些整理,发现对大家都很有作用,现在将这些详细讲解的程序单独列出来供大家参考,学习,讨论 ------------------------------------------------------------ 1.使用jdbc连接数据库--通过jdbc连接数据库,将SQL语句运行结果打印到控制台 package Connect...  阅读全文
posted @ 2006-03-24 02:39 konhon 优华 阅读(644) | 评论 (0)编辑 收藏

为什么GenericServlet在init(ServletConfig config)基础上增加了一个init()方法? 
 
  init()方法被GenericServlet.init(ServletConfig config)方法调用。
  
  init()方法方便了开发人员定制Servlet的初始化,而无须去维护ServletConfig对象的存储工作。
  
  重写GenericServlet.init(ServletConfig config)必须要显示的调用super.init(config)方法。
  
  ServletContext.getContext(java.lang.String uripath)的作用是什么?
  
  返回同一Server中指定的path对应的ServletContext对象,通过该对象可以实现与Server中的其他Context打交道。
  
  uripath必须是以"/"开始(该路径的含义是相对于整个Servlet文档的根路径,而不是当前ServletContext的根路径)。
  
  Servlet生命周期是什么?
  
  一般的Servlet(GenericServlet,即与协议无关的Servlet)的生命周期:init() --> GenericServlet.service(ServletRequest req, ServletResponse res) --> destroy.
  
  HttpServlet的生命周期: init() --> GenericServlet.service(ServletRequest req, ServletResponse res)---> service(HttpServletRequest req, HttpServletResponse resp) --> doXXXX()-->destroy.
  
  有没有必要重写GenericServlet.service()方法?
  
  对于HttpServlet来说没有必要。只需要重写它的doXXXX()方法就可以了。HttpServlet中service()方法会自动的根据用户请求类型把请求转发给相应的doXXXX()方法(例如doGet()方法)。
  
  ServletRequest.getReader()和ServletRequest.getInputStream()如何使用?
  
  注意两个方法不能同时使用。
  
  ServletRequest.getRealPath(String path)方法已经不推荐使用。
  
  请使用ServletContext.getRealPath(String path)方法。
  
  ServletResponse缺省的字符集(charset)是什么?

  ServletResponse缺省的字符集(charset)是ISO-8859-1,可以通过setContentType(java.lang.String)方法改变新的字符集。
  
  例如:setContentType("text/html; charset=Shift_JIS").
  
  关于字符集信息,可以浏览RFC 2045
  
  HttpServletRequest.getRequestURI()和HttpServletRequest.getRequestURL()区别是什么? 
 
  request.getRequestURI() 返回值类似:/xuejava/requestdemo.jsp
  
  request.getRequestURL() 返回值类似:http://localhost:8080/xuejava/requestdemo.jsp
  
  HttpServletRequest.encodeURL()和HttpServletRequest.encodeRedirectURL(()区别是什么?为什么要有两个不同的方法呢?
  
  当用URL-rewriting方式来管理Session的时候,需要用到以上的两个方法。
  
  两个方法的不同点是:两个方法确定是否需要包含session ID的逻辑不同。
  
  在调用HttpServletResponse.sendRedirect前,应该先调用encodeRedirectURL()方法,否则可能会丢失Sesssion信息。 ...
  
  如何使你的Servlet或者JSP实现Single Thread Model?  

  对于Servlet实现javax.single.SingleThreadModel接口。
  
  对于JSP,在Page Directive中写如下的语句<%@ page isThreadSafe="false" %>
  
  JSP Tag 和 JSP XML-based Tag
  
  ...
  
  如何把某一个JSP Page定义成为Error Page?为什么要这样做?
  
  实现方法: <%@ page isErrorPage="true" %>
  
  为什么? 因为需要获取Exception 对象(缺省情况下,在JSP Page中是不能直接使用“隐含对象” exception的)。
  
  JSP Page的执行顺序是如何的?
  
  JSP Page的执行顺序如下:
  
  JSP Page Translation. JSP Page --> Servlet source code.
  JSP Page Compilation. Servlet source code --> Servlet class.
  Load Class(First time or the server restarted)
  
  Create instance(可能会很多次,如果JSP Page中声明了<%@ page isThreadSafe="false" %>)
  
  Call jspInit method(一般的JSP Page都没有重写这个方法,重写需要在声明语句段中)。
  
  Call _jspService method(类似与一般HttpServlet的doGet和doPost方法,但是可以同时用来处理Post和Getq请求)。
  
  Call jspDestroy method(Server在卸载Servet的时候,例如当Servlet很久没有使用的情况)。
  
  JSP Page中有哪些隐含对象(Implicity Object)?各自的类型和作用是什么?
  
  request --
  reponse --
  session --
  application --
  out --
  page --
  pagecontext --
  exception -- 只有在当前JSP Page为Error Page的时候才有效。
  config --
  
  <jsp:include page="/foo/foo.jsp" %> 和 <@ include file="/foo/foo.jsp" %>的区别是什么?
  
  <jsp:include ... -- request time.
  <@ include ... -- Page translation time.
  
  Servlets/JSP Container(Engine)有几种运行方式?
  
  Standalone
  Tomcat standalone mode
  In-process
  Tomcat running inside Apache Web Server.
  Out-of-process
  Apache + mod_jk + Tomcat
  
  Servlet,Servlet开发人员,Servlet API, Servlet Container的关系是什么?
  
  Servlet,Servlet开发人员 --->Servlet API --> Servlet Container
  The parts of an HTTP message
  
  Message part Description
  The initial line: Specifies the purpose of the request or response message
  例子:GET /reports/sales/index.html HTTP/1.0
  The header section:Specifies the meta-information, such as size, type, and encoding,
  about the content of the message
  A blank line:
  An optional message body: The main content of the request or response message
  
  下面是一个Response的例子:
  
  HTTP/1.0 200 OK
  Date: Tue, 01 Dec 2001 23:59:59 GMT
  Content-Type: text/html
  Content-Length: 52
  
  <html>
  <body>
  <h1>Hello, John!</h1>
  </body>
  </html>
  
  HTTP规范中定义了哪些方法?各自有什么用途?
  
  GET
  HEAD
  POST
  从 Http 1.1规范开始,增加了以下的方法:
  
  PUT
  OPTIONS
  TRACE
  DELETE
  CONNECT
  
  ServetRequest中为什么要定义:getContentType(),getContentLength()方法。
  
  根据HTTP协议规范,Request 和 Response一样也有这些必不可少的内容!
  
  所以需要首先了解 HTTP Message的概念和其内容的格式,这些东西对于Request和Reponse是一样的。
  
  对于GET方式发送的请求,其内容类型为:null
  
  对于POST方式发送的请求,其内容类型为:application/x-www-form-urlencoded
  
  POST方式发送请求的内容类似于:username=xuejava.
  
  RequestDispatcher.forward()和HttpServletResponse.sendRedirect()的区别是什么?
  
  RequestDispatcher.forward()是在服务器端运行;HttpServletResponse.sendRedirect()是通过向客户浏览器发送命令来完成。
  
  所以RequestDispatcher.forward()对于浏览器来说是“透明的”;而HttpServletResponse.sendRedirect()则不是。
  
  另外,还要注意RequestDispatcher.forward()在调用的时候Response不能已经Commit了(Response.isCommitted())。
  
  ServletContext.getRequestDispatcher(String url)和ServletRequest.getRequestDispatcher(String url)的区别是什么?为什么?
  
  ServletContext.getRequestDispatcher(String url)中的url只能使用绝对路径;而ServletRequest.getRequestDispatcher(String url)中的url可以使用相对路径。
  
  因为ServletRequest具有相对路径的概念;而ServletContext对象无次概念。
  
  如何把请求转移到另外一个Web App中的某个地址?
  
  ServletContext.getRequestDispatcher(String url)和ServletRequest.getRequestDispatcher(String url)只能把请求转移到同一个Web App中的地址。
  
  如果需要把请求转移到另外一个Web App中的某个地址,可以按下面的做法:
  
  1. 获得另外一个Web App的ServletConext对象(currentServletContext.getContext(uripath)).
  
  2. 调用ServletContext.getRequestDispatcher(String url)方法。
posted @ 2006-03-23 21:53 konhon 优华 阅读(440) | 评论 (0)编辑 收藏

一、术语session
在我的经验里,session这个词被滥用的程度大概仅次于transaction,更加有趣的是transaction与session在某些语境下的含义是相同的。

session,中文经常翻译为会话,其本来的含义是指有始有终的一系列动作/消息,比如打电话时从拿起电话拨号到挂断电话这中间的一系列过程可以称之为一个 session。有时候我们可以看到这样的话“在一个浏览器会话期间,...”,这里的会话一词用的就是其本义,是指从一个浏览器窗口打开到关闭这个期间 ①。最混乱的是“用户(客户端)在一次会话期间”这样一句话,它可能指用户的一系列动作(一般情况下是同某个具体目的相关的一系列动作,比如从登录到选购商品到结账登出这样一个网上购物的过程,有时候也被称为一个transaction),然而有时候也可能仅仅是指一次连接,也有可能是指含义①,其中的差别只能靠上下文来推断②。

然而当session一词与网络协议相关联时,它又往往隐含了“面向连接”和/或“保持状态”这样两个含义, “面向连接”指的是在通信双方在通信之前要先建立一个通信的渠道,比如打电话,直到对方接了电话通信才能开始,与此相对的是写信,在你把信发出去的时候你并不能确认对方的地址是否正确,通信渠道不一定能建立,但对发信人来说,通信已经开始了。“保持状态”则是指通信的一方能够把一系列的消息关联起来,使得消息之间可以互相依赖,比如一个服务员能够认出再次光临的老顾客并且记得上次这个顾客还欠店里一块钱。这一类的例子有“一个TCP session”或者 “一个POP3 session”③。

而到了web服务器蓬勃发展的时代,session在web开发语境下的语义又有了新的扩展,它的含义是指一类用来在客户端与服务器之间保持状态的解决方案④。有时候session也用来指这种解决方案的存储结构,如“把xxx保存在session 里”⑤。由于各种用于web开发的语言在一定程度上都提供了对这种解决方案的支持,所以在某种特定语言的语境下,session也被用来指代该语言的解决方案,比如经常把Java里提供的javax.servlet.http.HttpSession简称为session⑥。

鉴于这种混乱已不可改变,本文中session一词的运用也会根据上下文有不同的含义,请大家注意分辨。
在本文中,使用中文“浏览器会话期间”来表达含义①,使用“session机制”来表达含义④,使用“session”表达含义⑤,使用具体的“HttpSession”来表达含义⑥

二、HTTP协议与状态保持
HTTP 协议本身是无状态的,这与HTTP协议本来的目的是相符的,客户端只需要简单的向服务器请求下载某些文件,无论是客户端还是服务器都没有必要纪录彼此过去的行为,每一次请求之间都是独立的,好比一个顾客和一个自动售货机或者一个普通的(非会员制)大卖场之间的关系一样。

然而聪明(或者贪心?)的人们很快发现如果能够提供一些按需生成的动态信息会使web变得更加有用,就像给有线电视加上点播功能一样。这种需求一方面迫使HTML逐步添加了表单、脚本、DOM等客户端行为,另一方面在服务器端则出现了CGI规范以响应客户端的动态请求,作为传输载体的HTTP协议也添加了文件上载、 cookie这些特性。其中cookie的作用就是为了解决HTTP协议无状态的缺陷所作出的努力。至于后来出现的session机制则是又一种在客户端与服务器之间保持状态的解决方案。

让我们用几个例子来描述一下cookie和session机制之间的区别与联系。笔者曾经常去的一家咖啡店有喝5杯咖啡免费赠一杯咖啡的优惠,然而一次性消费5杯咖啡的机会微乎其微,这时就需要某种方式来纪录某位顾客的消费数量。想象一下其实也无外乎下面的几种方案:
1、该店的店员很厉害,能记住每位顾客的消费数量,只要顾客一走进咖啡店,店员就知道该怎么对待了。这种做法就是协议本身支持状态。
2、发给顾客一张卡片,上面记录着消费的数量,一般还有个有效期限。每次消费时,如果顾客出示这张卡片,则此次消费就会与以前或以后的消费相联系起来。这种做法就是在客户端保持状态。
3、发给顾客一张会员卡,除了卡号之外什么信息也不纪录,每次消费时,如果顾客出示该卡片,则店员在店里的纪录本上找到这个卡号对应的纪录添加一些消费信息。这种做法就是在服务器端保持状态。

由于HTTP协议是无状态的,而出于种种考虑也不希望使之成为有状态的,因此,后面两种方案就成为现实的选择。具体来说cookie机制采用的是在客户端保持状态的方案,而session机制采用的是在服务器端保持状态的方案。同时我们也看到,由于采用服务器端保持状态的方案在客户端也需要保存一个标识,所以session机制可能需要借助于cookie机制来达到保存标识的目的,但实际上它还有其他选择。

三、理解cookie机制
cookie机制的基本原理就如上面的例子一样简单,但是还有几个问题需要解决:“会员卡”如何分发;“会员卡”的内容;以及客户如何使用“会员卡”。

正统的cookie分发是通过扩展HTTP协议来实现的,服务器通过在HTTP的响应头中加上一行特殊的指示以提示浏览器按照指示生成相应的cookie。然而纯粹的客户端脚本如JavaScript或者VBScript也可以生成cookie。

而cookie 的使用是由浏览器按照一定的原则在后台自动发送给服务器的。浏览器检查所有存储的cookie,如果某个cookie所声明的作用范围大于等于将要请求的资源所在的位置,则把该cookie附在请求资源的HTTP请求头上发送给服务器。意思是麦当劳的会员卡只能在麦当劳的店里出示,如果某家分店还发行了自己的会员卡,那么进这家店的时候除了要出示麦当劳的会员卡,还要出示这家店的会员卡。

cookie的内容主要包括:名字,值,过期时间,路径和域。
其中域可以指定某一个域比如.google.com,相当于总店招牌,比如宝洁公司,也可以指定一个域下的具体某台机器比如www.google.com或者froogle.google.com,可以用飘柔来做比。
路径就是跟在域名后面的URL路径,比如/或者/foo等等,可以用某飘柔专柜做比。
路径与域合在一起就构成了cookie的作用范围。
如果不设置过期时间,则表示这个cookie的生命期为浏览器会话期间,只要关闭浏览器窗口,cookie就消失了。这种生命期为浏览器会话期的 cookie被称为会话cookie。会话cookie一般不存储在硬盘上而是保存在内存里,当然这种行为并不是规范规定的。如果设置了过期时间,浏览器就会把cookie保存到硬盘上,关闭后再次打开浏览器,这些cookie仍然有效直到超过设定的过期时间。

存储在硬盘上的cookie 可以在不同的浏览器进程间共享,比如两个IE窗口。而对于保存在内存里的cookie,不同的浏览器有不同的处理方式。对于IE,在一个打开的窗口上按 Ctrl-N(或者从文件菜单)打开的窗口可以与原窗口共享,而使用其他方式新开的IE进程则不能共享已经打开的窗口的内存cookie;对于 Mozilla Firefox0.8,所有的进程和标签页都可以共享同样的cookie。一般来说是用javascript的window.open打开的窗口会与原窗口共享内存cookie。浏览器对于会话cookie的这种只认cookie不认人的处理方式经常给采用session机制的web应用程序开发者造成很大的困扰。

下面就是一个goolge设置cookie的响应头的例子
HTTP/1.1 302 Found
Location: http://www.google.com/intl/zh-CN/
Set-Cookie: PREF=ID=0565f77e132de138:NW=1:TM=1098082649:LM=1098082649:S=KaeaCFPo49RiA_d8; expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; domain=.google.com
Content-Type: text/html



这是使用HTTPLook这个HTTP Sniffer软件来俘获的HTTP通讯纪录的一部分


浏览器在再次访问goolge的资源时自动向外发送cookie


用Firefox可以很容易的观察现有的cookie的值
使用HTTPLook配合Firefox可以很容易的理解cookie的工作原理。


IE也可以设置在接受cookie前询问

四、理解session机制

session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息。

当程序需要为某个客户端的请求创建一个session的时候,服务器首先检查这个客户端的请求里是否已包含了一个session标识 - 称为 session id,如果已包含一个session id则说明以前已经为此客户端创建过session,服务器就按照session id把这个 session检索出来使用(如果检索不到,可能会新建一个),如果客户端请求不包含session id,则为此客户端创建一个session并且生成一个与此session相关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个 session id将被在本次响应中返回给客户端保存。

保存这个session id的方式可以采用cookie,这样在交互过程中浏览器可以自动的按照规则把这个标识发挥给服务器。一般这个cookie的名字都是类似于SEEESIONID,而。比如weblogic对于web应用程序生成的cookie,JSESSIONID= ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764,它的名字就是 JSESSIONID。

由于cookie可以被人为的禁止,必须有其他机制以便在cookie被禁止时仍然能够把session id传递回服务器。经常被使用的一种技术叫做URL重写,就是把session id直接附加在URL路径的后面,附加方式也有两种,一种是作为URL路径的附加信息,表现形式为http://...../xxx;jsessionid= ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
另一种是作为查询字符串附加在URL后面,表现形式为http://...../xxx?jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
这两种方式对于用户来说是没有区别的,只是服务器在解析的时候处理的方式不同,采用第一种方式也有利于把session id的信息和正常程序参数区分开来。
为了在整个交互过程中始终保持状态,就必须在每个客户端可能请求的路径后面都包含这个session id。

另一种技术叫做表单隐藏字段。就是服务器会自动修改表单,添加一个隐藏字段,以便在表单提交时能够把session id传递回服务器。比如下面的表单
<form name="testform" action="/xxx">
<input type="text">
</form>


在被传递给客户端之前将被改写成
<form name="testform" action="/xxx">
<input type="hidden" name="jsessionid" value="ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764">
<input type="text">
</form>


这种技术现在已较少应用,笔者接触过的很古老的iPlanet6(SunONE应用服务器的前身)就使用了这种技术。
实际上这种技术可以简单的用对action应用URL重写来代替。

在谈论session机制的时候,常常听到这样一种误解“只要关闭浏览器,session就消失了”。其实可以想象一下会员卡的例子,除非顾客主动对店家提出销卡,否则店家绝对不会轻易删除顾客的资料。对session来说也是一样的,除非程序通知服务器删除一个session,否则服务器会一直保留,程序一般都是在用户做log off的时候发个指令去删除session。然而浏览器从来不会主动在关闭之前通知服务器它将要关闭,因此服务器根本不会有机会知道浏览器已经关闭,之所以会有这种错觉,是大部分session机制都使用会话cookie来保存session id,而关闭浏览器后这个 session id就消失了,再次连接服务器时也就无法找到原来的session。如果服务器设置的cookie被保存到硬盘上,或者使用某种手段改写浏览器发出的HTTP请求头,把原来的session id发送给服务器,则再次打开浏览器仍然能够找到原来的session。

恰恰是由于关闭浏览器不会导致session被删除,迫使服务器为seesion设置了一个失效时间,当距离客户端上一次使用session的时间超过这个失效时间时,服务器就可以认为客户端已经停止了活动,才会把session删除以节省存储空间。

五、理解javax.servlet.http.HttpSession
HttpSession是Java平台对session机制的实现规范,因为它仅仅是个接口,具体到每个web应用服务器的提供商,除了对规范支持之外,仍然会有一些规范里没有规定的细微差异。这里我们以BEA的Weblogic Server8.1作为例子来演示。

首先,Weblogic Server提供了一系列的参数来控制它的HttpSession的实现,包括使用cookie的开关选项,使用URL重写的开关选项,session持久化的设置,session失效时间的设置,以及针对cookie的各种设置,比如设置cookie的名字、路径、域, cookie的生存时间等。

一般情况下,session都是存储在内存里,当服务器进程被停止或者重启的时候,内存里的session也会被清空,如果设置了session的持久化特性,服务器就会把session保存到硬盘上,当服务器进程重新启动或这些信息将能够被再次使用, Weblogic Server支持的持久性方式包括文件、数据库、客户端cookie保存和复制。

复制严格说来不算持久化保存,因为session实际上还是保存在内存里,不过同样的信息被复制到各个cluster内的服务器进程中,这样即使某个服务器进程停止工作也仍然可以从其他进程中取得session。

cookie生存时间的设置则会影响浏览器生成的cookie是否是一个会话cookie。默认是使用会话cookie。有兴趣的可以用它来试验我们在第四节里提到的那个误解。

cookie的路径对于web应用程序来说是一个非常重要的选项,Weblogic Server对这个选项的默认处理方式使得它与其他服务器有明显的区别。后面我们会专题讨论。

关于session的设置参考[5] http://e-docs.bea.com/wls/docs70/webapp/weblogic_xml.html#1036869

六、HttpSession常见问题
(在本小节中session的含义为⑤和⑥的混合)

1、session在何时被创建
一个常见的误解是以为session在有客户端访问时就被创建,然而事实是直到某server端程序调用 HttpServletRequest.getSession(true)这样的语句时才被创建,注意如果JSP没有显示的使用 <% @page session="false"%> 关闭session,则JSP文件在编译成Servlet时将会自动加上这样一条语句 HttpSession session = HttpServletRequest.getSession(true);这也是JSP中隐含的 session对象的来历。

由于session会消耗内存资源,因此,如果不打算使用session,应该在所有的JSP中关闭它。

2、session何时被删除
综合前面的讨论,session在下列情况下被删除a.程序调用HttpSession.invalidate();或b.距离上一次收到客户端发送的session id时间间隔超过了session的超时设置;或c.服务器进程被停止(非持久session)

3、如何做到在浏览器关闭时删除session
严格的讲,做不到这一点。可以做一点努力的办法是在所有的客户端页面里使用javascript代码window.oncolose来监视浏览器的关闭动作,然后向服务器发送一个请求来删除session。但是对于浏览器崩溃或者强行杀死进程这些非常规手段仍然无能为力。

4、有个HttpSessionListener是怎么回事
你可以创建这样的listener去监控session的创建和销毁事件,使得在发生这样的事件时你可以做一些相应的工作。注意是session的创建和销毁动作触发listener,而不是相反。类似的与HttpSession有关的listener还有 HttpSessionBindingListener,HttpSessionActivationListener和 HttpSessionAttributeListener。

5、存放在session中的对象必须是可序列化的吗
不是必需的。要求对象可序列化只是为了session能够在集群中被复制或者能够持久保存或者在必要时server能够暂时把session交换出内存。在 Weblogic Server的session中放置一个不可序列化的对象在控制台上会收到一个警告。我所用过的某个iPlanet版本如果 session中有不可序列化的对象,在session销毁时会有一个Exception,很奇怪。

6、如何才能正确的应付客户端禁止cookie的可能性
对所有的URL使用URL重写,包括超链接,form的action,和重定向的URL,具体做法参见[6]
http://e-docs.bea.com/wls/docs70/webapp/sessions.html#100770

7、开两个浏览器窗口访问应用程序会使用同一个session还是不同的session
参见第三小节对cookie的讨论,对session来说是只认id不认人,因此不同的浏览器,不同的窗口打开方式以及不同的cookie存储方式都会对这个问题的答案有影响。

8、如何防止用户打开两个浏览器窗口操作导致的session混乱
这个问题与防止表单多次提交是类似的,可以通过设置客户端的令牌来解决。就是在服务器每次生成一个不同的id返回给客户端,同时保存在session里,客户端提交表单时必须把这个id也返回服务器,程序首先比较返回的id与保存在session里的值是否一致,如果不一致则说明本次操作已经被提交过了。可以参看《J2EE核心模式》关于表示层模式的部分。需要注意的是对于使用javascript window.open打开的窗口,一般不设置这个id,或者使用单独的id,以防主窗口无法操作,建议不要再window.open打开的窗口里做修改操作,这样就可以不用设置。

9、为什么在Weblogic Server中改变session的值后要重新调用一次session.setValue
做这个动作主要是为了在集群环境中提示Weblogic Server session中的值发生了改变,需要向其他服务器进程复制新的session值。

10、为什么session不见了
排除session正常失效的因素之外,服务器本身的可能性应该是微乎其微的,虽然笔者在iPlanet6SP1加若干补丁的Solaris版本上倒也遇到过;浏览器插件的可能性次之,笔者也遇到过3721插件造成的问题;理论上防火墙或者代理服务器在cookie处理上也有可能会出现问题。
出现这一问题的大部分原因都是程序的错误,最常见的就是在一个应用程序中去访问另外一个应用程序。我们在下一节讨论这个问题。

七、跨应用程序的session共享

常常有这样的情况,一个大项目被分割成若干小项目开发,为了能够互不干扰,要求每个小项目作为一个单独的web应用程序开发,可是到了最后突然发现某几个小项目之间需要共享一些信息,或者想使用session来实现SSO(single sign on),在session中保存login的用户信息,最自然的要求是应用程序间能够访问彼此的session。

然而按照Servlet规范,session的作用范围应该仅仅限于当前应用程序下,不同的应用程序之间是不能够互相访问对方的session的。各个应用服务器从实际效果上都遵守了这一规范,但是实现的细节却可能各有不同,因此解决跨应用程序session共享的方法也各不相同。

首先来看一下Tomcat是如何实现web应用程序之间session的隔离的,从 Tomcat设置的cookie路径来看,它对不同的应用程序设置的cookie路径是不同的,这样不同的应用程序所用的session id是不同的,因此即使在同一个浏览器窗口里访问不同的应用程序,发送给服务器的session id也可以是不同的。


image

根据这个特性,我们可以推测Tomcat中session的内存结构大致如下。
image

笔者以前用过的iPlanet也采用的是同样的方式,估计SunONE与iPlanet之间不会有太大的差别。对于这种方式的服务器,解决的思路很简单,实际实行起来也不难。要么让所有的应用程序共享一个session id,要么让应用程序能够获得其他应用程序的session id。

iPlanet中有一种很简单的方法来实现共享一个session id,那就是把各个应用程序的cookie路径都设为/(实际上应该是/NASApp,对于应用程序来讲它的作用相当于根)。
<session-info>
<path>/NASApp</path>
</session-info>


需要注意的是,操作共享的session应该遵循一些编程约定,比如在session attribute名字的前面加上应用程序的前缀,使得 setAttribute("name", "neo")变成setAttribute("app1.name", "neo"),以防止命名空间冲突,导致互相覆盖。

在Tomcat中则没有这么方便的选择。在Tomcat版本3上,我们还可以有一些手段来共享session。对于版本4以上的Tomcat,目前笔者尚未发现简单的办法。只能借助于第三方的力量,比如使用文件、数据库、JMS或者客户端cookie,URL参数或者隐藏字段等手段。

我们再看一下Weblogic Server是如何处理session的。
image
image

从截屏画面上可以看到Weblogic Server对所有的应用程序设置的cookie的路径都是/,这是不是意味着在Weblogic Server中默认的就可以共享session了呢?然而一个小实验即可证明即使不同的应用程序使用的是同一个session,各个应用程序仍然只能访问自己所设置的那些属性。这说明Weblogic Server中的session的内存结构可能如下
image

对于这样一种结构,在 session机制本身上来解决session共享的问题应该是不可能的了。除了借助于第三方的力量,比如使用文件、数据库、JMS或者客户端 cookie,URL参数或者隐藏字段等手段,还有一种较为方便的做法,就是把一个应用程序的session放到ServletContext中,这样另外一个应用程序就可以从ServletContext中取得前一个应用程序的引用。示例代码如下,

应用程序A
context.setAttribute("appA", session); 


应用程序B
contextA = context.getContext("/appA");
HttpSession sessionA = (HttpSession)contextA.getAttribute("appA");


值得注意的是这种用法不可移植,因为根据ServletContext的JavaDoc,应用服务器可以处于安全的原因对于context.getContext("/appA");返回空值,以上做法在Weblogic Server 8.1中通过。

那么Weblogic Server为什么要把所有的应用程序的cookie路径都设为/呢?原来是为了SSO,凡是共享这个session的应用程序都可以共享认证的信息。一个简单的实验就可以证明这一点,修改首先登录的那个应用程序的描述符weblogic.xml,把cookie路径修改为/appA 访问另外一个应用程序会重新要求登录,即使是反过来,先访问cookie路径为/的应用程序,再访问修改过路径的这个,虽然不再提示登录,但是登录的用户信息也会丢失。注意做这个实验时认证方式应该使用FORM,因为浏览器和web服务器对basic认证方式有其他的处理方式,第二次请求的认证不是通过 session来实现的。具体请参看[7] secion 14.8 Authorization,你可以修改所附的示例程序来做这些试验。

八、总结
session机制本身并不复杂,然而其实现和配置上的灵活性却使得具体情况复杂多变。这也要求我们不能把仅仅某一次的经验或者某一个浏览器,服务器的经验当作普遍适用的经验,而是始终需要具体情况具体分析。
摘要:虽然session机制在web应用程序中被采用已经很长时间了,但是仍然有很多人不清楚session机制的本质,以至不能正确的应用这一技术。本文将详细讨论session的工作机制并且对在Java web application中应用session机制时常见的问题作出解答。
posted @ 2006-03-23 21:35 konhon 优华 阅读(408) | 评论 (0)编辑 收藏

编者注:本文和同系列的前面一文“基于Java的Web服务器工作原理”,都摘自“Tomcat 运行内幕”一书(一本有关 Tomcat 的教程)。在阅读本文前,最好先阅读前文,以巩固基础信息。在此,将介绍如何建立两个 servlet 容器。 随附本书的应用程序可以下载 ,如果您有兴趣,可以在近段时间内到 作者网站 下载

  本文介绍一个简单 servlet 容器的基本原理。现有两个 servlet 容器,第一个很简单,第二个则是根据第一个写出。为了使第一个容器尽量简单,所以没有做得很完整。复杂一些的 servlet 容器 (包括 TOMCAT 4 和 5) 在 TOMCAT 运行内幕的其他章节有介绍。

  两个 servlet 容器都处理简单的 servlet 及 staticResource 。您可以使用 webroot/ 目录下的 PrimitiveServlet 来测试它。复杂一些的 servlet会超出这些容器的容量,您可以从 TOMCAT  运行内幕 一书学习创建复杂的 servlet 容器。

  两个应用程序的类都封装在ex02.pyrmont 包下。在理解应用程序如何运作之前,您必须熟悉 javax.servlet.Servlet 接口。首先就来介绍这个接口。随后,就介绍 servlet 容器服务servlet 的具体内容。


  javax.servlet.Servlet 接口

  servlet 编程,需要引用以下两个类和接口:javax.servlet 和 javax.servlet.http,在这些类和接口中,javax.servlet.Servlet接口尤为重要。所有的 servlet 必须实现这个接口或继承已实现这个接口的类。
  Servlet 接口有五个方法,如下:

&#8226; public void init(ServletConfig config) throws ServletException
&#8226; public void service(ServletRequest request, ServletResponse response)  throws ServletException, java.io.IOException
&#8226; public void destroy()
&#8226; public ServletConfig getServletConfig()
&#8226; public java.lang.String getServletInfo()


  init、service和 destroy  方法是 Servlet 生命周期的方法。当 Servlet 类实例化后,容器加载 init,以通知 servlet 它已进入服务行列。init 方法必须被加载,Servelt 才能接收和请求。如果要载入数据库驱动程序、初始化一些值等等,程序员可以重写这个方法。在其他情况下,这个方法一般为空。

  service 方法由 Servlet 容器调用,以允许 Servlet 响应一个请求。Servlet 容器传递 javax.servlet.ServletRequest 对象和 javax.servlet.ServletResponse 对象。ServletRequest 对象包含客户端 HTTP 请求信息,ServletResponse 则封装servlet 响应。这两个对象,您可以写一些需要 servlet 怎样服务和客户怎样请求的代码。

  从 service 中删除 Servlet 实例之前,容器调用 destroy 方法。在 servlet 容器关闭或servlet 容器需要更多的内存时,就调用它。这个方法只有在servlet 的service 方法内的所有线程都退出的时候,或在超时的时候才会被调用。在 servlet 容器调用 destroy方法之后,它将不再调用 servlet的 service方法。destroy 方法给了 servlet 机会,来清除所有候住的资源(比如:内存,文件处理和线程),以确保在内存中所有的持续状态和 servlet的当前状态是同步的。Listing 2.1 包含了PrimitiveServlet 的代码,此servlet非常简单,您 可以用它来测试本文中的 servlet 容器应用程序。

  PrimitiveServlet 类实现了javax.servlet.Servlet 并提供了五个servlet方法的接口 。它做的事情也很简单:每次调用 init,service 或 destroy方法的时候,servlet就向控制口写入方法名。service 方法也从ServletResponsec对象中获得java.io.PrintWriter 对象,并发送字符串到浏览器。

Listing 2.1.PrimitiveServlet.java
import javax.servlet.*;
import java.io.IOException;
import java.io.PrintWriter;

public class PrimitiveServlet implements Servlet {
    public void init(ServletConfig config) throws ServletException {
        System.out.println("init");
    }

    public void service(ServletRequest request, ServletResponse  response) throws ServletException, IOException {
        System.out.println("from service");
        PrintWriter out = response.getWriter();
        out.println("Hello.Roses are red.");
        out.print("Violets are blue.");
    }

    public void destroy() {
        System.out.println("destroy");
    }

    public String getServletInfo() {
        return null;
    }

    public ServletConfig getServletConfig() {
        return null;
    }
}



  Application 1

  现在,我们从 servlet容器的角度来看看 servlet 编程。一个功能健全的 servlet容器对于每个 servlet 的 HTTP请求会完成以下事情:

&#8226; 当 servlet 第一次被调用的时候,加载了 servlet类并调用它的init方法(仅调用一次)
&#8226; 响应每次请求的时候 ,构建一个javax.servlet.ServletRequest 和 javax.servlet.ServletResponse实例。
&#8226; 激活 servlet 的 service 方法,传递 ServletRequest 和 ServletResponse 对象。
&#8226; 当 servlet 类关闭的时候,调用 servlet 的destroy 方法,并卸载 servlet 类。

  发生在 servlet 容器内部的事就复杂多了。只是这个简单的 servlet 容器的功能不很健全,所以,这它只能运行非常简单的servelt ,并不能调用 servlet 的 init 和destroy 方法。然而,它也执行了以下动作:

&#8226; 等待  HTTP 请求。
&#8226; 构建 ServletRequest 和  ServletResponse 对象  
&#8226; 如果请求的是一个staticResource,就会激活StaticResourceProcessor实例的 process方法,传递ServletRequest 和 ServletResponse 对象。
&#8226; 如果请求的是一个servlet ,载入该类,并激活它的service 方法,传递ServletRequest 和ServletResponse 对象。注意:在这个servlet 容器,每当 servlet被请求的时候该类就被载入。

  在第一个应用程序中,servlet容器由六个类组成 。

&#8226; HttpServer1
&#8226; Request
&#8226; Response
&#8226; StaticResourceProcessor
&#8226; ServletProcessor1
&#8226; Constants

  正如前文中的应用程序一样,这个程序的进入口(静态 main 方法)是HttpServer 类。这个方法创建了HttpServer实例,并调用它的await方法。这个方法等待 HTTP 请示,然后创建一个 request 对象和 response对象,根据请求是否是staticResource还是 servlet 来分派它们到 StaticResourceProcessor实例或ServletProcessor实例。

  Constants 类包含 static find WEB_ROOT,它是从其他类引用的。 WEB_ROOT 指明 PrimitiveServlet 位置 和容器服务的staticResource。
HttpServer1 实例等待 HTTP 请求,直到它收到一个 shutdown 命令。发布 shutdown命令和前文是一样的。

  这个应用程序中的每一个类将在下节介绍。
posted @ 2006-03-23 20:29 konhon 优华 阅读(509) | 评论 (0)编辑 收藏

全面解析JDBC(四)
作者:未知     文章来源:www.jspcn.net
访问次数: 次    加入时间:2005-01-19
如何利用JDBC发送SQL语句?

  Statement对象用于将SQL语句发送到数据库中。实际上有三种Statement对象,它们都作为在给定连接上执行SQL语句的包容器:Statement、PreparedStatement(它从Statement继承而来)和CallableStatement(它从PreparedStatement继承而来)。它们都专用于发送特定类型的SQL语句:Statement对象用于执行不带参数的简单SQL语句;PreparedStatement对象用于执行带或不带IN参数的预编译SQL语句;CallableStatement对象用于执行对数据库已存储过程的调用。

  Statement接口提供了执行语句和获取结果的基本方法;PreparedStatement接口添加了处理IN参数的方法;而CallableStatement添加了处理OUT参数的方法。

  1. 创建Statement对象

  建立了到特定数据库的连接之后,就可用该连接发送SQL语句。Statement对象用Connection的方法createStatement创建,如下列代码段中所示:

Connection con = DriverManager.getConnection(url,"sunny","");
Statement stmt = con.createStatement();


  为了执行Statement对象,被发送到数据库的SQL语句将被作为参数提供给Statement的方法:

  ResultSet rs = stmt.executeQuery("SELECT a,b,c FROM Table2");

  2. 使用Statement对象执行语句

  Statement接口提供了三种执行SQL语句的方法:executeQuery、executeUpdate和execute。使用哪一个方法由SQL语句所产生的内容决定。

  方法executeQuery用于产生单个结果集的语句,例如SELECT语句。方法executeUpdate用于执行INSERT、UPDATE或DELETE语句以及SQL DDL(数据定义语言)语句,例如CREATE TABLE和DROP TABLE。INSERT、UPDATE或DELETE语句的效果是修改表中零行或多行中的一列或多列。executeUpdate的返回值是一个整数,指示受影响的行数(即更新计数)。对于CREATE TABLE或DROP TABLE等不操作行的语句,executeUpdate的返回值总为零。

  执行语句的所有方法都将关闭所调用的Statement对象的当前打开结果集(如果存在)。这意味着在重新执行Statement对象之前,需要完成对当前ResultSet对象的处理。应注意,继承了Statement接口中所有方法的PreparedStatement接口都有自己的executeQuery、executeUpdate和execute方法。Statement对象本身不包含SQL语句,因而必须给Statement.execute方法提供SQL语句作为参数。PreparedStatement对象并不需要SQL语句作为参数提供给这些方法,因为它们已经包含预编译SQL语句。

  CallableStatement对象继承这些方法的PreparedStatement形式。对于这些方法的PreparedStatement或CallableStatement版本,使用查询参数将抛出SQLException。

  3. 语句完成

  当连接处于自动提交模式时,其中所执行的语句在完成时将自动提交或还原。语句在已执行且所有结果返回时,即认为已完成。对于返回一个结果集的executeQuery方法,在检索完ResultSet对象的所有行时该语句完成。对于方法executeUpdate,当它执行时语句即完成。但在少数调用方法execute的情况中,在检索所有结果集或它生成的更新计数之后语句才完成。

  有些DBMS将已存储过程中的每条语句视为独立的语句;而另外一些则将整个过程视为一个复合语句。在启用自动提交时,这种差别就变得非常重要,因为它影响什么时候调用commit方法。在前一种情况中,每条语句单独提交;在后一种情况中,所有语句同时提交。

  4. 关闭Statement对象

  Statement对象将由Java垃圾收集程序自动关闭。而作为一种好的编程风格,应在不需要Statement对象时显式地关闭它们。这将立即释放DBMS资源,有助于避免潜在的内存问题。

  5. 使用方法execute

  execute方法应该仅在语句能返回多个ResultSet对象、多个更新计数或ResultSet对象与更新计数的组合时使用。当执行某个已存储过程或动态执行未知SQL字符串(即应用程序程序员在编译时未知)时,有可能出现多个结果的情况,尽管这种情况很少见。例如,用户可能执行一个已存储过程,并且该已存储过程可执行更新,然后执行选择,再进行更新,再进行选择,等等。通常使用已存储过程的人应知道它所返回的内容。

  因为方法execute处理非常规情况,所以获取其结果需要一些特殊处理并不足为怪。例如,假定已知某个过程返回两个结果集,则在使用方法execute执行该过程后,必须调用方法getResultSet获得第一个结果集,然后调用适当的getXXX方法获取其中的值。要获得第二个结果集,需要先调用getMoreResults方法,然后再调用getResultSet方法。如果已知某个过程返回两个更新计数,则首先调用方法getUpdateCount,然后调用getMoreResults,并再次调用getUpdateCount。

  对于不知道返回内容,则情况更为复杂。如果结果是ResultSet对象,则方法execute返回true;如果结果是Javaint,则返回false。如果返回int,则意味着结果是更新计数或执行的语句是DL命令。在调用方法execute之后要做的第一件事情是调用getResultSet或getUpdateCount。调用方法getResultSet可以获得两个或多个ResultSet对象中第一个对象;或调用方法getUpdateCount可以获得两个或多个更新计数中第一个更新计数的内容。

  当SQL语句的结果不是结果集时,则方法getResultSet将返回null。这可能意味着结果是一个更新计数或没有其它结果。在这种情况下,判断null真正含义的唯一方法是调用方法getUpdateCount,它将返回一个整数。这个整数为调用语句所影响的行数;如果为-1则表示结果是结果集或没有结果。如果方法getResultSet已返回null(表示结果不是ResultSet对象),则返回值-1表示没有其它结果。也就是说,当下列条件为真时表示没有结果(或没有其它结果):

  ((stmt.getResultSet()==null)&&(stmt.getUpdateCount()==-1))

  如果已经调用方法getResultSet并处理了它返回的ResultSet对象,则有必要调用方法getMoreResults以确定是否有其它结果集或更新计数。如果getMoreResults返回true,则需要再次调用getResultSet来检索下一个结果集。如上所述,如果getResultSet返回null,则需要调用getUpdateCount来检查null是表示结果为更新计数还是表示没有其它结果。

  当getMoreResults返回false时,它表示该SQL语句返回一个更新计数或没有其它结果。因此需要调用方法getUpdateCount来检查它是哪一种情况。在这种情况下,当下列条件为真时表示没有其它结果:

  ((stmt.getMoreResults()==false)&&(stmt.getUpdateCount()==-1))

  下面的代码演示了一种方法用来确认已访问调用方法execute所产生的全部结果集和更新计数:

stmt.execute(queryStringWithUnknownResults);
while(true){
introwCount=stmt.getUpdateCount();
if(rowCount>0){//它是更新计数
System.out.println("Rows changed="+count);
stmt.getMoreResults();
continue;
}
if(rowCount==0){//DDL命令或0个更新
System.out.println("No rows changed or statement was DDL command");
stmt.getMoreResults();
continue;
}
//执行到这里,证明有一个结果集
//或没有其它结果
ResultSet rs=stmt.getResultSet();
if(rs!=null){
...//使用元数据获得关于结果集列的信息
while(rs.next()){
...//处理结果
stmt.getMoreResults();
continue;
}
break;//没有其它结果
 
posted @ 2006-03-22 20:46 konhon 优华 阅读(405) | 评论 (0)编辑 收藏

全面解析JDBC(三)
作者:未知     文章来源:www.jspcn.net
访问次数: 次    加入时间:2005-01-19
JDBC驱动管理内幕是怎么样的?

  DriverManager 类是 JDBC 的管理层,作用于用户和驱动程序之间。它跟踪可用的驱动程序,并在数据库和相应驱动程序之间建立连接。另外,DriverManager类也处理诸如驱动程序登录时间限制及登录和跟踪消息的显示等事务。

  对于简单的应用程序,一般程序员需要在此类中直接使用的唯一方法是DriverManager.getConnection。正如名称所示,该方法将建立与数据库的连接。JDBC允许用户调用DriverManager的方法getDriver、getDrivers和registerDriver及Driver的方法connect。但多数情况下,让DriverManager类管理建立连接的细节为上策。

  1. 跟踪可用驱动程序

  DriverManager类包含一列Driver类,它们已通过调用方法DriverManager.registerDriver对自己进行了注册。所有Driver类都必须包含有一个静态部分。它创建该类的实例,然后在加载该实例时DriverManager类进行注册。这样,用户正常情况下将不会直接调用DriverManager.registerDriver;而是在加载驱动程序时由驱动程序自动调用。加载Driver类,然后自动在DriverManager中注册的方式有两种:

  (1)调用方法Class.forName

  这将显式地加载驱动程序类。由于这与外部设置无关,因此推荐使用这种加载驱动程序的方法。以下代码加载类acme.db.Driver:Class.forName("acme.db.Driver")。

  如果将acme.db.Driver编写为加载时创建实例,并调用以该实例为参数的DriverManager.registerDriver(本该如此),则它在DriverManager的驱动程序列表中,并可用于创建连接。

  (2)将驱动程序添加到Java.lang.System的属性jdbc.drivers中

  这是一个由DriverManager类加载的驱动程序类名的列表,由冒号分隔:初始化DriverManager类时,它搜索系统属性jdbc.drivers,如果用户已输入了一个或多个驱动程序,则DriverManager类将试图加载它们。以下代码说明程序员如何在~/.hotJava/properties中输入三个驱动程序类(启动时,HotJava将把它加载到系统属性列表中):

  jdbc.drivers=foo.bah.Driver:wombat.sql.Driver:bad.test.ourDriver;

  对DriverManager方法的第一次调用将自动加载这些驱动程序类。注意:加载驱动程序的第二种方法需要持久的预设环境。如果对这一点不能保证,则调用方法Class.forName显式地加载每个驱动程序就显得更为安全。这也是引入特定驱动程序的方法,因为一旦DriverManager类被初始化,它将不再检查jdbc.drivers属性列表。

  在以上两种情况中,新加载的Driver类都要通过调用DriverManager.registerDriver类进行自我注册。如上所述,加载类时将自动执行这一过程。

  由于安全方面的原因,JDBC管理层将跟踪哪个类加载器提供哪个驱动程序。这样,当DriverManager类打开连接时,它仅使用本地文件系统或与发出连接请求的代码相同的类加载器提供的驱动程序。

  2. 建立连接

  加载Driver类并在DriverManager类中注册后,它们即可用来与数据库建立连接。当调用DriverManager.getConnection方法发出连接请求时,DriverManager将检查每个驱动程序,查看它是否可以建立连接。

  有时可能有多个JDBC驱动程序可以与给定的URL连接。例如,与给定远程数据库连接时,可以使用JDBC-ODBC桥驱动程序、JDBC到通用网络协议驱动程序或数据库厂商提供的驱动程序。在这种情况下测试驱动程序的顺序至关重要,因为DriverManager将使用它所找到的第一个可以成功连接到给定URL的驱动程序。

  首先DriverManager试图按注册的顺序使用每个驱动程序(jdbc.drivers中列出的驱动程序总是先注册)。它将跳过代码不可信任的驱动程序,除非加载它们的源与试图打开连接的代码的源相同。它通过轮流在每个驱动程序上调用方法Driver.connect,并向它们传递用户开始传递给方法DriverManager.getConnection的URL来对驱动程序进行测试,然后连接第一个认出该URL的驱动程序。这种方法初看起来效率不高,但由于不可能同时加载数十个驱动程序,因此每次连接实际只需几个过程调用和字符串比较。

  以下代码是通常情况下用驱动程序(例如JDBC-ODBC桥驱动程序)建立连接所需所有步骤的示例:



Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");//加载驱动程序
String url = "jdbc:odbc:fred";
DriverManager.getConnection(url,"userID","passwd");
posted @ 2006-03-22 20:45 konhon 优华 阅读(378) | 评论 (0)编辑 收藏

全面解析JDBC(二)
作者:未知     文章来源:www.jspcn.net
访问次数: 次    加入时间:2005-01-19
如何建立JDBC连接?

  Connection 对象代表与数据库的连接。连接过程包括所执行的 SQL 语句和在该连接上所返回的结果。一个应用程序可与单个数据库有一个或多个连接,或者可与许多数据库有连接。

  1. 打开连接

  与数据库建立连接的标准方法是调用DriverManager.getConnection方法。该方法接受含有某个URL的字符串。DriverManager类(即所谓的JDBC管理层)将尝试找到可与那个URL所代表的数据库进行连接的驱动程序。DriverManager类存有已注册的Driver类的清单。当调用方法getConnection时,它将检查清单中的每个驱动程序,直到找到可与URL中指定的数据库进行连接的驱动程序为止。Driver的方法connect使用这个URL来建立实际的连接。

  用户可绕过JDBC管理层直接调用Driver方法。这在以下特殊情况下将很有用:当两个驱动器可同时连接到数据库中,而用户需要明确地选用其中特定的驱动器。但一般情况下,让DriverManager类处理打开连接这种事将更为简单。

  下述代码显示如何打开一个与位于URL"jdbc:odbc:wombat"的数据库的连接。所用的用户标识符为"freely",口令为"ec":

  String url = "jdbc:odbc:wombat";
  Connection con = DriverManager.getConnection(url, "freely", "ec");

  2. 一般用法的URL

  由于URL常引起混淆,我们将先对一般URL作简单说明,然后再讨论JDBCURL。URL(统一资源定位符)提供在Internet上定位资源所需的信息。可将它想象为一个地址。URL的第一部份指定了访问信息所用的协议,后面总是跟着冒号。常用的协议有"ftp"(代表"文件传输协议")和"http"(代表"超文本传输协议")。如果协议是"file",表示资源是在某个本地文件系统上而非在Internet上(下例用于表示我们所描述的部分;它并非URL的组成部分)。

  ftp://Javasoft.com/docs/JDK-1_apidocs.zip
  http://Java.sun.com/products/jdk/CurrentRelease
  file:/home/haroldw/docs/books/tutorial/summary.html

  URL的其余部份(冒号后面的)给出了数据资源所处位置的有关信息。如果协议是file,则URL的其余部份是文件的路径。对于ftp和http协议,URL的其余部份标识了主机并可选地给出某个更详尽的地址路径。例如,以下是JavaSoft主页的URL。该URL只标识了主机:http://Java.sun.com。从该主页开始浏览,就可以进到许多其它的网页中,其中之一就是JDBC主页。JDBC主页的URL更为具体,它具体表示为:
http://Java.sun.com/products/jdbc

  3. JDBC URL

  JDBC URL提供了一种标识数据库的方法,可以使相应的驱动程序能识别该数据库并与之建立连接。实际上,驱动程序编程员将决定用什么JDBC URL来标识特定的驱动程序。用户不必关心如何来形成JDBC URL;他们只须使用与所用的驱动程序一起提供的URL即可。JDBC的作用是提供某些约定,驱动程序编程员在构造他们的JDBC URL时应该遵循这些约定。

  由于JDBC URL要与各种不同的驱动程序一起使用,因此这些约定应非常灵活。首先,它们应允许不同的驱动程序使用不同的方案来命名数据库。例如,odbc子协议允许(但并不是要求)URL含有属性值。

  其次,JDBC URL应允许驱动程序编程员将一切所需的信息编入其中。这样就可以让要与给定数据库对话的applet打开数据库连接,而无须要求用户去做任何系统管理工作。

  最后,JDBC URL应允许某种程度的间接性。也就是说,JDBC URL可指向逻辑主机或数据库名,而这种逻辑主机或数据库名将由网络命名系统动态地转换为实际的名称。这可以使系统管理员不必将特定主机声明为JDBC名称的一部份。网络命名服务(例如DNS、NIS和DCE)有多种,而对于使用哪种命名服务并无限制。
JDBC URL的标准语法如下所示。它由三部分组成,各部分间用冒号分隔:
jdbc:<子协遥荆海甲用?称??br>   JDBC URL的三个部分可分解如下:

  (1)jdbc协议:JDBC URL中的协议总是jdbc。

  (2)<子协议>:驱动程序名或数据库连接机制(这种机制可由一个或多个驱动程序支持)的名称。子协议名的典型示例是"odbc",该名称是为用于指定ODBC风格的数据资源名称的URL专门保留的。例如,为了通过JDBC-ODBC桥来访问某个数据库,可以用如下所示的URL:jdbc:odbc:book。本例中,子协议为"odbc",子名称"book"是本地ODBC数据资源。如果要用网络命名服务(这样JDBC URL中的数据库名称不必是实际名称),则命名服务可以作为子协议。例如,可用如下所示的URL:jdbc:dcenaming:accounts。本例中,该URL指定了本地DCE命名服务应该将数据库名称"accounts"解析为更为具体的可用于连接真实数据库的名称。

  (3)<子名称>:种标识数据库的方法。子名称可以依不同的子协议而变化。它还可以有子名称的子名称(含有驱动程序编程员所选的任何内部语法)。使用子名称的目的是为定位数据库提供足够的信息。前例中,因为ODBC将提供其余部份的信息,因此用"book"就已足够。然而,位于远程服务器上的数据库需要更多的信息。例如,如果数据库是通过Internet来访问的,则在JDBC URL中应将网络地址作为子名称的一部份包括进去,且必须遵循如下所示的标准URL命名约定://主机名:端口/子协议。

  假设"dbnet"是个用于将某个主机连接到Internet上的协议,则JDBC URL应为:jdbc:dbnet://wombat:356/fred。

  4. "odbc"子协议

  子协议odbc是一种特殊情况。它是为用于指定ODBC风格的数据资源名称的URL而保留的,并具有下列特性:允许在子名称(数据资源名称)后面指定任意多个属性值。odbc子协议的完整语法为:

  jdbc:odbc:<数据资源名称>[;<属性名>=<属性值>],因此,以下都是合法的jdbc:odbc名称:
  jdbc:odbc:qeor7
  jdbc:odbc:wombat
  jdbc:odbc:wombat;CacheSize=20;ExtensionCase=LOWER
  jdbc:odbc:qeora;UID=kgh;PWD=fooey

  5. 注册子协议

  驱动程序编程员可保留某个名称以将之用作JDBC URL的子协议名。当DriverManager类将此名称加到已注册的驱动程序清单中时,为之保留该名称的驱动程序应能识别该名称并与它所标识的数据库建立连接。例如,odbc是为JDBC-ODBC桥而保留的。假设有个Miracle公司,它可能会将"miracle"注册为连接到其Miracle DBMS上的JDBC驱动程序的子协议,从而使其他人都无法使用这个名称。

  JavaSoft目前作为非正式代理负责注册JDBC子协议名称。要注册某个子协议名称,请发送电子邮件到下述地址:jdbc@wombat.eng.sun.com。

  6. 发送SQL语句

  连接一旦建立,就可用来向它所涉及的数据库传送SQL语句。JDBC对可被发送的SQL语句类型不加任何限制。这就提供了很大的灵活性,即允许使用特定的数据库语句或甚至于非SQL语句。然而,它要求用户自己负责确保所涉及的数据库可以处理所发送的SQL语句,否则将自食其果。例如,如果某个应用程序试图向不支持储存程序的DBMS发送储存程序调用,就会失败并将抛出异常。JDBC要求驱动程序应至少能提供ANSI SQL-2 Entry Level功能才可算是符合JDBC标准TM的。这意味着用户至少可信赖这一标准级别的功能。

  JDBC提供了三个类,用于向数据库发送SQL语句。Connection接口中的三个方法可用于创建这些类的实例。下面列出这些类及其创建方法:

  (1)Statement:由方法createStatement所创建。Statement对象用于发送简单的SQL语句。

  (2)PreparedStatement:由方法prepareStatement所创建。PreparedStatement对象用于发送带有一个或多个输入参数(IN参数)的SQL语句。PreparedStatement拥有一组方法,用于设置IN参数的值。执行语句时,这些IN参数将被送到数据库中。PreparedStatement的实例扩展了Statement,因此它们都包括了Statement的方法。PreparedStatement对象有可能比Statement对象的效率更高,因为它已被预编译过并存放在那以供将来使用。

  (3)CallableStatement:由方法prepareCall所创建。CallableStatement对象用于执行SQL储存程序─一组可通过名称来调用(就象函数的调用那样)的SQL语句。CallableStatement对象从PreparedStatement中继承了用于处理IN参数的方法,而且还增加了用于处理OUT参数和INOUT参数的方法。

  不过通常来说createStatement方法用于简单的SQL语句(不带参数)、prepareStatement方法用于带一个或多个IN参数的SQL语句或经常被执行的简单SQL语句,而prepareCall方法用于调用已储存过程。

  7. 事务

  事务由一个或多个这样的语句组成:这些语句已被执行、完成并被提交或还原。当调用方法commit或rollback时,当前事务即告就结束,另一个事务随即开始。缺省情况下,新连接将处于自动提交模式。也就是说,当执行完语句后,将自动对那个语句调用commit方法。这种情况下,由于每个语句都是被单独提交的,因此一个事务只由一个语句组成。如果禁用自动提交模式,事务将要等到commit或rollback方法被显式调用时才结束,因此它将包括上一次调用commit或rollback方法以来所有执行过的语句。对于第二种情况,事务中的所有语句将作为组来提交或还原。

  方法commit使SQL语句对数据库所做的任何更改成为永久性的,它还将释放事务持有的全部锁。而方法rollback将弃去那些更改。有时用户在另一个更改生效前不想让此更改生效。这可通过禁用自动提交并将两个更新组合在一个事务中来达到。如果两个更新都是成功,则调用commit方法,从而使两个更新结果成为永久性的;如果其中之一或两个更新都失败了,则调用rollback方法,以将值恢复为进行更新之前的值。

  大多数JDBC驱动程序都支持事务。事实上,符合JDBC的驱动程序必须支持事务。DatabaseMetaData给出的信息描述DBMS所提供的事务支持水平。

  8. 事务隔离级别

  如果DBMS支持事务处理,它必须有某种途径来管理两个事务同时对一个数据库进行操作时可能发生的冲突。用户可指定事务隔离级别,以指明DBMS应该花多大精力来解决潜在冲突。例如,当事务更改了某个值而第二个事务却在该更改被提交或还原前读取该值时该怎么办。

  假设第一个事务被还原后,第二个事务所读取的更改值将是无效的,那么是否可允许这种冲突?JDBC用户可用以下代码来指示DBMS允许在值被提交前读取该值("dirty读取"),其中con是当前连接:
con.setTransactionIsolation(TRANSACTION_READ_UNCOMMITTED);

  事务隔离级别越高,为避免冲突所花的精力也就越多。Connection接口定义了五级,其中最低级别指定了根本就不支持事务,而最高级别则指定当事务在对某个数据库进行操作时,任何其它事务不得对那个事务正在读取的数据进行任何更改。通常,隔离级别越高,应用程序执行的速度也就越慢(由于用于锁定的资源耗费增加了,而用户间的并发操作减少了)。在决定采用什么隔离级别时,开发人员必须在性能需求和数据一致性需求之间进行权衡。当然,实际所能支持的级别取决于所涉及的DBMS的功能。

  当创建Connection对象时,其事务隔离级别取决于驱动程序,但通常是所涉及的数据库的缺省值。用户可通过调用setIsolationLevel方法来更改事务隔离级别。新的级别将在该连接过程的剩余时间内生效。要想只改变一个事务的事务隔离级别,必须在该事务开始前进行设置,并在该事务结束后进行复位。我们不提倡在事务的中途对事务隔离级别进行更改,因为这将立即触发commit方法的调用,使在此之前所作的任何更改变成永久性的。
posted @ 2006-03-22 20:44 konhon 优华 阅读(390) | 评论 (0)编辑 收藏

全面解析JDBC(一)
作者:未知     文章来源:www.jspcn.net
访问次数: 次    加入时间:2005-01-19
综述:Java数据库连接体系结构是用于Java应用程序连接数据库的标准方法。JDBC对Java程序员而言是API,对实现与数据库连接的服务提供商而言是接口模型。作为API,JDBC为程序开发提供标准的接口,并为数据库厂商及第三方中间件厂商实现与数据库的连接提供了标准方法。JDBC使用已有的SQL标准并支持与其它数据库连接标准,如ODBC之间的桥接。JDBC实现了所有这些面向标准的目标并且具有简单、严格类型定义且高性能实现的接口。

  如何选择合适的JDBC产品?

  有关JDBC最新的信息,有兴趣的读者可以查阅JDBC的官方网站--即JavaSoft的主页,其URL为:http://Java.sun.com/products/jdbc

  1. JavaSoft框架

  JavaSoft提供三种JDBC产品组件,它们是Java开发工具包(JDK)的组成部份:JDBC驱动程序管理器、JDBC驱动程序测试工具包和JDBC-ODBC桥。

  JDBC驱动程序管理器是JDBC体系结构的支柱。它实际上很小,也很简单;其主要作用是把Java应用程序连接到正确的JDBC驱动程序上,然后即退出。

  JDBC驱动程序测试工具包为使JDBC驱动程序运行您的程序提供一定的可信度。只有通过JDBC驱动程序测试的驱动程序才被认为是符合JDBC标准TM的。

  JDBC-ODBC桥使ODBC驱动程序可被用作JDBC驱动程序。它的实现为JDBC的快速发展提供了一条途径,其长远目标提供一种访问某些不常见的DBMS(如果对这些不常见的DBMS未实现JDBC)的方法。

  2. JDBC驱动程序的类型

  目前比较常见的JDBC驱动程序可分为以下四个种类:

  (1)JDBC-ODBC桥加ODBC驱动程序

  JavaSoft桥产品利用ODBC驱动程序提供JDBC访问。注意,必须将ODBC二进制代码(许多情况下还包括数据库客户机代码)加载到使用该驱动程序的每个客户机上。因此,这种类型的驱动程序最适合于企业网(这种网络上客户机的安装不是主要问题),或者是用Java编写的三层结构的应用程序服务器代码。

  (2)本地API

  这种类型的驱动程序把客户机API上的JDBC调用转换为Oracle、Sybase、Informix、DB2或其它DBMS的调用。注意,象桥驱动程序一样,这种类型的驱动程序要求将某些二进制代码加载到每台客户机上。

  (3)JDBC网络纯Java驱动程序

  这种驱动程序将JDBC转换为与DBMS无关的网络协议,之后这种协议又被某个服务器转换为一种DBMS协议。这种网络服务器中间件能够将它的纯Java客户机连接到多种不同的数据库上。所用的具体协议取决于提供者。通常,这是最为灵活的JDBC驱动程序。有可能所有这种解决方案的提供者都提供适合于Intranet用的产品。为了使这些产品也支持Internet访问,它们必须处理Web所提出的安全性、通过防火墙的访问等方面的额外要求。几家提供者正将JDBC驱动程序加到他们现有的数据库中间件产品中。

  (4)本地协议纯Java驱动程序

  这种类型的驱动程序将JDBC调用直接转换为DBMS所使用的网络协议。这将允许从客户机机器上直接调用DBMS服务器,是Intranet访问的一个很实用的解决方法。由于许多这样的协议都是专用的,因此数据库提供者自己将是主要来源,有几家提供者已在着手做这件事了。

  据专家预计第(3)、(4)类驱动程序将成为从JDBC访问数据库的首方法。第(1)、(2)类驱动程序在直接的纯Java驱动程序还没有上市前会作为过渡方案来使用。对第(1)、(2)类驱动程序可能会有一些变种,这些变种要求有连接器,但通常这些是更加不可取的解决方案。第(3)、(4)类驱动程序提供了Java的所有优点,包括自动安装(例如,通过使用JDBC驱动程序的appletapplet来下载该驱动程序)。

  3. JDBC驱动程序的获取



  目前已有几十个(1)类的驱动程序,即可与Javasoft桥联合使用的ODBC驱动程序的驱动程序。有大约十多个属于种类(2)的驱动程序是以DBMS的本地API为基础编写的。只有几个属于种类(3)的驱动程序,其首批提供者是SCO、OpenHorizon、Visigenic和WebLogic。此外,JavaSoft和数据库连接的领先提供者Intersolv还合作研制了JDBC-ODBC桥和JDBC驱动程序测试工具包。
posted @ 2006-03-22 20:44 konhon 优华 阅读(479) | 评论 (0)编辑 收藏

你插入数据的时候,用
/**
* 转变字符串的乱码函数
@param  str
@return
*/

public  String getStr(String str)
{
try {
String temp_p 
=  str;
byte  [] temp_t  =  temp_p.getBytes( " ISO8859-1 " );
String temp 
=   new  String(temp_t);
return  temp;
}

catch (Exception e) {
return   " null " ;
}

}
 
// 向bean里面赋值
public   void  setAction(String action)  {
this .action  =  getStr(action);
}


public   void  setAddmanagerid(String addmanagerid)  {
this .addmanagerid  =  getStr(addmanagerid);
}


转换一下,看看可以吗,我的数据库是ORACLE没问题。
posted @ 2006-03-22 19:06 konhon 优华 阅读(413) | 评论 (0)编辑 收藏

Java编程电子书籍下载


环境安装配置:

TOMCAT的配置
http://download.chinaitlab.com/soft/10791.htm

JAVA配置文件编写说明文档
http://download.chinaitlab.com/soft/10010.htm

一步一步学会配置Kjava开发环境
http://download.chinaitlab.com/soft/9483.htm

Weblogic7开发EJB的配置
http://download.chinaitlab.com/soft/4938.htm

图解JSP环境安装配置
http://download.chinaitlab.com/soft/2157.htm

Tomcat配置方法
http://download.chinaitlab.com/soft/931.htm

全程指导Linux下JAVA环境配置
http://download.chinaitlab.com/soft/11272.htm

学习方法:

通过JB4学习JAVA
http://download.chinaitlab.com/soft/7589.htm

10步学习 JavaScript
http://download.chinaitlab.com/soft/6492.htm

Java Sctipt学习不求人
http://download.chinaitlab.com/soft/6361.htm

JSP学习指南
http://download.chinaitlab.com/soft/6152.htm

JAVA学习文档
http://download.chinaitlab.com/soft/6114.htm

J2EE学习笔记
http://download.chinaitlab.com/soft/6048.htm

JavaScript学习
http://download.chinaitlab.com/soft/3029.htm

Java2 学习指南
http://download.chinaitlab.com/soft/2743.htm

Juniper学习指南
http://download.chinaitlab.com/soft/2227.htm

J2EE学习资料
http://download.chinaitlab.com/soft/1618.htm

JSP由浅入深
http://download.chinaitlab.com/soft/1315.htm

面向对象编程:

Java面向对象编程指南
http://download.chinaitlab.com/soft/9792.htm

JAVA的核心技术:面向对象编程
http://download.chinaitlab.com/soft/9093.htm

Java 与 UML 面向对象程序设计
http://download.chinaitlab.com/soft/6053.htm

Java 2 编程21天自学通
http://download.chinaitlab.com/soft/10507.htm

J2EE编程起步
http://download.chinaitlab.com/soft/10506.htm

Java面向对象编程指南
http://download.chinaitlab.com/soft/9792.htm

Java专业编程指南
http://download.chinaitlab.com/soft/9791.htm

Java服务器高级编程
http://download.chinaitlab.com/soft/9790.htm

J2EE EAI编程指南
http://download.chinaitlab.com/soft/9784.htm

J2MEMIDP无线设备编程指南
http://download.chinaitlab.com/soft/9765.htm

JAVA编程思想 中文版
http://download.chinaitlab.com/soft/9481.htm

Java XML编程指南
http://download.chinaitlab.com/soft/9097.htm

Java 数据库编程宝典
http://download.chinaitlab.com/soft/9095.htm

JAVA的核心技术:面向对象编程
http://download.chinaitlab.com/soft/9093.htm

JDBC API数据库编程实作教材
http://download.chinaitlab.com/soft/9087.htm

核心 JSF 编程
http://download.chinaitlab.com/soft/7946.htm

JAVA 2应用编程150例
http://download.chinaitlab.com/soft/6815.htm

JAVA数据库编程JDBC
http://download.chinaitlab.com/soft/6113.htm

深入掌握J2EE编程技术
http://download.chinaitlab.com/soft/6030.htm

实用J2EE设计模式编程指南
http://download.chinaitlab.com/soft/5033.htm

Java for Internet编程技术
http://download.chinaitlab.com/soft/4198.htm

Java安全性编程指南
http://download.chinaitlab.com/soft/3773.htm

J2ME无线设备编程
http://download.chinaitlab.com/soft/3669.htm

J2EE EJB编程实例
http://download.chinaitlab.com/soft/3141.htm

Java编程思想 第三版
http://download.chinaitlab.com/soft/2982.htm

Java 极限编程
http://download.chinaitlab.com/soft/1707.htm

Java2编程详解
http://download.chinaitlab.com/soft/1705.htm


网络编程:

J2EE网络编程标准教程
http://download.chinaitlab.com/soft/9100.htm

Java网络编程实例
http://download.chinaitlab.com/soft/9090.htm

Java P2P网络编程技术
http://download.chinaitlab.com/soft/6333.htm

Java网络编程
http://download.chinaitlab.com/soft/3871.htm

网络编程基础篇之 Java Script
http://download.chinaitlab.com/soft/3618.htm

Solaris Shell 编程
http://download.chinaitlab.com/soft/6426.htm

SUN Solaris管理手册
http://download.chinaitlab.com/soft/5732.htm

Solaris性能管理
http://download.chinaitlab.com/soft/11247.htm

Solaris9安装指南
http://download.chinaitlab.com/soft/5022.htm

SOLARIS高级系统管理员指南
http://download.chinaitlab.com/soft/3726.htm

Solaris操作环境安全
http://download.chinaitlab.com/soft/1500.htm

Solaris GNOME2.0桌面用户指南
http://download.chinaitlab.com/soft/1488.htm

Solaris 9 12/03 安装指南
http://download.chinaitlab.com/soft/1484.htm

Solaris管理员指南
http://download.chinaitlab.com/soft/1475.htm

中文Solaris9 系统管理员指南
http://download.chinaitlab.com/soft/1463.htm

Solaris安全性专题指导
http://download.chinaitlab.com/soft/746.htm


XML系列:

Java XML编程指南
http://download.chinaitlab.com/soft/9097.htm

Java程序设计EJB、XML与数据库
http://download.chinaitlab.com/soft/9094.htm

XML 终极教程
http://download.chinaitlab.com/soft/9057.htm

Java&XML应用
http://download.chinaitlab.com/soft/6211.htm

XML_JAVA指南
http://download.chinaitlab.com/soft/6163.htm

JDBC:

JDBC API数据库编程实作教材
http://download.chinaitlab.com/soft/9087.htm

JAVA数据库编程JDBC
http://download.chinaitlab.com/soft/6113.htm

JDBC API 参考教程第三版
http://download.chinaitlab.com/soft/6057.htm

JDBC与Java数据库程序设计
http://download.chinaitlab.com/soft/6018.htm

Java语言SQL接口 JDBCprogram
http://download.chinaitlab.com/soft/5938.htm

JSP应用程序开发指南
http://download.chinaitlab.com/soft/1546.htm

用 JDBC 管理数据库连接
http://download.chinaitlab.com/soft/935.htm

JDO:

全面了解JDO数据库编程
http://download.chinaitlab.com/soft/7992.htm

Struts:

Struts中文手册
http://download.chinaitlab.com/soft/10516.htm

Struts架构指导
http://download.chinaitlab.com/soft/7529.htm

精通struts技术
http://download.chinaitlab.com/soft/6801.htm

Struts 学习起歩问答
http://download.chinaitlab.com/soft/6156.htm

Hibernate:

Hibernate2.1.2参考手册中文版
http://download.chinaitlab.com/soft/8919.htm



JAVA与模式\J2EE模式:

J2EE 核心模式
http://download.chinaitlab.com/soft/9785.htm

Java 企业设计模式
http://download.chinaitlab.com/soft/9096.htm

Java简单工厂创立性模式介绍
http://download.chinaitlab.com/soft/7398.htm

EJB设计模式
http://download.chinaitlab.com/soft/6135.htm

JAVA设计模式
http://download.chinaitlab.com/soft/6112.htm

实用J2EE设计模式编程指南
http://download.chinaitlab.com/soft/5033.htm

Java与模式
http://download.chinaitlab.com/soft/3073.htm

设计模式Java版
http://download.chinaitlab.com/soft/1723.htm


JBuilder开发Servlet及JSP:

精通JBuilder
http://download.chinaitlab.com/soft/10565.htm

JBuilder速成资料
http://download.chinaitlab.com/soft/9714.htm

Jbuilder7和weblogic7整合开发手
http://download.chinaitlab.com/soft/9664.htm

JBUILDER9 软件开发项目实践
http://download.chinaitlab.com/soft/9089.htm

JbuilderX开发指南
http://download.chinaitlab.com/soft/9088.htm

Jbuilder x 指南
http://download.chinaitlab.com/soft/7984.htm

JBuilder4开发人员指南
http://download.chinaitlab.com/soft/5939.htm

JBuilder7 Weblogic7整和开发培训手册
http://download.chinaitlab.com/soft/4727.htm

JBuilder开发数据库应用程序
http://download.chinaitlab.com/soft/1701.htm

Java开发指南--Servlets和JSP篇
http://download.chinaitlab.com/soft/9793.htm

Java Servlets 编程指南
http://download.chinaitlab.com/soft/9098.htm

Oreilly Java Servlet
http://download.chinaitlab.com/soft/6522.htm

Java Servlet开发与实例
http://download.chinaitlab.com/soft/6029.htm

深入Java Servlet 网络编程
http://download.chinaitlab.com/soft/9783.htm

用JSP_Servlet构建三层式管理信息系统
http://download.chinaitlab.com/soft/6034.htm

Java Servlet帮助文档
http://download.chinaitlab.com/soft/2981.htm

JSP网站编程教程
http://download.chinaitlab.com/soft/11256.htm

JSP语法分析
http://download.chinaitlab.com/soft/11257.htm

JSP实用教程
http://download.chinaitlab.com/soft/10792.htm

JSP语法(1)--HTML注释
http://download.chinaitlab.com/soft/10790.htm

JSP应用开发详解
http://download.chinaitlab.com/soft/10025.htm

JSP技术揭秘
http://download.chinaitlab.com/soft/9387.htm

JSP技术大全
http://download.chinaitlab.com/soft/9388.htm

JSP网上书店实例详解
http://download.chinaitlab.com/soft/9386.htm

JSP动态网页新技术
http://download.chinaitlab.com/soft/8920.htm

JSP 技术大全
http://download.chinaitlab.com/soft/7782.htm

JSP高级开发与应用
http://download.chinaitlab.com/soft/7633.htm

JSP 完全探索
http://download.chinaitlab.com/soft/7546.htm

JSP 高级开发与应用
http://download.chinaitlab.com/soft/7116.htm

JSP编程技巧
http://download.chinaitlab.com/soft/7114.htm

JSP速成教程
http://download.chinaitlab.com/soft/6882.htm

JSP网络编程技术
http://download.chinaitlab.com/soft/6880.htm

JSP程序设计指南
http://download.chinaitlab.com/soft/6690.htm

最新JSP入门与应用
http://download.chinaitlab.com/soft/6697.htm

JSP快速入门
http://download.chinaitlab.com/soft/6636.htm

JSP网页编程
http://download.chinaitlab.com/soft/6527.htm

JSP 实用教程
http://download.chinaitlab.com/soft/6334.htm

JSP入门与提高
http://download.chinaitlab.com/soft/6326.htm

JSP语法
http://download.chinaitlab.com/soft/6116.htm

掌握自定义JSP标签
http://download.chinaitlab.com/soft/6096.htm

JSP 动态网站技术入门与提高
http://download.chinaitlab.com/soft/6019.htm

JSP实例入门
http://download.chinaitlab.com/soft/4377.htm

JSP教程之与数据库通信
http://download.chinaitlab.com/soft/3673.htm

如何成为优秀的JSP 程序员
http://download.chinaitlab.com/soft/3002.htm

JSP数据库编程指南
http://download.chinaitlab.com/soft/2946.htm

JSP 高级编程
http://download.chinaitlab.com/soft/2635.htm

JSP实用编程实例集锦
http://download.chinaitlab.com/soft/2154.htm

JSP程序设计精彩实例
http://download.chinaitlab.com/soft/2151.htm

JSP即时应用
http://download.chinaitlab.com/soft/1547.htm

JSP程序设计精彩实例
http://download.chinaitlab.com/soft/1543.htm

JSP实用编程实例集锦
http://download.chinaitlab.com/soft/1537.htm

JSP基础
http://download.chinaitlab.com/soft/894.htm

Eclipse开发Servlet及JSP:

Eclipse+Tomcat集成开发servle
http://download.chinaitlab.com/soft/6134.htm

J2EE Jboss Ejb With Eclipse 2003
http://download.chinaitlab.com/soft/6045.htm
posted @ 2006-03-21 07:27 konhon 优华 阅读(2007) | 评论 (0)编辑 收藏

仅列出标题
共21页: First 上一页 4 5 6 7 8 9 10 11 12 下一页 Last