JavaWeb《后端内容:1.Tomcat--Servlet--Thymeleaf》

news2024/12/23 23:10:05

目录

1. 基础概念

1.1 BS架构和CS架构

1.2 Tomcat图解

2.TomCat

2.1 IDEA配置web项目和tomcat

2.2 idea启动TomCat因为端口号失败的问题

3.Servlet使用流程

3.1 Servlet简单图解

3.2 Servlet导入依赖

 3.3 编写Servlet和add.html

3.4 试着使用Jdbc和Dao层连接水果库存数据库

0. 基础结构的代码

1. 数据库表创建

2. 根据水果javabean和JDBC写出FruitDaoJDBC

3. 重写此时servlets下的AddServlet

4.Servlet细节知识点

4.1 Servlet设置编码字符集

4.2 Servlet继承关系

1. 继承关系

2. 相关方法

javax.servlet.Servlet 接口

4.3 Servlet生命周期

1.生命周期

2. 默认情况

3. Servlet初始化时机

4. Servlet在容器中是单例的,是线程不安全的

4.4 HTTP协议

1. HTTP基础概念

2. 请求报文

3. 响应报文

4.5 会话

1.Http是无状态的

​2. 会话跟踪技术

3. session保存作用域

4.6 服务器内部转发以及客户端重定向

1. 服务器内部转发​编辑

2. 客户端重定向

4.7 Thymeleaf视图技术

1. thymeleaf的部分标签

4.8 保存作用域

1. request:一次请求响应范围

2. session:一次会话范围有效

3. application: 一次应用程序范围有效

4. 补充:相对路径和绝对路径 

4.9 水果管理系统项目继续升级


1. 基础概念

1.1 BS架构和CS架构

CS:客户端服务器架构模式

优点︰充分利用客户端机器的资源,减轻服务器的负荷

(一部分安全要求不高的计算任务存储任务放在客户端执行,不需要把所有的计算和存储都在服务器端执行,从而能够减轻服务器的压力,也能够减轻网络负荷)

缺点∶需要安装;升级维护成本较高

BS∶浏览器服务器架构模式

优点︰客户端不需要安装;维护成本较低

缺点∶所有的计算和存储任务都是放在服务器端的,服务器的负荷较重﹔在服务端计算完成之后把结果再传输给客户端,因此客户端和服务器端会进行非常频繁的数据通信,从而网络负荷较重

1.2 Tomcat图解

2.TomCat

2.1 IDEA配置web项目和tomcat

创建一个java项目之后,右键项目文件夹,选择添加框架支持,然后选择javaee中的web即可添加web项目

在IDAE右上方选择编辑配置

点击加入工件,然后加入

服务器这里填写tomcat的根目录

2.2 idea启动TomCat因为端口号失败的问题

1099被占用,可以进入cmd控制台,然后进行搜索被占用的进程,然后将它关闭

netstat -aon | find "1099"

tasklist | find "5680"

在任务管理器中找到对应的这个进程然后关闭。 

但是有可能出现搜索占用1099端口号但并没有任何进程占用的情况,这种时候,可以进行关闭hyper-v然后重启电脑并重新打开

1. 关闭hyper-v 它会询问是否立即重启,必须进行重启

dism.exe /Online /Disable-Feature:Microsoft-Hyper-V

2. 排除ipv4动态端口占用 startport 起始端口 numberofports 端口数

netsh int ipv4 add excludedportrange protocol=tcp startport=1099 numberofports=1

3. 启动hyper-v后重启电脑

dism.exe /Online /Enable-Feature:Microsoft-Hyper-V /All

3.Servlet使用流程

3.1 Servlet简单图解

3.2 Servlet导入依赖

我们使用AddServlet去承接add操作,需要继承自HttpServlet包,但是这个包在tomcat下,需要我们在idea的项目结构里面导入tomcat依赖

 3.3 编写Servlet和add.html

我们需要完成一个,用户通过tomcat服务器访问add.html,通过add.html下的表单,输入对应的信息,这个操作在表单的声明中action = add,当我们提交表单信息的时候,服务器收取到这个post请求,通过servlet的mapping找到对应action=add映射的servlet映射名称对应同名的servlet,然后找到它所声明类的绝对路径,通过这个路径下的类中重写父类HttpServket的doPost方法,将用户Post请求下的要求按照我们所写的方法进行操作

