Tomcat 5.5.XX数据源配置出现连接问题的解决方案–修改版

参见另外一篇文章:http://www.ishere.cn/article.asp?id=79

根本原因是版本不同了,Tomcat5.5的数据源配置更加简洁和人性化了,以下是两种方法,修改相应参数为自己的.

第一种方法:修改conf/server.xml的context,在</host>之前添加以下内容

<Context path="/myapp" docBase=E:myapp" debug="5" reloadable="true" crossContext="true">
 <Resource name="jdbc/connectDB"
 type="javax.sql.DataSource"
 driverClassName="com.microsoft.jdbc.sqlserver.SQLServerDriver"
 maxIdle="2"
 maxWait="5000"
 username="sa"
 password="sa"
 url="jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=myapp"
 maxActive="4"/>
</Context>

第二种方法:就是新建一个xml文档,名字任意取,把上述内容写到这个xml文件中,然后把它放到confCatalinalocalhost目录下,就不用修改conf/server.xml了,这样做之后每次tomcat启动时候会自动根据JNDI服务找到这些配置,这种配置方法不用修改任何东西,特别适合多个应用程序公用一个tomcat服务器的情况,使得管理起来很方便,具体写法很简单:
<?xml version=’1.0′ encoding=’utf-8′?>
把上面一段原封不动粘贴在这里就可以了
附:JNDI——Java Naming and Directory Interface是一套提供naming和 directory功能的 API,Java应用程式开发者透过使用 JNDI,在naming和 directory方面的应用上就有了共通的准则。

勾勾手

歌手:郭美美 专辑:不怕不怕(台湾版)

——–
心被海风吹过
咸咸的自由
还隐隐作痛
当太阳沉没
我们的故事全剧终

伤被时间追过
今天的自由
是一道彩虹
带我迎接
明天最亮的晴空
抹乾眼泪往前走
用微笑召唤幸福海鸥
我们勾勾手
不准不快乐
就把舍不得
都丢进回忆的黑洞尘封
如果遇见更好的人
一定要更坚强
勇敢去追求

要比谁都快乐
都丢进成长的黑洞尘封
一定更坚强把爱
勇敢追求
—-

努力奔跑的人

最近一真熬到深夜,还是有收获的,JSP已经学到两种模式了,越来越感觉jsp是个好东西,jspBean,servlet,struts,这些关键的技术正在一步步的被我消化.做一个好学的人,做一个努力奔跑的人!

前两天的一件事对我来说,对我们来说都是一件大事!

Struts 用户指南<备用>

1. 介绍
1.1 Model-View-Controller (MVC) 设计模式
FIXME – 需要一个对该模式一般性的介绍。(译注:可以参考机械工业出版社的《设计模式》。)
1.2 将MVC概念映射到Struts组件中
Struts 的体系结构实现了Model-View-Controller设计模式的概念,它将这些概念映射到web应用程序的组件和概念中.
这一体系结构中每个主要的组件都将在下面做详细的讨论。

1.3 Model: 系统状态和商业逻辑JavaBeans
基于MVC的系统中的 Model 部分可以细分为两个概念 — 系统的内部状态, 能够改变状态的行为。用语法术语来说,我们可以把状态信息当作名词(事物),把行为当作动词(事物状态的改变)。
通常说来,你的应用程序将系统内部的状态表示为一组一个或多个的JavaBeans,使用属性(properties)来表示状态的细节。依赖于你的应用程序的复杂度,这些beans可以是自包含的(以某种方式知道怎样永久地保存它们的状态信息),或者可以是正面的(facades),知道当被请求时怎样从外部数据源(例如数据库)中取得信息。Entity EJBs通常也用来表示内部状态。

大型应用程序经常将系统可能的商业逻辑行为表示为可以被维护状态信息的beans调用的方法。举个例子,你有一个为每个当前用户保存在session中的购物车bean,里面是表示当前用户决定购买物品的属性。这个bean有一个checkOut()方法用来验证用户的信用卡,将定单发给库房以选择货品和出货。别的系统分别地表示同样的行为,或许使用Session EJBs。

在一些小型应用程序中,同样的行为又可能嵌入到作为Controller一部分的 Action 类中。这在逻辑非常简单或者并不想要在其它环境中重用这些商业逻辑时是恰当的。Struts框架支持所有这些方法,但建议将商业逻辑(“做什么”)和 Action 类(“决定做什么”)分离开。

1.4 View: JSP页面和表示组件
基于Struts的应用程序中的 View 部分通常使用JSP技术来构建。JSP页面包含称为“模版文本”的静态HTML(或XML)文本,加上插入的基于对特殊行为标记解释的动态内容。JSP环境包括了其用途由JSP规范来描述的一套标准的行为标记,例如 <jsp:useBean> 。另外,还有一个用来定义你自己标记的标准机制,这些自定义的标记组织在“定制标记库”中。
Struts包括了一个广阔的便于创建用户界面,并且充分国际化的定制标记库,与作为系统 Model 部分一部分的ActionForm beans美妙地相互配合。这些标记的使用将在后面做详细讨论。

除了JSP页面和其包含的行为及定制标记,商业对象经常需要能够基于它们在被请求时的当前状态将自己处理成HTML(或XML)。从这些对象处理过的输出可以很容易地使用 <jsp:include> 标准行为标记包括在结果的JSP页面中。

1.5 Controller: ActionServlet和ActionMapping
应用程序的 Controller 部分集中于从客户端接收请求(典型情况下是一个运行浏览器的用户),决定执行什么商业逻辑功能,然后将产生下一步用户界面的责任委派给一个适当的View组件。在Struts中,controller的基本组件是一个 ActionServlet 类的servlet。这个servlet通过定义一组映射(由Java接口 ActionMapping 描述)来配置。每个映射定义一个与所请求的URI相匹配的路径和一个 Action 类(一个实现 Action 接口的类)完整的类名,这个类负责执行预期的商业逻辑,然后将控制分派给适当的View组件来创建响应。
Struts也支持使用包含有运行框架所必需的标准属性之外的附加属性的 ActionMapping 类的能力。这允许你保存特定于你的应用程序的附加信息,同时仍可利用框架其余的特性。另外,Struts允许你定义控制将重定向到的逻辑名,这样一个行为方法可以请求“主菜单”页面(举例),而不需要知道相应的JSP页面的实际名字是什么。这个功能极大地帮助你分离控制逻辑(下一步做什么)和显示逻辑(相应的页面的名称是什么)。

