Servlet注解的使用,简化配置 以及,使用模板方法设计模式优化oa项目

Servlet注解的使用,简化配置 以及,使用模板方法设计模式优化oa项目

每博一文案

有句谚语说:“一怒之下踢石头,只有痛着脚趾头。”
比一件糟糕的事情更可拍的,是你用糟糕的态度去面对它。看过一个很有意思的故事:
有个男人清早洗漱的时候,把自己的手表放在了桌子上。他的儿子不小心把手表碰倒地上摔坏了,男人
气得儿子揍了一顿,还埋怨妻子没看好儿子,两个人吵了起来。
男人气急败坏地摔门出去,路上想起有一份重要文件忘记带了,他匆忙回家取。可没有人在家,
他只得打电话让妻子回来送钥匙。妻子赶回家时,不小心撞翻了路边的一个小吃摊,赔了一笔钱。
男人没等到妻子回家,因为迟到也遭受了罚款。
费斯汀格法则认为:“10%的生活,由发生在你身上的事情组成,而另外的90%,则取决于你做出的反应。”
这个故事中。摔坏手表就是其中10%,而后面的一系列的事情则是另外的90%,是由埋怨引起的。
面对人生得失时,一颗平常心,比一百种智慧更有力量。
人这一生,得与失,都是常态。不是此处得,彼处失,就是彼处得,此处失,得得失失,失失得得,才构成了“人间事”。
正如席慕容的那首诗《写给幸福》:“挫折会来,也会过去。热泪会流下来,也会收起。没有什么
可以让我气馁的,因为我有着长长的一生。”
别为昨日忧愁,别为琐事烦忧,因为真正的人间清醒,是努力活着。
余生,愿你所有快乐,无需假装;愿你此生尽兴,赤诚善良。
                                   ——————  《一禅心灵庙语》

@

1. web.xml 的缺点分析

分析 oa项目中的 web.xml文件 具体的可以移步至:🔜🔜🔜 使用“纯”Servlet做一个单表的CRUD操作_ChinaRainbowSea的博客-CSDN博客

  • 现在只是一个单标的CRUD,没有复杂的业务逻辑,很简单的一丢丢功能。web.xml文件中就有如此多的配置信息。如果采用这种方式,对于一个大的项目来说,这样的话 web.xml文件会非常庞大,有可能最终会达到几十兆。
  • web.xml文件中进行 servlet信息的配置,显然开发效率比较低,每一个都需要配置一下。
  • 而且在web.xml文件中的配置是很少被修改的,所以这种配置信息能不能直接写到java类当中呢?可以的。

Servlet3.0版本之后,推出了各种Servlet基于注解式开发。优点是什么?

  • 开发效率高,不需要编写大量的配置信息。直接在 java 类上使用注解进行标注。

  • web.xml文件体积变小了。

  • 并不是说注解有了之后,web.xml文件就不需要了:

    • 有一些需要变化的信息,还是要配置到web.xml文件中。一般都是 注解+配置文件 的开发模式。
    • 一些不会经常变化修改的配置建议使用注解。一些可能会被修改的建议写到配置文件中。

2. @WebServlet 注解

注解对象的使用格式:

@注解名称(属性名=属性值, 属性名=属性值, 属性名=属性值....)
// 如果注解当中的属性赋值的类型是数组,格式如下
@注解名称(属性名={属性值1,属性值2,属性值3},属性名=属性值)
// 如果注解当中还有注解的赋值如下:
@注解名称(属性名=属性值,注解名称(属性名=属性值,属性名=属性值),属性名=属性值)

想要了解更多的注解信息的内容,大家可以移步至:🔜🔜🔜 Java “框架 = 注解 + 反射 + 设计模式” 之 注解详解_ChinaRainbowSea的博客-CSDN博客

如下是 @WebServlet 注解基于 Tomcat 10 的源码:


package jakarta.servlet.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebServlet {

    /**
     * @return name of the Servlet
     */
    String name() default "";

    /**
     * A convenience method, to allow extremely simple annotation of a class.
     *
     * @return array of URL patterns
     * @see #urlPatterns()
     */
    String[] value() default {};

    /**
     * @return array of URL patterns to which this Filter applies
     */
    String[] urlPatterns() default {};

    /**
     * @return load on startup ordering hint
     */
    int loadOnStartup() default -1;

    /**
     * @return array of initialization params for this Servlet
     */
    WebInitParam[] initParams() default {};

    /**
     * @return asynchronous operation supported by this Servlet
     */
    boolean asyncSupported() default false;

    /**
     * @return small icon for this Servlet, if present
     */
    String smallIcon() default "";

    /**
     * @return large icon for this Servlet, if present
     */
    String largeIcon() default "";

    /**
     * @return description of this Servlet, if present
     */
    String description() default "";

    /**
     * @return display name of this Servlet, if present
     */
    String displayName() default "";
}