1. 用户发请求,action=add
2. 项目中,web.xml中找到url-pattern = /add  -----> 第12行
3. 找第11行的servlet-name = AddServlet
4. 找和servlet-mapping中servlet-name一致的servlet 找到第7行
5. 找第八行的servlet-class -> com.fanxy.servlets.AddServlet
6. 用户发送的是post请求(method = post) 因此tomcat会执行AddServlet中的doPost方法

add.html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="add" method="post">
    <label for="fname">名称:</label>
    <input type="text" name="fname" id="fname" required><br>

    <label for="price">价格:</label>
    <input type="text" name="price" id="price" required><br>

    <label for="fcount">库存:</label>
    <input type="text" name="fcount" id="fcount" required><br>

    <label for="remark">备注:</label>
    <input type="text" name="remark" id="remark" required><br>

    <input type="submit" value="添加">
</form>
</body>
</html>

com.fanxy.servlets.AddServlet类

public class AddServlet extends HttpServlet {

    @Override
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        String fname = request.getParameter("fname");
        Integer price = Integer.parseInt(request.getParameter("price"));
        Integer fcount = Integer.parseInt(request.getParameter("fcount"));
        String remark = request.getParameter("remark");

        System.out.println("fname = "+ fname);
        System.out.println("price = "+ price);
        System.out.println("fcount = "+ fcount);
        System.out.println("remark = "+ remark);
    }
}

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>
        <servlet-name>AddServlet</servlet-name>
        <servlet-class>com.fanxy.servlets.AddServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>AddServlet</servlet-name>
        <url-pattern>/add</url-pattern>
    </servlet-mapping>
</web-app>

3.4 试着使用Jdbc和Dao层连接水果库存数据库

如果想中途想导入包,需要删除out下的artifacts和production下的子文件夹,然后导入到项目外部的包,然后在项目结构里面的problem的fix中添加入web项目

0. 基础结构的代码

Fruit的javabean

public class Fruit {
    private Integer fid ;
    private String fname ;
    private Integer price ;
    private Integer fcount ;
    private String remark ;

    public Fruit(){}

    public Fruit(Integer fid, String fname, Integer price, Integer fcount, String remark) {
        this.fid = fid;
        this.fname = fname;
        this.price = price;
        this.fcount = fcount;
        this.remark = remark;
    }


    public Integer getFid() {
        return fid;
    }

    public void setFid(Integer fid) {
        this.fid = fid;
    }

    public String getFname() {
        return fname;
    }

    public void setFname(String fname) {
        this.fname = fname;
    }

    public Integer getPrice() {
        return price;
    }

    public void setPrice(Integer price) {
        this.price = price;
    }

    public Integer getFcount() {
        return fcount;
    }

    public void setFcount(Integer fcount) {
        this.fcount = fcount;
    }

    public String getRemark() {
        return remark;
    }

    public void setRemark(String remark) {
        this.remark = remark;
    }

    @Override
    public String toString() {
        return fid + "\t\t" + fname + "\t\t" + price +"\t\t" + fcount +"\t\t" + remark ;
    }
}

FruitDAO的抽象接口

public interface FruitDAO {
    //查询库存列表
    List<Fruit> getFruitList() throws Exception;

    //新增库存
    boolean addFruit(Fruit fruit) throws SQLException;

    //修改库存
    boolean updateFruit(Fruit fruit) throws SQLException;

    //根据名称查询特定库存
    Fruit getFruitByFname(String fname) throws Exception;

    //删除特定库存记录
    boolean delFruit(String fname) throws SQLException;
}

1. 数据库表创建

CREATE DATABASE fruitdb charset utf8;
USE fruitdb ;
CREATE TABLE `t_fruit` (
  `fid` int(11) NOT NULL AUTO_INCREMENT,
  `fname` varchar(20) NOT NULL,
  `price` int(11) DEFAULT NULL,
  `fcount` int(11) DEFAULT NULL,
  `remark` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`fid`)
) ENGINE=InnoDB AUTO_INCREMENT=33 DEFAULT CHARSET=utf8;

insert  into `t_fruit`(`fid`,`fname`,`price`,`fcount`,`remark`)
values
(1,'红富士',5,16,'红富士也是苹果!'),
(2,'大瓜',5,100,'王校长的瓜真香'),
(3,'南瓜',4,456,'水果真好吃'),
(4,'苦瓜',5,55,'苦瓜很好吃'),
(5,'莲雾',9,99,'莲雾是一种神奇的水果'),
(6,'羊角蜜',4,30,'羊角蜜是一种神奇的瓜'),
(7,'啃大瓜',13,123,'孤瓜');

