使用“純”Servlet做一個單表的CRUD操作

Sea發表於2023-04-11

使用“純”Servlet做一個單表的CRUD操作

在這裡插入圖片描述

每博一文案

莊子說:"獨往獨來,是謂獨有。獨有之人,是謂至貴"。熱鬧是別人的狂歡,而孤獨是自己的自由。
相聚總是短暫,唯孤獨綿長,學會孤獨為伍,才能找到浮世清歡。
金庸曾筆下人物說:"天上的白雲聚了又聚,散了又散,人生離合,亦復如斯。" 人生一世,所有的人來人往,
聚散離合,都是緣分使然。緣來便聚,緣盡則散,聚是一團火,散是滿天星。往後餘生,與孤獨和解,做自己的知音。

都說幸福的樣子千篇一律,不幸的人卻各有各的不幸。很多人都只能看到別人所擁有的,卻沒看到人所失去的。
沒有人的生活是真正的容易。當你站在橋上看風景的時候,可能自己亦是別人眼中的風景。
所以不必羨慕別人,珍惜現在所擁有的,過好自己的人生才最重要。
                 
                                 ——————   《一禪心靈廟語》

@

1. 專案說明

介紹: 這裡我們使用 純粹Servlet 完成單表【對部門的】的增刪改查操作。(B/S結構的。)

結構圖

在這裡插入圖片描述

初始的歡迎頁面

在這裡插入圖片描述

部門列表頁面

在這裡插入圖片描述

部門詳情

在這裡插入圖片描述

修改部門
在這裡插入圖片描述

刪除部門:

在這裡插入圖片描述

新增部門:

在這裡插入圖片描述

2. 具體對應的功能的程式碼實現

2.1 準備工作:

  1. 首先我們使用資料庫,儲存資料,這裡我們使用的資料庫是 MYSQL
  2. 我們需要準備一個名為 dept的資料表,並插入一些資料。
create table dept (
  depton int primary key,
  dname varchar(255),
  loc varchar(255)
);

insert into dept(depton,dname,loc) values(10,'xiaoShouBu','BEIJING');
insert into dept(depton,dname,loc) values(20,'YanFaBu','SHANGHAI');
insert into dept(depton,dname,loc) values(30,'JisShuBu','GUANGZHOU');
insert into dept(depton,dname,loc) values(40,'MeiTiBu','SHENZHEN');
select * from dept;

小技巧: MySQL 在 cmd 命令中,批次執行 sql語句的方法:如下,首先將編寫好的 .sql 檔案儲存起來。如下圖所示,
在這裡插入圖片描述

再開啟cmd 進入命令視窗,再進入到Mysql當中,輸入如下命令:

source 後接檔案路徑(要執行的批次的.sql檔案)

在這裡插入圖片描述

當前資料表 dept 的資訊內容如下:

在這裡插入圖片描述

  1. 為該模組匯入 MYSQL的 JDBC 的 jar 包。

注意: 因為我們是在 Tomcat 伺服器當中部署專案的,所以我們需要在 WEB-INF 的目錄下,建立一個名為 lib 的目錄資料夾,用來存放相關的 依賴jar 包,注意路徑位置不可以修改,目錄檔案必須為 lib 不然,當你啟動的Tocmat 伺服器的時候,是無法找到該對應的 依賴jar 包的。具體如下,我們將 Mysql對應的 jdbc jar 包匯入其中。

  • 建立一個webapp(給這個webapp新增servlet-api.jar和jsp-api.jar到classpath當中。)
  • 向webapp中新增連線資料庫的jar包(mysql驅動)
    • 必須在WEB-INF目錄下新建lib目錄,然後將mysql的驅動jar包複製到這個lib目錄下。這個目錄名必須叫做lib,全部小寫的。

在這裡插入圖片描述

2.2 模組目錄結構

在這裡插入圖片描述

2.3 工具類 DBUtil

這裡因為我們要連線資料庫,所以我們編寫一個連線Mysql 資料庫的 工具類,這裡我們名為一個 DBUtil 的工具類。

