分享
 
 
 

Struts原理与实践(三)

王朝java/jsp·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

一、JDBC的工作原理

Struts在本质上是java程序,要在Struts应用程序中访问数据库,首先,必须搞清楚Java Database Connectivity API(JDBC)的工作原理。正如其名字揭示的,JDBC库提供了一个底层API,用来支持独立于任何特定SQL实现的基本SQL功能。提供数据库访问的基本功能。它是将各种数据库访问的公共概念抽取出来组成的类和接口。JDBC API包括两个包:java.sql(称之为JDBC内核API)和javax.sql(称之为JDBC标准扩展)。它们合在一起,包含了用Java开发数据库应用程序所需的类。这些类或接口主要有:

Java.sql.DriverManager

Java.sql.Driver

Java.sql.Connection

Java.sql.Statement

Java.sql.PreparedStatement

Java.sql.ResultSet等

这使得从Java程序发送SQL语句到数据库变得比较容易,并且适合所有SQL方言。也就是说为一种数据库如Oracle写好了java应用程序后,没有必要再为MS SQL Server再重新写一遍。而是可以针对各种数据库系统都使用同一个java应用程序。这样表述大家可能有些难以接受,我们这里可以打一个比方:联合国开会时,联合国的成员国的与会者(相当我们这里的具体的数据库管理系统)往往都有自己的语言(方言)。大会发言人(相当于我们这里的java应用程序)不可能用各种语言来发言。你只需要使用一种语言(相当于我们这里的JDBC)来发言就行了。那么怎么保证各成员国的与会者都听懂发言呢,这就要依靠同声翻译(相当于我们这里的JDBC驱动程序)。实际上是驱动程序将java程序中的SQL语句翻译成具体的数据库能执行的语句,再交由相应的数据库管理系统去执行。因此,使用JDBC API访问数据库时,我们要针对不同的数据库采用不同的驱动程序,驱动程序实际上是适合特定的数据库JDBC接口的具体实现,它们一般具有如下三种功能:

建立一个与数据源的连接

发送SQL语句到数据源

取回结果集

那么,JDBC具体是如何工作的呢?

Java.sql.DriverManager装载驱动程序,当Java.sql.DriverManager的getConnection()方法被调用时,DriverManager试图在已经注册的驱动程序中为数据库(也可以是表格化的数据源)的URL寻找一个合适的驱动程序,并将数据库的URL传到驱动程序的acceptsURL()方法中,驱动程序确认自己有连接到该URL的能力。生成的连接Connection表示与特定的数据库的会话。Statement(包括PreparedStatement和CallableStatement)对象作为在给定Connection上执行SQL语句的容器。执行完语句后生成ResultSet结果集对象,通过结果集的一系列getter就可以访问表中各列的数据。

这里,是讲的JDBC的基本工作过程,实际应用中,往往会使用JDBC扩展对象如DataSource等,限于篇幅,就不在此详细讨论了。

二、访问数据库所要做的基本配置

我们以访问MS SQL Server2000数据库为例,介绍其基本的配置情况。首先,要到微软网站去下载JDBC的驱动程序,运行setup.exe将得到的三个文件:msbase.jar、mssqlserver.jar及msutil.jar放在/webapps/mystruts/WEB-INF/lib目录下。

在struts-config.xml文件中配置数据源

这里,有一点要引起大家的注意的,就是,struts-config.xml中配置的各个项目是有一定的顺序要求的,几个主要项目的顺序大致是这样的:

data-sources

form-beans

action-mappings

message-resources

plug-in

在配置时要遵守上述顺序

<data-sources>

<data-source key="A" type="org.apache.commons.dbcp.BasicDataSource">

<set-property property="driverClassName"

value="com.microsoft.jdbc.sqlserver.SQLServerDriver" />

<set-property property="url"

value="jdbc:microsoft:sqlserver://127.0.0.1:1433;DatabaseName=mystruts;SelectMethod=cursor" />

<set-property property="username" value="sa" />

<set-property property="password" value="yourpwd" />

<set-property property="maxActive" value="10" />