2. 根据水果javabean和JDBC写出FruitDao
JDBC数据库连接技术学习笔记https://blog.csdn.net/weixin_44981126/article/details/130424791

public class FruitDao extends BaseDao implements FruitDAO {

    @Override
    public List<Fruit> getFruitList() throws Exception {
        List<Fruit> fruits = executeQuery(Fruit.class, "select * from t_fruit;");
        return fruits;
    }

    @Override
    public boolean addFruit(Fruit fruit) throws SQLException {
        String sql = "INSERT INTO t_fruit(fid, fname, price, fcount, remark) values(0, ?, ?, ?, ?);";
        int count = executeUpdate(sql, fruit.getFname(), fruit.getPrice(),
                fruit.getFcount(), fruit.getRemark());
        return count > 0;
    }

    @Override
    public boolean updateFruit(Fruit fruit) throws SQLException {
        String sql = "UPDATE t_fruit SET fcount = ? WHERE fid = ?;";
        int count = executeUpdate(sql, fruit.getFcount(), fruit.getFid());
        return count > 0;
    }

    @Override
    public Fruit getFruitByFname(String fname) throws Exception {
        String sql = "SELECT * FROM t_fruit WHERE fname = ?";
        List<Fruit> fruits = executeQuery(Fruit.class, sql, fname);
        if(fruits != null && fruits.size() > 0) {
            return fruits.get(0);
        }
        return null;
    }

    @Override
    public boolean delFruit(String fname) throws SQLException {
        String sql = "DELETE FROM t_fruit WHERE fname LIKE ? ;";
        int count = executeUpdate(sql, fname);
        return count > 0;
    }
}

3. 重写此时servlets下的AddServlet

public class AddServlet extends HttpServlet {

    @Override
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {

        request.setCharacterEncoding("UTF-8");
        String fname = request.getParameter("fname");
        Integer price = Integer.parseInt(request.getParameter("price"));
        Integer fcount = Integer.parseInt(request.getParameter("fcount"));
        String remark = request.getParameter("remark");

        FruitDao fruitDao = new FruitDao();
        boolean flag = false;
        try {
            flag = fruitDao.addFruit(new Fruit(1, fname, price, fcount, remark));
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        System.out.println(flag ? "添加成功!" : "添加失败!");
    }
}

1. 新建项目 - 新建模块
2. 在模块中添加web
3. 创建artifact - 部署包
4. lib - artifact
   先有artifact,后来才添加的mysql.jar。此时,这个jar包并没有添加到部署包中
   那么在projectSettings中有一个Problems中会有提示的,我们点击fix选择add to...
   另外,我们也可以直接把lib文件夹直接新建在WEB-INF下。
   这样不好的地方是这个lib只能是当前这个moudle独享。如果有第二个moudle我们需要再次重复的新建lib。
5. 在部署的时候,修改application Context。然后再回到server选项卡,检查URL的值。
   URL的值指的是tomcat启动完成后自动打开你指定的浏览器,然后默认访问的网址。
   启动后,报错404.404意味着找不到指定的资源。
   如果我们的网址是:http://localhost:8080/pro01/ , 那么表明我们访问的是index.html.
   我们可以通过<welcome-file-list>标签进行设置欢迎页(在tomcat的web.xml中设置,或者在自己项目的web.xml中设置)
6. 405问题。当前请求的方法不支持。比如,我们表单method=post , 那么Servlet必须对应doPost。否则报405错误。
7. 空指针或者是NumberFormatException 。因为有价格和库存。如果价格取不到,结果你想对null进行Integer.parseInt()就会报错。错误的原因大部分是因为 name="price"此处写错了,结果在Servlet端还是使用request.getParameter("price")去获取。
8. <url-pattern>中以斜杠开头

4.Servlet细节知识点

4.1 Servlet设置编码字符集

POST提交方式:在继承HttpServlet的类中接受request的信息之前,先进行如下设置

request.setCharacterEncoding("UTF-8");

get提交方式,tomcat8之前设置比较麻烦, tomcat8之后无需设置:

String fname = request.getParameter("fname");
byte[] = bytes = fname.getBytes("iso-8859-1");
fname = new String(bytes, "UTF-8");

4.2 Servlet继承关系

1. 继承关系

javax.servlet.Servlet 接口

|----------javax.servlet.GenericServlet 抽象类

