JSPmvc

news2024/11/27 22:27:22

一、JSP 概述

JSP (全称: Java Server Pages ): Java 服务端页面。 是一种动态的网页技术,其中既可以定义 HTML JS CSS 等静态内 容,还可以定义 Java 代码的动态内容,也就是 JSP = HTML + Java 。如下就是 jsp 代码
<html>
<head>
<title> Title </title>
</head>
<body>
<h1> JSP,Hello World </h1>
<%
System . out . println ( "hello,jsp~" );
%>
</body>
</html>
上面代码 h1 标签内容是展示在页面上,而 Java 的输出语句是输出在 idea 的控制台。
那么, JSP 能做什么呢?现在我们只用 servlet 实现功能,看存在什么问题。如下图所示,当我们登陆成功后,需要在页面 上展示用户名

上图的用户名是动态展示,也就是谁登陆就展示谁的用户名。只用 servlet 如何实现呢?在今天的资料里已经提供好了一 个 LoginServlet ,该 servlet 就是实现这个功能的,现将资料中的 LoginServlet.java 拷贝到 request - demo 项目中 来演示。接下来启动服务器并访问登陆页面

输入了 zhangsan 用户的登陆信息后点击 登陆 按钮,就能看到如下图效果

当然如果是 lisi 登陆的,在该页面展示的就是 lisi, 欢迎您 ,动态的展示效果就实现了。那么 LoginServlet 到底是如何 实现的,我们看看它里面的内容

上面的代码有大量使用到 writer 对象向页面写标签内容,这样我们的代码就显得很麻烦;将来如果展示的效果出现了问 题,排错也显得有点力不从心。而 JSP 是如何解决这个问题的呢?在资料中也提供了一个 login.jsp 页面,该页面也能实 现该功能,现将该页面拷贝到项目的 webapp 下,需要修改 login.html 中表单数据提交的路径为下图

重新启动服务器并进行测试,发现也可以实现同样的功能。那么 login.jsp 又是如何实现的呢?那我们来看看 login.jsp 的代码

上面代码可以看到里面基本都是 HTML 标签,而动态数据使用 Java 代码进行展示;这样操作看起来要比用 servlet 实现要 舒服很多。
JSP 作用:简化开发,避免了在 Servlet 中直接输出 HTML 标签。

二、JSP 快速入门

接下来我们做一个简单的快速入门代码。

2.1 搭建环境

创建一个maven web 项目,项目结构如下:

pom.xml 文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns = "http://maven.apache.org/POM/4.0.0"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd" >
<modelVersion> 4.0.0 </modelVersion>
<groupId> org.example </groupId>
<artifactId> jsp-demo </artifactId>
<version> 1.0-SNAPSHOT </version>
<packaging> war </packaging>
<properties>
<maven.compiler.source> 8 </maven.compiler.source>
<maven.compiler.target> 8 </maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId> javax.servlet </groupId>
<artifactId> javax.servlet-api </artifactId>
<version> 3.1.0 </version>
<scope> provided </scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId> org.apache.tomcat.maven </groupId>
<artifactId> tomcat7-maven-plugin </artifactId>
<version> 2.2 </version>
</plugin>
</plugins>
</build>
</project>

2.2 导入 JSP 依赖

dependencies 标签中导入 JSP 的依赖,如下

<dependency>
<groupId> javax.servlet.jsp </groupId>
<artifactId> jsp-api </artifactId>
<version> 2.2 </version>
<scope> provided </scope>
</dependency>
该依赖的 scope 必须设置为 provided ,因为 tomcat 中有这个 jar 包了,所以在打包时我们是不希望将该依赖打进到我们 工程的war 包中。

2.3 创建 jsp 页面

在项目的 webapp 下创建jsp页面

通过上面方式创建一个名为 hello.jsp 的页面。

2.4 编写代码

hello.jsp 页面中书写 HTML 标签和 Java 代码,如下

<% @ page contentType = "text/html;charset=UTF-8" language = "java" %>
<html>
<head>
<title> Title </title>
</head>
<body>
<h1> hello jsp </h1>
<%
System . out . println ( "hello,jsp~" );
%>
</body>
</html>

2.5 测试

启动服务器并在浏览器地址栏输入 http://localhost:8080/jsp - demo/hello.jsp ,我们可以在页面上看到如下内容

同时也可以看到在 idea 的控制台看到输出的 hello,jsp~ 内容。

三、JSP 原理

我们之前说 JSP 就是一个页面,那么在 JSP 中写 html 标签,我们能理解,但是为什么还可以写 Java 代码呢?
因为 JSP 本质上就是一个 Servlet 接下来我们聊聊访问 jsp 时的流程

1. 浏览器第一次访问 hello.jsp 页面
2. tomcat 会将 hello.jsp 转换为名为 hello_jsp.java 的一个 Servlet
3. tomcat 再将转换的 servlet 编译成字节码文件 hello_jsp.class
4. tomcat 会执行该字节码文件,向外提供服务
我们可以到项目所在磁盘目录下找 target\tomcat\work\Tomcat\localhost\jsp - demo\org\apache\jsp 目录,而这个 目录下就能看到转换后的 servlet

打开 hello_jsp.java 文件,来查看里面的代码

由上面的类的继承关系可以看到继承了名为 HttpJspBase 这个类,那我们在看该类的继承关系。到资料中的找如下目录:
资料 \tomcat 源码 \apache - tomcat - 8.5.68 - src\java\org\apache\jasper\runtime ,该目录下就有 HttpJspBase 类,查 看该类的继承关系

可以看到该类继承了 HttpServlet ;那么 hello_jsp 这个类就间接的继承了 HttpServlet ,也就说明 hello_jsp 是一 个 servlet
继续阅读 hello_jsp 类的代码,可以看到有一个名为 _jspService() 的方法,该方法就是每次访问 jsp 时自动执行的方 法,和 servlet 中的 service 方法一样 。
而在 _jspService() 方法中可以看到往浏览器写标签的代码:

以前我们自己写 servlet 时,这部分代码是由我们自己来写,现在有了 jsp 后,由 tomcat 完成这部分功能。

四、JSP 脚本

JSP脚本用于在 JSP页面内定义 Java代码。在之前的入门案例中我们就在 JSP 页面定义的 Java 代码就是 JSP 脚本。

4.1 JSP 脚本分类

JSP 脚本有如下三个分类:
  • <%...%>:内容会直接放到_jspService()方法之中
  • <%=…%>:内容会放到out.print()中,作为out.print()的参数
  • <%!…%>:内容会放到_jspService()方法之外,被类直接包含
代码演示:
hello.jsp 中书写
<%
System . out . println ( "hello,jsp~" );
int i = 3 ;
%>
通过浏览器访问 hello.jsp 后,查看转换的 hello_jsp.java 文件, i 变量定义在了 _jspService() 方法中

hello.jsp 中书写
<% = "hello" %>
<% = i %>
通过浏览器访问 hello.jsp 后,查看转换的 hello_jsp.java 文件,该脚本的内容被放在了 out.print() 中,作为参数

hello.jsp 中书写
<% !
void show (){}
String name = "zhangsan" ;
%>
通过浏览器访问 hello.jsp 后,查看转换的 hello_jsp.java 文件,该脚本的内容被放在了成员位置

4.2 案例

1.需求

使用JSP脚本展示品牌数据

说明:
  • 在资料 资料\1. JSP案例素材 中提供了 brand.html 静态页面
  • 在该案例中数据不从数据库中查询,而是在 JSP 页面上写死

2.实现

  • 将资料 资料\1. JSP案例素材 中的 Brand.java 文件放置到项目的 com.itheima.pojo 包下
  • 在项目的 webapp 中创建 brand.jsp ,并将 brand.html 页面中的内容拷贝过来。 brand.jsp 内容如下