2. 创建Model组件
2.1 概述
你用到的应用程序的需求文档很可能集中于创建用户界面。然而你应该保证每个提交的请求所需要的处理也要被清楚的定义。通常说来,Model 组件的开发者集中于创建支持所有功能需求的JavaBeans类。一个特殊应用要求的beans的精确特性依赖于具体需求变化会非常的大,但是它们通常可以分成下面讨论的几种类型。然而,首先对“范围”概念做一个简短的回顾是有用的,因为它与beans有关。
2.2 JavaBeans和范围
在一个基于web的应用程序中,JavaBeans可以被保存在(并从中访问)一些不同“属性”的集合中。每一个集合都有集合生存期和所保存的beans可见度的不同的规则。总的说来,定义生存期和可见度的这些规则被叫做这些beans的 范围 。JSP规范中使用以下术语定义可选的范围(在圆括号中定义servlet API中的等价物):
page – 在一个单独的JSP页面中可见的Beans,生存期限于当前请求。(service()方法中的局部变量) request – 在一个单独的JSP页面中可见的Beans,也包括所有包含于这个页面或从这个页面重定向到的页面或servlet。(Request属性)
session – 参与一个特定的用户session的所有的JSP和servlet都可见的Beans,跨越一个或多个请求。(Session属性)
application – 一个web应用程序的所有JSP页面和servlet都可见的Beans。(Servlet context属性)
记住同一个web应用程序的JSP页面和servlets共享同样一组bean集合是很重要的。例如,一个bean作为一个request属性保存在一个servlet中,就象这样:
MyCart mycart = new MyCart(…);
request.setAttribute("cart", mycart);
将立即被这个servlet重定向到的一个JSP页面使用一个标准的行为标记看到,就象这样:
<jsp:useBean id="cart" scope="request"
class="com.mycompany.MyApp.MyCart"/>
2.3 ActionForm Beans
Struts框架通常假定你已经为每一个你的应用程序中请求的输入创建了一个 ActionForm bean(即一个实现了 ActionForm 接口的类)。如果你在你的 ActionMapping 配置文件中定义了这样的beans(见“创建Controller组件”),Struts的controller servlet在调用适当的 Action 方法前将自动为你执行如下的服务:
用适当的关键字检查用户的session中是否有适当的类的bean的一个实例。
如果没有这样的session范围的bean,自动建立一个新的bean并添加到用户的session中。
对每个名字对应于bean中的一个属性的请求参数,调用相应的set方法。这个操作类似于当你以通配符“*”选择所有属性使用标准的JSP行为标记 <jsp:setProperty> 。
更新的ActionForm bean在被调用时将被传递给Acton类的perform()方法,以使这些值能够立即生效。
当你在写你的ActionForm beans时,记住以下的原则:
ActionForm 接口本身不需要特殊的实现方法。它是用来标识这些特定的beans在整个体系结构中的作用。典型情况下,一个ActionForm bean只包括属性的get方法和set方法,没有商业逻辑。
通常在一个ActionForm bean中只有很少的输入验证逻辑。这样的beans存在的主要理由是保存用户为相关的表单所输入的大部分近期值 — 甚至在错误被检测到时 — 这样同样的页面可以被重建,伴随有一组出错信息,这样用户仅仅需要纠正错误的字段。用户输入的验证应该在 Action 类中执行(如果是很简单的话),或者在适当的商业逻辑beans中执行。
为每个表单中出现的字段定义一个属性(用相关的getXxx()和setXxx()方法)。字段名和属性名必须按照JavaBeans的约定相匹配。例如,一个名为 username 的输入字段将引起 setUsername() 方法被调用。
你应该注意一个“表单”在这里讨论时的意义并不必须对应于用户界面中的一个单独的JSP页面。在很多应用程序中一个“表单”(从用户的观点)延伸至多个页面也是很平常的。想想看,例如,通常在安装新的应用程序时使用的导航安装程序的用户界面。Struts鼓励你定义一个包含所有字段属性的单独的ActionForm bean。不管字段实际上是显示在哪个页面上。同样的,同一表单的不同的页面应该提交到相同的Action类。如果你遵照这个建议,在大多数情况下,页面设计者可以重新组织不同页面中的字段而不需要改变处理逻辑。
2.4 系统状态Beans
系统的实际状态通常表示为一组一个或多个的JavaBeans类,其属性定义当前状态。例如,一个购物车系统包括一个表示购物车的bean,这个bean为每个单独的购物者维护,这个bean中包括(在其它事物之中)一组购物者当前选择购买的项目。分别地,系统也包括保存用户信息(包括他们的信用卡和送货地址)、可获得项目的目录和它们当前库存水平的不同的beans。
对于小规模的系统,或者对于不需要长时间保存的状态信息,一组系统状态beans可以包含所有系统曾经经历的特定细节的信息。或者经常是,系统状态beans表示永久保存在一些外部数据库中的信息(例如CustomerBean对象对应于表 CUSTOMERS 中的特定的一行),在需要时从服务器的内存中创建或清除。在大规模应用程序中,Entity EJBs也用于这种用途。

2.5 商业逻辑Beans
你应该把你的应用程序中的功能逻辑封装成对为此目的设计的JavaBeans的方法调用。这些方法可以是用于系统状态beans的相同的类的一部分,或者可以是在专门执行商业逻辑的独立的类中。在后一种情况下,你通常需要将系统状态beans传递给这些方法作为参数处理。
为了代码最大的可重用性,商业逻辑beans应该被设计和实现为它们不知道自己被执行于web应用环境中。如果你发现在你的bean中你必须import一个 javax.servlet.* 类,你就把这个商业逻辑捆绑在了web应用环境中。考虑重新组织事物使你的 Action 类(Controller任务的一部分,在下面描述)翻译所有从HTTP请求中请求被处理为对你的商业逻辑beans属性set方法调用的信息,然后可以发出一个对 execute() 的调用。这样的一个商业逻辑类可以被重用在除它们最初被构造的web应用程序以外的环境中。

依赖于你的应用程序的复杂度和范围,商业逻辑beans可以是与作为参数传递的系统状态beans交互作用的普通的JavaBeans,或者使用JDBC调用访问数据库的普通的JavaBeans。而对于较大的应用程序,这些beans经常是有状态或无状态的EJBs。

2.6 题外话: 访问关系数据库
很多web应用程序利用一个关系数据库(通过一个JDBC driver访问)来保存应用程序相关的永久数据。其它应用程序则使用Entity EJBs来实现这个目的,他们委派EJBs自己来决定怎样维护永久状态。如果你是使用EJBs来实现这个目的,遵照EJB规范中描述的客户端设计模式。
对于基于直接数据库访问的web应用程序,一个普通的设计问题是当需要访问低层数据库时怎样产生一个适当的JDBC连接对象。解决这个问题有几种方法 — 以下原则描述了推荐的一种方法:

创建或得到一个允许一组数据库连接被多个用户共享的ConnectionPool类。Struts(当前)没有包括这样的一个类,但是有很多这样的类可以得到。
当应用程序初始化时,在应用程序展开(deployment)描述符中定义一个有一个“启动时加载”值的servlet。我们将把这个servlet叫做 启动 servlet。在大多数情况下,这个servlet不需要处理任何的请求,所以没有一个 <servlet-mapping> 会指向它。
在启动servlet的 init() 方法中,配置并初始化一个ConnectionPool类的实例,将其保存为一个servlet context属性(从JSP的观点看等同于一个application范围的bean)。通常基于传递给启动servlet初始化参数来配置联接缓冲池是很方便的。
在启动servlet的 destroy() 方法中,包含了释放联接缓冲池所打开的联接的逻辑。这个方法将在servlet容器结束这个应用程序的时候被调用。
当 Action 类需要调用一个需要数据库联接的商业逻辑bean中的方法(例如“insert a new customer”)时,将执行下面的步骤:
为这个web应用程序从servelt context属性中得到一个联接缓冲池对象。
调用联接缓冲池对象的 open() 方法来得到一个在 Action 类调用中使用的联接。
调用商业逻辑bean中合适的方法,将数据库联接对象作为一个参数传递给它。
调用分配的联接中的 close() 方法,这将引起这个联接为了以后其它请求的重用被返回到缓冲池中。
一个通常的编程错误是忘记了把数据库联接返回给缓冲池,这将最终导致用完所有的联接。一定要确信 Action 类的逻辑总是返回联接,甚至在商业逻辑bean抛出一个违例时。
遵照上面推荐的设计模式意味着你能够编写你的商业逻辑类而不需要担心它们怎样得到一个JDBC联接来使用– 简单地在任何需要访问数据库的方法中包含一个Connection参数。当你的商业逻辑类在一个web应用程序中被利用时,分配和释放适当的联接是 Action 类的责任。当你使用相同的商业逻辑类时,例如,在一个批处理工作中,提供一个适当的联接是那个应用程序的责任(这不需要从缓冲池中获得,因为大多数批处理工作运行于一个单线程环境中)。
3. 创建View组件
3.1 概述
这一章集中于创建应用程序中的 View 组件的任务,主要使用JSP技术建立。特别的,Struts除了提供了与输入表单的交互外还提供了建立国际化应用程序的支持。几个其它的与View相关的主题也被简单地讨论。
3.2 国际化消息
几年之前,应用程序开发者能够考虑到仅仅支持他们本国的只使用一种语言(或者有时候是两种)和通常只有一种数量表现方式(例如日期、数字、货币值)的居民。然而,基于web技术的应用程序的爆炸性增长,以及将这些应用程序展开在Internet或其它被广泛访问的网络之上,已经在很多情况下使得国家的边界淡化到不可见。这种情况转变成为一种对于应用程序支持国际化(经常被称做“i18n”,因为18是字母“i”和字母“n”之间的字母个数)和本地化的需求。
Struts建立于Java平台之上为建立国际化和本地化的应用程序提供帮助。需要熟悉的关键概念是:

Locale – 基础的支持国际化的Java类是 java.util.Locale 。每个 Locale 代表一个特别的国家和语言选择(加上一个可选的语言变量),以及一套格式假定,例如数字和日期等等。
ResourceBundle – java.util.ResourceBundle 类提供支持多种语言消息的基本工具。查看文档中关于ResourceBundle 类以及你的JDK版本的文档包中关于国际化的更多内容。
PropertyResourceBundle – 一个 ResourceBundle 类的标准实现允许你使用与初始化properties文件同样的“name=value”语法来定义资源。这对于使用为用于一个web应用程序的消息准备资源包是非常方便的,因为这些消息通常都是面向文本的。
MessageFormat – java.text.MessageFormat 类允许你使用运行时指定的参数替换一个消息字符串中的一部分(在这种情况下,是一个从一个资源包得到的消息)。这在你创建一个句子的场合中是有用的,但是词会以不同的语言按照不同的顺序出现。消息中的占位符字符串{0}用第一个运行时参数替换,{1}用第二个运行时参数替换,以此类推。
MessageResources – Struts的类 org.apache.struts.util.MessageResources 使你能够将一套资源包视做一个数据库,并且允许你为一个特定的Locale(通常是与当前用户相对应)请求一个特定的消息,而不是为服务器运行在其中的缺省的Locale请求消息。
对于一个国际化的应用程序,遵照JDK文档包中国际化文档所描述的步骤来创建一个包含每种语言的消息的属性文件。下面举一个例子说明。
假设你的源代码建立在包 com.mycompany.mypackage 中,因此它保存于一个叫做(相对于你的源目录)com/mycompany/mypackage 的目录中。为创建一个叫做 com.mycompany.mypackage.MyResources 的资源包,你应该在目录 com/mycompany/mypackage 中创建下列文件:

MyResources.properties – 包含你的服务器的缺省语言的消息。如果你的缺省语言是英语,你可能有一个这样的条目:
prompt.hello=Hello
MyResources_xx.properties – 包含ISO语言编程为“xx”(查看ResourceBundle的Java文档页面得到一个当前列表的联接)的同样的消息。对于上面的消息的法语版,你可以有这个条目:
prompt.hello=Bonjour
你可以有你需要的任意多的语言的资源包文件。
当你在web应用程序展开描述符中配置controller servlet时,你需要在一个初始化参数中定义的一件事是应用程序的资源包的基础名。在上述的情况中,这应该是 com.mycompany.mypackage.MyResources 。
3.3 表单和FormBean的交互
大部分web开发者曾经使用HTML的标准性能来建立表单,例如使用 <input> 标记。用户希望交互程序具有一定的行为,这些期待中的一个与错误处理有关 — 如果用户出现一个错误,应用程序应该允许他们仅仅修改需要修改的部分 — 而不需要重新敲入当前页面或表单中的任何其它信息。
使用标准的HTML和JSP编程来完全实现这个期望是单调而繁重的。举例来说,一个用户名字段的输入元素看起来可以象是这样(在JSP中)

<input type="text" name="username"
value="<%= loginBean.getUsername() %>">
这很难敲对,会把没有编程概念的HTML开发者搞糊涂,并且会在HTML编辑器中造成问题。取而代之的是,Struts提供了一种全面的基于JSP 1.1的定制标记库功能的机制来建立表单。上面的情况使用Struts处理后将象是这样:
<struts:text name="username"/>
没有必要再显式地涉及到从中获得初始值的JavaBean。这将由框架自动处理。
3.3.1 使用Struts建立表单
一个完整的注册表单将演示Struts相对于直接使用HTML和标准的JSP功能怎样极大地减轻了处理表单的痛苦。考虑以下称为logon.jsp的页面(来自Struts的例子程序):

<%@ page language="java" %><%@ taglib uri="/WEB-INF/struts.tld" prefix="struts" %><html><head><title><struts:message key="logon.title"/></title><body bgcolor="white"><struts:errors/><struts:form action="logon.do" name="logonForm"type="org.apache.struts.example.LogonForm"/><table border="0" width="100%"><tr><th align="right"><struts:message key="prompt.username"/></th><td align="left"><struts:text name="username" size="16"/></td></tr><tr><th align="right"><struts:message key="prompt.password"/></th><td align="left"><struts:password name="password" size="16"/></td></tr><tr><td align="right"><struts:submit><struts:message key="button.submit"/></struts:submit></td><td align="right"><struts:reset><struts:message key="button.reset"/></struts:reset></td></tr></table></struts:form></body></html>