                |----------javax.servlet.http.HttpServlet 抽象类

2. 相关方法

javax.servlet.Servlet 接口:

     |---1. void init(config) - 初始化方法

     |---2. void service(request, response) - 服务方法 抽象的

     |---3. void destory() - 销毁方法

     javax.servlet.GenericServlet 抽象类

           |---1. void service(request, response) - 仍然是抽象的

       javax.servlet.http.HttpServlet 抽象类

             |---1. void service(request, response) - 不是抽象的,当有请求过来的时候,service方

             |    法会自动响应(其实是tomcat容器调用的)在HttpSrvlet中我们会去分析请求的方

             |    式:到底是get,post,head还是delete等等  然后决定调用哪个do开头的方法,

             |    那么在HttpServlet中这些do方法默认都是405的实现风格-要我们子类去实现对应

             |    的方法,否则默认会报405错误,因此我们新建Servlet时,我们才会考虑请求方法       

                 |----1.1 String method = req.getMethod() - 获得请求的方式

                 |----1.2 各种 if 判断,根据请求方式的不同,决定调哪个do方法

                 |----1.3 在HttpServlet这个抽象类中,do方法都差不多,类似如下

if (method.equals("GET")) {
    this.doGet(req, resp);
} else if (method.equals("HEAD")) {
    this.doHead(req, resp);
} else if (method.equals("POST")) {
    this.doPost(req, resp);
} else if (method.equals("PUT")) {
    this.doPut(req, resp);
}

4.3 Servlet生命周期

1.生命周期:

从出生到死亡的过程就是生命周期,对应Servlet的三个方法:init(),service(),destroy() 

2. 默认情况:

第一次接收到请求的时候,这个Servlet会进行实例化(调用构造方法),初始化(调用init( )),然后服务(调用service( )

通过案例我们发现:Servlet实例tomcat只会创建一个,所有的请求都是这个实例去响应,默认情况下,第一次请求时,tomcat才会去实例化(通过反射创建),初始化,然后再服务

好处:提高系统启动速度

坏处:第一次启动时耗时太长

如果应该提高系统启动速度应该按此方法设置,当前默认情况就是这样。如果要提高响应速度,应该设置Servlet初始化时机

3. Servlet初始化时机

默认第一次接受请求时实例化,初始化

我们也可以通过<load-on-startup>来设置servlet启动的先后顺序,数字越小启动越靠前,最小为0

4. Servlet在容器中是单例的,是线程不安全的

单例:所有的请求都是同一个实例去响应

线程不安全:一个线程需要根据这个实例中的某个成员变量值做逻辑判断,但是在中间某个时机,另一个线程改变了这个值,导致前面的线程会进行错误的行为

启发:尽量不要在Servlet中定义成员变量,如果不得不定义成员变量

1. 不要修改成员变量的值

2. 不要根据成员变量的值进行逻辑判断

4.4 HTTP协议

HTTP协议详细笔记icon-default.png?t=N3I4https://heavy_code_industry.gitee.io/code_heavy_industry/pro001-javaweb/lecture/chapter06/

1. HTTP基础概念

HTTP:Hyper Text Transfer Protocol超文本传输协议。HTTP最大的作用就是确定了请求和响应数据的格式。浏览器发送给服务器的数据:请求报文;服务器返回给浏览器的数据:响应报文。

HTTP是无状态的,请求响应包含两个部分:请求和响应

2. 请求报文

请求包含三个部分:

1.请求行 (一般包含:1.请求的方式 2.请求的URL 3.请求的协议,一般都是HTTP1.1)

2.请求消息头(包含了客户端告诉浏览器的信息,比如:浏览器型号,版本,能接受内容的类型,给你发的内容的类型,内容的长度等等)

3. 请求体

三种情况:

get方式,没有请求体,但有queryString

post方式,有请求体,form data

json格式,有请求体,request payload

3. 响应报文

响应也包含三个部分:

1.响应状态行 (一般包含:1.响应的协议,一般都是HTTP1.1 2.响应状态码 3.响应状态) 

2.响应消息头(包含了服务器告诉客户端的信息,比如:内容的媒体类型,编码,内容的长度等等) 

3. 响应体 

服务器返回的数据主体,有可能是各种数据类型。

4.5 会话

1.Http是无状态的

1. HTTP 无状态 :服务器无法判断这两次请求是同一个客户端发过来的,还是不同的客户端发过来的
2. 无状态带来的现实问题:第一次请求是添加商品到购物车,第二次请求是结账;如果这两次请求服务器无法区分是同一个用户的,那么就会导致混乱
3. 通过会话跟踪技术来解决无状态的问题

2. 会话跟踪技术

1. 客户端第一次发请求给服务器,服务器获取session,获取不到,则创建新的,然后响应给客户端
2. 下次客户端给服务器发请求时,会把sessionID带给服务器,那么服务器就能获取到了,那么服务器就判断这一次请求和上次某次请求是同一个客户端,从而能够区分开客户端

3. 常用的API:
          request.getSession() -> 获取当前的会话,没有则创建一个新的会话
          request.getSession(true) -> 效果和不带参数相同
          request.getSession(false) -> 获取当前会话,没有则返回null,不会创建新的

          session.getId() -> 获取sessionID
          session.isNew() -> 判断当前session是否是新的
          session.getMaxInactiveInterval() -> 返回session的非激活间隔时长,默认1800秒
          session.setMaxInactiveInterval() -> 设置session的非激活间隔时长,默认1800秒
          session.invalidate() -> 强制性让会话立即失效

          ....

3. session保存作用域

 1.session保存作用域是和具体的某一个session对应的
      2. 常用的API:
        void session.setAttribute(key, value) -> 向当前session保存作用域保存一个对应key为value的数据
        Object session.getAttribute(key) -> 获取当前session中key对应的value值
        void removeAttribute(key) -> 删除当前session中key对应的value值

4.6 服务器内部转发以及客户端重定向

1. 服务器内部转发

request.getRequestDispatcher("...").forward(request,response);

- 一次请求响应的过程,对于客户端而言,内部经过了多少次转发,客户端是不知道的
- 地址栏没有变化

2. 客户端重定向

response.sendRedirect("....");

- 两次请求响应的过程。客户端肯定知道请求URL有变化
- 地址栏有变化 302重定向

4.7 Thymeleaf视图技术

这里和前面的webmoudule1类似,使用水果数据库,使用尚硅谷jdbcUtilsV2和BaoseDao重写FruitDao,然后引入一些简单的css,imgs和index.html文件。同时添加thymeleaf的jar包到库中,并添加到组件。

Thymeleaf详细语法icon-default.png?t=N3I4https://heavy_code_industry.gitee.io/code_heavy_industry/pro001-javaweb/lecture/chapter08/verse03.html

1. thymeleaf的部分标签

1) 使用步骤: 添加thymeleaf的jar包

2) 新建一个Servlet类ViewBaseServlet (从上面的链接复制)