2.1 @WebServlet注解当中常用的一些属性的说明

WebServlet 当中存在着不少的属性,这里我们只介绍一些常用的属性。其他的大家感兴趣的可以去学习。

  • name 属性的作用:用来指定 Servle t的名字。等同于web.xml 当中的 : 。如下图所示的

/**
* @return name of the Servlet
*/
String name() default "";  // default 表示该属性的默认值为 "" 空字符串
  • urlPatterns 属性的作用:用来指定 Servlet 的映射路径 url 。可以指定多个字符串(多个url)。等同于web.xml 当中的 :。如下图所示的.

注意: 需要注意的是:urlPatterns 所赋值的字符串映射的url 路径要带 / 开始的

/**
* @return array of URL patterns to which this Filter applies
*/
String[] urlPatterns() default {};  // 是一个字符串数组的类型,因为一个Servlet 可以有多个映射路径 url
// 注意 urlPatterns 属性值和 web.xml 当中的  <url-pattern> 带 "/" 开始

举例:

package com.RainbowSea.servlet;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;

// 注意: urlPatterns 属性值以 "/" 开始
@WebServlet(name="Test",urlPatterns = {"/test","/test2","/test3"})
public class TestWebServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            IOException {
        // 设置在浏览器端显示的格式类型,以及字符集编码
        response.setContentType("text/html;charSet=UTF-8");
        PrintWriter writer = response.getWriter();

        // 获取到本Servlet Name
        String name = getServletName();
        writer.println("注解当中的name 值也就是web.xml中的<servlet-name>的值: " + name + "<br>");

        // 获取到该类当中 web.xml 中的 url-pattern 的值
        // 如果有多个的话,获取到的是你使用的那一个(在浏览器地址栏上显示的那一个url)
        String servletPath = request.getServletPath();
        writer.println("该类注解当中的urlPatterns也就是web.xml 中的 url-pattern的值:" + servletPath + "<br>");


    }
}

  • value 属性的作用: 和 urlPatterns 属性的作用是一样的:用来指定 Servlet 的映射路径 url 。可以指定多个字符串(多个url)。等同于web.xml 当中的 :
  • 可以使用模糊查询
@WebServlet("/dept/*")  // 可以使用模糊查询,* 任意字符串 ;表示只要是 /dept/xxx的任意都可以访问该Servlet

这里为什么要设置两个作用一样的属性值呢?

因为 是一个 Servlet 当中最常用,而且是必须要有的。如果注解当中只对一个的属性赋值,并且该属性名名为 value的话。那么这个 value 的属性名就可以省略不写。 这里再说一点就是如果一个注解当中属性类型为数组,但是该数组只赋一个值的话,可以省略数组的 {} 花括号。

举例:

package com.RainbowSea.servlet;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;

// 注意: urlPatterns 属性值以 "/" 开始
//@WebServlet(value = {"/test"})
// 可以省略为如下方式: 注解当只对名为 value属性名赋值,可以省略 value 属性名,数组如果只有一个值,可以省略{}
@WebServlet("/test") // 同样url 映射路径是以 "/" 开始的
public class TestWebServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            IOException {
        // 设置在浏览器端显示的格式类型,以及字符集编码
        response.setContentType("text/html;charSet=UTF-8");
        PrintWriter writer = response.getWriter();

        // 获取到该类当中 web.xml 中的 url-pattern 的值
        // 如果有多个的话,获取到的是你使用的那一个(在浏览器地址栏上显示的那一个url)
        String servletPath = request.getServletPath();
        writer.println("该类注解当中的urlPatterns也就是web.xml 中的 url-pattern的值:" + servletPath + "<br>");




    }
}

  • initParams 属性作用:表示的是 Servlet对象的配置信息对象的信息,一个Servlet 就有一个 Servlet 配置对象的信息,封装在了 标签当中的 标签当中设置。如下图所示