這裡我們透過讀取配置jdbc.properties的配置檔案的方式,註冊相對應的Mysql驅動

如下是相關: jdbc.properties 的配置資訊

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
user=root
password=123

再編寫好相關的:DBUtil 類 ,具體程式碼的編寫內容如下:

package com.RainbowSea.DBUtil;


import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ResourceBundle;

/**
 * JDBC工具類
 */
public class DBUtil {

    // 靜態變數,在類載入時執行
    // 都是一些從 jdbc.properties 讀取到的配置檔案的資訊
    // 該方法僅僅只會讀取 “.properties" 的字尾的檔案,注意:預設是從src目錄開始的,有子目錄需要寫明子目錄
    private static ResourceBundle bundle = ResourceBundle.getBundle("com/RainbowSea/resources/jdbc");
    private static String driver = bundle.getString("driver"); // 根據properties中的name讀取對應的value值
    private static String url = bundle.getString("url");
    private static String user = bundle.getString("user");
    private static String password = bundle.getString("password");


    static {
        // 註冊驅動(註冊驅動只需要註冊一次,放在靜態程式碼當中,DBUtil類載入的時候執行)
        // "com.mysql.jdbc.Driver"是連線資料庫的驅動,不能寫死,因為以後可能還會連線Oracle資料庫。
        // OCP開閉原則: 對擴充套件開放,對修改關閉(什麼是符合 OCP呢?在進行功能擴充套件的時候,不需要修改java原始碼)
        // Class.forName("com.mysql.jdbc.Driver")

        try {
            Class.forName(driver);  // 載入驅動
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }

    }