<% @ page contentType = "text/html;charset=UTF-8" language = "java" %>
<!DOCTYPE html>
<html lang = "en" >
<head>
<meta charset = "UTF-8" >
<title> Title </title>
</head>
<body>
<input type = "button" value = " 新增 " ><br>
<hr>
<table border = "1" cellspacing = "0" width = "800" >
<tr>
<th> 序号 </th>
<th> 品牌名称 </th>
<th> 企业名称 </th>
<th> 排序 </th>
<th> 品牌介绍 </th>
<th> 状态 </th>
<th> 操作 </th>
</tr>
<tr align = "center" >
<td> 1 </td>
<td> 三只松鼠 </td>
<td> 三只松鼠 </td>
<td> 100 </td>
<td> 三只松鼠,好吃不上火 </td>
<td> 启用 </td>
<td><a href = "#" > 修改 </a> <a href = "#" > 删除 </a></td>
</tr>
<tr align = "center" >
<td> 2 </td>
<td> 优衣库 </td>
<td> 优衣库 </td>
<td> 10 </td>
<td> 优衣库,服适人生 </td>
<td> 禁用 </td>
<td><a href = "#" > 修改 </a> <a href = "#" > 删除 </a></td>
</tr>
<tr align = "center" >
<td> 3 </td>
<td> 小米 </td>
<td> 小米科技有限公司 </td>
<td> 1000 </td>
<td> 为发烧而生 </td>
<td> 启用 </td>
<td><a href = "#" > 修改 </a> <a href = "#" > 删除 </a></td>
</tr>
</table>
</body>
</html>
现在页面中的数据都是假数据。
  • brand.jsp 中准备一些数据
<%
// 查询数据库
List < Brand > brands = new ArrayList < Brand > ();
brands . add ( new Brand ( 1 , " 三只松鼠 " , " 三只松鼠 " , 100 , " 三只松鼠,好吃不上火 " , 1 ));
brands . add ( new Brand ( 2 , " 优衣库 " , " 优衣库 " , 200 , " 优衣库,服适人生 " , 0 ));
brands . add ( new Brand ( 3 , " 小米 " , " 小米科技有限公司 " , 1000 , " 为发烧而生 " , 1 ));
%>
注意: 这里的类是需要导包的
  • brand.jsp 页面中的 table 标签中的数据改为动态的

<table border = "1" cellspacing = "0" width = "800" >
<tr>
<th> 序号 </th>
<th> 品牌名称 </th>
<th> 企业名称 </th>
<th> 排序 </th>
<th> 品牌介绍 </th>
<th> 状态 </th>
<th> 操作 </th>
</tr>
<%
for ( int i = 0 ; i < brands . size (); i ++ ) {
// 获取集合中的 每一个 Brand 对象
Brand brand = brands . get ( i );
}
%>
<tr align = "center" >
<td> 1 </td>
<td> 三只松鼠 </td>
<td> 三只松鼠 </td>
<td> 100 </td>
<td> 三只松鼠,好吃不上火 </td>
<td> 启用 </td>
<td><a href = "#" > 修改 </a> <a href = "#" > 删除 </a></td>
</tr>
</table>
上面的 for 循环需要将 tr 标签包裹起来,这样才能实现循环的效果,代码改进为
<table border = "1" cellspacing = "0" width = "800" >
<tr>
<th> 序号 </th>
<th> 品牌名称 </th>
<th> 企业名称 </th>
<th> 排序 </th>
<th> 品牌介绍 </th>
<th> 状态 </th>
<th> 操作 </th>
</tr>
<%
for ( int i = 0 ; i < brands . size (); i ++ ) {
// 获取集合中的 每一个 Brand 对象
Brand brand = brands . get ( i );
%>
<tr align = "center" >
<td> 1 </td>
<td> 三只松鼠 </td>
<td> 三只松鼠 </td>
<td> 100 </td>
<td> 三只松鼠,好吃不上火 </td>
<td> 启用 </td>
<td><a href = "#" > 修改 </a> <a href = "#" > 删除 </a></td>
</tr>
<%
}
%>
</table>
注意: <%%> 里面写的是 Java 代码,而外边写的是 HTML 标签
上面代码中的 td 标签中的数据都需要是动态的,所以还需要改进
<table border = "1" cellspacing = "0" width = "800" >
<tr>
<th> 序号 </th>
<th> 品牌名称 </th>
<th> 企业名称 </th>
<th> 排序 </th>
<th> 品牌介绍 </th>
<th> 状态 </th>
<th> 操作 </th>
</tr>
<%
for ( int i = 0 ; i < brands . size (); i ++ ) {
// 获取集合中的 每一个 Brand 对象
Brand brand = brands . get ( i );
%>
<tr align = "center" >
<td> <% = brand . getId ()%> </td>
<td> <% = brand . getBrandName ()%> </td>
<td> <% = brand . getCompanyName ()%> </td>
<td> <% = brand . getOrdered ()%> </td>
<td> <% = brand . getDescription ()%> </td>
<td> <% = brand . getStatus () == 1 ? " 启用 " : " 禁用 " %> </td>
<td><a href = "#" > 修改 </a> <a href = "#" > 删除 </a></td>
</tr>
<%
}
%>
</table>

3.成品代码

<% @ page import = "com.itheima.pojo.Brand" %>
<% @ page import = "java.util.List" %>
<% @ page import = "java.util.ArrayList" %>
<% @ page contentType = "text/html;charset=UTF-8" language = "java" %>
<%
// 查询数据库
List < Brand > brands = new ArrayList < Brand > ();
brands . add ( new Brand ( 1 , " 三只松鼠 " , " 三只松鼠 " , 100 , " 三只松鼠,好吃不上火 " , 1 ));
brands . add ( new Brand ( 2 , " 优衣库 " , " 优衣库 " , 200 , " 优衣库,服适人生 " , 0 ));
brands . add ( new Brand ( 3 , " 小米 " , " 小米科技有限公司 " , 1000 , " 为发烧而生 " , 1 ));
%>
<!DOCTYPE html>
<html lang = "en" >
<head>
<meta charset = "UTF-8" >
<title> Title </title>
</head>
<body>
<input type = "button" value = " 新增 " ><br>
<hr>
<table border = "1" cellspacing = "0" width = "800" >
<tr>
<th> 序号 </th>
<th> 品牌名称 </th>
<th> 企业名称 </th>
<th> 排序 </th>
<th> 品牌介绍 </th>
<th> 状态 </th>
<th> 操作 </th>
</tr>
<%
for ( int i = 0 ; i < brands . size (); i ++ ) {
Brand brand = brands . get ( i );
%>
<tr align = "center" >
<td> <% = brand . getId ()%> </td>
<td> <% = brand . getBrandName ()%> </td>
<td> <% = brand . getCompanyName ()%> </td>
<td> <% = brand . getOrdered ()%> </td>
<td> <% = brand . getDescription ()%> </td>
<td> <% = brand . getStatus () == 1 ? " 启用 " : " 禁用 " %> </td>
<td><a href = "#" > 修改 </a> <a href = "#" > 删除 </a></td>
</tr>
<%
}
%>
</table>
</body>
</html>

4.测试

在浏览器地址栏输入 http://localhost:8080/jsp-demo/brand.jsp ,页面展示效果如下

4.3 JSP 缺点

