↑↑Java语法基础 —> 小型项目练习 —> MySQL 更多学习内容均更新在专栏了,记得关注专栏哦 ↑↑
JDBC:从CRUD开始,理解JDBC中的各个对象作用,掌握Druid的使用
Maven从安装到手把手教学进行项目管理
MyBatis完成代理方式查询数据以及核心文件配置、
Mybatis:使用映射配置文件实现CRUD操作,能够使用注解实现CRUD操作
HTML标签大全,存起来总有一天用的上
Java后端学习之CSS内容
JavaWeb学习内容之3万字核心JavaScript笔记
了解JavaWeb开发的技术栈从掌握Tomcat的使用开始,手把手带你用Maven创建web项目
JavaWeb:完成用户登录注册案例的实现,SqlSessionFactory工具类的抽取
JavaWeb:理解JSP以及原理,能在JSP中使用 EL表达式和JSTL标签
今日目标:
- 理解
MVC模式
和三层架构
- 能完成品牌数据的增删改查功能
MVC 模式和三层架构是一些理论的知识,将来我们使用了它们进行代码开发会让我们代码维护性和扩展性更好。
MVC 是一种分层开发的模式,其中:
M:Model,业务模型,处理业务
V:View,视图,界面展示
C:Controller,控制器,处理请求,调用模型和视图
控制器(serlvlet)用来接收浏览器发送过来的请求,控制器调用模型(JavaBean)来获取数据,比如从数据库查询数据;控制器获取到数据后再交由视图(JSP)进行数据展示。
MVC 好处:
职责单一,互不影响。每个角色做它自己的事,各司其职。
有利于分工协作。
有利于组件重用
三层架构是将我们的项目分成了三个层面,分别是 表现层
、业务逻辑层
、数据访问层
。
注册业务功能
,我们会先调用 数据访问层
的 selectByName()
方法判断该用户名是否存在,如果不存在再调用 数据访问层
的 insert()
方法进行数据的添加操作而整个流程是,浏览器发送请求,表现层的Servlet接收请求并调用业务逻辑层的方法进行业务逻辑处理,而业务逻辑层方法调用数据访问层方法进行数据的操作,依次返回到serlvet,然后servlet将数据交由 JSP 进行展示。
三层架构的每一层都有特有的包名称:
com.itheima.controller
或者 com.itheima.web
com.itheima.service
com.itheima.dao
或者 com.itheima.mapper
后期我们还会学习一些框架,不同的框架是对不同层进行封装的
通过 MVC 和 三层架构 的学习,有些人肯定混淆了。那他们有什么区别和联系?
如上图上半部分是 MVC 模式,上图下半部分是三层架构。 MVC 模式
中的 C(控制器)和 V(视图)就是 三层架构
中的表现层,而 MVC 模式
中的 M(模型)就是 三层架构
中的 业务逻辑层 和 数据访问层。
可以将 MVC 模式
理解成是一个大的概念,而 三层架构
是对 MVC 模式
实现架构的思想。 那么我们以后按照要求将不同层的代码写在不同的包下,每一层里功能职责做到单一,将来如果将表现层的技术换掉,而业务逻辑层和数据访问层的代码不需要发生变化。
需求:完成品牌数据的增删改查操作
这个功能我们之前一直在做,而这个案例是将今天学习的所有的内容(包含 MVC模式 和 三层架构)进行应用,并将整个流程贯穿起来。
环境准备工作,我们分以下步骤实现:
创建新的模块 brand_demo,引入坐标
创建三层架构的包结构
数据库表 tb_brand
实体类 Brand
MyBatis 基础环境
Mybatis-config.xml
BrandMapper.xml
BrandMapper接口
创建新的模块 brand_demo,引入坐标。我们只要分析出要用到哪儿些技术,那么需要哪儿些坐标也就明确了
pom.xml
内容如下:
<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.0modelVersion>
<groupId>org.examplegroupId>
<artifactId>brand-demoartifactId>
<version>1.0-SNAPSHOTversion>
<packaging>warpackaging>
<properties>
<maven.compiler.source>8maven.compiler.source>
<maven.compiler.target>8maven.compiler.target>
properties>
<dependencies>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.5version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.34version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>3.1.0version>
<scope>providedscope>
dependency>
<dependency>
<groupId>javax.servlet.jspgroupId>
<artifactId>jsp-apiartifactId>
<version>2.2version>
<scope>providedscope>
dependency>
<dependency>
<groupId>jstlgroupId>
<artifactId>jstlartifactId>
<version>1.2version>
dependency>
<dependency>
<groupId>taglibsgroupId>
<artifactId>standardartifactId>
<version>1.1.2version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.mavengroupId>
<artifactId>tomcat7-maven-pluginartifactId>
<version>2.2version>
plugin>
plugins>
build>
project>
创建不同的包结构,用来存储不同的类。包结构如下
-- 删除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);
在 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 +
'}';
}
}
定义核心配置文件 Mybatis-config.xml
,并将该文件放置在 resources
下
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>
<package name="com.itheima.mapper"/>
mappers>
configuration>
在 resources
下创建放置映射配置文件的目录结构 com/itheima/mapper
,并在该目录下创建映射配置文件 BrandMapper.xml
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>
当我们点击 index.html
页面中的 查询所有
这个超链接时,就能查询到上图右半部分的数据。
对于上述的功能,点击 查询所有
超链接是需要先请后端的 servlet
,由 servlet
跳转到对应的页面进行数据的动态展示。而整个流程如下图:
在 mapper
包下创建创建 BrandMapper
接口,在接口中定义 selectAll()
方法
/**
* 查询所有
* @return
*/
@Select("select * from tb_brand")
List<Brand> selectAll();
在 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;
}
}
在 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;
}
}
在 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);
}
}
将资料 资料\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" %>
Title
序号
品牌名称
企业名称
排序
品牌介绍
状态
操作
<%--${brand.id} --%>
${status.count}
${brand.brandName}
${brand.companyName}
${brand.ordered}
${brand.description}
启用
禁用
修改 删除
启动服务器,并在浏览器输入 http://localhost:8080/brand-demo/index.html
,看到如下 查询所有
的超链接,点击该链接就可以查询出所有的品牌数据
为什么出现这个问题呢?是因为查询到的字段名和实体类对象的属性名没有一一对应。相比看到这大家一定会解决了,就是在映射配置文件中使用 resultMap
标签定义映射关系。映射配置文件内容如下:
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();
重启服务器,再次访问就能看到我们想要的数据了
上图是做 添加 功能流程。点击 新增
按钮后,会先跳转到 addBrand.jsp
新增页面,在该页面输入要添加的数据,输入完毕后点击 提交
按钮,需要将数据提交到后端,而后端进行数据添加操作,并重新将所有的数据查询出来。整个流程如下:
接下来我们根据流程来实现功能:
在 BrandMapper
接口,在接口中定义 add(Brand brand)
方法
@Insert("insert into tb_brand values(null,#{brandName},#{companyName},#{ordered},#{description},#{status})")
void add(Brand brand);
在 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();
}
我们需要在该页面表格的上面添加 新增
按钮
<input type="button" value="新增" id="add"><br>
并给该按钮绑定单击事件,当点击了该按钮需要跳转到 brand.jsp
添加品牌数据的页面
<script>
document.getElementById("add").onclick = function (){
location.href = "/brand-demo/addBrand.jsp";
}
script>
==注意:==该
script
标签建议放在body
结束标签前面。
从资料 资料\2. 品牌增删改查案例\静态页面
中将 addBrand.html
页面拷贝到项目的 webapp
下,并改成 addBrand.jsp
动态页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
添加品牌
添加品牌
在 web
包下创建 AddServlet
的 servlet
,该 servlet
的逻辑如下:
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);
}
}
点击 brand.jsp
页面的 新增
按钮,会跳转到 addBrand.jsp
页面
点击 提交
按钮,就能看到如下页面,里面就包含我们刚添加的数据
点击每条数据后面的 编辑
按钮会跳转到修改页面,如下图:
在该修改页面我们可以看到将 编辑
按钮所在行的数据 回显 到表单,然后需要修改那个数据在表单中进行修改,然后点击 提交
的按钮将数据提交到后端,后端再将数据存储到数据库中。
从上面的例子我们知道 修改
功能需要从两方面进行实现,数据回显和修改操作。
上图就是回显数据的效果。要实现这个效果,那当点击 修改
按钮时不能直接跳转到 update.jsp
页面,而是需要先带着当前行数据的 id
请求后端程序,后端程序根据 id
查询数据,将数据存储到域对象中跳转到 update.jsp
页面进行数据展示。整体流程如下
在 BrandMapper
接口,在接口中定义 selectById(int id)
方法
/**
* 根据id查询
* @param id
* @return
*/
@Select("select * from tb_brand where id = #{id}")
@ResultMap("brandResultMap")
Brand selectById(int id);
在 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;
}
在 web
包下创建 SelectByIdServlet
的 servlet
,该 servlet
的逻辑如下:
id
BrandService
的 selectById()
方法进行数据查询的业务逻辑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);
}
}
拷贝 addBrand.jsp
页面,改名为 update.jsp
并做出以下修改:
title
标签内容改为 修改品牌
form
标签的 action
属性值改为 /brand-demo/updateServlet
input
标签要进行数据回显,需要设置 value
属性
品牌名称:
企业名称:
排序:
textarea
标签要进行数据回显,需要在标签体中使用 EL表达式
描述信息:
单选框使用 if
标签需要判断 brand.status
的值是 1 还是 0 在指定的单选框上使用 checked
属性,表示被选中状态
状态:
禁用
启用
禁用
启用
综上,update.jsp
代码如下:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
修改品牌
修改品牌
做完回显数据后,接下来我们要做修改数据了,而下图是修改数据的效果:
在修改页面进行数据修改,点击 提交
按钮,会将数据提交到后端程序,后端程序会对表中的数据进行修改操作,然后重新进行数据的查询操作。整体流程如下:
在 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);
在 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();
}
在 web
包下创建 AddServlet
的 servlet
,该 servlet
的逻辑如下:
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--%>
update.jsp
页面的最终代码如下:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
修改品牌
修改品牌
ption);
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--%>
update.jsp
页面的最终代码如下:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
修改品牌
修改品牌