新建ViewBaseServlet(有两个方法)

3) 在web.xml添加配置(从上面的链接复制)

配置两个<context-param> : view-prefix , view-suffix

这里 / 代表web根目录

4) 使得我们的Servlet继承ViewBaseServlet

//Servlet从3.0版本支持注解方式的注册
@WebServlet("/index")
public class IndexServlet extends ViewBaseServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        FruitDao fruitDao = new FruitDao();
        List<Fruit> fruitList = fruitDao.getFruitList();
        HttpSession session = req.getSession();
        session.setAttribute("fruitList", fruitList);
        //此处的视图名称是  index
        //那么thymeleaf会将这个 逻辑视图名称 对应到 物理视图 名称上
        //逻辑视图名称 : index
        //物理视图名称 : view-prefix + 逻辑视图名称 + view-suffix
        //真实的视图名称是:    /        index     .html
        super.processTemplate("index", req, resp);
    }
}

运用thymeleaf中的部分标签: <th:if> , <th:unless> , <th:each> , <th:text>重写index.html文件,即可完成把Servlet中通过session会话获取到的数据库数据存放的fruitlist,然后我们的Servlet写了存储作用域的句子,把它放入session作用域,我们html写入mxlns:th:http://www.thymeleaf.org的html下,写thymeleaf语法标签即可