<set-property property="maxWait" value="5000" />

<set-property property="defaultAutoCommit" value="false" />

<set-property property="defaultReadOnly" value="false" />

</data-source>

</data-sources>

我们来对这段配置代码做一个简单的说明:

这句中,如果您的struts应用程序中只配置一个数据源则key="A"可以不要,而配置多个数据源时就要用这个键值区别,也就是说,可以为一个应用程序配置多个数据源让它访问多个数据库。

<set-property property="url"

value="jdbc:microsoft:sqlserver://127.0.0.1:1433;DatabaseName=mystruts;

SelectMethod=cursor" />

这句中的sqlserver://127.0.0.1:1433;DatabaseName=mystruts;的数据库服务器名(本例是用代表本机的ip地址)和数据库名称要与您的具体情况相同。同时,还要注意访问数据库的用户名和口令也要合乎您的实际情况。

表示最大的活动连接数,这也说明这些连接是池化(pooling)的。

表示对数据库的增、删、改操作必须显式地提交。即必须使用connect.commit();这样的命令才能真正让数据库表中的记录作相应的改变。设置成这样方便用户组织自己的数据库事务。

三、现在我们就来扩展前面我们讲的那个登录的例子,让它访问存储在数据库表中的用户名和口令信息,同时也让它给出的出错信息更明确一些。

为此,我们先要做一些准备工作,如果您还没有安装MS SQL Server2000请先安装,并下载最新的补丁包。再建一个名为mystruts的数据库,并在该数据库中建一个名为userInfo的表,该表有两个字段既:username和password,它们的字段类型都为varchar(10),其中username为主键。在该表中输入一条记录,username和password的字段值分别为lhb和awave。到此准备工作就基本做好了。

为了访问数据库,首先,要修改Action类,修改后的代码清单如下:

package action;

import java.io.IOException;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpSession;

import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.Action;

import org.apache.struts.action.ActionError;

import org.apache.struts.action.ActionErrors;

import org.apache.struts.action.ActionForm;

import org.apache.struts.action.ActionForward;

import org.apache.struts.action.ActionMapping;

import org.apache.struts.action.ActionServlet;

import bussness.UserInfoBo;

import entity.UserInfoForm;

import javax.sql.DataSource;

import java.sql.Connection;

import java.sql.SQLException;

public final class LogonAction extends Action {

public ActionForward execute(ActionMapping mapping,

ActionForm form,

HttpServletRequest request,

HttpServletResponse response)

throws IOException, ServletException {

UserInfoForm userInfoForm = (UserInfoForm) form;

//从web层获得用户名和口令

String username = userInfoForm.getUsername().trim();

String password = userInfoForm.getPassword().trim();

//声明错误集对象

ActionErrors errors = new ActionErrors();

//声明数据源和连接对象

DataSource dataSource;

Connection cnn=null;

//校验输入

if(username.equals("")){

ActionError error=new ActionError("error.missing.username");

errors.add(ActionErrors.GLOBAL_ERROR,error);

}

if(password.equals("")){

ActionError error=new ActionError("error.missing.password");

errors.add(ActionErrors.GLOBAL_ERROR,error);

}

//调用业务逻辑

if(errors.size()==0){

String validated = "";

try{

//取得数据库连接

dataSource = getDataSource(request,"A");

cnn = dataSource.getConnection();

UserInfoBo userInfoBo=new UserInfoBo(cnn);

validated =userInfoBo.validatePwd(username,password);

if(validated.equals("match")){

//一切正常就保存用户信息并转向成功的页面

HttpSession session = request.getSession();

session.setAttribute("userInfoForm", form);

return mapping.findForward("success");

}

}

catch(Throwable e){

//处理可能出现的错误

e.printStackTrace();

ActionError error=new ActionError(e.getMessage());

errors.add(ActionErrors.GLOBAL_ERROR,error);

}

}

//如出错就转向输入页面,并显示相应的错误信息

saveErrors(request, errors);

return new ActionForward(mapping.getInput());

}

}