    /**
     * 獲取資料庫連線
     * @return Connection
     */
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url,user,password);
    }


    /**
     * 關閉連線
     * @param connection
     * @param statement
     * @param resultSet
     */
    public static void close(Connection connection, Statement statement, ResultSet resultSet) {
        // 注意:分開try,最後使用的資源,優先關閉
        if(resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }

        if(statement != null) {
            try {
                statement.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }


        if(connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

2.4 功能一: index.html 該專案的歡迎頁面如下:

預設在 web 當中,一個全域性配置資訊當中,會將名為 index.html 的檔案,設定為該專案的歡迎頁面。相應的具體內容大家可以移步至 ??? 關於Web的歡迎頁面的開發設定_ChinaRainbowSea的部落格-CSDN部落格

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>歡迎使用OA系統</title>
</head>
<body>
    <!--注意:對應前端的資源獲取基本上都是要加專案名的,並且要"/"開始-->
    <a href="/servlet09/dept/list/">檢視部門列表</a>
</body>
</html>

2.5 功能二:部門列表 DeptListServlet

注意:因為我們這裡使用的是 純 Servlet 編寫的一個專案,所以在後端想要將相關的 HTML 標籤相應到前端瀏覽器,被瀏覽器渲染的話,則需要特殊的方法:如下

// 設定將後端的字串的 html 標籤相應到瀏覽器端執行處理,並設定相應的字符集編碼
response.setContentType("text/html;charSet=UTF-8");
PrintWriter writer = response.getWriter();

思路:

  1. DeptListServlet類的doGet方法中連線資料庫,查詢所有的部門,動態的展示部門列表頁面.
  • 分析 html 頁面中哪部分是固定死的,哪部分是需要動態展示的。

  • html頁面中的內容所有的雙引號要替換成單引號,因為out.print("")這裡有一個雙引號,容易衝突。

  • 現在寫完這個功能之後,你會有一種感覺,感覺開發很繁瑣,只使用servlet寫程式碼太繁瑣了

  1. 我們需要連線資料庫,從資料庫中獲取到資料,顯示到前端瀏覽器當中。
  2. 注意我們這裡上面的 index.html 中是透過超連結的方式,跳轉到該 部門列表頁面的。超連結是 doGet 請求。
package com.RainbowSea.servlet;

import com.RainbowSea.DBUtil.DBUtil;
import jakarta.servlet.ServletException;
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;


/**
 * 部門列表
 */
public class DeptListServlet extends HttpServlet {

    /*
    說明:這裡使用了doGet,和 goPost 的原因是,我們前端的 DeptSaveServlet 的新增部門,
    的請求是doPost,從 doPost 請求  "轉發"出來的同樣是 doPost請求的,而 重定向就是doGet請求了,無論是doPost,doGet請求都是
    所以這裡為了,處理接受到 DeptSaveServlet 的新增部門的 "轉發"請求,寫了一個doPost 請求處理
     */


    // 最佳化,將轉發,替換成了 重定向的機制,(重定向的機制)是自發到瀏覽器前端的位址列上的,後自發的執行
    // 位址列上是 doGet 請求的,就不需要 doPost 請求了。
   /* @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            IOException {
        doGet(request, response); // 呼叫本身這裡的doGet()請求

    }*/

    /*
        因為我們前端使用的是 <a>超連結,是goGet請求所以,
        前後端的請求保持一致。
         */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            IOException {
        // 設定前端瀏覽器顯示的格式型別,以及編碼
        response.setContentType("text/html;charSet=UTF-8");
        PrintWriter writer = response.getWriter();
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        // 獲取到該webapp的專案根路徑:也就是在Tomcat 當中設定的訪問的專案路徑
        // 注意的是: getContextPath()獲取返回的路徑是帶有 "/專案名"的,所以不要多寫了 / 
        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>");

    }
}

2.6 功能三:檢視部門詳情 DeptDetailServlet

建議:從前端往後端一步一步實現。首先要考慮的是,使用者點選的是什麼?使用者點選的東西在哪裡?

一定要先找到使用者點的“詳情”在哪裡。這裡我們使用超連結的形式處理

<a href='寫一個路徑'>詳情</a>

詳情 是需要連線資料庫的,所以這個超連結點選之後也是需要執行一段java程式碼的。所以要將這個超連結的路徑修改一下。

注意:修改路徑之後,這個路徑是需要加專案名的。"/servlet09/dept/detail"

技巧:

out.print("<a href='"+contextPath+"/dept/detail?deptno="+deptno+"'>詳情</a>");

重點:向伺服器提交資料的格式:uri?name=value&name=value&name=value&name=value 。這裡的問號,必須是英文的問號。不能中文的問號。

思路:

中文思路(思路來源於:你要做什麼?目標:檢視部門詳細資訊。)
第一步:獲取部門編號
第二步:根據部門編號查詢資料庫,獲取該部門編號對應的部門資訊。
第三步:將部門資訊響應到瀏覽器上。(顯示一個詳情。)

package com.RainbowSea.servlet;

import com.RainbowSea.DBUtil.DBUtil;
import jakarta.servlet.ServletException;
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;


/**
 * 部門詳情
 */
public class DeptDetailServlet extends HttpServlet {

    // 前端超連線是 doGet()請求
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            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>");
    }
}

2.7 功能四:刪除部門 DeptDelServlet

怎麼開始?從哪裡開始?從前端頁面開始,使用者點選刪除按鈕的時候,應該提示使用者是否刪除。因為刪除這個動作是比較危險的。任何系統在進行刪除操作之前,是必須要提示使用者的,因為這個刪除的動作有可能是使用者誤操作。(在前端頁面上寫JS程式碼,來提示使用者是否刪除。)

<a href="javascript:void(0)" onclick="del(30)" >刪除</a>
<script type="text/javascript">
	function del(dno){
		if(window.confirm("親,刪了不可恢復哦!")){
			document.location.href = "/oa/dept/delete?deptno=" + dno;
		}
	}
</script>

以上的前端程式要寫到後端的java程式碼當中:DeptListServlet類的doGet方法當中,使用out.print()方法,將以上的前端程式碼輸出到瀏覽器上。

刪除成功或者失敗的時候的一個處理(這裡我們一開始使用的選擇的是轉發,後面最佳化使用的是重定向機制。)

刪除成功:我們跳轉到部門列表當中。DeptListServlet

刪除失敗:我們跳轉到一個失敗的頁面當中。這裡我們將該失敗的頁面名為: error.html 頁面如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>error</title>
</head>
<body>
<h1> 操作失敗: </h1>
<a href='/servlet09/dept/list/' onclick="window.history.back()">返回</a>
</body>
</html>

具體的 Servlet 編寫如下

package com.RainbowSea.servlet;

import com.RainbowSea.DBUtil.DBUtil;
import jakarta.servlet.ServletException;
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.SQLException;


/**
 * 刪除部門
 */
public class DeptDelServlet extends HttpServlet {


    /*
    注意前端是超連結的方式:是get請求
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            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/");
        }


    }
}

2.8 功能五:新增部門 DeptSaveServlet

思路:

獲取到前端 form 表單提交的資料,這裡我們 form 表單 中的 metod = post 設定為了 doPost 請求。

再連線資料庫,新增一條記錄。

新增成功:我們跳轉到部門列表當中。DeptListServlet

新增失敗:我們跳轉到一個失敗的頁面當中。這裡我們將該失敗的頁面名為: error.html

注意點:

最後儲存成功之後,跳轉到 /dept/list 的時候,如果你使用的是 轉發 機制的話,這裡因為你是從 doPost 請求 轉發過去的(轉發是一次請求,之前是post,之後還是post,因為它是一次請求。),所以對應接收該 doPost 請求的也要是 doPost 方法處理該請求,不然會報 405 錯誤。

而同時:這時候接收該 轉發 的 /dept/list Servlet當中只有一個doGet方法。就會報 405 錯誤。

怎麼解決?兩種方案

  • 第一種:在/dept/list Servlet中新增doPost方法,然後在doPost方法中呼叫doGet。
  • 第二種:使用重定向的方式,進行跳轉,重定向的機制是改變瀏覽器的請求路徑URL,讓瀏覽器重新傳送跳轉之後的 URL 地址,該方式是從瀏覽器位址列上跳轉的,所以是 doGet 請求,就不要編寫 doPost 請求了。

具體程式碼編寫如下:

package com.RainbowSea.servlet;

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

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


/**
 * 增加部門資料
 */
public class DeptSaveServlet extends HttpServlet {
    // 前端是註冊資訊,是post 請求
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            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");
        }


    }
}

2.9 功能六:跳轉到修改部門的頁面 DepEditServlet

思路:
獲取到提交的過來的 部門編號
根據部門編號修改資訊,注意:部門編號是唯一的不要被修改了
連線資料庫,查詢到相關資訊顯示到瀏覽器頁面當中,方便使用者修改

具體的程式碼編寫如下

package com.RainbowSea.servlet;

import com.RainbowSea.DBUtil.DBUtil;
import jakarta.servlet.ServletException;
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;


/**
 * 跳轉到修改部門的頁面
 */
public class DepEditServlet extends HttpServlet {
    // 超連結是 doGet()請求

    // http://127.0.0.1:8080/servlet09/dept/edit?deptno=10
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            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>");


    }
}