通过上面的案例,我们可以看到 JSP 的很多缺点。
由于 JSP 页面内,既可以定义 HTML 标签,又可以定义 Java 代码,造成了以下问题:
书写麻烦:特别是复杂的页面
既要写 HTML 标签,还要写 Java 代码
阅读麻烦
上面案例的代码,相信你后期再看这段代码时还需要花费很长的时间去梳理
复杂度高:运行需要依赖于各种环境, JRE JSP 容器, JavaEE…
占内存和磁盘: JSP 会自动生成 .java .class 文件占磁盘,运行的是 .class 文件占内存
调试困难:出错后,需要找到自动生成的 .java 文件进行调试
不利于团队协作:前端人员不会 Java ,后端人员不精 HTML
如果页面布局发生变化,前端工程师对静态页面进行修改,然后再交给后端工程师,由后端工程师再将该页面改为 JSP
页面
由于上述的问题, JSP 已逐渐退出历史舞台, 以后开发更多的是使用 HTML + Ajax 来替代。 Ajax 是我们后续会重点学习的 技术。有个这个技术后,前端工程师负责前端页面开发,而后端工程师只负责前端代码开发。下来对技术的发展进行简单的 说明

1. 第一阶段:使用 servlet 即实现逻辑代码编写,也对页面进行拼接。这种模式我们之前也接触过
2. 第二阶段:随着技术的发展,出现了 JSP ,人们发现 JSP 使用起来比 Servlet 方便很多,但是还是要在 JSP 中嵌套 Java 代码,也不利于后期的维护
3. 第三阶段:使用 Servlet 进行逻辑代码开发,而使用 JSP 进行数据展示

4. 第四阶段:使用 servlet 进行后端逻辑代码开发,而使用 HTML 进行数据展示。而这里面就存在问题, HTML 是静态页
面,怎么进行动态数据展示呢?这就是 ajax 的作用了。
那既然 JSP 已经逐渐的退出历史舞台,那我们为什么还要学习 JSP 呢?原因有两点:
  • 一些公司可能有些老项目还在用 JSP ,所以要求我们必须动 JSP
  • 我们如果不经历这些复杂的过程,就不能体现后面阶段开发的简单
使用 EL 表达式 JSTL 标签库替换 JSP 中的 Java 代码。

五、EL 表达式

5.1 概述

EL (全称 Expression Language )表达式语言,用于简化 JSP 页面内的 Java 代码。
EL 表达式的主要作用是 获取数据 。其实就是从域对象中获取数据,然后将数据展示在页面上。
EL 表达式的语法也比较简单, ${expression} 。例如: ${brands} 就是获取域中存储的 key brands 的数据。

5.2 代码演示

定义servlet,在 servlet 中封装一些数据并存储到 request 域对象中并转发到 el-demo.jsp 页面。

@WebServlet ( "/demo1" )
public class ServletDemo1 extends HttpServlet {
@Override
protected void doGet ( HttpServletRequest request , HttpServletResponse response ) throws
ServletException , IOException {
//1. 准备数据
List < Brand > brands = new ArrayList < Brand > ();
brands . add ( new Brand ( 1 , " 三只松鼠 " , " 三只松鼠 " , 100 , " 三只松鼠,好吃不上火 " , 1 ));
brands . add ( new Brand ( 2 , " 优衣库 " , " 优衣库 " , 200 , " 优衣库,服适人生 " , 0 ));
brands . add ( new Brand ( 3 , " 小米 " , " 小米科技有限公司 " , 1000 , " 为发烧而生 " , 1 ));
//2. 存储到 request 域中
request . setAttribute ( "brands" , brands );
//3. 转发到 el-demo.jsp
request . getRequestDispatcher ( "/el-demo.jsp" ). forward ( request , response );
}
@Override
protected void doPost ( HttpServletRequest request , HttpServletResponse response ) throws
ServletException , IOException {
this . doGet ( request , response );
}
}

注意: 此处需要用转发,因为转发才可以使用 request 对象作为域对象进行数据共享
  • el-demo.jsp 中通过 EL表达式 获取数据

<% @ page contentType = "text/html;charset=UTF-8" language = "java" %>
<html>
<head>
<title> Title </title>
</head>
<body>
${brands}
</body>
</html>
  • 在浏览器的地址栏输入 http://localhost:8080/jsp-demo/demo1 ,页面效果如下:

5.3 域对象

JavaWeb 中有四大域对象,分别是:
  • page:当前页面有效
  • request:当前请求有效
  • session:当前会话有效
  • application:当前应用有效
el 表达式获取数据,会依次从这 4 个域中寻找,直到找到为止。而这四个域对象的作用范围如下图所示

例如: ${brands} el 表达式获取数据,会先从 page 域对象中获取数据,如果没有再到 requet 域对象中获取数据,如果再 没有再到 session 域对象中获取,如果还没有才会到 application 中获取数据。

六、JSTL标签

6.1 概述

JSP标准标签库(Jsp Standarded Tag Library) ,使用标签取代JSP页面上的Java代码。如下代码就是JSTL标签

<c:if test = "${flag == 1}" >
</c:if>
<c:if test = "${flag == 2}" >
</c:if>
上面代码看起来是不是比 JSP 中嵌套 Java 代码看起来舒服好了。而且前端工程师对标签是特别敏感的,他们看到这段代码是 能看懂的。
JSTL 提供了很多标签,如下图

我们只对两个最常用的标签进行讲解, <c:forEach> 标签和 <c:if> 标签。
JSTL 使用也是比较简单的,分为如下步骤:
  • 导入坐标
<dependency>
<groupId> jstl </groupId>
<artifactId> jstl </artifactId>
<version> 1.2 </version>
</dependency>
<dependency>
<groupId> taglibs </groupId>
<artifactId> standard </artifactId>
<version> 1.1.2 </version>
</dependency>
  • JSP页面上引入JSTL标签库
<% @ taglib prefix = "c" uri = "http://java.sun.com/jsp/jstl/core" %>
  • 使用标签

6.2 if 标签

<c:if> :相当于 if 判断
  • 属性:test,用于定义条件表达式
<c:if test = "${flag == 1}" >
</c:if>
<c:if test = "${flag == 2}" >
</c:if>
代码演示:
  • 定义一个 servlet ,在该 servlet 中向 request 域对象中添加 键是 status ,值为 1 的数据

@WebServlet ( "/demo2" )
public class ServletDemo2 extends HttpServlet {
@Override
protected void doGet ( HttpServletRequest request , HttpServletResponse response ) throws
ServletException , IOException {
//1. 存储数据到 request 域中
request . setAttribute ( "status" , 1 );
//2. 转发到 jstl-if.jsp
数据 request . getRequestDispatcher ( "/jstl-if.jsp" ). forward ( request , response );
}
@Override
protected void doPost ( HttpServletRequest request , HttpServletResponse response ) throws
ServletException , IOException {
this . doGet ( request , response );
}
}
  • 定义 jstl-if.jsp 页面,在该页面使用 <c:if> 标签

<% @ page contentType = "text/html;charset=UTF-8" language = "java" %>
<% @ taglib prefix = "c" uri = "http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title> Title </title>
</head>
<body>
<% --
c : if :来完成逻辑判断,替换 java if else
-- %>
<c:if test = "${status ==1}" >
启用
</c:if>
<c:if test = "${status ==0}" >
禁用
</c:if>
</body>
</html>
注意: 在该页面已经要引入 JSTL 核心标签库
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

6.3 forEach 标签

<c:forEach> :相当于 for 循环。java中有增强for循环和普通for循环,JSTL 中的 <c:forEach> 也有两种用法

1.用法一

类似于 Java 中的增强 for 循环。涉及到的 <c:forEach> 中的属性如下
  • items:被遍历的容器
  • var:遍历产生的临时变量
  • varStatus:遍历状态对象