注意: initPatams属性的类型是 WebInitParam这个注解 数组。

/**
* @return array of initialization params for this Servlet
*/
WebInitParam[] initParams() default {};

如下是 @WebInitParam的源码:

该@WebInitParam 注解当中的 name 表示:XXX的值,而 value 表示 : XXX/param-value> 的值。

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package jakarta.servlet.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebInitParam {

    /**
     * @return name of the initialization parameter
     */
    String name();

    /**
     * @return value of the initialization parameter
     */
    String value();

    /**
     * @return description of the initialization parameter
     */
    String description() default "";
}

举例:

package com.RainbowSea.servlet;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebInitParam;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;

// 注意: urlPatterns 属性值以 "/" 开始
//@WebServlet(value = {"/test"})
// 可以省略为如下方式: 注解当只对名为 value属性名赋值,可以省略 value 属性名,数组如果只有一个值,可以省略{}
@WebServlet(value = "/test", initParams = {@WebInitParam(name = "user", value = "root"),
        @WebInitParam(name = "password", value = "11235813")}) // 同样url 映射路径是以 "/" 开始的
public class TestWebServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            IOException {
        // 设置在浏览器端显示的格式类型,以及字符集编码
        response.setContentType("text/html;charSet=UTF-8");
        PrintWriter writer = response.getWriter();

        // 获取到该类当中 web.xml 中的 url-pattern 的值
        // 如果有多个的话,获取到的是你使用的那一个(在浏览器地址栏上显示的那一个url)
        String servletPath = request.getServletPath();
        writer.println("该类注解当中的urlPatterns也就是web.xml 中的 url-pattern的值:" + servletPath + "<br>");


        // 获取到初始化参数,对应的一个Servlet 标签当中的 <init-param> 标签当中设置
        String username = getInitParameter("user"); // 根据对应的配置的 name 获取到对应的 value 值
        writer.println("该类注解当中initParams的值也就是本Servlet当中的 配置对象的为 user的值: " + username + "<br>");

        String password = getInitParameter("password");
        writer.println("该类注解当中initParams的值也就是本Servlet当中的 配置对象的为 password的值: " + password + "<br>");


    }
}

  • loadOnStartUp属性的作用:用来指定在服务器启动阶段是否加载该Servlet的构造器(默认是加载Servlet 是不会立即就调用其中的构造器的,而是访问的时候才会调用该Servlet的构造器)。等同于:整数值 其中数值越小,优先被调用执行。
/**
* @return load on startup ordering hint
 */
int loadOnStartup() default -1;

举例:

package com.RainbowSea.servlet;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebInitParam;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;


@WebServlet(value = "/test",loadOnStartup = 1) 
public class TestWebServlet extends HttpServlet {
    
    public TestWebServlet() n{
        System.out.println("TestWebServlet 的构造器执行");
    }
}

3. 使用模板方法设计模式优化oa项目

注意:建议: 如果你阅读到这里时,非常感谢您的大力支持,如果还要继续阅读的话,建议先移步至:🔜🔜🔜 使用“纯”Servlet做一个单表的CRUD操作_ChinaRainbowSea的博客-CSDN博客 博客,方便后续的内容上的理解阅读。

上面的@WebServlet 注解解决了配置文件的问题。但是现在的oa项目仍然存在一个比较臃肿的问题。

  • 一个单标的CRUD,就写了6个Servlet。如果一个复杂的业务系统,这种开发方式,显然会导致类爆炸。(类的数量太大。)

  • 怎么解决这个类爆炸问题?可以使用模板方法设计模式(定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。"父类定义骨架,子类实现某些细节。")。具体的模板方法设计模式,大家可以移步至:🔜🔜🔜 23种设计模式之 : 模板方法设计模式_ChinaRainbowSea的博客-CSDN博客

怎么解决类爆炸问题?

  • 以前的设计是一个请求一个 Servlet类。1000个请求对应1000个Servlet类。导致类爆炸。
  • 可以这样做:一个请求对应一个方法。一个业务对应一个Servlet类。比如:处理部门相关业务的对应一个DeptServlet。处理用户相关业务的对应一个UserServlet。处理银行卡卡片业务对应一个CardServlet

思想:

模板方法:父类定义骨架,子类实现某些细节。而这里我们将骨架定义为一个核心的方法也就是这里重写的父类中的 protected void service(HttpServletRequest request, HttpServletResponse response)的方法,需要注意的是:重写的 service 就没有 405 错误的提示了。