<html xmlns:th="http://www.thymeleaf.org">
	<head>
		<meta charset="utf-8">
		<link rel="stylesheet" href="css/index.css">
	</head>
	<body>
		<div id="div_container">
			<div id="div_fruit_list">
				<table id="tbl_fruit">
					<tr>
						<th class="w20">名称</th>
						<th class="w20">单价</th>
						<th class="w20">库存</th>
						<th>操作</th>
					</tr>
					<tr th:if="${#lists.isEmpty(session.fruitList)}">
						<td colspan="4">对不起,库存为空</td>
					</tr>
					<tr th:unless="${#lists.isEmpty(session.fruitList)}" th:each="fruit : ${session.fruitList}">
						<td th:text="${fruit.fname}">苹果</td>
						<td th:text="${fruit.price}">5</td>
						<td th:text="${fruit.fcount}">20</td>
						<td><img src="imgs/del.jpg" class="delImg"/></td>
					</tr>
				</table>
			</div>
		</div>
	</body>
</html>

4.8 保存作用域

原始情况下,保存作用域我们可以认为有四个: page(页面级别,现在几乎不用) , request(一次请求响应范围) , session(一次会话范围) , application(整个应用程序范围)

1. request:一次请求响应范围

这里使用demo01和demo02进行request保存作用域的判定,发现同一个request保存作用域在当前request下有效。客户端重定向因为发起了新的request导致无法取到之前保存的数据,而服务端内部转发,因为没有产生新的request,所以可以取到,但是客户端以为保存在demo01下,其实保存在demo02下,服务器内部帮助完成了转发任务

2. session:一次会话范围有效

使用session进行作用域的保存,可以发现无论是重定向还是服务器内部转发都可以获取到,只要是用一个session内即可 

3. application: 一次应用程序范围有效

即便不同的浏览器访问demo05都可以获得作用域保存的数据 

4. 补充:相对路径和绝对路径 

从根目录一直写到文件的路径写法是绝对路径,从当前文件为目录起点,利用 .. / 写的路径是相对路径。

<base href="http://localhost:8080/pro10/" /> 的作用是:当前页面上的所有路径都以此路径为基础,默认写别的绝对路径,可以从这个路径下来当作绝对路径的起点

在thymeleaf中,th:href="@{}" 和上面的base设置相同

例如可以: <link th:href="@{/css/shopping.css}">

4.9 水果管理系统项目继续升级

加入链接编辑Servlet,给水果加入链接,点水果名称跳转到edit.do的Servlet下,完成数据编辑,但是直接加超链接会因为thymeleaf的标签覆盖,导致里面的超链接被覆盖我们写的thymeleaf的数据,所以我们只需要把thymeleaf的表达式放到超链接就行

为了使得我们对网页的相应水果进行操作,我们实际情况下会在链接中填入 ?fid=xxx

为了使得我们填入对应信息可以选择相对应的水果,需要给html的水果栏的href修改参数,但thymeleaf无法判断字符串和参数,根据thymeleaf语法需要修改为:

但是我们参数数量多的情况下,这样比较繁琐,可以按语法如下写(不写问号,用小括号加键值对的书写来进行,如果参数不止一个,只需要用逗号作为分隔符,填入新的键值对):

为了使用到fid我们需要进行字符串判断

因为我们经常要进行这样的判断,这个方法不如封装到utils下作为StringUtil  

同时为了Dao层的FruitDao能根据fid查询到对应的水果信息,我们可以给基础的FruitDAO接口加入一个抽象的getFruitById方法,然后在继承BaseDao和实现FruitDAO接口的FruitDao重写父接口的这个方法,完成Dao层方法的封装 

 

 此时我们的EditServlet方法写为,我们渲染到edit页面,所以我们需要重写一个edit.html的页面

 这里按thymeleaf的语法,把request作用域保存的fruit的信息放入我们编辑页面的文本框中

 此时效果图如下

但是每个属性都需要加fruit前缀,很冗余,我们只需要在祖先节点使用thymeleaf语法写入th:object="${fruit}",那么我们所有的后代节点只需要写th:value="*{属性值}"即可用*代表祖先节点声明的变量

同时我们需要把这个输入的内容存储到一个表单,以更新的方式进行修改我们的数据,所以我们把table写入一个表单中,action为 post

此时只需要重写update.do对应的Servlet类 