如下代码,是从域对象中获取名为 brands 数据,该数据是一个集合;遍历遍历,并给该集合中的每一个元素起名为 brand ,是 Brand 对象。在循环里面使用 EL 表达式获取每一个 Brand 对象的属性值
<c:forEach items = "${brands}" var = "brand" >
<tr align = "center" >
<td> ${brand.id} </td>
<td> ${brand.brandName} </td>
<td> ${brand.companyName} </td>
<td> ${brand.description} </td>
</tr>
</c:forEach>
代码演示:
  • servlet 还是使用之前的名为 ServletDemo1
  • 定义名为 jstl-foreach.jsp 页面,内容如下:
<% @ page contentType = "text/html;charset=UTF-8" language = "java" %>
<% @ taglib prefix = "c" uri = "http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html lang = "en" >
<head>
<meta charset = "UTF-8" >
<title> Title </title>
</head>
<body>
<input type = "button" value = " 新增 " ><br>
<hr>
<table border = "1" cellspacing = "0" width = "800" >
<tr>
<th> 序号 </th>
<th> 品牌名称 </th>
<th> 企业名称 </th>
<th> 排序 </th>
<th> 品牌介绍 </th>
<th> 状态 </th>
<th> 操作 </th>
</tr>
<c:forEach items = "${brands}" var = "brand" varStatus = "status" >
<tr align = "center" >
<% --< td > $ { brand . id } </ td >-- %>
<td> ${status.count} </td>
<td> ${brand.brandName} </td>
<td> ${brand.companyName} </td>
<td> ${brand.ordered} </td>
<td> ${brand.description} </td>
<c:if test = "${brand.status == 1}" >
<td> 启用 </td>
</c:if>
<c:if test = "${brand.status != 1}" >
<td> 禁用 </td>
</c:if>
<td><a href = "#" > 修改 </a> <a href = "#" > 删除 </a></td>
</tr>
</c:forEach>
</table>
</body>
</html>

2.用法二

类似于 Java 中的普通 for 循环。涉及到的 <c:forEach> 中的属性如下
  • begin:开始数
  • end:结束数
  • step:步长
实例代码:
0 循环到 10 ,变量名是 i ,每次自增 1
<c:forEach begin = "0" end = "10" step = "1" var = "i" >
${i}
</c:forEach>

七、MVC模式和三层架构

MVC 模式和三层架构是一些理论的知识,将来我们使用了它们进行代码开发会让我们代码维护性和扩展性更好。

7.1 MVC模式

MVC 是一种分层开发的模式,其中:
  • MModel,业务模型,处理业务
  • VView,视图,界面展示
  • CController,控制器,处理请求,调用模型和视图

控制器( serlvlet )用来接收浏览器发送过来的请求,控制器调用模型( JavaBean )来获取数据,比如从数据库查询数据;
控制器获取到数据后再交由视图( JSP )进行数据展示。
MVC 好处:
  • 职责单一,互不影响。每个角色做它自己的事,各司其职。
  • 有利于分工协作。
  • 有利于组件重用

7.2 三层架构

三层架构是将我们的项目分成了三个层面,分别是 表现层 业务逻辑层 数据访问层

  • 数据访问层:对数据库的CRUD基本操作
  • 业务逻辑层:对业务逻辑进行封装,组合数据访问层层中基本功能,形成复杂的业务逻辑功能。例如 注册业务功能 ,我 们会先调用 数据访问层 selectByName() 方法判断该用户名是否存在,如果不存在再调用 数据访问层 insert() 方法进行数据的添加操作
  • 表现层:接收请求,封装数据,调用业务逻辑层,响应数据
而整个流程是,浏览器发送请求,表现层的 Servlet 接收请求并调用业务逻辑层的方法进行业务逻辑处理,而业务逻辑层方法 调用数据访问层方法进行数据的操作,依次返回到serlvet ,然后 servlet 将数据交由 JSP 进行展示。
三层架构的每一层都有特有的包名称:
  • 表现层: com.itheima.controller 或者 com.itheima.web
  • 业务逻辑层: com.itheima.service
  • 数据访问层: com.itheima.dao 或者 com.itheima.mapper
后期我们还会学习一些框架,不同的框架是对不同层进行封装的

7.3 MVC 和 三层架构

通过 MVC 和 三层架构 的学习,有些人肯定混淆了。那他们有什么区别和联系?

如上图上半部分是 MVC 模式,上图下半部分是三层架构。 MVC 模式 中的 C (控制器)和 V (视图)就是 三层架构 中的表 现层,而 MVC 模式 中的 M (模型)就是 三层架构 中的 业务逻辑层 和 数据访问层。
可以将 MVC 模式 理解成是一个大的概念,而 三层架构 是对 MVC 模式 实现架构的思想。 那么我们以后按照要求将不同层的 代码写在不同的包下,每一层里功能职责做到单一,将来如果将表现层的技术换掉,而业务逻辑层和数据访问层的代码不需 要发生变化。

八、案例

需求:完成品牌数据的增删改查操作

这个功能我们之前一直在做,而这个案例是将今天学习的所有的内容(包含 MVC 模式 和 三层架构)进行应用,并将整个流 程贯穿起来。

8.1 环境准备

环境准备工作,我们分以下步骤实现:
  • 创建新的模块 brand_demo,引入坐标
  • 创建三层架构的包结构
  • 数据库表 tb_brand
  • 实体类 Brand
  • MyBatis 基础环境
        Mybatis-config.xml
        BrandMapper.xml
        BrandMapper接口

1.创建工程

创建新的模块 brand_demo ,引入坐标。我们只要分析出要用到哪儿些技术,那么需要哪儿些坐标也就明确了
  • 需要操作数据库。mysql的驱动包
  • 要使用mybatis框架。mybaits的依赖包
  • web项目需要用到servletjspservletjsp的依赖包
  • 需要使用 jstl 进行数据展示。jstl的依赖包
pom.xml 内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns = "http://maven.apache.org/POM/4.0.0"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd" >
<modelVersion> 4.0.0 </modelVersion>
<groupId> org.example </groupId>
<artifactId> brand-demo </artifactId>
<version> 1.0-SNAPSHOT </version>
<packaging> war </packaging>
<properties>
<maven.compiler.source> 8 </maven.compiler.source>
<maven.compiler.target> 8 </maven.compiler.target>
</properties>
<dependencies>
<!-- mybatis -->
<dependency>
<groupId> org.mybatis </groupId>
<artifactId> mybatis </artifactId>
<version> 3.5.5 </version>
</dependency>
<!--mysql-->
<dependency>
<groupId> mysql </groupId>
<artifactId> mysql-connector-java </artifactId>
<version> 5.1.34 </version>
</dependency>
<!--servlet-->
<dependency>
<groupId> javax.servlet </groupId>
<artifactId> javax.servlet-api </artifactId>
<version> 3.1.0 </version>
<scope> provided </scope>
</dependency>
<!--jsp-->
<dependency>
<groupId> javax.servlet.jsp </groupId>
<artifactId> jsp-api </artifactId>
<version> 2.2 </version>
<scope> provided </scope>
</dependency>
<!--jstl-->
<dependency>
<groupId> jstl </groupId>
<artifactId> jstl </artifactId>
<version> 1.2 </version>
</dependency>
<dependency>
<groupId> taglibs </groupId>
<artifactId> standard </artifactId>
<version> 1.1.2 </version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId> org.apache.tomcat.maven </groupId>
<artifactId> tomcat7-maven-plugin </artifactId>
<version> 2.2 </version>
</plugin>
</plugins>
</build>
</project

2.创建包

创建不同的包结构,用来存储不同的类。包结构如下

3.创建表