这里我们使用@WebServlet 注解的方式,进行一个 url 映射路径的配置。

该核心方法思路是: 通过浏览器地址栏上访问的不同的 url ,对应不同的功能访问。我们就可以使用request.getServletPath()获取到浏览器地址栏上的 url 的字符串,再根据获取到的不同的 url 字符串进行一个功能上的匹配equals 对应不同的功能,我们使用方法将该功能实现。

可以为 value 属性值设置为 模糊查询

@WebServlet("/dept/*") // 可以使用模糊查询,* 任意字符串,表示/dept/xxx的任意都可以访问该Servlet

具体代码如下:

package com.RainbowSea.oa;

import com.RainbowSea.DBUtil.DBUtil;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;


/**
 * 采用模板方法设计模式,重新设计一个 OA 系统
 */

//@WebServlet(value = {"/dept/list","/dept/save","/dept/edit","/dept/detail","/dept/delete","/dept/modify"}

//@WebServlet({"/dept/list", "/dept/save", "/dept/edit", "/dept/detail", "/dept/delete", "/dept/modify"})
@WebServlet("/dept/*") // 可以使用模糊查询,* 任意字符串,表示/dept/xxx的任意都可以访问该Servlet
public class DeptServlet extends HttpServlet {

    // 模板方法
    // 重写其中的 servlet 方法(并没有重写其中的doGet()方法,405错误没有了)

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            IOException {

        // 获取到该对应请求地址栏上的 url ,也就是一个servet对应当中的 url 映射路径
        // 这里是对应浏览器地址栏上的 url
        String servletPath = request.getServletPath();  // 返回的 url 的开头是带了 "/" 的

        if ("/dept/list".equals(servletPath)) {
            doList(request, response);
        } else if ("/dept/save".equals(servletPath)) {
            doSave(request, response);
        } else if ("/dept/edit".equals(servletPath)) {
            doEdit(request, response);
        } else if ("/dept/detail".equals(servletPath)) {
            doDetail(request, response);
        } else if ("/dept/delete".equals(servletPath)) {
            doDel(request, response);
        } else if ("/dept/modify".equals(servletPath)) {
            doModify(request, response);
        }

    }


