专栏首页NetCoreStruts原理与实践

Struts原理与实践

一、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编程,对我们在编程中经常遇到的乱码问题也一同作些分析。 参考文献: 《JSP Web 编程指南》---电子工业出版社 Jayson Falkner等著 司光亚 牛红等译 《Java数据库编程宝典》John O'Donahue等著 甑广启 于耀等译 《Struts in Action》Ted Husted Cedric Dumoulin George Franciscus David Winterfeldt著 《Programming Jakarta Struts》Chuck Cavaness著 《Mastering Jakarta Struts》James Goodwill著 《Struts Kick Start》James Turner Kevin Bedell著

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 终于搞定了网卡 开始正式体验windows7了

    昨天说到网卡驱动没有,今天去下载了一个vista版本的,不过不支持安装,只能在设备管理器里手动来选择驱动程序。 现在可以上网了,试了下IE8,郁闷,开了10次窗...

    脑洞的蜂蜜
  • 利用反射自己写的一个ModelHelper类

    开发中 很多人都会使用BLL Model这种开发,我也是,虽然现在有很多的自动生成工具,能在几秒内生成cs的模板,但我个人还不是很喜欢,我还是喜欢自己一个一个去...

    脑洞的蜂蜜
  • Do You Kown Asp.Net Core - 根据实体类自动创建Razor Page CURD页面模板

    Scaffolding Template Intro 我们知道在Asp.Net MVC中,如果你使用的EF的DBContext的话,你可以在vs中通过右键解决方...

    脑洞的蜂蜜
  • mybatis 详解(一)------JDBC

    1、什么是MyBatis?   MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundat...

    IT可乐
  • 初识数据库连接池开源框架Druid

    Druid是阿里巴巴的一个数据库连接池开源框架,准确来说它不仅仅包括数据库连接池这么简单,它还提供强大的监控和扩展功能。本文仅仅是在不采用Spring框架对Dr...

    用户1148394
  • 使用Python生成pdf文件

        Python平台的优秀PDF报表类库Reportlab。它不属于Python的标准类库,所以必须手动下载类库包并安装:       yum insta...

    py3study
  • 附加文件时候的提示“无法重新生成日志,原因是数据库关闭时存在打开的事务/用户,该数据库没有检查点或者该数据库是只读的 ”

    【SQLServer】【恢复挂起的解决方案】附加文件时候的提示“无法重新生成日志,原因是数据库关闭时存在打开的事务/用户,该数据库没有检查点或者该数据库是只读的...

    逸鹏
  • 一篇文章教会你理解Scrapy网络爬虫框架的工作原理和数据采集过程

    在这春节之际,Python进阶者祝福小伙伴们身体健康,工作顺利,心想事成,万事如意,阖家幸福欢乐,猪年诸事顺心顺利。

    Python进阶者
  • 一篇文章教会你理解Scrapy网络爬虫框架的工作原理和数据采集过程

    Scrapy是一个使用Python编程语言编写的爬虫框架,任何人都可以根据自己的需求进行修改,并且使用起来非常的方便。它可以应用在数据采集、数据挖掘、网络异常用...

    Python进阶者
  • 技术规范(2): 后端技术开发规范

    PEP8 规范: https://www.python.org/dev/peps/pep-0008/

    机械视角

扫码关注云+社区

领取腾讯云代金券