-- 删除 tb_brand
drop table if exists tb_brand;
-- 创建 tb_brand
create table tb_brand
(
-- id 主键
id int primary key auto_increment ,
-- 品牌名称
brand_name varchar ( 20 ) ,
-- 企业名称
company_name varchar ( 20 ) ,
-- 排序字段
ordered int ,
-- 描述信息
description varchar ( 100 ) ,
-- 状态: 0 :禁用 1 :启用
status int
) ;
-- 添加数据
insert into tb_brand ( brand_name, company_name, ordered, description, status )
values ( ' 三只松鼠 ' , ' 三只松鼠股份有限公司 ' , 5 , ' 好吃不上火 ' , 0 ) ,
( ' 华为 ' , ' 华为技术有限公司 ' , 100 , ' 华为致力于把数字世界带入每个人、每个家庭、每个组织,构建万物互联的智
能世界 ' , 1 ) ,
( ' 小米 ' , ' 小米科技有限公司 ' , 50 , 'are you ok' , 1 ) ;

4.创建实体类

pojo 包下创建名为 Brand 的类。

public class Brand {
// id 主键
private Integer id ;
// 品牌名称
private String brandName ;
// 企业名称
private String companyName ;
// 排序字段
private Integer ordered ;
// 描述信息
private String description ;
// 状态: 0 :禁用 1 :启用
private Integer status ;
public Brand () {
}
public Brand ( Integer id , String brandName , String companyName , String description ) {
this . id = id ;
this . brandName = brandName ;
this . companyName = companyName ;
this . description = description ;
}
public Brand ( Integer id , String brandName , String companyName , Integer ordered , String
description , Integer status ) {
this . id = id ;
this . brandName = brandName ;
this . companyName = companyName ;
this . ordered = ordered ;
this . description = description ;
this . status = status ;
}
public Integer getId () {
return id ;
}
public void setId ( Integer id ) {
this . id = id ;
}
public String getBrandName () {
return brandName ;
}
public void setBrandName ( String brandName ) {
this . brandName = brandName ;
}
public String getCompanyName () {
return companyName ;
}
public void setCompanyName ( String companyName ) {
this . companyName = companyName ;
}
public Integer getOrdered () {
return ordered ;
}
public void setOrdered ( Integer ordered ) {
this . ordered = ordered ;
}
public String getDescription () {
return description ;
}
public void setDescription ( String description ) {
this . description = description ;
}
public Integer getStatus () {
return status ;
}
public void setStatus ( Integer status ) {
this . status = status ;
}
@Override
public String toString () {
return "Brand{" +
"id=" + id +
", brandName='" + brandName + '\'' +
", companyName='" + companyName + '\'' +
", ordered=" + ordered +
", description='" + description + '\'' +
", status=" + status +
'}' ;
}
}

5.准备mybatis环境

定义核心配置文件 Mybatis - config.xml ,并将该文件放置在 resources
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 起别名 -->
<typeAliases>
<package name = "com.itheima.pojo" />
</typeAliases>
<environments default = "development" >
<environment id = "development" >
<transactionManager type = "JDBC" />
<dataSource type = "POOLED" >
<property name = "driver" value = "com.mysql.jdbc.Driver" />
<property name = "url" value = "jdbc:mysql:///db1?
useSSL=false&useServerPrepStmts=true" />
<property name = "username" value = "root" />
<property name = "password" value = "1234" />
</dataSource>
</environment>
</environments>
<mappers>
<!-- 扫描 mapper-->
<package name = "com.itheima.mapper" />
</mappers>
</configuration>
resources 下创建放置映射配置文件的目录结构 com/itheima/mapper ,并在该目录下创建映射配置文件 BrandMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace = "com.itheima.mapper.BrandMapper" >
</mapper>

8.2 查询所有

当我们点击 index.html 页面中的 查询所有 这个超链接时,就能查询到上图右半部分的数据。
对于上述的功能,点击 查询所有 超链接是需要先请后端的 servlet ,由 servlet 跳转到对应的页面进行数据的动态展 示。而整个流程如下图:

1.编写BrandMapper

mapper 包下创建创建 BrandMapper 接口,在接口中定义 selectAll() 方法

/**
* 查询所有
* @return
*/
@Select ( "select * from tb_brand" )
List < Brand > selectAll ();

2.编写工具类

com.itheima 包下创建 utils 包,并在该包下创建名为 SqlSessionFactoryUtils 工具类

public class SqlSessionFactoryUtils {

private static SqlSessionFactory sqlSessionFactory ;
static {
// 静态代码块会随着类的加载而自动执行,且只执行一次
try {
String resource = "mybatis-config.xml" ;
InputStream inputStream = Resources . getResourceAsStream ( resource );
sqlSessionFactory = new SqlSessionFactoryBuilder (). build ( inputStream );
} catch ( IOException e ) {
e . printStackTrace ();
}
}
public static SqlSessionFactory getSqlSessionFactory (){
return sqlSessionFactory ;
}
}

3.编写BrandService

service 包下创建 BrandService

public class BrandService {
SqlSessionFactory factory = SqlSessionFactoryUtils . getSqlSessionFactory ();
/**
* 查询所有
* @return
*/
public List < Brand > selectAll (){
// 调用 BrandMapper.selectAll()
//2. 获取 SqlSession
SqlSession sqlSession = factory . openSession ();
//3. 获取 BrandMapper
BrandMapper mapper = sqlSession . getMapper ( BrandMapper . class );
//4. 调用方法
List < Brand > brands = mapper . selectAll ();
sqlSession . close ();
return brands ;
}
}

4.编写Servlet

web 包下创建名为 SelectAllServlet servlet ,该 servlet 的逻辑如下:
  • 调用 BrandService selectAll() 方法进行业务逻辑处理,并接收返回的结果
  • 将上一步返回的结果存储到 request 域对象中
  • 跳转到 brand.jsp 页面进行数据的展示
具体的代码如下:
@WebServlet ( "/selectAllServlet" )
public class SelectAllServlet extends HttpServlet {
private BrandService service = new BrandService ();
@Override
protected void doGet ( HttpServletRequest request , HttpServletResponse response ) throws
ServletException , IOException {
//1. 调用 BrandService 完成查询
List < Brand > brands = service . selectAll ();
//2. 存入 request 域中
request . setAttribute ( "brands" , brands );
//3. 转发到 brand.jsp
request . getRequestDispatcher ( "/brand.jsp" ). forward ( request , response );
}
@Override
protected void doPost ( HttpServletRequest request , HttpServletResponse response ) throws
ServletException , IOException {
this . doGet ( request , response );
}
}

5.编写brand.jsp页面

将资料 资料 \2. 品牌增删改查案例 \ 静态页面 下的 brand.html 页面拷贝到项目的 webapp 目录下,并将该页面改成 brand.jsp 页面,而 brand.jsp 页面在表格中使用 JSTL EL 表达式 request 域对象中获取名为 brands 的集合数据 并展示出来。页面内容如下:
<% @ page contentType = "text/html;charset=UTF-8" language = "java" %>
<% @ taglib prefix = "c" uri = "http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html lang = "en" >
<head>
<meta charset = "UTF-8" >
<title> Title </title>
</head>
<body>
<hr>
<table border = "1" cellspacing = "0" width = "80%" >
<tr>
<th> 序号 </th>
<th> 品牌名称 </th>
<th> 企业名称 </th>
<th> 排序 </th>
<th> 品牌介绍 </th>
<th> 状态 </th>
<th> 操作 </th>
</tr>
<c:forEach items = "${brands}" var = "brand" varStatus = "status" >
<tr align = "center" >
<% --< td > $ { brand . id } </ td >-- %>
<td> ${status.count} </td>
<td> ${brand.brandName} </td>
<td> ${brand.companyName} </td>
<td> ${brand.ordered} </td>
<td> ${brand.description} </td>
<c:if test = "${brand.status == 1}" >
<td> 启用 </td>
</c:if>
<c:if test = "${brand.status != 1}" >
<td> 禁用 </td>
</c:if>
<td><a href = "/brand-demo/selectByIdServlet?id=${brand.id}" > 修改 </a> <a href = "#" >
</a></td>
</tr>
</c:forEach>
</table>
</body>
</html>

6.测试

启动服务器,并在浏览器输入 http://localhost:8080/brand - demo/index.html ,看到如下 查询所有 的超链接,点击该 链接就可以查询出所有的品牌数据

为什么出现这个问题呢?是因为查询到的字段名和实体类对象的属性名没有一一对应。相比看到这大家一定会解决了,就是 在映射配置文件中使用 resultMap 标签定义映射关系。映射配置文件内容如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace = "com.itheima.mapper.BrandMapper" >
<resultMap id = "brandResultMap" type = "brand" >
<result column = "brand_name" property = "brandName" ></result>
<result column = "company_name" property = "companyName" ></result>
</resultMap>
</mapper>
并且在 BrandMapper 接口中的 selectAll() 上使用 @ResuleMap 注解指定使用该映射
/**
* 查询所有
* @return
*/
@Select ( "select * from tb_brand" )
@ResultMap ( "brandResultMap" )
List < Brand > selectAll ();
重启服务器,再次访问就能看到我们想要的数据了

8.3 添加

上图是做 添加 功能流程。点击 新增 按钮后,会先跳转到 addBrand.jsp 新增页面,在该页面输入要添加的数据,输入完 毕后点击 提交 按钮,需要将数据提交到后端,而后端进行数据添加操作,并重新将所有的数据查询出来。整个流程如下:

接下来我们根据流程来实现功能:

1.编写BrandMapper方法

BrandMapper 接口,在接口中定义 add(Brand brand) 方法

@Insert ( "insert into tb_brand values(null,#{brandName},#{companyName},#{ordered},#
{description},#{status})" )
void add ( Brand brand );

2.编写BrandService方法

BrandService 类中定义添加品牌数据方法 add(Brand brand)

/**
* 添加
* @param brand
*/
public void add ( Brand brand ){
//2. 获取 SqlSession
SqlSession sqlSession = factory . openSession ();
//3. 获取 BrandMapper
BrandMapper mapper = sqlSession . getMapper ( BrandMapper . class );
//4. 调用方法
mapper . add ( brand );
// 提交事务
sqlSession . commit ();
// 释放资源
sqlSession . close ();
}

3.改进brand.jsp页面

我们需要在该页面表格的上面添加 新增 按钮

<input type="button" value="新增" id="add"><br>

并给该按钮绑定单击事件,当点击了该按钮需要跳转到 brand.jsp 添加品牌数据的页面

<script>
document . getElementById ( "add" ). onclick = function (){
location . href = "/brand-demo/addBrand.jsp" ;
}
</script>
注意: script 标签建议放在 body 结束标签前面。

4.编写addBrand.jsp页面

从资料 资料 \2. 品牌增删改查案例 \ 静态页面 中将 addBrand.html 页面拷贝到项目的 webapp 下,并改成 addBrand.jsp 动 态页面
<% @ page contentType = "text/html;charset=UTF-8" language = "java" %>
<!DOCTYPE html>
<html lang = "en" >
<head>
<meta charset = "UTF-8" >
<title> 添加品牌 </title>
</head>
<body>
<h3> 添加品牌 </h3>
<form action = "/brand-demo/addServlet" method = "post" >
品牌名称: <input name = "brandName" ><br>
企业名称: <input name = "companyName" ><br>
排序: <input name = "ordered" ><br>
描述信息: <textarea rows = "5" cols = "20" name = "description" ></textarea><br>
状态:
<input type = "radio" name = "status" value = "0" > 禁用
<input type = "radio" name = "status" value = "1" > 启用 <br>
<input type = "submit" value = " 提交 " >
</form>
</body>
</html>

5.编写servlet

web 包下创建 AddServlet servlet ,该 servlet 的逻辑如下 :
  • 设置处理post请求乱码的字符集
  • 接收客户端提交的数据
  • 将接收到的数据封装到 Brand 对象中
  • 调用 BrandService add() 方法进行添加的业务逻辑处理
  • 跳转到 selectAllServlet 资源重新查询数据
具体的代码如下:
@WebServlet ( "/addServlet" )
public class AddServlet extends HttpServlet {
private BrandService service = new BrandService ();
@Override
protected void doGet ( HttpServletRequest request , HttpServletResponse response ) throws
ServletException , IOException {
// 处理 POST 请求的乱码问题
request . setCharacterEncoding ( "utf-8" );
//1. 接收表单提交的数据,封装为一个 Brand 对象
String brandName = request . getParameter ( "brandName" );
String companyName = request . getParameter ( "companyName" );
String ordered = request . getParameter ( "ordered" );
String description = request . getParameter ( "description" );
String status = request . getParameter ( "status" );
// 封装为一个 Brand 对象
Brand brand = new Brand ();
brand . setBrandName ( brandName );
brand . setCompanyName ( companyName );
brand . setOrdered ( Integer . parseInt ( ordered ));
brand . setDescription ( description );
brand . setStatus ( Integer . parseInt ( status ));
//2. 调用 service 完成添加
service . add ( brand );
//3. 转发到查询所有 Servlet
request . getRequestDispatcher ( "/selectAllServlet" ). forward ( request , response );
}
@Override
protected void doPost ( HttpServletRequest request , HttpServletResponse response ) throws
ServletException , IOException {
this . doGet ( request , response );
  }
}

6.测试

点击 brand.jsp 页面的 新增 按钮,会跳转到 addBrand.jsp 页面

点击 提交 按钮,就能看到如下页面,里面就包含我们刚添加的数据

8.4 修改

点击每条数据后面的 编辑 按钮会跳转到修改页面,如下图:

在该修改页面我们可以看到将 编辑 按钮所在行的数据 回显 到表单,然后需要修改那个数据在表单中进行修改,然后点击 提 交 的按钮将数据提交到后端,后端再将数据存储到数据库中。
从上面的例子我们知道 修改 功能需要从两方面进行实现,数据回显和修改操作。

8.4.1 回显数据

上图就是回显数据的效果。要实现这个效果,那当点击 修改 按钮时不能直接跳转到 update.jsp 页面,而是需要先带着当 前行数据的 id 请求后端程序,后端程序根据 id 查询数据,将数据存储到域对象中跳转到 update.jsp 页面进行数据展 示。整体流程如下

1.编写BrandMapper方法

BrandMapper 接口,在接口中定义 selectById(int id) 方法

/**
* 根据 id 查询
* @param id
* @return
*/
@Select ( "select * from tb_brand where id = #{id}" )
@ResultMap ( "brandResultMap" )
Brand selectById ( int id );

2.编写BrandService方法

BrandService 类中定义根据id查询数据方法 selectById(int id)

/**
* 根据 id 查询
* @return
*/
public Brand selectById ( int id ){
// 调用 BrandMapper.selectAll()
//2. 获取 SqlSession
SqlSession sqlSession = factory . openSession ();
//3. 获取 BrandMapper
BrandMapper mapper = sqlSession . getMapper ( BrandMapper . class );
//4. 调用方法
Brand brand = mapper . selectById ( id );
sqlSession . close ();
return brand ;
}

3.编写servlet

web 包下创建 SelectByIdServlet servlet ,该 servlet 的逻辑如下 :
  • 获取请求数据 id
  • 调用 BrandService selectById() 方法进行数据查询的业务逻辑
  • 将查询到的数据存储到 request 域对象中
  • 跳转到 update.jsp 页面进行数据真实
具体代码如下:
@WebServlet ( "/selectByIdServlet" )
public class SelectByIdServlet extends HttpServlet {
private BrandService service = new BrandService ();
@Override
protected void doGet ( HttpServletRequest request , HttpServletResponse response ) throws
ServletException , IOException {
//1. 接收 id
String id = request . getParameter ( "id" );
//2. 调用 service 查询
Brand brand = service . selectById ( Integer . parseInt ( id ));
//3. 存储到 request
request . setAttribute ( "brand" , brand );
//4. 转发到 update.jsp
request . getRequestDispatcher ( "/update.jsp" ). forward ( request , response );
}
@Override
protected void doPost ( HttpServletRequest request , HttpServletResponse response ) throws
ServletException , IOException {
this . doGet ( request , response );
}
}

4.编写update.jsp页面

拷贝 addBrand.jsp 页面,改名为 update.jsp 并做出以下修改:
  • title 标签内容改为 修改品牌
  • form 标签的 action 属性值改为 /brand-demo/updateServlet
  • input 标签要进行数据回显,需要设置 value 属性
品牌名称: <input name = "brandName" value = "${brand.brandName}" ><br>
企业名称: <input name = "companyName" value = "${brand.companyName}" ><br>
排序: <input name = "ordered" value = "${brand.ordered}" ><br>
  • textarea 标签要进行数据回显,需要在标签体中使用 EL表达式
描述信息: <textarea rows = "5" cols = "20" name = "description" > ${brand.description} </textarea>
<br>
  • 单选框使用 if 标签需要判断 brand.status 的值是 1 还是 0 在指定的单选框上使用 checked 属性,表示被选中状态
状态:
<c:if test = "${brand.status == 0}" >
<input type = "radio" name = "status" value = "0" checked > 禁用
<input type = "radio" name = "status" value = "1" > 启用 <br>
</c:if>
<c:if test = "${brand.status == 1}" >
<input type = "radio" name = "status" value = "0" > 禁用
<input type = "radio" name = "status" value = "1" checked > 启用 <br>
</c:if>
综上, update.jsp 代码如下:
<% @ page contentType = "text/html;charset=UTF-8" language = "java" %>
<% @ taglib prefix = "c" uri = "http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html lang = "en" >
<head>
<meta charset = "UTF-8" >
<title> 修改品牌 </title>
</head>
<body>
<h3> 修改品牌 </h3>
<form action = "/brand-demo/updateServlet" method = "post" >
品牌名称: <input name = "brandName" value = "${brand.brandName}" ><br>
企业名称: <input name = "companyName" value = "${brand.companyName}" ><br>
排序: <input name = "ordered" value = "${brand.ordered}" ><br>
描述信息: <textarea rows = "5" cols = "20" name = "description" > ${brand.description} </textarea>
<br>
状态:
<c:if test = "${brand.status == 0}" >
<input type = "radio" name = "status" value = "0" checked > 禁用
<input type = "radio" name = "status" value = "1" > 启用 <br>
</c:if>
<c:if test = "${brand.status == 1}" >
<input type = "radio" name = "status" value = "0" > 禁用
<input type = "radio" name = "status" value = "1" checked > 启用 <br>
</c:if>
<input type = "submit" value = " 提交 " >
</form>
</body>
</html>

8.4.2 修改数据

做完回显数据后,接下来我们要做修改数据了,而下图是修改数据的效果:

在修改页面进行数据修改,点击 提交 按钮,会将数据提交到后端程序,后端程序会对表中的数据进行修改操作,然后重新 进行数据的查询操作。整体流程如下:

1.编写BrandMapper方法

BrandMapper 接口,在接口中定义 update(Brand brand) 方法

/**
* 修改
* @param brand
*/
@Update ( "update tb_brand set brand_name = #{brandName},company_name = #{companyName},ordered =
#{ordered},description = #{description},status = #{status} where id = #{id}" )
void update ( Brand brand );

2.编写BrandService方法

BrandService 类中定义根据id查询数据方法 update(Brand brand)

/**
* 修改
* @param brand
*/
public void update ( Brand brand ){
//2. 获取 SqlSession
SqlSession sqlSession = factory . openSession ();
//3. 获取 BrandMapper
BrandMapper mapper = sqlSession . getMapper ( BrandMapper . class );
//4. 调用方法
mapper . update ( brand );
// 提交事务
sqlSession . commit ();
// 释放资源
sqlSession . close ();
}

3.编写servlet

web 包下创建 AddServlet servlet ,该 servlet 的逻辑如下 :
  • 设置处理post请求乱码的字符集
  • 接收客户端提交的数据
  • 将接收到的数据封装到 Brand 对象中
  • 调用 BrandService update() 方法进行添加的业务逻辑处理
  • 跳转到 selectAllServlet 资源重新查询数据
具体的代码如下:
@WebServlet ( "/updateServlet" )
public class UpdateServlet extends HttpServlet {
private BrandService service = new BrandService ();
@Override
protected void doGet ( HttpServletRequest request , HttpServletResponse response ) throws
ServletException , IOException {
// 处理 POST 请求的乱码问题
request . setCharacterEncoding ( "utf-8" );
//1. 接收表单提交的数据,封装为一个 Brand 对象
String id = request . getParameter ( "id" );
String brandName = request . getParameter ( "brandName" );
String companyName = request . getParameter ( "companyName" );
String ordered = request . getParameter ( "ordered" );
String description = request . getParameter ( "description" );
String status = request . getParameter ( "status" );
// 封装为一个 Brand 对象
Brand brand = new Brand ();
brand . setId ( Integer . parseInt ( id ));
brand . setBrandName ( brandName );
brand . setCompanyName ( companyName );
brand . setOrdered ( Integer . parseInt ( ordered ));
brand . setDescription ( description );
brand . setStatus ( Integer . parseInt ( status ));
//2. 调用 service 完成修改
service . update ( brand );
//3. 转发到查询所有 Servlet
request . getRequestDispatcher ( "/selectAllServlet" ). forward ( request , response );
}
@Override
protected void doPost ( HttpServletRequest request , HttpServletResponse response ) throws
ServletException , IOException {
this . doGet ( request , response );
}
}
存在问题: update.jsp 页面提交数据时是没有携带主键数据的,而后台修改数据需要根据主键进行修改。
针对这个问题,我们不希望页面将主键 id 展示给用户看,但是又希望在提交数据时能将主键 id 提交到后端。此时我们就想到 了在学习 HTML 时学习的隐藏域,在 update.jsp 页面的表单中添加如下代码:
<% -- 隐藏域,提交 id -- %>
<input type = "hidden" name = "id" value = "${brand.id}" >
update.jsp 页面的最终代码如下:
<% @ page contentType = "text/html;charset=UTF-8" language = "java" %>
<% @ taglib prefix = "c" uri = "http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html lang = "en" >
<head>
<meta charset = "UTF-8" >
<title> 修改品牌 </title>
</head>
<body>
<h3> 修改品牌 </h3>
<form action = "/brand-demo/updateServlet" method = "post" >
<% -- 隐藏域,提交 id -- %>
<input type = "hidden" name = "id" value = "${brand.id}" >
品牌名称: <input name = "brandName" value = "${brand.brandName}" ><br>
企业名称: <input name = "companyName" value = "${brand.companyName}" ><br>
排序: <input name = "ordered" value = "${brand.ordered}" ><br>
描述信息: <textarea rows = "5" cols = "20" name = "description" > ${brand.description} </textarea>
<br>
状态:
<c:if test = "${brand.status == 0}" >
<input type = "radio" name = "status" value = "0" checked > 禁用
<input type = "radio" name = "status" value = "1" > 启用 <br>
</c:if>
<c:if test = "${brand.status == 1}" >
<input type = "radio" name = "status" value = "0" > 禁用
<input type = "radio" name = "status" value = "1" checked > 启用 <br>
</c:if>
<input type = "submit" value = " 提交 " >
</form>
</body>
</html>

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

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

相关文章

【金融】新成立基金建仓时点、行业分布与市场行情关系探究

需要进一步交流&#xff0c;获取数据和代码的同学欢迎私信奥~基于新成立基金建仓带入市场的巨量资金会推动市场行情这一逻辑&#xff0c;开展了一系列研究。首先提出了通过基金净值识别建仓行为&#xff08;累计绝对值涨跌幅法&#xff09;和通过基金β值识别建仓行为&#xff…

Vue知识系列-VS Code的安装+Vue环境的搭建+Vue指令

一、VS Code下载地址 Visual Studio Code - Code Editing. Redefined 二、VS Code初始化设置 1.安装插件 在安装好的VSCode软件的扩展菜单中查找安装如下4个插件 2、创建项目 vscode本身没有新建项目的选项&#xff0c;所以要先创建一个空的文件夹&#xff0c;如project_xx…

自主异常检测算法(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

AppScan 扫描web应用程序

系列文章 AppScan介绍和安装 第二节-AppScan 扫描web应用程序 1.环境布置 我们准备了如下一个靶场用来做实验 2.扫描步骤 1.启动AppScan 2.选择 【扫描web应用程序】 3.输入起始URL&#xff0c;点击【下一步】 http://127.0.0.1:83/4.选择【不使用代理】,点击【下一…

ctfshow php特性[125-135]

&#x1f60b;大家好&#xff0c;我是YAy_17&#xff0c;是一枚爱好网安的小白&#xff0c;自学ing。 本人水平有限&#xff0c;欢迎各位大佬指点&#xff0c;一起学习&#x1f497;&#xff0c;一起进步⭐。⭐此后如竟没有炬火&#xff0c;我便是唯一的光。⭐web 125<?php…

word怎么转换成pdf?其实很简单,看这里即可!

转眼间又到了校招的季节&#xff0c;想必许多小伙伴都在忙着编辑自己的简历吧。不过&#xff0c;咱们编辑的时候常常用到的都是word文件&#xff0c;但是当我们要将文件投递出去的时候就需要用到pdf了。其实不仅仅是投递简历&#xff0c;许多地方在要求我们发送正式文件的时候都…

vue+node+mysql全栈项目完整记录

文章目录vuenodemysql全栈项目完整记录写在前面项目最终界面展示项目框架搭建后端创建后端项目编写入口文件数据库及数据库使用前端创建前端项目使用elementUI必要包安装设置跨域访问&#xff0c;全局挂载axios删除无用的文件和代码设置统一的页面样式主页面页面设计路由设计登…

【C语言】柔性的数组是什么?C/C++程序的内存开辟又是?

本文主要讲解柔性数组的相关知识点&#xff0c;并穿插一下C/C程序的内存开辟&#xff0c;涉及到动态内存管理函数&#xff0c;如有不了解的&#xff0c;请参考这一篇文章【C语言】小王带您轻松实现动态内存管理&#xff08;简单易懂&#xff09;_小王学代码的博客-CSDN博客 目录…

【C++】类和对象【下篇】--初始化列表,static成员,友元,内部类,匿名对象

文章目录一、再谈构造函数1.构造函数体赋值2.初始化列表1.概念2.特性二、隐式类型转换1.概念2.构造函数的类型转换3.explict关键字4.类型转换的意义三、Static成员1.概念2.static成员变量3.static成员函数四、友元1.友元函数2.友元类五、内部类六、匿名对象七、拷贝对象时的一些…

深入使用noexcept

深入使用noexcept简介好处坏处适用场景不适用场景实验结果总结参考资料简介 noexcept是C11引入的&#xff0c;表明函数是否会抛出异常。正确使用它可以优化性能&#xff0c;错误使用则会带来麻烦。 noexcept使用语法有两种&#xff1a; noexcpetnoexcept(expression) 第二种…

如何提高系统稳定性?

1、系统稳定性的评判标准 在开始谈稳定性保障之前&#xff0c;我们先来聊聊业内经常提及的一个词SLA&#xff01;业内喜欢用SLA &#xff08;服务等级协议&#xff0c;全称&#xff1a;service level agreement&#xff09;来衡量系统的稳定性&#xff0c;对互联网公司来说&am…

测试开发知识总结(一)

本文内容顺序&#xff1a;测试基础理论、测试岗经常被问到的场景题、智力题、测试岗高频算法题、数据库、Linux知识点。常用自动化测试工具1、Appium官网&#xff1a;http://appium.ioAppUI自动化测试Appium 是一个移动端自动化测试开源工具&#xff0c;支持iOS 和Android 平台…

为什么很少拿神经网络来直接做滤波器呢?

其实无论是IIR&#xff08;RNN&#xff09;还是FIR(CNN)滤波器都可以看成一个简单神经网络&#xff0c;而且有严格的推理&#xff0c;可解释性比神经网络强多了&#xff0c;而已易于工程实现&#xff0c;因此在工程中大量应用。你说的含色噪声和其他乱七八糟的噪声难以滤除时&a…

ROS | Realsense中的IMU解算orientation

文章目录概述一、定义介绍二、操作教程(一)、下载并编译imu_tools功能包1.创建工作空间并初始化2.下载imu_tools并编译(二)、修改配置1.修改imu_tools源码2.修改launch文件3.启动解算概述 本文详细介绍了如何使用ROS自带的工具解算6轴IMU&#xff0c;获取其位姿。 一、定义介绍…

mybatis之动态SQL常见标签的使用

引入where标签的原因&#xff1a; 在上篇文章使用if语句的查询中&#xff0c;我们在SQL语句后面都写入了where 11&#xff0c;以保证每次都能够查询出结果&#xff0c;但这种方法并不是最合理的&#xff0c;假设我们现在将where后面的11去掉&#xff1a; 如下所示&#xff1a…

上午摆摊,下午写代码,35岁程序员的双面人生超爽!

最近看到一个程序员发帖分享自己的工作&#xff1a;白天出摊卖馄饨&#xff0c;下午在家为海外公司全职远程工作。“年入百万是可以的&#xff0c;并且我老家是三线城市&#xff0c;没有房租、通勤费用&#xff0c;性价比还是很高的。” 对比在大城市天天996的程序员&#xff0…

【JavaEE】多线程之线程安全(synchronized篇),死锁问题

目录 线程安全问题 观察线程不安全 线程安全问题的原因 从原子性入手解决线程安全问题 ——synchronized synchronized的使用方法 synchronized的互斥性和可重入性 死锁 死锁的三个典型情况 死锁的四个必要条件 破除死锁 线程安全问题 在前面的章节中&#xff0c…

Wav2Vec HuBert 自监督语音识别模型

文章目录Wav2Vec: Unsupervised pre-training for speech recognitionabstractmethodwav2vec 2.0: A Framework for Self-Supervised Learning of Speech RepresentationsabstractintroductionmethodMODEL arch损失函数finetuneexprimentHuBERT: Self-Supervised Speech Repres…

商品期货跨期套利实战笔记

合约对&#xff1a;IC2301&IC2302 价差计算 前-后&#xff08;6082-6079.8&#xff09; 做多价差开仓&#xff08;预期价差变大&#xff09; 买2301&#xff08;前&#xff09; 卖2302&#xff08;后&#xff09; 做空价差开仓&#xff08;预期价差变小&#xff09;&…

【Java|golang】1813. 句子相似性 III

一个句子是由一些单词与它们之间的单个空格组成&#xff0c;且句子的开头和结尾没有多余空格。比方说&#xff0c;“Hello World” &#xff0c;“HELLO” &#xff0c;“hello world hello world” 都是句子。每个单词都 只 包含大写和小写英文字母。 如果两个句子 sentence1…