    private void doList(HttpServletRequest request, HttpServletResponse response) throws IOException {

        // 设置前端浏览器显示的格式类型,以及编码
        response.setContentType("text/html;charSet=UTF-8");
        PrintWriter writer = response.getWriter();
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        // 获取到该webapp的项目根路径:也就是在Tomcat 当中设置的访问的项目路径
        String contextPath = request.getContextPath();


        int i = 0;

        writer.println("     <!DOCTYPE html>");
        writer.println("<html lang='en'>");

        writer.println("<head>");
        writer.println("    <meta charset='UTF-8'>");
        writer.println("   <title>部门列表页面</title>");
        writer.println("</head>");

        writer.println("    <script type = 'text/javascript' >");
        writer.println("            function del(dno) {");
        // 弹出确认框,用户点击确定,返回true,点击取消返回false
        writer.println("        var ok = window.confirm('亲,删了不可恢复哦!');");
        writer.println("        if (ok) {");
        // 发送请求进行删除数据的操作
        // 在js代码当中如何发送请求给服务
        // document.location.href='请求路径
        // document.location = '请求路径'")
        // window.location.href = '请求路径
        // window.location = '请求路径'
        // 注意是根据所传的部门编号删除数据的
        writer.println("             document.location.href = '" + contextPath + "/dept/delete?deptno=' + dno");
        writer.println("          }");
        writer.println("       }");
        writer.println("</script >");

        writer.println("<body>");
        writer.println("    <h1 align='center'>部门列表</h1>");
        writer.println("   <table border='1px' align='center' width='50%'>");
        writer.println("      <tr>");
        writer.println("          <th>序号</th>");
        writer.println("         <th>部门编号</th>");
        writer.println("         <th>部门名称</th>");
        writer.println("     </tr>");

        try {
            // 连接数据库,查询所有部门:
            // 1. 注册驱动,获取连接
            connection = DBUtil.getConnection();
            // 2. 获取操作数据库对象,预编译sql语句
            String sql = "select depton as det,dname,loc from dept"; // 在mysql中测试一下是否正确
            preparedStatement = connection.prepareStatement(sql);

            // 3. 执行sql语句
            resultSet = preparedStatement.executeQuery();

            // 4. 处理查询结果集
            while (resultSet.next()) {
                String det = resultSet.getString("det");  // 有别名要使用别名
                String dname = resultSet.getString("dname");
                String loc = resultSet.getString("loc");

                writer.print("			<tr>");
                writer.print("				<td>" + (++i) + "</td>");
                writer.print("				<td>" + det + "</td>");
                writer.print("				<td>" + dname + "</td>");
                writer.print("				<td>");
                writer.print("					<a href='javascript:void(0)' onclick= 'del(" + det + ")'>删除</a>");
                // 将部门编号传过去,用户数据库查询修改
                writer.print("					<a href='" + contextPath + "/dept/edit?deptno=" + det + "'>修改</a>");
                //注意这里的是前端的资源,需要加项目名,但是这里的项目名我们通过 getContestPath()方法动态获取
                // 并且将部门名传过去,再从数据库当中查找出来对应的部门的详细信息:注意: ?(间隔) Http传输协议
                writer.print("					<a href='" + contextPath + "/dept/detail?deptno=" + det + "'>详情</a>");
                writer.print("				</td>");
                writer.print("			</tr>");
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {

            // 5. 关闭资源
            DBUtil.close(connection, preparedStatement, resultSet);
        }

        writer.println("</table>");
        writer.println("<hr>");
        // 前端的资源路径访问需要加项目名
        writer.println("<a href='" + contextPath + "/add.html'>新增部门</a>");
        writer.println("</body>");
        writer.println("</html>");

    }


    private void doSave(HttpServletRequest request, HttpServletResponse response) throws IOException {
         /*
        思路:
         获取到前端的提交的数据,注意 编码设置post 请求
         连接数据库: 进行添加数据
         添加成功: 返回部门列表页面
         添加失败: 返回失败的页面
         */

        request.setCharacterEncoding("UTF-8");

        // 获取到前端的数据,建议 name 使用复制
        String deptno = request.getParameter("deptno");
        String dname = request.getParameter("dname");
        String loc = request.getParameter("loc");

        // 连接数据库,添加数据
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        // 影响数据库的行数
        int count = 0;

        try {
            // 1. 注册驱动,连接数据库
            connection = DBUtil.getConnection();

            // 2. 获取操作数据库对象,预编译sql语句,Sql测试
            String sql = "insert into dept(depton,dname,loc) values(?,?,?)";
            preparedStatement = connection.prepareStatement(sql);

            // 3. 填充占位符, 真正执行sql语句,
            // 注意: 占位符的填充是从 1 开始的,基本上数据库相关的起始下标索引都是从 1下标开始的
            preparedStatement.setString(1, deptno);
            preparedStatement.setString(2, dname);
            preparedStatement.setString(3, loc);

            // 返回影响数据库的行数
            count = preparedStatement.executeUpdate();

            // 5.释放资源
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            DBUtil.close(connection, preparedStatement, null);
        }

        // 保存成功,返回部门列表页面
        if (count == 1) {
            // 这里应该使用,重定向
            // 这里用的转发,是服务器内部的,不要加项目名
            //request.getRequestDispatcher("/dept/list/").forward(request, response);

            // 重定向
            response.sendRedirect(request.getContextPath() + "/dept/list/");
        } else {
            // 保存失败
            // web当中的 html资源,这里的 "/" 表示 web 目录
            //request.getRequestDispatcher("/error.html").forward(request, response);

            response.sendRedirect(request.getContextPath() + "/error.html");
        }
    }


    private void doEdit(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType("text/html;charSet=UTF-8");
        PrintWriter writer = response.getWriter();

        writer.println("     <!DOCTYPE html>");
        writer.println("<html lang='en'>");

        writer.println("<head>");
        writer.println("    <meta charset='UTF-8'>");
        writer.println("   <title>部门列表页面</title>");
        writer.println("</head>");
        writer.println("<body>");
        writer.println("    <h1>修改部门</h1>");


        writer.println("   <form action='" + request.getContextPath() + "/dept/modify' method='post'>");



        /*
        思路:
        获取到提交的过来的 部门编号
        根据部门编号修改信息,注意:部门编号是唯一的不要被修改了
        连接数据库,查询到相关信息显示到浏览器页面当中,方便用户修改
         */

        String deptno = request.getParameter("deptno");

        // 连接数据库
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        try {
            // 1. 注册驱动,连接数据库
            connection = DBUtil.getConnection();

            // 2. 获取到操作数据库对象,预编译SQL语句,sql测试
            String sql = "select dname,loc from dept where depton = ?";

            preparedStatement = connection.prepareStatement(sql);

            // 3. 填充占位符,真正执行sql语句
            preparedStatement.setString(1, deptno);
            resultSet = preparedStatement.executeQuery();

            // 4. 处理查询结果集
            while (resultSet.next()) {
                String dname = resultSet.getString("dname");  // 查询使用的别名,要用别名
                String loc = resultSet.getString("loc");

                // <!-- readonly 表示只读,不可修改的作用
                writer.println("      部门编号: <input type='text' name='deptno' value='" + deptno + "' readonly /><br>");
                writer.println("     部门名称: <input type='text' name='dname' value=" + dname + " /><br>");
                writer.println("    部门位置: <input type='text' name='loc' value=" + loc + " /><br>");

            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            // 5.释放资源,最后使用的优先关闭(因为如果是关闭优先使用的话,再最后面使用的可能需要前面的资源,才能执行)
            DBUtil.close(connection, preparedStatement, resultSet);
        }

        writer.println(" <input type='submit' value='修改' />");
        writer.println(" </form>");
        writer.println("</body>");
        writer.println("</html>");
    }


    private void doDetail(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 设置前端浏览器格式类型和字符集编码
        response.setContentType("text/html;charSet=UTF-8");
        PrintWriter writer = response.getWriter();
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        //中文思路(思路来源于:你要做什么?目标:查看部门详细信息。)
        // 第一步:获取部门编号
        // 第二步:根据部门编号查询数据库,获取该部门编号对应的部门信息。
        // 第三步:将部门信息响应到浏览器上。(显示一个详情。)


        // 1.
        // http://127.0.0.1:8080/servlet09/dept/detail?deptno=40
        String deptno = request.getParameter("deptno");  // 注意是我们前端提交的数据,建议复制name

        try {
            // 2. 连接数据库,根据部门编号查询数据库
            // 1.注册驱动,连接数据库
            connection = DBUtil.getConnection();

            // 2. 预编译SQL语句,sql要测试
            String sql = "select dname,loc from dept where depton = ?";  // ? 占位符
            preparedStatement = connection.prepareStatement(sql);

            // 3. 填充占位符,真正执行sql语句
            preparedStatement.setString(1, deptno);
            resultSet = preparedStatement.executeQuery();

            // 4. 处理查询结果集
            while (resultSet.next()) {
                String dname = resultSet.getString("dname");
                String loc = resultSet.getString("loc");


                // 注意将 “双引号转换为单引号,因为在Java当中不可以嵌套多个双引号,除非是字符串的拼接
                // 所以使用 '单引号
                writer.println("    <body>");
                writer.println("  <h1>部门详情</h1>");
                writer.println("              部门编号: " + deptno + " <br>");
                writer.println("          部门名称: " + dname + "<br>");
                writer.println("     部门位置: " + loc + "<br>");
                writer.println("  <input type='button' value='后退' onclick='window.history.back()'  />");

            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            // 5. 释放资源
            DBUtil.close(connection, preparedStatement, resultSet);
        }


        writer.println("</body>");
        writer.println("</html>");
    }


    private void doDel(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType("text/html;charSet=UTF-8");
        PrintWriter writer = response.getWriter();
        request.setCharacterEncoding("UTF-8");

        // 思路:
        /*
        根据部门编号删除信息,
        删除成功,跳转回原来的部门列表页面
        删除失败,跳转删除失败的页面
         */

        Connection connection = null;
        PreparedStatement preparedStatement = null;

        // 记录删除数据库的行数
        int count = 0;

        // 获取到前端提交的数据
        String deptno = request.getParameter("deptno");


        // 连接数据库进行删除操作

        try {
            // 1.注册驱动,连接数据库
            connection = DBUtil.getConnection();

            // 开启事务(取消自动提交机制),实现可回滚
            connection.setAutoCommit(false);

            // 2. 预编译sql语句,sql测试
            String sql = "delete from dept where depton = ?"; // ? 占位符
            preparedStatement = connection.prepareStatement(sql);

            // 3. 填充占位符,真正的执行sql语句
            preparedStatement.setString(1, deptno);
            // 返回影响数据库的行数
            count = preparedStatement.executeUpdate();
            connection.commit();  // 手动提交数据
        } catch (SQLException e) {
            // 遇到异常回滚
            if (connection != null) {
                try {
                    // 事务的回滚
                    connection.rollback();
                } catch (SQLException ex) {
                    throw new RuntimeException(ex);
                }
            }
            throw new RuntimeException(e);
        } finally {
            // 4. 释放资源
            // 因为这里是删除数据,没有查询操作,所以 没有 ResultSet 可以传null
            DBUtil.close(connection, preparedStatement, null);
        }

        if (count == 1) {
            // 删除成功
            // 仍然跳转到部门列表页面
            // 部门列表页面的显示需要执行另外一个Servlet,怎么办,可以使用跳转,不过这里最后是使用重定向
            // 注意:转发是在服务器间的,所以不要加“项目名” 而是 / + web.xml 映射的路径即可
            //request.getRequestDispatcher("/dept/list/").forward(request,response);

            // 优化:使用重定向机制 注意: 重定向是自发到前端的地址栏上的,前端所以需要指明项目名
            // 注意: request.getContextPath() 返回的根路径是,包含了 "/" 的
            response.sendRedirect(request.getContextPath() + "/dept/list/");
        } else {
            // 删除失败
            // web当中的 html资源,这里的 "/" 表示 web 目录
            //request.getRequestDispatcher("/error.html/").forward(request, response);

            // 优化,使用重定向
            response.sendRedirect(request.getContextPath() + "/error.html/");
        }
    }


    private void doModify(HttpServletRequest request, HttpServletResponse response) throws IOException {
         /*
        思路:
        获取到提交的数据信息,
        根据提交的信息,连接数据库修改
        修改成功,跳转到部门列表页面,
        修改失败:跳转到失败的页面
         */

        request.setCharacterEncoding("UTF-8");  // 设置获取的的信息的编码集
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        // 影响数据库的行数
        int count = 0;


        String deptno = request.getParameter("deptno");
        String dname = request.getParameter("dname");
        String loc = request.getParameter("loc");


        try {
            // 1. 注册驱动,连接数据库
            connection = DBUtil.getConnection();

            // 2. 获取到操作数据库的对象,预编译sql语句,sql测试
            String sql = "update dept set dname = ?,loc = ? where depton = ?";
            preparedStatement = connection.prepareStatement(sql);

            // 3. 填充占位符,真正执行sql语句
            // 从下标 1开始
            preparedStatement.setString(1, dname);
            preparedStatement.setString(2, loc);
            preparedStatement.setString(3, deptno);

            count = preparedStatement.executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            // 4. 释放资源,最后使用的优先被释放
            DBUtil.close(connection, preparedStatement, null);
        }

        if (count == 1) {
            // 更新成功
            // 跳转到部门列表页面(部门列表表面是通过java程序动态生成的,所以还需要再次执行另一个Servlet)
            // 转发是服务器内部的操作,“/” 不要加项目名
            // request.getRequestDispatcher("/dept/list/").forward(request,response);

            // 优化使用重定向,自发前端(需要指明项目名)
            response.sendRedirect(request.getContextPath() + "/dept/list/");

        } else {
            // 更新失败
            // request.getRequestDispatcher("/error.html").forward(request,response);

            // 优化重定向
            // request.getContextPath()  获取的根路径是,带了 "/" 的
            response.sendRedirect(request.getContextPath() + "/error.html");
        }
    }
}

4. 总结:

  1. @WebServlet 注解,提高开发效率高,不需要编写大量的配置信息。直接在 java 类上使用注解进行标注。web.xml文件体积变小了。
  2. @WebServlet 注解当中的valueurlPatterns 的作用都是一样的,表示 映射的url路径,注意 : 设置赋值的 url 要以 / 开始。不要忘记了,不要漏掉了。
  3. @WebServlet 常用的属性的作用和使用。
  4. 模板方法的核心思想:父类定义骨架,子类实现某些细节。也可以一个方法为骨架,多个方法实现某些细节。
  5. 注解可以使用模糊查询
@WebServlet("/dept/*")  // 可以使用模糊查询,* 任意字符串

5. 最后:

限于自身水平,其中存在的错误,希望大家,给予指教,韩信点兵——多多益善,谢谢大家,江湖再见,后会有期!!!

热门相关:无量真仙   霸皇纪   寂静王冠   刺客之王   天启预报