2.10 功能七:修改部門 DeptSaveServlet

思路:

獲取到前端的提交的資料,注意 編碼設定post 請求
連線資料庫: 進行新增資料
新增成功: 返回部門列表頁面
新增失敗: 返回失敗的頁面

具體的程式碼編寫如下:

package com.RainbowSea.servlet;

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

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


/**
 * 增加部門資料
 */
public class DeptSaveServlet extends HttpServlet {
    // 前端是註冊資訊,是post 請求
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            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");
        }


    }
}

3. 最後的 web.xml 配置資訊

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">


    <!--     檢視部門列表-->
    <servlet>
        <!--        注意: 兩個name值要保持一致-->
        <servlet-name>list</servlet-name>
        <servlet-class>com.RainbowSea.servlet.DeptListServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>list</servlet-name>
        <!--        注意是: / 開始-->
        <url-pattern>/dept/list/</url-pattern>
    </servlet-mapping>


    <!--    部門詳情-->
    <servlet>
        <servlet-name>detail</servlet-name>
        <servlet-class>com.RainbowSea.servlet.DeptDetailServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>detail</servlet-name>
        <url-pattern>/dept/detail</url-pattern>
    </servlet-mapping>


    <!--    刪除部門-->
    <servlet>
        <servlet-name>delete</servlet-name>
        <servlet-class>com.RainbowSea.servlet.DeptDelServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>delete</servlet-name>
        <!--        / 開始-->
        <url-pattern>/dept/delete</url-pattern>
    </servlet-mapping>

    <!--    新增部門-->
    <servlet>
        <servlet-name>save</servlet-name>
        <servlet-class>com.RainbowSea.servlet.DeptSaveServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>save</servlet-name>
        <url-pattern>/dept/save</url-pattern>
    </servlet-mapping>

    <!--    跳轉到修改部門-->
    <servlet>
        <!--        兩個 name 保持一致-->
        <servlet-name>edit</servlet-name>
        <servlet-class>com.RainbowSea.servlet.DepEditServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>edit</servlet-name>
        <!--        "/" 開始-->
        <url-pattern>/dept/edit</url-pattern>
    </servlet-mapping>


    <!--    修改部門資訊-->
    <servlet>
        <servlet-name>modify</servlet-name>
        <servlet-class>com.RainbowSea.servlet.DeptModifyServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>modify</servlet-name>
        <url-pattern>/dept/modify</url-pattern>
    </servlet-mapping>