但我们查询数据应该是按主键查询会快,但是主键此时没查,我们可以查询,但是没必要给用户看到主键或者修改主键,所以我们可以在edit.html中写入查询主键的input,但是使用type为hidden,这个时候用户端就不会显式的呈现这一个输入框,但是我们从后台拿到了主键fid,同时这里可以把th:object写到form中,还可以使用th:value="*{属性值}",如果不移动,就只能通过${}的书写方式

 然后重写FruitDao的更新方法,使用在upDateServlet中的FruitDao的private实例对象,调用它这个方法,把我们从edit.html发送过来的表单数据提取并更新到数据库中

 但是此时还是有问题的,我们修改数据发现,页面的数据没有变化,但是查看数据库,发现数据库的数据已经修改了,这是为什么呢?其实processTemplate的作用相当于服务器重定向,这个时候获取的数据还是Session的数据,此时的Session的数据还是之前查到的数据,没有把更新的数据放入

super.processTemplate("index", req, resp);

 我们为了能够查到新的信息,可以使用客户端重定向,让客户端发一个新的请求给IndexServlet,重新获取fruitList,然后覆盖到Session中,这样就可以显示新的数据了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/491011.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

超细Redis(一)

目录 概述 Redis是什么&#xff1f; Redis能干嘛&#xff1f; 特性 如何学习 Linux安装 测试性能 概述 Redis是什么&#xff1f; Redis &#xff08;Remote Dictionary Server&#xff09;,即远程字典服务 是一个开源使用ANSI C语言编写、支持网络、可基于内存亦可持…

阿里版ChatGPT——通义千问,开箱初体验

所有行业、所有应用、所有服务都值得基于新型人工智能技术重做一遍&#xff0c;在带来创造性客户体验的同时&#xff0c;生产范式、工作范式、生活范式也将发生变化。——阿里集团董事会主席兼CEO 张勇 2023阿里云峰会上&#xff0c;通义千问大语言模型对外发布&#xff0c;宣称…

【语义分割】LinkNet 从0到1 和代码实现

文章目录 前言1.网络结构1.1 网络结构示意图1.2 创建LinkNet模型 2.代码 前言 已经有了U-net了&#xff0c;为什么需要linkNet&#xff1f; unet见这个文章【语义分割】unet结构和代码实现:https://blog.csdn.net/weixin_40293999/article/details/129648032 它引入了resNet&a…

“SDL 入门指南:了解 SDL,快速上手 SDL 的安装和配置”——VS2022

前言 欢迎来到小K的SDL专栏第一小节&#xff0c;本节为大家介绍一下SDL是什么&#xff0c;能做什么&#xff0c;可以在哪些平台运行以及SDL的安装和VS2022配置SDL、导出模板、cmake运行SDL&#xff0c;同时我也会在资源里为大家上传SDL2.26的安装包&#xff0c;为在github上下载…

扫地机洗地机语音芯片ic一体方案 WTV多功能语音芯片

​随着智能家居的快速普及&#xff0c;扫拖一体机语音芯片ic逐渐成为了家庭清洁的必备之物。在智能家居、商业清洁服务、医院清洁服务、办公室清洁等领域得到广泛应用&#xff1b;而语音芯片方案的应用让清洁机器设备使用起来更加方便和智能化。 编辑搜图 目前大多数扫地机厂家…

2023/5/4总结

刷题&#xff1a; 第二周任务 - Virtual Judge (vjudge.net) 这一题用到了素筛,然后穷举即可 #include<stdio.h> #define Maxsize 500000 int a[Maxsize]; long long b[Maxsize]; long long max0; int sushu() {a[0]a[1]0;int i,j,k;for(i2,k0;i<Maxsize;i){if(a[i…

jeecgboot online代码生成 一对多

首先把前后端环境都起起来&#xff0c;此处略 点击online表单开发&#xff0c;设计主附表&#xff0c;表示一对多(一对一)关系&#xff0c;“一”对应主表&#xff0c;“多”对应附表&#xff0c;如图 表设计完成&#xff0c;点击同步可直接在数据库中生成数据表。 附表注意…

卢北辰:数据点亮梦想,能力驱动人生 | 提升之路系列(九)

导读 为了发挥清华大学多学科优势&#xff0c;搭建跨学科交叉融合平台&#xff0c;创新跨学科交叉培养模式&#xff0c;培养具有大数据思维和应用创新的“π”型人才&#xff0c;由清华大学研究生院、清华大学大数据研究中心及相关院系共同设计组织的“清华大学大数据能力提升项…

今天的事务的基础上继续找出错点

今天的事务的基础上继续找出错点 2023-05-05 08:21:40.362 ERROR 5560 --- [nio-8080-exec-7] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested…

手机摄影笔记(一)