注意:dataSource = getDataSource(request,"A");这句中,如果配置中只有一个数据源,且没有key="A",则这句应写为dataSource = getDataSource(request);

从清单上可以看出,主要就是增加了访问数据库的代码。同时,我们的业务对象的形式也发生了一个变化,原来没有参数,现在有一个代表数据库连接的参数cnn,因此我们也要对业务对象进行适当地修改。

更改后的业务对象代码清单如下:

package bussness;

import entity.UserInfoForm;

import java.sql.Connection;

import java.sql.SQLException;

import java.lang.Exception;

import db.UserInfoDao;

public class UserInfoBo {

private Connection cnn=null;

public UserInfoBo(Connection cnn){

this.cnn=cnn;

}

public String validatePwd(String username,String password){

String validateResult="";

try{

UserInfoDao userInfoDao = new UserInfoDao(cnn);

validateResult=userInfoDao.validatePwd(username,password);

if(validateResult.equals("error.logon.invalid")){

//如果用户名与口令不匹配则报此错

throw new RuntimeException("error.logon.invalid");

}

else if(validateResult.equals("error.removed.user")){

//如果找不到用户则报此错,这样用户看到的出错信息会更详细

throw new RuntimeException("error.removed.user");

}

}

catch(Exception e){

throw new RuntimeException(e.getMessage());

}

finally{

try{

if(cnn!=null){

cnn.close();

}

}

catch(SQLException sqle){

sqle.printStackTrace();

throw new RuntimeException("error.unexpected");

}

}

return validateResult;

}

}

这个业务对象的代码还是比较简单的,重点要讲的就是它在validatePwd方法中调用了一个名叫UserInfoDao的对象,它就是真正进行数据库操作的数据访问对象。其代码清单如下:

package db;

import entity.UserInfoForm;

import java.sql.*;

public class UserInfoDao {

private Connection con;

public UserInfoDao(Connection con) {

this.con=con;

}

public String validatePwd(String username,String password){

PreparedStatement ps=null;

ResultSet rs=null;

String validated="error.logon.invalid";

UserInfoForm userInfoForm=null;

String sql="select * from userInfo where username=?";

try{

if(con.isClosed()){

throw new IllegalStateException("error.unexpected");

}

ps=con.prepareStatement(sql);

ps.setString(1,username);

rs=ps.executeQuery();

if(rs.next()){

if(!rs.getString("password").trim().equals(password)){

return validated;//口令不正确返回口令不匹配信息

}

else{

validated = "match";//口令正确返回口令匹配信息

return validated;

}

}else{

validated="error.removed.user";//没有找到该用户

return validated;

}

}catch(SQLException e){

e.printStackTrace();

throw new RuntimeException("error.unexpected");

}finally{

try{

if(ps!=null)

ps.close();

if(rs!=null)

rs.close();

}catch(SQLException e){

e.printStackTrace();

throw new RuntimeException("error.unexpected");

}

}

}

}

下面,简单地分析一下数据访问对象的工作过程:

要访问数据库,一般要经历的如下几个步骤:

获得到数据库的连接

创建SQL语句

执行SQL语句

管理结果集

其中,得到数据库的连接本例中是在Action类中完成的,代码如下:

dataSource = getDataSource(request,"A");

cnn = dataSource.getConnection();

Action在调用业务对象时将连接作为一个参数传给业务对象,再由业务对象传给数据库访问对象。

要说明一点的是,要将struts-legacy.jar文件放在/webapps/mystruts/WEB-INF/lib目录下。

我们要在/webapps/mystruts/WEB-INF/classes目录下再建一个名叫db的子目录,将数据访问类以UserInfoDao.java文件名保存在该子目录中。按照上篇文章介绍的方法,编译各个包中的.java文件。就可以启动Tomcat重新运行您的程序了。

细心一点的读者可能都注意到了,到目前为止,我们程序中的各种消息都不是用中文表示的,在下一篇文章中,我们将讨论Struts的国际化编程即所谓的i18n编程,对我们在编程中经常遇到的乱码问题也一同作些分析。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有