下面的条目基于这个例子演示在Struts中处理表单的关键的特性:
taglib指令告诉JSP页面编译器从哪里找到Struts标记库的 标记库描述符 。在这种情况下,我们使用struts作为前缀来标识来自这个库中的标记,但是可以使用任何你想用的前缀。
这个页面使用了几个 message 标记来从一个包含有这个应用程序所有资源的 MessageResources 对象中查找国际化的消息字符串。为了让这个页面能够工作,以下的消息关键字必须在这些资源中被定义:
logon.title – 注册页面的标题
prompt.username – 一个 “Username:” 提示字符串
prompt.password – 一个 “Password:” 提示字符串
button.submit – “Submit”按钮的标签
button.reset – “Reset”按钮的标签
当用户注册时,应用程序可以在用户的session中保存一个 Locale 对象。这个 Locale 将用来选择适当语言的消息。这使得给用户一个切换语言的可选项实现起来变的容易了 — 仅仅改变保存的 Locale 对象,所有的消息就会自动切换。
errors 标记显示由一个商业逻辑组件保存的任何出错消息,或者如果没有出错消息保存就什么都没有。这个标记将在下面做深入的描述。
form 标记基于指定的属性对一个HTML <form> 元素进行处理。它也将所有在这个表单中的字段与一个保存在关键字 logonForm 下的session范围的FormBean相关联。这个bean用来为所有的具有与bean中的属性名匹配的名字的输入字段提供初始值。如果适当的bean没有找到,一个新的bean将会被自动建立,使用指定的Java类名。
text 标记对一个类型为“text”的HTML <input> 元素进行处理。在这种情况下,占据浏览器屏幕的字符位置的数字也被指定。当页面被执行时,是相对应的bean的 username 属性的当前值(也就是 getUsername() 的返回值)。
password 标记使用方法类似。不同之处是当用户敲入他们的口令时浏览器将回应星号字符,而不是输入值。
submit 和 reset 标记在表单低部生成相应的按钮。每个按钮的文本标签使用 message 标记建立,同时带有提示,这样这些值就是国际化的。
3.3.2 输入字段类型支持
Struts为所有以下类型的输入字段定义了标记,带有与其相应的参考信息的超联接。
checkboxes
hidden 字段
password 输入字段
radio 按钮
reset 按钮
select 列表和嵌入的
options
submit 按钮
text 输入字段
textareas
在所有情况下,一个字段标记都必须嵌套在一个 form 标记中,这样字段才知道使用哪个bean来初始化显示的值。
3.3.3 其它有用的表示标记
在Struts的标记库中有几个其它的标记对于建立用户界面是有帮助的:
enumerate 为一个指定集合的每个元素重复一次标记体(可以是一个Enumeration,一个Hashtable,一个Vector或一个对象数组)。
getProperty 从指定的bean中得到指定的属性,并且在本页面的其余部分作为一个page范围的bean存在。这是访问一个被 enumerate 使用的集合的方便的方法。
ifAttributeExists 只有在一个指定的属性存在于一个指定的范围中时才对标记体求值。
ifAttributeMissing 只有在一个指定的属性不存在于一个指定的范围中时才对标记体求值。
ifParameterEquals 只有在一个指定的请求参数具有一个指定的值时才对标记体求值。
ifParameterNotEquals 只有在一个指定的请求参数不具有一个指定的值或者不存在时才对标记体求值。
ifParameterNotNull 只有在一个指定的请求参数包含在这个请求中并且长度大于0时才对标记体求值。
ifParameterNull 只有在一个指定的请求参数不包含在这个请求中或者长度等于0时才对标记体求值。
iterate 为一个指定集合中的每个元素重复一次标记体(可以是一个Collection,一个Iterator,一个Map,或者一个对象数组)。这个标记在Java2环境中代替了 enumerate 标记。
link 生成一个超联接,当没有cookie支持时自动应用URL编程来维护session状态。
parameter 处理指定请求参数的值,适当地过滤HTML中有特殊含义的字符。
property 显示一个表单中命名的bean属性 — 在属性应该是只读时使用这个标记而不是 text 标记。
3.3.4 自动表单验证
除了上面描述的表单和bean的交互外,如果你的bean知道怎样验证它接收的输入字段,Struts还提供一种附加的机制。为了利用这个特性,使你的bean类实现 ValidatingActionForm 接口,而不是 ActionForm 接口。一个 ValidatingActionForm 增加了一个附加的方法签名:
public String[] validate()
对于一个被controller servlet在bean属性已经组装但是在相应的行为类的 perform() 方法被调用之前调用的方法,validate() 方法有以下可选项:
执行适当的验证发现没有错误 — 返回 null 或者一个非0长度字符串数组,并且controller servlet将继续调用适当的 Action 类的 perform() 方法。
执行适当的验证发现有错误 — 返回一个内容为应该被显示的出错消息关键字(进入应用程序的MessageResources 包)的字符串数组。controller servlet将作为适合于 <struts:errors> 标记使用的请求属性保存这个数组,并且将控制重定向回输入表单(由这个 ActionMapping 的 inputForm 属性标识)。
正如以前提到的,这个特性完全是可选的。如果你的form bean 仅仅实现了 ActionForm 接口,controller servlet将假设任何请求的验证由action类完成。
3.4 其它的表示技术
尽管你的应用程序的外表和感觉可以完全基于标准的JSP能力和Struts的定制标记库构建,你也应该考虑展开其它改进组件重用、减少管理负担或者减少出错的技术。在下面的部分讨论几个可选的技术。
3.4.1 特定于应用程序的定制标记
在使用Struts库提供的定制标记之外,很容易建立特定于你创建的应用程序的标记来帮助建立用户界面。Struts包括的例子程序用建立以下仅用于实现这个应用程序的标记演示了这个原则:
checkLogon – 检查一个特殊的会话对象的存在,如果不存在将控制重定向到注册页面。这是用来捕捉这样的情况,用户在你的应用程序执行的中间把一个页面做成书签并且试图跳过注册,或者用户的会话超时。
linkSubscription – 为一个详细的定单页面生成一个超联接,它将需要的主关键字值作为一个请求属性传递。这在列出与一个用户相关的定单并提供编辑或删除定单的联接时使用。
linkUser – 为一个用户的一个具体的页面生成一个超联接,它将它将需要的主关键字值作为一个请求属性传递。
这些标记的源代码在 src/example 目录中,在包 org.apache.struts.example 里,还带有一些其它的用在这个应用程序中的Java类。
3.4.2 有包含文件的页面组件
在一个JSP文件(包含定制标记和beans用来访问请求的动态数据)中创建完整的表示是一种非常普通的设计方法,在Struts包括的例子程序中被采用。然而很多应用程序要求在单独一个页面中显示你的应用程序的多个逻辑上独立的部分。
举例来说,一个入口应用程序可以在入口的主页面上有一些或者全部以下的功能:

访问这个入口的一个搜索引擎。
一个或更多的“提供新闻”的显示,含有按照用户的注册信息定制的感兴趣的标题。
访问与这个入口相关的讨论的主题。
如果你的入口提供免费邮件帐号,还要有一个“邮件等待”的提示。
如果你能够将工作划分开,分配不同的开发者去做不同的片段,那么这个站点不同片段的开发就会更加简单。然后,你可以使用JSP技术的 include 能力来将这些片段组合进一个单独的页面。有两种 include 可用,依赖于你希望输出的组合发生在什么时间:
include 指令 (<%@ include file="xxxxx" %>)在JSP页面被编译时处理。它用于包括不需要在请求时改变的HTML代码。它把包括进来的文本当作静态文本,很象C或C++中的 #include 指令。
include 行为 (<jsp:include page="xxxxx" flush="true" />)在请求时处理,并且是由服务器透明处理。这意味着你可以通过把它嵌套在一个类似ifParameterEquals的标记中有条件地执行include 。
3.4.3 图片处理组件
一些应用程序要求动态生成图片,就象一个股市报告站点的价格图一样。通常使用两种不同的方法来实现这个需求:
处理一个执行一个servlet请求的URL的超联接。这个servlet将使用一个图象库来生成图片,设置适当的content类型(例如 image/gif),并且将图片的字节流发送回浏览器。浏览器就会象从一个静态文件中接收到的一样显示图片。
处理HTML代码需要下载一个创建请求的图象的Java applet。你可以通过为在处理的代码中的这个applet设置适当的初始化参数配置这个图象,或者你可以让这个applet与服务器建立自己联接来接收这些参数。
4. 创建Controller组件
4.1 概述
现在我们理解了怎样构造你的应用程序的Model和View组件,现在是集中到 Controller 组件的时候了。Struts包括一个实现映射一个请求URI到一个行为类的主要功能的servlet。因此你的与Controller有关的主要责任是:
为每一个可能接收的逻辑请求写一个 Action 类(也就是,一个 Action 接口的实现)
写一个定义类名和与每个可能的映射相关的其它信息的 ActionMapping 类(也就是,一个 ActionMapping 接口的实现)
写行为映射配置文件(用XML)用来配置controller servlet。
为你的应用程序更新web应用程序展开描述符文件(用XML)用来包括必需的Struts组件。
给你的应用程序添加适当的Struts组件。
4.2 Action类
Action 接口定义一个单一的必须由一个 Action 类实现的方法,就象下面这样:
public ActionForward perform(ActionServlet servlet,
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException;
一个 Action 类的目标是处理这个请求,然后返回一个标识JSP页面的 ActionForward 对象,控制应该重定向这个JSP页面以生成相应的响应。在 Model 2 设计模式中,一个典型的 Action 类将在它的 perform() 方法中实现下面的逻辑:
验证用户session的当前状态(例如,检查用户已经成功地注册)。如果 Action 类发现没有注册存在,请求应该重定向到显示用户名和口令用于注册的JSP页面。应该这样做是因为用户可能试图从“中间”(也就是,从一个书签)进入你的应用程序,或者因为session已经超时并且servlet容器创建了一个新的session。
如果验证还没有发生(由于使用一个实现 ValidatingActionForm 接口的form bean),验证这个 form bean 的属性是必须的。如果发现一个问题,当作一个请求属性保存合适的出错信息关键字,然后将控制重定向回输入表单这样错误可以被纠正。
执行要求的处理来处理这个请求(例如在数据库里保存一行)。这可以用嵌入 Action 类本身的代码来完成,但是通常应该调用一个商业逻辑bean的一个合适的方法来执行。
更新将用来创建下一个用户界面页面的服务器端对象(典型情况下是request范围或session范围beans,定义你需要在多长时间内保持这些项目可获得)。
返回一个标识生成响应的JSP页面的适当的 ActionForward 对象,基于新近更新的beans。典型情况下,你将通过在你接收到的 ActionMapping 对象(如果你使用一个局部于与这个映射上的逻辑名)或者在controller servlet 本身(如果你使用一个全局于应用程序的逻辑名)上调用 findForward() 得到一个对这样一个对象的引用。
当为 Action 类编程时要记住的设计要点包括以下这些:
controller servlet仅仅创建一个你的 Action 类的实例,用于所有的请求。这样你需要编写你的 Action 类使其能够在一个多线程环境中正确运行,就象你必须安全地编写一个servlet的 service() 方法一样。
帮助线程安全编程的最重要的原则就是在你的 Action 类中仅仅使用局部变量而不是实例变量。局部变量创建于一个分配给(由你的JVM)每个请求线程的栈中,所以没有必要担心会共享它们。
尽管不应该,代表你的系统中Model部分的的beans仍有可能抛出违例。你应该在你的 perform() 方法的逻辑中捕捉所有这样的违例,并且通过执行以下语句将它们记录在应用程序的日志文件中(包括相应的栈跟踪信息):
servlet.log("Error message text", exception);
作为一个通用的规则,分配很少的资源并在来自同一个用户(在用户的session中)的请求间保持它们会导致可伸缩性的问题。你应该在将控制重定向到适当的View组件前努力释放这样的资源(例如数据库联接) — 甚至在你调用的一个bean抛出了一个违例时。
另外,你将会想要防止出现非常大的 Action 类。最简单的实现途径是将你的功能逻辑嵌入到 Action 类本身,而不是将其写在独立的商业逻辑beans中。除了使 Action 类难于理解和维护外,这种方法也使得难于重用这些商业逻辑代码,因为代码被嵌入到一个组件(Action 类)中并被捆绑运行于web应用程序环境中。
包括在Struts中的例子程序某种程度上延伸了这个设计原则,因为商业逻辑本身是嵌入到 Action 类中的。这应该被看作是在这个样本应用程序设计中的一个bug,而不是一个Struts体系结构中的固有特性,或者是一个值得仿效的方法。

4.3 ActionMapping实现
为了成功地运行,Struts的controller servlet需要知道关于每个URI该怎样映射到一个适当的 Action 类的几件事。需要了解的知识封装在一个叫做 ActionMapping 的Java接口中,它有以下属性:
actionClass – 用于这个映射的 Action 类完整的Java类名。第一次一个特定的映射被使用,一个这个类的实例将被创建并为以后重用而保存。
formAttribute – session范围的bean的名字,当前的这个映射的 ActionForm 被保存在这个bean之下。如果这个属性没有被定义,没有 ActionForm 被使用。
formClass – 用于这个映射的 ActionForm 类完整的Java类名。如果你在使用对form beans的支持,这个类的一个实例将被创建并保存(在当前的用户会话中)
path – 匹配选择这个映射的请求的URI路径。看下面如何匹配的例子。
Struts在一个叫做 ActionMappingBase 的类中包括了一个 ActionMapping 接口的方便的实现。如果你不需要为你自己的映射定义任何附加的属性,尽管把这个类作为你的 ActionMapping 类好了,就向下面部分描述的那样配置。然而,定义一个 ActionMapping 实现(多半是扩展 ActionMappingBase 类)来包含附加的属性也是可能的。controller servlet知道怎样自动配置这些定制属性,因为它使用Struts的Digester模块来读配置文件。
包括在Struts的例子程序中,这个特性用来定义两个附加的属性:

failure – 如果Action类检测到它接收的输入字段的一些问题,控制应该被重定向到的上下文相关的URI。典型情况下是请求发向的JSP页面名,它将引起表单被重新显示(包含Action类设置的出错消息和大部分最近的来自ActionForm bean的输入值)。
success – 如果Action类成功执行请求的功能,控制应该被重定向到的上下文相关的URI。典型情况下是准备这个应用程序的会话流的下一个页面的JSP页面名。
使用这两个额外的属性,例子程序中的 Action 类几乎完全独立于页面设计者使用的实际的JSP页面名。这个页面可以在重新设计时被重命名,然而几乎不会影响到 Action 类本身。如果“下一个”JSP页面的名字被硬编码到 Action 类中,所有的这些类也需要被修改。
4.4 Action映射配置文件
controller servlet怎样知道你想要得到的映射?写一个简单地初始化新的 ActionMapping 实例并且调用所有适当的set方法的小的Java类是可能的(但是很麻烦)。为了使这个处理简单些,Struts包括一个Digester模块能够处理一个想得到的映射的基于XML的描述,同时创建适当的对象。看 API 文档 以获得关于Digester更多的信息。
开发者的责任是创建一个叫做 action.xml 的XML文件,并且把它放在你的应用程序的WEB-INF目录中。(注意这个文件并不需要 DTD,因为实际使用的属性对于不同的用户可以是不同的)最外面的XML元素必须是<action-mappings>,在这个元素之中是嵌入的0个或更多的 <action> 元素 — 每一个对应于你希望定义的一个映射。

来自例子程序的 action.xml 文件包括“注册”功能的以下映射条目,我们用来说明这个需求:

<action-mappings><forward name="logon" path="/logon.jsp"/><action path="/logon"actionClass="org.apache.struts.example.LogonAction"formAttribute="logonForm"formClass="org.apache.struts.example.LogonForm"inputForm="/logon.jsp"><forward name="success" path="/mainMenu.jsp"/></action></action-mappings>

就象你所看到的,这个映射匹配路径 /logon (实际上,因为例子程序使用扩展匹配,你在一个JSP页面指定的请求的URI结束于/logon.do)。当接收到一个匹配这个路径的请求时,一个 LogonAction 类的实例将被创建(仅仅在第一次)并被使用。controller servlet将在关键字 logonForm 下查找一个session范围的bean,如果需要就为指定的类创建并保存一个bean。
这个 action 元素也定义了一个逻辑名“success”,它在 LogonAction 类中被用来标识当一个用户成功注册时使用的页面。象这样使用一个逻辑名允许将 action 类隔离于任何由于重新设计位置而可能发生的页面名改变。

这是第二个在任何 action 之外宣告的 forward 元素,这样它就可以被所有的action全局地获得。在这个情况下,它为注册页面定义了一个逻辑名。当你调用 mapping.findForward() 时在你的 action 代码中,Struts首先查找这个action本地定义的逻辑名。如果没有找到,Struts会自动为你查找全局定义的逻辑名。

4.5 Web应用程序展开描述符
设置应用程序最后的步骤是配置应用程序展开描述符(保存在文件WEB-INF/web.xml中)以包括所有必需的Struts组件。作为一个指南使用例子程序的展开描述符,我们看到下面的条目需要被创建或修改。
4.5.1 配置Action Servlet实例
添加一个条目定义action servlet本身,同时包括适当的初始化参数。这样一个条目看起来象是这样:

<servlet><servlet-name>action</servlet-name><servlet-class>org.apache.struts.action.ActionServlet</servlet-class><init-param><param-name>application</param-name><param-value>org.apache.struts.example.ApplicationResources</param-value></init-param><init-param><param-name>config</param-name><param-value>/WEB-INF/action.xml</param-value></init-param><init-param><param-name>debug</param-name><param-value>2</param-value></init-param><init-param><param-name>mapping</param-name><param-value>org.apache.struts.example.ApplicationMapping</param-value></init-param><load-on-startup>2</load-on-startup></servlet>

controller servlet支持的初始化参数在下面描述,拷贝自 ActionServlet 类的 Javadocs 。方括号描述如果你没有为那个初始化参数提供一个值时假设的缺省值。
application – 应用程序资源包基类的Java类名。[NONE].
config – 包含配置信息的XML资源的上下文相关的路径。[/WEB-INF/action.xml]
debug – 这个servlet的调试级别,它控制记录多少信息到日志中。[0]
digester – 我们在 initMapping() 中利用的Digester的调试级别,它记录到System.out而不是servlet的日志中。[0]
forward – 使用的ActionForward实现的Java类名。[org.apache.struts.action.ActionForward]
mapping – 使用的ActionMapping实现的Java类名。[org.apache.struts.action.ActionMappingBase]
nocache – 如果设置为 true,增加HTTP头信息到所有响应中使浏览器对于生成或重定向到的任何响应不做缓冲。[false]
null – 如果设置为 true,设置应用程序资源使得如果未知的消息关键字被使用则返回 null。否则,一个包括不欢迎的消息关键字的出错消息将被返回。[true]
4.5.2 配置Action Servlet映射
有两种通常的方法来定义将被controller servlet处理的URL — 前缀匹配和扩展匹配。每种方法的一个适当的映射条目将在下面被描述。
前缀匹配意思是你想让所有以一个特殊值开头(在上下文路径部分之后)的URL传递给这个servlet。这样一个条目看起来可以象是这样:

<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>/execute/*</url-pattern>
</servlet-mapping>
它意味着一个匹配前面描述的 /logon 路径的请求的URL看起来象是这样:
http://www.mycompany.com/myapplication/execute/logon
这里 /myapplicationis 是你的应用程序展开所在的上下文路径。
另一方面,扩展映射基于URL以一个跟着定义的一组字符的句点结束的事实而将URL匹配到action servlet 。例如,JSP处理servlet映射到 *.jsp 模式这样它在每个JSP页面请求时被调用。为了使用 *.do 扩展(它意味着“做某件事”)映射条目看起来应该象是这样:

<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
并且一个匹配以前描述的 /logon 路径的请求的URI可以看起来象是这样:
http://www.mycompany.com/myapplication/logon.do
4.5.3 配置Struts标记库
下一步,你必须添加一个定义Struts标记库的条目。这个条目看起来应该象是这样:
<taglib>
<taglib-uri>/WEB-INF/struts.tld</taglib-uri>
<taglib-location>/WEB-INF/struts.tld</taglib-location>
</taglib>
它告诉JSP系统到哪里去找这个库的标记库描述符(在你的应用程序的WEB-INF目录,而不是在外部互联网上的某个地方)。
4.5.4 添加Struts组件到你的应用程序中
为了在你的应用程序运行时使用Struts,你必须将 struts.tld 文件拷贝到你的 WEB-INF 目录,将 struts.jar 文件拷贝到你的 WEB-INF/lib 。

痴人说梦

世界上没有完美的事,想的太理想只能让自己更受伤,不要再想那么多了,顺其自然!

你如果没有勇气,那你就什么都干不成!

温室里的花儿啊,你什么时候才能经得起风吹雨打?!

只要勇敢的迈出第一步,你就不会再害怕!

精诚所至,金石为开!

在这个月的最后一天,我写下这些梦话,希望下个月有个新的开始。

在这里,等你……

深入equals方法

equals方法的重要性毋须多言,只要你想比较的两个对象不愿是同一对象,你就应该实现equals方法,让对象用你认为相等的条件来进行比较。

  下面的内容只是API的规范,没有什么太高深的意义,但我之所以最先把它列在这儿,是因为这些规范在事实中并不是真正能保证得到实现。

  • 对于任何引用类型, o.equals(o) == true成立。
  • 如果 o.equals(o1) == true 成立,那么o1.equals(o)==true也一定要成立。
  • 如果 o.equals(o1) == true 成立且 o.equals(o2) == true 成立,那么o1.equals(o2) == true 也成立。
  • 如果第一次调用o.equals(o1) == true成立再o和o1没有改变的情况下以后的任何次调用都成立。
  • o.equals(null) == true 任何时间都不成立。

  以上几条规则并不是最完整的表述,详细的请参见API文档。

  对于Object类,它提供了一个最最严密的实现,那就是只有是同一对象是,equals方法才返回true,也就是人们常说的引用比较而不是值比较。这个实现严密得已经没有什么实际的意义,所以在具体子类(相对于Object来说)中,如果我们要进行对象的值比较,就必须实现自己的equals方法。

  先来看一下以下这段程序:

 public boolean equals(Object obj)
 {
 if (obj == null) return false;
 if (!(obj instanceof FieldPosition))
 return false;
 FieldPosition other = (FieldPosition) obj;
 if (attribute == null) {
 if (other.attribute != null) {
 return false;
 }
 }
 else if (!attribute.equals(other.attribute)) {
 return false;
 }
 return (beginIndex == other.beginIndex
 && endIndex == other.endIndex
 && field == other.field);
 }

  这是JDK中java.text.FieldPosition的标准实现,似乎没有什么可说的. 我相信大多数或绝大多数程序员认为,这是正确的合法的equals实现.毕竟它是JDK的API实现啊. 还是让我们以事实来说话吧:

Quote
package debug;
import java.text.*;
public class Test {
public static void main(String[] args) {
FieldPosition fp = new FieldPosition(10);
FieldPosition fp1 = new MyTest(10);
System.out.println(fp.equals(fp1));
System.out.println(fp1.equals(fp));
}
}
class MyTest extends FieldPosition{
int x = 10;
public MyTest(int x){
super(x);
this.x = x;
}
public boolean equals(Object o){
if(o==null) return false;
if(!(o instanceof MyTest )) return false;
return ((MyTest)o).x == this.x;
}
}

   运行一下看看会打印出什么:

System.out.println(fp.equals(fp1));打印true
System.out.println(fp1.equals(fp));打印flase  两个对象,出现了不对称的equals算法.问题出在哪里(脑筋急转弯:当然出在JDK实现的BUG)?

  我相信有太多的程序员(除了那些根本不知道实现equals方法的程序员外)在实现equals方法时都用过instanceof运行符来进行短路优化的,实事求是地说很长一段时间我也这么用过。太多的教程,文档都给了我们这样的误导。而有些稍有了解的程序员可能知道这样的优化可能有些不对但找不出问题的关键。另外一种极端是知道这个技术缺陷的骨灰级专家就提议不要这样应用。

  我们知道,"通常"要对两个对象进行比较,那么它们"应该"是同一类型。所以首先利用nstanceof运行符进行短路优化,如果被比较的对象不和当前对象是同一类型则不用比较返回false,但事实上,"子类是父类的一个实例",所以如果 子类 o instanceof 父类,始终返回true,这时肯定不会发生短路优化,下面的比较有可能出现多种情况,一种是不能造型成子类而抛出异常,另一种是父类的private 成员没有被子类继承而不能进行比较,还有就是形成上面这种不对称比较。可能会出现太多的情况。

  那么,是不是就不能用 instanceof运行符来进行优化?答案是否定的,JDK中仍然有很多实现是正确的,如果一个class是final的,明知它不可能有子类,为什么不用 instanceof来优化呢?

  为了维护SUN的开发小组的声誉,我不说明哪个类中,但有一个小组成员在用这个方法优化时在后加加上了加上了这样的注释:

if (this == obj) // quick check
return true;
if (!(obj instanceof XXXXClass)) // (1) same object?
return false;  可能是有些疑问,但不知道如何做(不知道为什么没有打电话给我……)

  那么对于非final类,如何进行类型的quick check呢?

if(obj.getClass() != XXXClass.class) return false;
  用被比较对象的class对象和当前对象的class比较,看起来是没有问题,但是,如果这个类的子类没有重新实现equals方法,那么子类在比较的时候,obj.getClass() 肯定不等于XXXCalss.class,也就是子类的equals将无效,所以if(obj.getClass() != this.getClass()) return false;才是正确的比较。
另外一个quick check是if(this==obj) return true;

  是否equals方法一定比较的两个对象就一定是要同一类型?上面我用了"通常",这也是绝大多数程序员的愿望,但是有些特殊的情况,我们可以进行不同类型的比较,这并不违反规范。但这种特殊情况是非常罕见的,一个不恰当的例子是,Integer类的equals可以和Sort做比较,比较它们的value是不是同一数学值。(事实上JDK的API中并没有这样做,所以我才说是不恰当的例子)。在完成quick check以后,我们就要真正实现你认为的“相等”。对于如果实现对象相等,没有太高的要求,比如你自己实现的“人”类,你可以认为只要name相同即认为它们是相等的,其它的sex,ago都可以不考虑。这是不完全实现,但是如果是完全实现,即要求所有的属性都是相同的,那么如何实现equals方法?

class Human{
private String name;
private int ago;
private String sex;
………………..
public boolean equals(Object obj){
quick check…….
Human other = (Human)ojb;
return this.name.equals(other.name)
&& this.ago == ohter.ago
&& this.sex.equals(other.sex);
}
}
   这是一个完全实现,但是,有时equals实现是在父类中实现,而要求被子类继承后equals能正确的工作,这时你并不事实知道子类到底扩展了哪些属性,所以用上面的方法无法使equals得到完全实现。

  一个好的方法是利用反射来对equals进行完全实现:

public boolean equals(Object obj){
quick check…….
Class c = this.getClass();
Filed[] fds = c.getDeclaredFields();
for(Filed f:fds){
if(!f.get(this).equals(f.get(obj)))
return false;
}
return true;
}
  为了说明的方便,上明的实现省略了异常,这样的实现放在父类中,可以保证你的子类的equals可以按你的愿望正确地工作。

  关于equals方法的最后一点是:如果你要是自己重写(正确说应该是履盖)了equals方法,那同时就一定要重写hashCode().为是规范,否则………….

  我们还是看一下这个例子:

public final class PhoneNumber {
private final int areaCode;
private final int exchange;
private final int extension;
public PhoneNumber(int areaCode, int exchange, int extension) {
rangeCheck(areaCode, 999, "area code");
rangeCheck(exchange, 99999999, "exchange");
rangeCheck(extension, 9999, "extension");
this.areaCode = areaCode;
this.exchange = exchange;
this.extension = extension;
}
private static void rangeCheck(int arg, int max, String name) {
if(arg < 0 || arg > max)
throw new IllegalArgumentException(name + ": " + arg);
}
public boolean equals(Object o) {
if(o == this)
return true;
if(!(o instanceof PhoneNumber))
return false;
PhoneNumber pn = (PhoneNumber)o;
return pn.extension == extension && pn.exchange == exchange && pn.areaCode == areaCode;
}
}
  注意这个类是final的,所以这个equals实现没有什么问题。

  我们来测试一下:

public static void main(String[] args) {
Map hm = new HashMap();
PhoneNumber pn = new PhoneNumber(123, 38942, 230);
hm.put(pn, "I love you");
PhoneNumber pn1 = new PhoneNumber(123, 38942, 230);
System.out.println(pn);
System.out.println("pn.equals(pn1) is " + pn.equals(pn1));
System.out.println(hm.get(pn1));
System.out.println(hm.get(pn));
}
  既然pn.equals(pn1),那么我put(pn,"I love you");后,get(pn1)这什么是null呢?

  答案是因为它们的hashCode不一样,而hashMap就是以hashCode为主键的。

  所以规范要求,如果两个对象进行equals比较时如果返回true,那么它们的hashcode要求返回相等的值。

  好了,休息,休息一下。。。。。。。。。。。。。。。。

  转载自dev2dev网友axman的go deep into java专栏。

安装OpenCms6.2.2(利用jTDS驱动)

昨儿个从网上DOWN了个OpenCms,感觉不错,于是乎,安装!到了连接数据库这一步,选择MS SQL Server以后就是连接不上,说是数据库已关闭,或是提供的连接语句无法连接数据库,推荐使用jTDS,什么是jTDS,百度一下,原来也是个数据库驱动,于是乎,下载!

http://www.ishere.cn/uploads/200610/26_114835_login_logo.png

OpenCms6.2.2下载地址:
http://www.opencms.org/export/download/opencms/opencms_6.2.2.zip

jTDS1.2下载地址:
http://sourceforge.net/project/showfiles.php?group_id=33291

下载下来后,解压,将包里的jtds-1.2.jar拷贝到 D:/program files/Java/Tomcat5.5/Common/lin/目录下,
数据库连接语句改为:jdbc:jtds:sqlserver://localhost:1433;DatabaseName=yourDataBase

OK,然后再从OpenCms里进行安装,哈哈,成了!!!

通过JDBC实现JAVA连接SQL Server 2000(实例讲解)

注意,sql server 2000必须是sp3

用jdbc访问数据库

(1) 该实例已经运行通过

jsp连接Sql Server7.0/2000数据库
  testsqlserver.jsp如下:
  <%@ page contentType="text/html;charset=gb2312"%>
  <%@ page import="java.sql.*"%>
  <html>
  <body>
  <%Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver").newInstance();
  String url="jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=MDB"; //MDB为你的数据库的
  String user="sa";
  String password="123456";
  
  Connection conn= DriverManager.getConnection(url,user,password);
  Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
  String sql="select * from test";
  ResultSet rs=stmt.executeQuery(sql);
  while(rs.next()) {%>
  您的第一个字段内容为:<%=rs.getString(1);%>
  您的第二个字段内容为:<%=rs.getString(2);%>
  <%}%>
  <%out.print("数据库操作成功,哈哈");%>
  <%rs.close();
  stmt.close();
  conn.close();
  
  %>
  </body>
  </html>

(2)java访问sqlserver服务器

第一步:安装jdbc

点击SQL Server for JDBC驱动程序安装程序setup.exe(可以到微软网站下载 http://msdn.microsoft.com/library/default.asp?rul=/downloads/list/sqlserver.asp下载)

第二步:设置系统变量classpath

假设SQL Server for JDBC 驱动程序安装在d:jdbc,则classpath应该设置如下:

classpath:=.;…;d:jdbclib; d:jdbclibmssqlserver.jar; d:jdbclibmsutil.jar; d:jdbclibmsbase.jar;

注意:设置时要在最前面的点号和分号

第三步:编辑java程序并且运行

实例1如下:

import java.sql.*;

import java.net.URL;

class insert

{
public static void main(String[] args)
{
String url="jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=northwind";

String query="select * from categories";

String query1="insert categories values(10,’Hanbao’,'Sweet’)";

String query2="insert categories values(11,’Naicha’,'Coffee taste’)";

try

{

Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver");

Connection con=DriverManager.getConnection(url,"sa","739555");

Statement stmt=con.createStatement();

stmt.executeUpdate(query1);

stmt.executeUpdate(query2);

stmt.close();

con.close();

}

catch(SQLException ex)
{

}

catch(java.lang.Exception ex)
{
ex.printStackTrace();
}
}
}

实例2如下:

import java.sql.*;

import java.net.URL;

class java2sqlserver
{
public static void main(String[] args)

{
String url="jdbc:microsoft:sqlserver://localhost:1433;User=sa;Password=739555;DatabaseName=northwind";

String query="Select * From Categories";

try

{

Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver");

//DriverManager.setLogStream(System.out);

Connection con=DriverManager.getConnection(url);

checkForWarning(con.getWarnings());

Statement stmt=con.createStatement();

ResultSet rs=stmt.executeQuery(query);

dispResultSet(rs);

rs.close();

stmt.close();

con.close();

}

catch(SQLException ex)

{

System.out.println(ex.toString()+"—-SQLException caught—-");

while(ex!=null)

{

System.out.print("SQLState:"+ex.getSQLState());

System.out.print("Message:"+ex.getMessage());

System.out.print("Vendor:"+ex.getErrorCode());

ex=ex.getNextException();

System.out.println("");

}

}

catch(java.lang.Exception ex)

{

ex.printStackTrace();

}

}

private static boolean checkForWarning(SQLWarning warn)

{
boolean rc=false;

if(warn!=null)

{

System.out.println("—-Warning—-");

rc=true;

while(warn!=null)

{

System.out.print("SQLState:"+warn.getSQLState());

System.out.print("Message:"+warn.getMessage());

System.out.print("Vendor:"+warn.getErrorCode());

System.out.println("");

warn=warn.getNextWarning();

}

}

return rc;

}

private static void dispResultSet(ResultSet rs) throws SQLException

{

int i;

ResultSetMetaData rsmd=rs.getMetaData();

int numCols=rsmd.getColumnCount();

for(i=1;i<=numCols;i++)

{

if(i>1) System.out.print(", ");

System.out.print(rsmd.getColumnLabel(i));

}

System.out.println("");

boolean more=rs.next();

while(more)

{

for(i=1;i<numCols;i++)

{

if(i<1) System.out.print(", ");

System.out.println(rs.getString(i));

}

System.out.println("");

more=rs.next();

}

}

//System.out.println("Hello World!");

}

sql server2000+jdbc常见问题及解决

在通常的理解下,只要我们装了sqlserver driver for jdbc 我们便可进行数据库编程,事实则不然,首先我们看下边的代码:
/***********************************************
/*
/*dbtest.java
/*http://www.ishere.cn
/******************************************* */

import java.sql.*;

public class dbtest {

connection con;
statement sta;
resultset rs;

string driver;
string url;
string user;
string pwd;
public dbtest()
{
driver = “com.microsoft.jdbc.sqlserver.sqlserverdriver”;;
url = “jdbc:microsoft:sqlserver:/localhost:1433;databasename =storemanager”;
user = “sa”;
pwd = “123456″;
init();
}
public void init()
{
try{
class.forname(driver);
system.out.println(“driver is ok”);
con = drivermanager.getconnection(url,user,pwd);
system.out.println(“conection is ok”);
sta = con.createstatement();
rs = sta.executequery(“select * from room”);
while(rs.next())
system.out.println(rs.getint(“roomnum”));
}catch(exception e)
{
e.printstacktrace();
}
}

public static void main(string args [])/自己替换[]
{
new dbtest();
}

这段代码跟得代码与JDBC-ODBC连接方式的差别在于驱动,还有url,这是在使用sqlserver driver for jdbc 中遇到的困惑。按道理讲,上边这段代码应该没错,可首先我们来看一下,如果sql server服务器没有升级到sp3(在使用jdbc时,如果系统是xp或者2003务必要把sqlserver 升级到sp3,网开一面上到处都有下的),我们看看运行结果

driver is ok
java.sql.sqlexception: [microsoft][sqlserver 2000 driver for jdbc]error establis
hing socket.
at com.microsoft.jdbc.base.baseexceptions.createexception(unknown source
)
at com.microsoft.jdbc.base.baseexceptions.getexception(unknown source)
at com.microsoft.jdbc.base.baseexceptions.getexception(unknown source)
at com.microsoft.jdbc.sqlserver.tds.tdsconnection.(unknown source)

at com.microsoft.jdbc.sqlserver.sqlserverimplconnection.open(unknown sou
rce)
at com.microsoft.jdbc.base.baseconnection.getnewimplconnection(unknown s
ource)
at com.microsoft.jdbc.base.baseconnection.open(unknown source)
at com.microsoft.jdbc.base.basedriver.connect(unknown source)
at java.sql.drivermanager.getconnection(drivermanager.java:523)
at java.sql.drivermanager.getconnection(drivermanager.java:171)
at dbtest.init(dbtest.java:32)
at dbtest.(dbtest.java:25)
at dbtest.main(dbtest.java:46)
press any key to continue…

出现上边错误的主要原因是默认的数据库服务器端口 1433没有打开,无法直接连接 。

如果升级到sp3则这个问题可以结决,我们再来看看升级之后,程序运行的结果

driver is ok
conection is ok
java.sql.sqlexception: [microsoft][sqlserver 2000 driver for jdbc][sqlserver]对
象名 'room' 无效。
at com.microsoft.jdbc.base.baseexceptions.createexception(unknown source
)
at com.microsoft.jdbc.base.baseexceptions.getexception(unknown source)
at com.microsoft.jdbc.sqlserver.tds.tdsrequest.processerrortoken(unknown
source)
at com.microsoft.jdbc.sqlserver.tds.tdsrequest.processreplytoken(unknown
source)
at com.microsoft.jdbc.sqlserver.tds.tdsexecuterequest.processreplytoken(
unknown source)
at com.microsoft.jdbc.sqlserver.tds.tdsrequest.processreply(unknown sour
ce)
at com.microsoft.jdbc.sqlserver.sqlserverimplstatement.getnextresulttype
(unknown source)
at com.microsoft.jdbc.base.basestatement.commontransitiontostate(unknown
source)
at com.microsoft.jdbc.base.basestatement.postimplexecu
te(unknown source)

at com.microsoft.jdbc.base.basestatement.commonexecute(unknown source)
at com.microsoft.jdbc.base.basestatement.executequeryinternal(unknown so
urce)
at com.microsoft.jdbc.base.basestatement.executequery(unknown source)
at dbtest.init(dbtest.java:35)
at dbtest.(dbtest.java:25)
at dbtest.main(dbtest.java:46)
press any key to continue…

在这儿,用户已经登陆上去,但是却不能访问里边的数据表,出现这个问题的原因在于sa用户为系统用户,它虽然能够登陆数据库,但是storemanager数据库里边却没有这个用户的访问权限,所以,我们现在为这个数据库重新建立一个用户share ,建立过程如下:在storemanager数据库中选重用户 —〉新建用户 — 〉名称选择(这一步中有两个关键点 1:身份验证选sql身份验证,默认数据库选storemanager)-〉建立新教色share ,此时更改程序,将用户登陆名和密码修改一下,重新运行程序

driver is ok
conection is ok
1001
1002
1003
1004
1005
1006
press any key to continue…

这次顺利通过测试

总结:

sql server和jdbc 的融合问题,关键涉及到sp3补丁(端口开放)还有用户问题,解决这两个问题之后,剩余的便是sqlserver 操作问题了,还有一点在远程操作的时候,要把sqlserver 组设置一下,在安全性里边亦将身份验证更改为sql server 验证即可。

SQL Server安装挂起解决办法

安装SQL 2000数据库和补丁时,如果出现“以前进行的程序安装创建了挂起的文件操作。运行程序之前,必须重新起动计算机。”的提示,这时往往重启无用,解决办法如下:

点击“开始菜单”->“运行”,输入:regedit, 点击确定按钮进入注册表,然后到HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlSession Manager中,删除PendingFileRenameOperations,然后重新安装即可。