</web-app>

在這裡插入圖片描述

4. 最佳化方案: @WebServlet 註解 + 模板方法

由於設計到文章的篇幅過多,大家想要了解的可以移步至:??? Servlet註解的使用,簡化配置 以及,使用模板方法設計模式最佳化oa專案_ChinaRainbowSea的部落格-CSDN部落格

5. 總結:

  1. 每次前端提交的資料都透過瀏覽器 F12 檢查的方式,檢視我們提交的資料是否,是我們需要的,是否滿足條件。

  2. 每次後端從前端瀏覽器獲取到的資料,同樣都是需要列印或者除錯看看,我們獲取的資料是否存在錯誤,或者亂碼的情況。

  3. 如果對SQL語句不太熟練的話,建議無論是否是簡單的 SQL語句都,可以先在對應的資料庫中執行測試看看,是否存在錯誤。

  4. 儘可能的做到,每實現一點功能就測試一下,是否存在錯誤,而不是一頓操作下來,雖然所以程式碼都編寫完了,但是到最後測試的時候,一堆 BUG 。

  5. 我們應該怎麼去實現一個功能呢?

    • 建議:你可以從後端往前端一步一步寫。也可以從前端一步一步往後端寫。都可以。但是千萬要記住不要想起來什麼寫什麼。你寫程式碼的過程最好是程式的執行過程。也就是說:程式執行到哪裡,你就寫哪裡。這樣一個順序流下來之後,基本上不會出現什麼錯誤、意外。
    • 從哪裡開始?
      • 假設從前端開始,那麼一定是從使用者點選按鈕那裡開始的
  6. 分析清楚哪裡使用的是 doGet 請求 ,哪裡使用的是 doPost 請求。

  7. 分析清楚哪裡使用的是 伺服器端的轉發 ,哪裡使用的是 重定向機制。

  8. 注意: 在伺服器當需要使用到的 jar 包,必須在 WEB-INF 的目錄下,建立一個名為 lib 的目錄資料夾,用來存放相關的 依賴jar 包,注意路徑位置不可以修改,目錄檔案必須為 lib 不然,當你啟動的Tocmat 伺服器的時候,是無法找到該對應的 依賴jar 包的。

6. 最後:

限於自身水平,其中存在的錯誤,希望大家,給予指教,韓信點兵——多多益善,謝謝大家,江湖再見,後會有期!!!

在這裡插入圖片描述

相關文章