人像裁剪注意&#xff1a; 不要在人像的脚踝&#xff0c;膝盖&#xff0c;手肘&#xff0c;手腕处进行裁剪&#xff0c;这样会显得残缺&#xff0c;因为这些位置属于转折位置&#xff0c;不光滑的&#xff0c;上下差别很大。而在手臂&#xff0c;大腿等其他地方进行裁剪&#x…

ThingsBoard教程(三八):规则节点解析 筛选脚本 script,路由分支 switch

前言 本篇文件继续介绍规则节点中的 筛选类型的节点, 筛选脚本 script 可以通过js编程,使用消息体中的书籍,来实现返回true或false。 路由分支 switch : 将传入消息路由到一个或多个输出连接。节点执行配置的TBEL(推荐)或返回字符串数组(连接名称)的JavaScript函数。 …

《花雕学AI》30:ChatGPT的资料来源比例排名前20名是什么?

引言&#xff1a;ChatGPT是一款由OpenAI开发的人工智能聊天机器人&#xff0c;它可以回答各种问题&#xff0c;并生成创意内容&#xff0c;如诗歌、故事、代码等。 ChatGPT的核心技术是基于GPT-3.5和GPT-4的大型语言模型&#xff0c;它可以利用从网路上收集的大量文本资料来进行…

《一》Node 基础

Node&#xff1a;是一个基于 V8 引擎的 JavaScript 运行的环境。 V8 引擎可以嵌入到任何 C 应用程序中&#xff0c;无论是 Chrome 浏览器还是 Node&#xff0c;事实上都是嵌入了 V8 引擎来执行 JavaScript 代码的。 Node 架构&#xff1a; 编写的 JavaScript 代码首先经过 V8 引…

Springframework和Hibernate版本对应关系

org.springframework 3.0.x对应org.hibernate4.0.x版本 org.springframework 3.2.x对应org.hibernate4.2.x版本 org.springframework 4.0.x对应org.hibernate4.3.x版本 org.springframework 4.1.x对应org.hibernate5.0.x版本 org.springframework 4.3.x对应org.hibernate5.…

TOB企业如何构建自身的生态力

众所周知&#xff0c;企业服务赛道业务涉及范围非常多&#xff0c;其面向的客户分布广、触达过程长、需求场景复杂、功能集成和持续服务要求高等特点&#xff0c;就决定了To B企业无法通过单枪匹马的运营模式&#xff0c;来满足增长和健康运营的需求。 随着市场的竞争日益加剧…

【Docker】镜像与docker数据卷

文章目录 一、镜像1、镜像2、镜像原理之联合文件系统3、镜像原理之分层4、commit镜像 二、数据卷1、数据卷2、-v使用数据卷3、实战&#xff1a;MySQL 同步数据4、docker volume相关指令5、匿名和具名挂载6、数据卷之Dockerfile7、数据卷容器 一、镜像 1、镜像 镜像是一种轻量级…

k8s笔记25--k8s 跨主机网络flannel

k8s笔记25-- k8s 跨主机网络flannel 简介不同机器上网络设备区别flannel 网络常见三大后端模式UDPVXLANhost-gw 如何查看集群用哪种网络模式阿里云flannel容器网络 alloc参考文档 简介 在单机环境下&#xff0c;容器间可以通过 docker0 网桥来通信&#xff0c;但其无法实现不同…

QT QGridLayout网格布局控件

本文详细的介绍了QHBoxLayout控件的各种操作&#xff0c;例如&#xff1a;新建界面、控件布局、获取行、获取列、某行伸缩系数、某列伸缩系数、某列最小宽度、某行最小宽度、总单元格数、移除布局条目、移动布局条目、其它文章等操作。 实际开发中&#xff0c;一个界面上可能包…

【小程序】 键盘和表情同时存在时候,输入框上移问题

键盘和表情 效果图实现方法引入的js文件&#xff0c;文件名emoji.js&#xff0c;存放在untils路径下 效果图 实现过程&#xff0c;监听键盘高度的同时&#xff0c;判断是否获取到焦点样式上&#xff0c;swiper实现左右按页滑动效果 实现方法 <template><view class&…

IDEA常用配置及使用技巧

文章目录 下载插件JRebel and XRebelChinese Language PackMybatis XEasyCodeTranslationAuto filling Java call argumentsCodota AI Autocomplete for Java and JavaScriptAlibaba Java Coding GuidelinesEasyYapiGenerateAllSetterGit Commit TemplateGitToolBoxSQL Params …