2、!! Mybatis框架:boot(2-2、3-1/2)
此框架是目前最流行的——————>数据持久层框架:是对JDBC代码进行了封装,
程序员只需要————>通过注解或配置文件的方式提供需要执行的SQL语句,
框架会————>自动根据SQL语句生成出JDBC代码,从而提高执行效率!
00.如何创建使用Mybatis框架?
- ①创建boot2-2工程 , 创建工程时需要勾选3个内容分别是:
- a.Web->Spring Web
- b.SQL-> Mybatis Framework
- c.SQL-> MySQL Driver
②在application.properties配置文件中书写连接数据库的信息
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.url=jdbc:mysql://localhost:3306/empdb?characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false
③
启动该boot2-2项目,若报错,就删除项目,重新创建时,试着换一下下载的网址
然后重复上面的步骤即可!
页面中可以修改创建工程的网址:
- https://start.spring.io 默认
- https://start.springboot.io 备用1
- https://start.aliyun.com 备用2
01.英雄表的{增删改查}:boot2-2
(0)需创建hero表,否则运行会报错:
上图中说表不存在,可以添加英雄表来解决:
(1)把老师的emp.zip(在IDEA中Jdbc01的文件里)解压出来得到一个emp.sql文件,
建议把这个文件放到某个盘的根目录 比如 F盘根目录,
然后在mysql客户端执行指令:————>格式:source 路径;
例: source f:/danei/Java/emp/emp.sql;
(2)在mysql客户端测试以下SQL语句 检查是否成功:
show databases; //检查里面是否多了一个empdb;
show tables; //会出现两个表 emp 和dept
select * from emp; //检查是否出现了数据, 如果格式错乱 正常
(3)如果出现乱码执行 set names utf8;
(1)添加英雄的功能: —增
1.创建boot2-2 打钩 3个
2.在application.properties配置文件中添加内容
3.停止之前工程, 运行新工程测试是否能正常运行
4.创建index.html 页面 里面添加超链接 添加英雄 访问地址为add.html
5.创建add.html页面 里面添加form表单 请求地址为 /add
6.创建controller.HeroController, 里面添加@Controller注解,添加@RequestMapping注
解处理 /add请求 并添加add方法,
7.创建entity.Hero实体类 并提供get和set方法 还有tostring方法
8.在HeroController的add方法参数列表中声明Hero对象 用来接收传递过来的参数 ,此时打桩
输出hero对象 检查是否接收到了参数
9.创建mapper.HeroMapper接口, 里面添加@Mapper注解, 声明一个insert方法通过@Insert注
解修饰,注解里面添加插入数据的SQL语句
10.在HeroController里面 通过@Autowired注解把HeroMapper装配进来,
在add方法里面调用mapper.insert方法把接收到的hero对象传递进去,
重启工程测试即可!
1.添加英雄功能并在数据库可以查到
新建类User、Hero、HeroController
新建接口HeroMapper 新建页面index.html、add.html
(1)①在boot2-2下:src/mian/java/cn/tedu/boot22下:创建包:entity
②在entity下创建User类 和 Hero类,
写上成员变量后alt+insert调用get、set、toString方法
③创建接口:
在boot2-2下:src/mian/java/cn/tedu/boot22下:创建包mapper,包内新建类HeroMapper
(2)在boot2-2下的src/main/resources/static下:新建index.html(添加英雄的页面)
(3)①创建controller去处理请求:在src/main/java/cn/tedu/boot22下创建包:controller
②在①中的controller包下创建HeroController
(4)测试:http://localhost:8080/add 会跳转到一个 添加完成的页面!
(5)创建页面add.html——>在boot2-2下的src/main/resources/static下:新建add.html(添加英雄的页面)
(6)①在Hero中写一个@Autowired注解:
@Autowired
HeroMapper mapper;
②测试:http://localhost:8080/add.html
输入信息,点击添加——>跳转到添加成功页面!
打开数据库:输入:
use empdb;
select * from hero;
会发现有刚才添加的几个任务信息!成功!
package cn.tedu.boot22.entity;
public class User {
private Integer id;
private String username;
private String password;
private String nick;
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", nick='" + nick + '\'' +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getNick() {
return nick;
}
public void setNick(String nick) {
this.nick = nick;
}
}
package cn.tedu.boot22.entity;
public class Hero {
//Integer 默认值为null 保存到数据库里 可以表示未赋值的状态
private Integer id;
private String name;
private Integer money;
@Override
public String toString() {
return "Hero{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getMoney() {
return money;
}
public void setMoney(Integer money) {
this.money = money;
}
}
package cn.tedu.boot22.controller;
import cn.tedu.boot22.entity.Hero;
import cn.tedu.boot22.mapper.HeroMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
@Controller
public class HeroController {
/*
此注解的作用是:
Spring框架结合Mybatis框架会自动将HeroMapper生成一个实现类和实现里面的方法,
而且会自动实例化对象,required = false告诉idea编译器此对象是非必要的,
因此"mapper"若报错的话需要这样写: @Autowired(required = false)
*/
@Autowired
HeroMapper mapper;
/** 添加英雄功能 */
@RequestMapping("/add")
@ResponseBody
public String add(Hero hero){
System.out.println("hero = "+hero);
mapper.insert(hero);
return "添加完成!<a href='/'>返回首页</a>";
}
}
package cn.tedu.boot22.mapper;
import cn.tedu.boot22.entity.Hero;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface HeroMapper {
/*
#{xxxx}:
会从下面方法的参数列表找到同名的变量,
若找不到同名变量则进入到对象里面查找同名的get方法
Mybatis框架会根据此方法声明生成具体的实现类实现此方法,方法内部就是jbdc代码
*/
@Insert("insert into hero values(null,#{name},#{money})")
void insert(Hero hero);
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<a href="add.html">添加英雄</a>
</form>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>添加英雄页面</h1>
<form action="/add">
<input type="text" name="name" placeholder="英雄名">
<input type="text" name="money" placeholder="英雄价格">
<input type="submit" value="添加">
</form>
</body>
</html>
(2)删除英雄的功能: —删
1.在首页中添加删除英雄超链接 访问地址为 delete.html
2.创建delete.html页面 在里面添加form表单 提交地址为/delete
3.在HeroController 中创建delete方法 处理路径为/delete
参数列表中声明name 接收页面传递过来的名字
4.在HeroMapper里面添加deleteByName方法,
通过@Delete注解修饰,里面填写 删除的SQL语句
5.在HeroController里面的delete方法中调用mapper的deleteByName方法
2.删除英雄
改动的页面index 新建页面delete
改动的类HeroController 改动的接口HeroMapper
(1)index页面添加删除页面代码 ; static下添加delete删除页面
(2)HeroController类里添加删除英雄功能的方法
(3)HeroMapper接口里 写一个删除name的条件
(4)回到HeroController用mapper调用HeroMapper的deleteByName方法
(5)测试:http://localhost:8080/delete.html
输入(孙悟空),点击删除——>跳转到删除完成页面
打开数据库,输入:
use empdb;
select * from hero;
发现 孙悟空 被删除成功!!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<a href="add.html">添加英雄</a>
<a href="delete.html">删除英雄</a>
</form>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>请输入要删除的英雄名称</h1>
<form action="/delete">
<input type="text" name="name">
<input type="submit" value="删除">
</form>
</body>
</html>
package cn.tedu.boot22.controller;
import cn.tedu.boot22.entity.Hero;
import cn.tedu.boot22.mapper.HeroMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class HeroController {
/*
此注解的作用是:
Spring框架结合Mybatis框架会自动将HeroMapper生成一个实现类和实现里面的方法,
而且会自动实例化对象,required = false告诉idea编译器此对象是非必要的,
因此若报错的话需要这样写: @Autowired(required = false)
*/
@Autowired
HeroMapper mapper;
/** 添加英雄功能 */
@RequestMapping("/add")
@ResponseBody
public String add(Hero hero){
System.out.println("hero = "+hero);
mapper.insert(hero);
return "添加完成!";
}
/** 删除英雄的功能 */
@RequestMapping("/delete")
@ResponseBody
public String delete(String name){
//调mapper里面删除的方法
mapper.deleteByName(name);
return "删除完成";
}
}
package cn.tedu.boot22.mapper;
import cn.tedu.boot22.entity.Hero;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface HeroMapper {
/*
#{xxxx}:
会从下面方法的参数列表找到同名的变量,
若找不到同名变量则进入到对象里面查找同名的get方法
Mybatis框架会根据此方法声明生成具体的实现类实现此方法,方法内部就是jbdc代码
*/
@Insert("insert into hero values(null,#{name},#{money})")
void insert(Hero hero);
@Delete("delete from hero where name=#{name}")
void deleteByName(String name);
}
(3)修改英雄的功能: —改
1.在首页添加修改英雄超链接, 地址为update.html页面
2.创建update.html页面 并添加form表单 提交地址为/update
3.在Controller中添加update方法 处理/update请求
4.在HeroMapper里面添加update方法 通过@Update注解进行修饰,里面添加修改的SQL语句
5.在HeroController里面的update方法中调用mapper的update方法把接收到的hero对象
传递到方法中
3.修改英雄
改动的页面index 新建页面update(修改英雄页面)
改动的类HeroController 改动的接口HeroMapper
(1)index页面添加修改英雄代码 ;static下添加update修改英雄页面
(2)HeroController类里添加修改英雄功能的方法
(3)HeroMapper接口里 写一个修改英雄的条件
(4)回到HeroController用mapper调用HeroMapper的update方法
(5)测试:http://localhost:8080/update.html
输入信息(1/神龙啊/2000),点击修改——>跳转到修改完成的页面
打开数据库,输入:
use empdb;
select * from hero;
发现 第一个id 的信息被改为神龙啊,即刚才浏览器页面中修改的内容!成功!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<a href="add.html">添加英雄</a>
<a href="delete.html">删除英雄</a>
<a href="update.html">修改英雄</a>
</form>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>修改英雄页面</h1>
<form action="/update">
<input type="text" name="id" placeholder="请输入修改的id">
<input type="text" name="name" placeholder="名字">
<input type="text" name="money" placeholder="价格">
<input type="submit" value="修改">
</form>
</body>
</html>
package cn.tedu.boot22.controller;
import cn.tedu.boot22.entity.Hero;
import cn.tedu.boot22.mapper.HeroMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class HeroController {
/*
此注解的作用是:
Spring框架结合Mybatis框架会自动将HeroMapper生成一个实现类和实现里面的方法,
而且会自动实例化对象,required = false告诉idea编译器此对象是非必要的,
因此若报错的话需要这样写: @Autowired(required = false)
*/
@Autowired
HeroMapper mapper;
/** 添加英雄功能 */
@RequestMapping("/add")
@ResponseBody
public String add(Hero hero){
System.out.println("hero = "+hero);
mapper.insert(hero);
return "添加完成!";
}
/** 删除英雄的功能 */
@RequestMapping("/delete")
@ResponseBody
public String delete(String name){
//调mapper里面删除的方法
mapper.deleteByName(name);
return "删除完成";
}
/** 修改英雄的功能 */
@RequestMapping("/update")
@ResponseBody
public String update(Hero hero){
System.out.println("hero = "+hero);
//调用mapper的update方法
mapper.update(hero);
return "修改完成";
}
}
package cn.tedu.boot22.mapper;
import cn.tedu.boot22.entity.Hero;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Update;
@Mapper
public interface HeroMapper {
/*
#{xxxx}:
会从下面方法的参数列表找到同名的变量,
若找不到同名变量则进入到对象里面查找同名的get方法
Mybatis框架会根据此方法声明生成具体的实现类实现此方法,方法内部就是jbdc代码
*/
@Insert("insert into hero values(null,#{name},#{money})")
void insert(Hero hero);
@Delete("delete from hero where name=#{name}")
void deleteByName(String name);
@Update("update hero set name=#{name},money=#{money} where id=#{id} ")
void update(Hero hero);
}
(4)查询英雄的功能: —查
1.在首页添加查询的超链接,请求地址为/select
2.在HeroController中添加select方法处理/select请求
3.在HeroMapper里面添加select方法用@Select注解进行修饰,里面写查询的SQL语句
4.在HeroController的select方法中 调用mapper的select方法,
把查询到的List集合返回给客户端展示
4.查询英雄
改动的页面index 改动的类HeroController
(1)index页面中添加一个查询英雄的代:【<a href="/select">查询英雄</a>】
(2)HeroController类里添加查询英雄功能的方法
(3)HeroMapper接口里 写一个查询英雄的条件,把hero装在list集合里
(4)回到HeroController用mapper调用HeroMapper的List中的select方法,
并将查询到的集合的hero信息转成字符串返回给客户端
(5)测试:http://localhost:8080
点击超链接:
查询英雄——>跳转到一个集合显示的hero信息的页面
成功!!!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<a href="/add.html">添加英雄</a>
<a href="/delete.html">删除英雄</a>
<a href="/update.html">修改英雄</a>
<a href="/select">查询英雄</a>
</form>
</body>
</html>
package cn.tedu.boot22.controller;
import cn.tedu.boot22.entity.Hero;
import cn.tedu.boot22.mapper.HeroMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
@Controller
public class HeroController {
/*
此注解的作用是:
Spring框架结合Mybatis框架会自动将HeroMapper生成一个实现类和实现里面的方法,
而且会自动实例化对象,required = false告诉idea编译器此对象是非必要的,
因此若报错的话需要这样写: @Autowired(required = false)
*/
@Autowired
HeroMapper mapper;
/** 添加英雄功能 */
@RequestMapping("/add")
@ResponseBody
public String add(Hero hero){
System.out.println("hero = "+hero);
mapper.insert(hero);
return "添加完成!";
}
/** 删除英雄的功能 */
@RequestMapping("/delete")
@ResponseBody
public String delete(String name){
//调mapper里面删除的方法
mapper.deleteByName(name);
return "删除完成";
}
/** 修改英雄的功能 */
@RequestMapping("/update")
@ResponseBody
public String update(Hero hero){
System.out.println("hero = "+hero);
//调用mapper的update方法
mapper.update(hero);
return "修改完成";
}
/** 查询英雄的功能 */
@RequestMapping("/select")
@ResponseBody
public String select(){
List<Hero> list = mapper.select();
//将查询到的集合的hero信息转成字符串返回给客户端:
return list.toString();
}
}
package cn.tedu.boot22.mapper;
import cn.tedu.boot22.entity.Hero;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
public interface HeroMapper {
/*
#{xxxx}:
会从下面方法的参数列表找到同名的变量,
若找不到同名变量则进入到对象里面查找同名的get方法
Mybatis框架会根据此方法声明生成具体的实现类实现此方法,方法内部就是jbdc代码
*/
@Insert("insert into hero values(null,#{name},#{money})")
void insert(Hero hero);
@Delete("delete from hero where name=#{name}")
void deleteByName(String name);
@Update("update hero set name=#{name},money=#{money} where id=#{id} ")
void update(Hero hero);
@Select("select * from hero")
List<Hero> select();
}
02.商品表的{增删改查}:boot3-1
(1)添加商品的功能:—增
0.前提:若创建项目运行时报错,需要配置文件
(1)在application.properties配置文件中书写连接数据库的信息:
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.url=jdbc:mysql://localhost:3306/empdb?characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false
(2)创建商品表:在数据库中完成以下两个指令:
use empdb;
create table product(id int primary key auto_increment,title varchar(100),price double(10,2),sale_count int)charset=utf8;
(3)创建boot3-1工程 打3个勾
(4)从别的工程中复制application.properties配置文件的信息到新工程, 启动工程测试是否成功.
1.添加商品的信息
新建页面index、insert 新建类ProductController 新建接口ProductMapper
(1)①在boot3-1下:src/main/resources/static下:创建页面:index
②在boot3-1下:src/main/resources/static下:创建页面:insert
③启动该项目进行测试:http://localhost:8080
只有超链接:添加商品,可以跳转到添加的页面
(2)①在boot3-1下:src/main/java/cn/tedu/boot31下:创建包:controller
②在从controller下创建类:ProductController
(3)①在boot3-1下:src/main/java/cn/tedu/boot31下:创建包:entity
②在entity下创建Product类,写上成员变量后alt+insert调用get、set、toString方法
(4)创建接口:在boot3-1下:src/main/java/cn/tedu/boot31下:
创建包mapper,包内新建类ProductMapper
(5)测试:http://localhost:8080
输入信息(哈哈哈/200/20),点击添加——>会跳转到添加成功的页面!
打开数据库:输入:
use empdb;
select * from product;
会发现有刚才添加的商品信息!成功!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>商品管理首页</h1>
<a href="/insert.html">添加商品</a>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>添加商品页面</h1>
<form action="/insert">
<input type="text" name="title" placeholder="商品标题">
<input type="text" name="price" placeholder="商品价格">
<input type="text" name="saleCount" placeholder="商品销量">
<input type="submit" value="添加">
</form>
</body>
</html>
package cn.tedu.boot31.controller;
import cn.tedu.boot31.entity.Product;
import cn.tedu.boot31.mapper.ProductMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
//此注解相当于在每一个方法上面添加ResponseBody注解
@RestController
public class ProductController {
/*
此注解的作用是:
Spring框架结合Mybatis框架会自动将HeroMapper生成一个实现类和实现里面的方法,
而且会自动实例化对象,required = false告诉idea编译器此对象是非必要的,
因此"mapper"若报错的话需要这样写: @Autowired(required = false)
*/
@Autowired
ProductMapper mapper;
/** 增加商品的功能 */
@RequestMapping("/insert")
public String insert(Product product){
System.out.println("product = " + product);
mapper.insert(product);
return "添加完成!<a href='/'>返回首页</a>";
}
}
package cn.tedu.boot31.mapper;
import cn.tedu.boot31.entity.Product;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface ProductMapper {
@Insert("insert into product values(null,#{title},#{price},#{saleCount})")
void insert(Product product);
}
(2)查询商品的功能:—查
2.查询商品的信息
改动的类ProductController 改动的接口ProductMapper
改动的页面index
(1)index页面中添加一个查询商品的代码:【<a href="/select">查询商品</a>】
(2)ProductController类添加查询商品的功能方法
(3)在ProductMapper接口中写一个查询商品的条件,把商品信息装到list集合里
测试:http://localhost:8080
点击查询商品超链接,会跳转到list集合表示的商品信息页面
(4)回到ProductController用mapper调用ProductMapper的List中的select方法,
并把商品数据装进表格标签里面,
测试:http://localhost:8080
点击超链接
查询商品——>跳转到:商品列表页面,显示刚才添加的信息,
成功!!!
package cn.tedu.boot31.controller;
import cn.tedu.boot31.entity.Product;
import cn.tedu.boot31.mapper.ProductMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
//此注解相当于在每一个方法上面添加ResponseBody注解
@RestController
public class ProductController {
/*
此注解的作用是:
Spring框架结合Mybatis框架会自动将HeroMapper生成一个实现类和实现里面的方法,
而且会自动实例化对象,required = false告诉idea编译器此对象是非必要的,
因此"mapper"若报错的话需要这样写: @Autowired(required = false)
*/
@Autowired
ProductMapper mapper;
/** 增加商品的功能 */
@RequestMapping("/insert")
public String insert(Product product){
System.out.println("product = " + product);
mapper.insert(product);
return "添加完成!<a href='/'>返回首页</a>";
}
/** 查询商品的功能 */
@RequestMapping("/select")
public String select(){
List<Product> list = mapper.select();
String html = "<table border=1>";
html+="<caption>商品列表</caption>";
html += "<tr><th>标题</th><th>价格</th><th>销量</th></tr>";
//把商品数据装进表格标签里面
for (Product p: list) {
html+="<tr>";
html+="<td>"+p.getTitle()+"</td>";
html+="<td>"+p.getPrice()+"</td>";
html+="<td>"+p.getSaleCount()+"</td>";
html+="</tr>";
}
html+="</table>";
return html;
}
}
package cn.tedu.boot31.mapper;
import cn.tedu.boot31.entity.Product;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface ProductMapper {
@Insert("insert into product values(null,#{title},#{price},#{saleCount})")
void insert(Product product);
@Select("select * from product")
@Result(column = "sale_count",property = "saleCount")
List<Product> select();
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>商品管理首页</h1>
<a href="/insert.html">添加商品</a>
<a href="/select">查询商品</a>
</body>
</html>
(3)删除商品的信息:—删
3.删除商品的信息
改动的类ProductController 改动的接口ProductMapper
(1)①在ProductController中的【查询商品的功能】下的:
【把商品数据装进表格标签里面】中的for中,再写一个html删除的代码:
html+="<td><a href='/delete'>删除</a></td>";
②在①中的html删除代码中:把id传过去
③在下面写一个【删除商品的功能】的方法
(2)测试:http://localhost:8080
点击查询商品超链接,跳转到商品列表页面,
点击删除,跳转到删除成功页面!
但是返回商品列表页面暂时还不会真的被删除。
此时idea的控制台会输出【id=1】
(3)ProductController中的删除商品的方法中用mapper调用删除id方法:mapper.deleteById(id);
(4)ProductMapper中写一个删除商品的条件
(5)测试:http://localhost:8080
点击查询商品超链接,跳转到商品列表页面,
点击删除,跳转到删除成功页面!
此时返回商品列表页面发现商品已经被删除。
成功!!!
打开数据库:输入:
use empdb;
select * from product;
会发现在浏览器的页面中被删除的商品已经没有了!成功!
package cn.tedu.boot31.controller;
import cn.tedu.boot31.entity.Product;
import cn.tedu.boot31.mapper.ProductMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
//此注解相当于在每一个方法上面添加ResponseBody注解
@RestController
public class ProductController {
/*
此注解的作用是:
Spring框架结合Mybatis框架会自动将HeroMapper生成一个实现类和实现里面的方法,
而且会自动实例化对象,required = false告诉idea编译器此对象是非必要的,
因此"mapper"若报错的话需要这样写: @Autowired(required = false)
*/
@Autowired
ProductMapper mapper;
/** 增加商品的功能 */
@RequestMapping("/insert")
public String insert(Product product){
System.out.println("product = " + product);
mapper.insert(product);
return "添加完成!<a href='/'>返回首页</a>";
}
/** 查询商品的功能 */
@RequestMapping("/select")
public String select(){
List<Product> list = mapper.select();
String html = "<table border=1>";
html+="<caption>商品列表</caption>";
html += "<tr><th>标题</th><th>价格</th><th>销量</th></tr>";
//把商品数据装进表格标签里面
for (Product p: list) {
html+="<tr>";
html+="<td>"+p.getTitle()+"</td>";
html+="<td>"+p.getPrice()+"</td>";
html+="<td>"+p.getSaleCount()+"</td>";
//把id传过去: ?是请求地址和请求参数的分隔符:
html+="<td><a href='/delete?id="+p.getId()+"'>删除</a></td>";
html+="</tr>";
}
html+="</table>";
return html;
}
/** 删除商品的功能 */
@RequestMapping("/delete")
public String delete(int id){
System.out.println("id = "+id);
mapper.deleteById(id);
return "删除完成!<a href='/select'>返回列表页面<a>";
}
}
package cn.tedu.boot31.mapper;
import cn.tedu.boot31.entity.Product;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
public interface ProductMapper {
//insert into product values(null,‘手机’,500,100);
@Insert("insert into product values(null,#{title},#{price},#{saleCount})")
void insert(Product product);
@Select("select * from product")
@Result(column = "sale_count",property = "saleCount")
List<Product> select();
@Delete("delete from product where id=#{id}")
void deleteById(int id);
}
(4)修改商品的功能:—改
4.修改商品的功能
新建商品修改页面update 改动的类ProductController
改动的接口ProductMapper
(1)static下新建修改商品的页面:update
(2)①ProductController类中添加一个修改商品的功能方法
②ProductController中的修改商品的方法中用mapper调用修改方法:mapper.update(product);;
(3)ProductMapper中写一个修改商品的条件
(4)测试:http://localhost:8080
点击修改
输入信息(2/大哥大/200/2),点击修改
跳转到修改完成页面!
返回点击查询商品超链接,发现之前的id序号为2的已经被修改位刚才改的!
成功!!!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>修改商品信息</h1>
<form action="/update">
<input type="text" name="id" placeholder="请输入修改商品的id">
<input type="text" name="title" placeholder="商品标题">
<input type="text" name="price" placeholder="商品价格">
<input type="text" name="saleCount" placeholder="商品销量">
<input type="submit" value="修改">
</form>
</body>
</html>
package cn.tedu.boot31.controller;
import cn.tedu.boot31.entity.Product;
import cn.tedu.boot31.mapper.ProductMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
//此注解相当于在每一个方法上面添加ResponseBody注解
@RestController
public class ProductController {
/*
此注解的作用是:
Spring框架结合Mybatis框架会自动将HeroMapper生成一个实现类和实现里面的方法,
而且会自动实例化对象,required = false告诉idea编译器此对象是非必要的,
因此"mapper"若报错的话需要这样写: @Autowired(required = false)
*/
@Autowired
ProductMapper mapper;
/** 增加商品的功能 */
@RequestMapping("/insert")
public String insert(Product product){
System.out.println("product = " + product);
mapper.insert(product);
return "添加完成!<a href='/'>返回首页</a>";
}
/** 查询商品的功能 */
@RequestMapping("/select")
public String select(){
List<Product> list = mapper.select();
String html = "<table border=1>";
html+="<caption>商品列表</caption>";
html += "<tr><th>id</th><th>标题</th><th>价格</th><th>销量</th></tr>";
//把商品数据装进表格标签里面
for (Product p: list) {
html+="<tr>";
html+="<td>"+p.getId()+"</td>";
html+="<td>"+p.getTitle()+"</td>";
html+="<td>"+p.getPrice()+"</td>";
html+="<td>"+p.getSaleCount()+"</td>";
//把id传过去: ?是请求地址和请求参数的分隔符:
html+="<td><a href='/delete?id="+p.getId()+"'>删除</a></td>";
html+="</tr>";
}
html+="</table>";
return html;
}
/** 删除商品的功能 */
@RequestMapping("/delete")
public String delete(int id){
System.out.println("id = "+id);
mapper.deleteById(id);
return "删除完成!<a href='/select'>返回列表页面<a>";
}
/** 修改商品的功能 */
@RequestMapping("/update")
public String update(Product product){
System.out.println("product = " + product);
mapper.update(product);
return "修改完成!<a href='/select'>返回列表页面</a>";
}
}
package cn.tedu.boot31.mapper;
import cn.tedu.boot31.entity.Product;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
public interface ProductMapper {
//insert into product values(null,‘手机’,500,100);
@Insert("insert into product values(null,#{title},#{price},#{saleCount})")
void insert(Product product);
@Select("select * from product")
@Result(column = "sale_count",property = "saleCount")
List<Product> select();
@Delete("delete from product where id=#{id}")
void deleteById(int id);
@Update("update product set title=#{title},price=#{price}" +
",sale_count=#{saleCount} where id=#{id}")
void update(Product product);
}
03.同步请求和异步请求
同步: 指单线程依次做几件事
异步: 指多线程同时做几件事
同步请求: 指客户端浏览器只有一个主线程, 此线程负责页面的渲染和发出请求等操作,
如果此主线程发出请求的话则停止渲染而且会清空页面显示的内容 直到服务器响应了
数据后才能再次显示, 由于主线程清空了原有显示的内容所以只能实现
页面的整体刷新(整体改变) 。
异步请求: 指客户端的主线程只负责页面渲染相关操作,发请求的事儿由新的子线程操作,
这样子线程发出请求时页面不需要清空,而且可以将查询回来的数据展示在原有页面基
础之上, 这样实现的效果就叫做 页面的局部刷新。
(1)客户端发出请求的几种方式
通过浏览器的地址栏中发出请求 同步请求
通过html页面中的超链接发出请求 同步请求
通过html页面中的form 表单发出请求 同步请求
通过前端框架发出请求 异步请求
(2)Get请求和Post请求
从字面意思理解, Get是跟服务器要数据, Post是给服务器传数据
Get: 请求参数写在请求地址的后面(可见),请求参数有大小限制只能传几k的数据
(不能处理文件上传)
应用场景: 查询请求一般都会使用get,删除也会使用get请求
————————————————————————————————————————————————————————————————————————
Post: 请求参数放在请求体里面(不可见),参数没有大小限制
应用场景: 文件上传, 带有敏感信息的请求(比如注册登录时有密码)
(3)客户端发出:异步请求get传参:boot3-1
一、客户端如何发出异步请求:——————>异步请求get 时如何传参?
改动的页面:index 新建页面helloAxios
创建AxiosController
01.通过Axios框架发出异步请求
02.此框架就是一个普通的js文件 页面需要使用此框架时需要将此文件引入到页面中
03.从苍老师文档服务器中找到axios框架地址:
配置文件下载——>JavaScript 组件库 CDN——>引入axios:
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>
04.打开boot3-1工程:
(1)①src/main/resources/static下新建 商品管理首页 页面:helloAxios
引入vue 和 axios本地文件
②index页面中添加:【<hr> 、 <a href="/helloAxios.html">异步请求测试</a>】
(2)①打开之前的Web工程:eui目录下的js文件夹复制到:src/main/resources/static下
②把js文件夹下的eui.js文件删掉,再从上边第三步【引入axios】中复制地址,
进入浏览器将内容保存下来并存放到该js文件夹下命名为"axios.min.js"文件
此时js文件夹下有:axios.min.js 和 vue.js
(3)启动工程进行测试:http://localhost:8080
点击超链接异步请求测试:显示:{{info}},
说明vue.js没有正常运转,页面右键检查发现html源码报错:vue.js404 axios.min.js404
此时因为改动了static中的东西————>需要重新build编译一下!
发现此时页面再次点击(超链接异步请求测试)时:
成功显示:【测试vue】文本页面!
05.(1)helloAxios中:
①添加测试异步请求get按钮,并用事件绑定(@事件名="方法":@click 点击事件)调用f1()方法
②在下边写一个methods,并在其中写f1方法并写一个点击事件(即点击某个按钮时触发弹窗)
【alert("xxx");】
③再Build一下进行测试:http://localhost:8080 点击测试异步请求,会弹出框显示:xxx
④注释掉上边的②中的代码
⑤f1方法里继续添加【发出异步get请求】代码
06.(1)①在src/main/java/cn/tedu/boot31/controller下新建:AxiosController类
②类上加注解:@RestController 并写一个hello1方法用来返回“请求成功”
③build一下后重新启动工程:快捷键(shift+F10),
此时发现弹出框内容变为:“请求成功“!
————>即完成了异步请求(页面的局部刷新)
07.(1)在helloAxios中:f1方法调用get中,“/hello1Axios”加个参数:?info=tom
变为:【axios.get("/hello1Axios?info=tom")】
(2)AxiosController中:hello方法中添加参数:String info
并改动return为:【return "请求成功! info="+info;】
08.重新启动并测试:http://localhost:8080
点击超链接异步请求测试:显示:【测试vue 测试异步请求get按钮】
点击按钮,弹出框会显示参数info:【请求成功! info=tom】
即实现了发请求的时候如何传参!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>商品管理首页</h1>
<a href="/insert.html">添加商品</a>
<a href="/select">查询商品</a>
<a href="/update.html">修改商品</a>
<hr>
<a href="/helloAxios.html">异步请求测试</a>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<h1>{{info}}</h1>
<input type="button" value="测试异步请求get" @click="f1()">
</div>
<script src="js/vue.js"></script>
<script src="js/axios.min.js"></script>
<script>
let v = new Vue({
el:"div",
data:{
info:"测试vue",
},
methods:{
f1(){
// alert("xxx");
//发出异步请求:
axios.get("/hello1Axios").then(function (response){
//response代表服务器响应对象
//response.data代表服务响应的数据
alert(response.data);
})
}
}
})
</script>
</body>
</html>
package cn.tedu.boot31.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AxiosController {
@RequestMapping("/hello1Axios")
public String hello1(String info){
return "请求成功! info="+info;
}
}
(4)客户端发出:异步请求post传参:boot3-1
2、异步请求post时 如何传参?
改动页面helloAxios 改动类AxiosController
01.(1)helloAxios中:
①添加测试异步请求post按钮,并用事件绑定(@事件名="方法":@click 点击事件)调用f2()方法:
<input type="button" value="测试异步请求post" @click="f2()">
②f2方法里继续添加【发出异步post请求】代码:
(2)按【ctrl+shift+F9】重新编译一下该页面!
测试:http://localhost:8080
点击超链接异步请求测试时,
显示:【测试vue 测试异步请求get按钮 测试异步请求post按钮】
点击post按钮,不会弹出框,
因为AxiosController中需要添加异步post请求条件!
(3)在AxiosController中添加条件:【异步请求post时:进行点击事件弹窗的传参用info表示】
测试:http://localhost:8080
点击超链接异步请求测试时,
显示:【测试vue 测试异步请求get按钮 测试异步请求post按钮】
点击post按钮,会弹出框,
会显示参数info:【请求成功! info='刘德华'】
成功!!!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<h1>{{info}}</h1>
<input type="button" value="测试异步请求get" @click="f1()">
<input type="button" value="测试异步请求post" @click="f2()">
</div>
<script src="js/vue.js"></script>
<script src="js/axios.min.js"></script>
<script>
let v = new Vue({
el:"div",
data:{
info:"测试vue"
},
methods:{
f1(){
// alert("xxx");
//发出异步请求
axios.get("/hello1Axios?info=tom").then(function (response) {
//response代表服务器响应对象
//response.data代表服务响应的数据
alert(response.data);
})
},
f2(){
//发出异步post请求
axios.post("/hello2Axios",{info: "刘德华"}).then(function (response) {
alert(response.data);
})
}
}
})
</script>
</body>
</html>
package cn.tedu.boot31.controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AxiosController {
/** 异步请求get时:进行点击事件弹窗的传参用info表示 */
@RequestMapping("/hello1Axios")
public String hello1(String info){
return "请求成功! info="+info;
}
/** 异步请求post时:进行点击事件弹窗的传参用info表示 */
//@RequestBody注解作用, 当客户端发出post请求并且提交的是自定义对象时,
// 服务器端接收参数必须使用此注解 否则得不到传递过来的参数.
@RequestMapping("/hello2Axios")
public String hello2(@RequestBody String info){
return "请求成功! info="+info;
}
}
04.注册登录(弹出框显示):boot3-2 写3遍!
实现注册登录功能:boot3-2
新建页面index、reg、login
新建类UserController、User、UserMapper
注:若没有User表,需要:
(1)首先在mysql客户端例:
①use empdb;
②create table user(id int primary key auto_increment,username varchar(50),password varchar(50),nick varchar(50))charset=utf8;
③执行【工程Jdbc01Demo09】的程序后在控制台用户名唐僧 密码root 昵称root
一、前提:
(1)创建工程boot3-2 打钩3个
(2)从之前工程中复制application.properties里面的连接数据库的信息,
复制完之后立即启动工程,测试是否创建成功,
如果工程启动不起来:检查报错是不是因为端口被占用, 如果不是,刷新maven 再次启动,
如果还是启动不了,删除工程重新创建新工程,直至启动成功。
二、实现注册(1-6)、登录功能(7-8)
1.创建index.html页面 在里面添加两个超链接访问注册和登录页面
2.创建reg.html页面 在页面中引入vue和axios两个框架文件 从之前工程中复制js文件
夹(检查里面是不是包含这两个框架文件) 在页面中 点击按钮时向/reg发出异步请求,
同时把用户输入的信息提交给服务器,然后在then方法里面判断返回的response.data值
为1代表注册成功 显示首页 值为2代表用户名已存在
3.src/main/java/cn/tedu/boot32下:
创建包controller 下:创建UserController
并且boot32下:创建包entity 下:创建User实体类
4.在UserController里面添加reg方法处理/reg请求
声明User变量用来接收传递过来的用户信息
5.src/main/java/cn/tedu/boot32下:创建包mapper 下:创建类UserMapper
在里面提供两个方法,分别是 selectByUsername 和 insert两个方法
6.回到UserController里面把UserMapper通过@Autowired装配进来,
在reg方法中先调用mapper里面的selectByUsername方法 判断返回值是否有值
如果有值则给客户端返回2,代表用户名已存在,
如果没有值,则调用mapper的insert方法,把用户信息保存到数据库,
最后返回1代表注册成功.
(1)测试:http://localhost:8080
点击注册,输入信息(admin/root/admin)————>点击注册,出现提示框:注册成功
点击注册,输入信息(admin/root/admin)————>点击注册,出现提示框:用户名已存在
7.下一步开始实现登录功能, 步骤类似注册
(1)先创建login.html页面 然后在页面中通过Vue对内容进行管理
当点击登录按钮的时候 向/login发出异步请求
(2)同样把输入的用户信息提交给服务器, 在then方法中判断response.data的值:
值为1代表登录成功显示首页,值为2代表用户名不存在, 值为3代表密码错误
8.在UserController里面添加login方法【处理/login请求】,
用注解@RequestBody:声明User对象接收传递过来的用户信息,
在方法中调用mapper的selectByUsername 通过用户输入的用户名查询对应的用户信息,
如果没有查询到 直接返回2:代表用户名不存在,
如果查询到了 继续通过输入的密码和查询到的密码比较
如果一直返回1代表登录成功, 如果不一直返回2代表密码错误.
(1)测试:http://localhost:8080
点击登录,输入信息(admin/root/admin)————>点击注册,出现提示框:登录成功
点击登录,输入信息(admin/123)————>点击登录,出现提示框:密码错误
点击登录,输入信息(dd/123)————>点击登录,出现提示框:用户名不存在
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>工程首页</h1>
<a href="/reg.html">注册</a>
<a href="/login.html">登录</a>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>注册页面</h1>
<div>
<input type="text" v-model="user.username" placeholder="请输入用户名">
<input type="text" v-model="user.password" placeholder="请输入密码">
<input type="text" v-model="user.nick" placeholder="请输入昵称">
<input type="button" value="注册" @click="reg()">
</div>
<script src="js/vue.js"></script>
<script src="js/axios.min.js"></script>
<script>
let v = new Vue({
el:"div",
data:{
user:{
username:"",
password:"",
nick:""
}
},
methods:{
reg(){
//发出post 把装着用户信息的user对象提交
axios.post("/reg",v.user).then(function (response) {
//判断如果注册成功 返回首页
if (response.data==1){
alert("注册成功!");
//让浏览器显示到首页:
location.href="/";
}else{
alert("用户名已存在!");
}
})
}
}
})
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>登录页面</h1>
<div>
<input type="text" v-model="user.username" placeholder="用户名">
<input type="text" v-model="user.password" placeholder="密码">
<input type="button" value="登录" @click="login()">
</div>
<script src="js/vue.js"></script>
<script src="js/axios.min.js"></script>
<script>
let v = new Vue({
el:"div",
data:{
user:{
username:"",password:""
}
},
methods:{
login(){
//发出异步post请求
axios.post("/login",v.user).then(function (response) {
if (response.data==1){
alert("登录成功!");
location.href = "/";//显示到首页
}else if(response.data==2){
alert("用户名不存在!")
}else{
alert("密码错误!");
}
})
}
}
})
</script>
</body>
</html>
package cn.tedu.boot32.controller;
import cn.tedu.boot32.entity.User;
import cn.tedu.boot32.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
//此注解相当于在每一个方法上面添加ResponseBody注解
@RestController
public class UserController {
/*
此注解的作用是:
Spring框架结合Mybatis框架会自动将HeroMapper生成一个实现类和实现里面的方法,
而且会自动实例化对象,required = false告诉idea编译器此对象是非必要的,
因此"mapper"若报错的话需要这样写: @Autowired(required = false)
*/
@Autowired
UserMapper mapper;
/** 处理/reg注册请求 */
@RequestMapping("/reg")
public int reg(@RequestBody User user) {
//拿用户输入的用户名去数据库中查询用户信息
User u = mapper.selectByUsername(user.getUsername());
if (u != null) {
return 2; //代表用户名已存在
}
//执行注册
mapper.insert(user);
return 1; //代表注册成功
}
/** 处理/login登录请求 */
//@RequestBody注解作用, 当客户端发出post请求并且提交的是自定义对象时,
// 服务器端接收参数必须使用此注解 否则得不到传递过来的参数.
@RequestMapping("/login")
public int login(@RequestBody User user){
//拿用户输入的用户名查询数据库里面的用户信息
User u = mapper.selectByUsername(user.getUsername());
if (u!=null){//代表查询到了用户信息
//判断用户输入的密码和查询到的密码是否一致
if (user.getPassword().equals(u.getPassword())){
return 1;//登录成功!
}
return 3; //密码错误!
}
return 2;//代表用户名不存在
}
}
package cn.tedu.boot32.entity;
public class User {
private Integer id;
private String username;
private String password;
private String nick;
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", nick='" + nick + '\'' +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getNick() {
return nick;
}
public void setNick(String nick) {
this.nick = nick;
}
}
package cn.tedu.boot32.mapper;
import cn.tedu.boot32.entity.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
@Mapper
public interface UserMapper {
//如果返回值为一个对象而非List集合 要求查询回来的结果必须是0条或1条
//如果出现了大于1条的情况会报错
@Select("select * from user where username=#{username}")
User selectByUsername(String username);
@Insert("insert into user values(null,#{username},#{password},#{nick})")
void insert(User user);
}
3、!!后端MVC设计模式:boot4-*1和2
(1)后端的MVC设计模式
把实现一个业务的代码划分为三部分,分别是: 页面相关(V),业务逻辑相关(C),数据相关(M)
M:Model 数据模型, 对应的代码是数据库相关的Mapper部分
V:View 视图, 对应所有页面相关内容
C:Controller 控制器,对应的是Controller相关代码
实现一个业务的顺序:
V页面相关代码->C Controller相关代码->M 数据库Mapper相关代码
排错时也是从这三部分代码中找问题
后端MVC涉及模式中的V页面相关,前端工程师将页面又划分为了MVC三部分
(2)前后端分离
①:
如果前后端不分离, 后端服务器需要两套代码来应对 手机客户端和浏览器客户端,
因为不同的客户端的需求内容是不一样的,这样后端的开发效率就会受影响。
②:
前后端分离:
指在Controller中不再处理页面相关内容, 浏览器客户端需要先请求页面,页面加载完之后
从页面中再次发出请求获取数据, 得到数据后把数据展示在页面中,
这个过程属于页面的局部刷新,
同步请求只能实现页面的整体刷新无法实现局部刷新,
所以以后不再使用同步请求, 全部使用异步请求,因为以后工作基本全是前后端分离思想。
(3)JSON
————>JSON是一种轻量级的数据交换格式(数据封装格式)
————>客户端和服务器之间需要互相传递数据,
当需要传递复杂数据时需要按照特定的格式将数据进行封装,
JSON就是这样一个通用格式。
登录成功
用户名已存在
密码错误
1
2
3
"id=1, title=手机 , price=1000, saleCount=500"
下面的代码是JSOn格式: list集合中装着product对象
[{"id":1,"title":"阿迪袜子","price":10.0,"saleCount":1000},
{"id":3,"title":"裤子","price":50.0,"saleCount":400},
{"id":4,"title":"袜子","price":5.0,"saleCount":100}]
(4)服务器和客户端之间如何传递复杂数据
《客户端向服务器请求数据发请求,服务器返回的list集合,客户端拿到的数组》流程:
————>客户端向服务器请求数据发请求。
————>服务器从数据库中查询出一个List集合
————>因为客户端是JavaScript语言(没有list集合但有JSON字符串),
服务器是Java语言(跨平台了);
————>list集合不具备跨平台,所以【SpringMVC框架将list集合转为JSON格式的字符串】,
再通过网络传输(传输的是2进制数据010101..)给客户端,
————>客户端收到后先将2进制数据010101转为JSON格式的字符串,
————>客户端得到JSON格式的字符串,再【通过Axios框架将其转为数组】,
——>因为页面当中用到的是数组(页面中把假数据的数组换成真数据的数组后页面的内容
才能跟着改变),所以JSON格式字符串不能直接用。
————>客户端向服务器请求数据发请求,服务器返回的list集合,客户端拿到的数组
——————————————————————————————————————————————————————
代码演示:
《ProductController》:
@RequestMapping("/select")
public List<Product> select(){
// SpringMVC框架当发现返回值类型为集合或自定义的对象类型时,
//会将集合或对象转成JSON格式的字符串,然后再将JSON格式字符串转成二进制数据进行网络传输
/*
此时测试: http://localhost:8080/select
可以跳转到一个用list集合显示数据库里的product的信息的页面!
[{"id":5,"title":"狮子","price":520.0,"saleCount":22},
{"id":6,"title":"老虎狮子","price":2000.0,"saleCount":2155},
{"id":7,"title":null,"price":null,"saleCount":null}]
*/
//此时想看数据库真实商品信息时,需要把这句代码注释后,改为下边两句
// return mapper.select();
List<Product> list = mapper.select();
return list;
}
-------------------------------------------------------
《list.html》:
axios.get("/select").then(function (response) {
//服务器返回的数据直接给到arr数组 由于页面和数组进行了绑定
//数组值发生改变时页面会自动发生改变
//服务器传递过来的是二进制的数据先将二进制的数据转回JSON格式的字符串
//转完字符串之后Axios框架会再将JSON格式的字符串转成数组或对象
v.arr = response.data;
01. !!!商品管理步骤:*boot4-1
(0)问题:代码没错但功能有问题:
代码可以确定没有错误,
但是boot4-1工程【添加商品】的时候:
弹出框显示成功
但商品列表里是空的
这个空的商品信息但是能修改成功后就有信息;
经过仔细查看该工程,发现boot4-1 的pom.xmlmaven里有一个和boot3-1相关的:
<dependency>
<groupId>cn.tedu</groupId>
<artifactId>boot3-1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
删除掉:【<dependency>....</dependency>】中内容重写进行功能测试
发现可以正常添加商品了!!!
(1)增—添加商品提示框显示添加成功:
0.前提:若没有商品product表,现在数据库创建:
(1)创建商品表:在数据库中完成以下两个指令:
use empdb;
create table product(id int primary key auto_increment,title varchar(100),price double(10,2),sale_count int)charset=utf8;
1.创建工程 3个打钩 修改application.properties配置文件,
启动工程测试是否成功
2.创建index.html首页 和 insert.html页面
3.在insert.html页面中添加三个文本框和一个添加按钮, 通过Vue对页面内容进行管理,
此时需要把之前工程中的js文件夹复制到新工程, 当点击添加按钮时向/insert地址发出
异步post请求把用户输入的商品信息提交
4.创建controller.ProductController 添加insert方法处理/insert请求
创建Product实体类 在insert方法中声明用来接收传递过来的参数(此参数需要通过
@RequestBody注解进行修饰
5.创建ProductMapper 在里面添加insert方法通过@Insert注解修饰
6.在Controller中将ProductMapper装配进来, 在insert方法中调用mapper的insert方法
把接收到的Product对象传递过去
0、前提:
(1)创建工程boot4-1 打钩3个
(2)从之前工程中复制application.properties里面的连接数据库的信息,
复制完之后立即启动工程,测试是否创建成功,
如果工程启动不起来:检查报错是不是因为端口被占用, 如果不是,刷新maven 再次启动,
如果还是启动不了,删除工程重新创建新工程,直至启动成功。
一、商品管理步骤:
0.前提:若没有商品product表,现在数据库创建:
(1)创建商品表:在数据库中完成以下两个指令:
use empdb;
create table product(id int primary key auto_increment,title varchar(100),price double(10,2),sale_count int)charset=utf8;
1、添加商品提示框显示添加成功:
新建页面index、insert 新建类ProductController、Product
新建接口ProductMapper
(1)创建index.html商品管理首页页面 在里面添加两个超链接添加商品和商品列表
(2)①创建insert页面
②引入vue.js 和 axios.min.js文件
把:js文件夹从之前的工程粘到static下
(3)在src/main/java/cn/tedu/boot41下:创建包controller 下:创建ProductController类
(4)在src/main/java/cn/tedu/boot41下:创建包entity 下:创建Product实体类
(4)①把数据保存在Mapper里 创建在src/main/java/cn/tedu/boot41下:创建包mapper
②mapper下创建接口:ProductMapper
测试:http://localhost:8080
点击添加商品超链接:跳转到添加商品信息页面,
输入信息(小米手机/100/100),点击添加,
弹出框显示:添加完成
成功!!!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>商品管理首页</h1>
<a href="/insert.html">添加商品</a>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>添加商品</h1>
<div>
<input type="text" placeholder="商品标题">
<input type="text" placeholder="商品价格">
<input type="text" placeholder="商品销量">
<input type="button" value="添加" @click="insert()">
</div>
<script src="js/vue.js"></script>
<script src="js/axios.min.js"></script>
<script>
let v = new Vue({
el:"div",
data:{
p:{
title:"",
price:"",
saleCount:"",
}
},
methods:{
insert(){
//发出异步请求:
axios.post("/insert",v.p).then(function (response){
alert("添加完成!");
location.href="/";
})
}
}
})
</script>
</body>
</html>
package cn.tedu.boot41.controller;
import cn.tedu.boot41.mapper.ProductMapper;
import cn.tedu.boot41.entity.Product;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
//此注解相当于在每一个方法上面添加ResponseBody注解
@RestController
public class ProductController {
/*
此注解的作用是:
Spring框架结合Mybatis框架会自动将HeroMapper生成一个实现类和实现里面的方法,
而且会自动实例化对象,required = false告诉idea编译器此对象是非必要的,
因此"mapper"若报错的话需要这样写: @Autowired(required = false)
*/
@Autowired(required = false)
ProductMapper mapper;
@RequestMapping("/insert")
public void insert(@RequestBody Product product){
System.out.println("product = " + product);
mapper.insert(product);
}
}
package cn.tedu.boot41.entity;
public class Product {
private Integer id;
private String title;
private Double price;
private Integer saleCount;
@Override
public String toString() {
return "Product{" +
"id=" + id +
", title='" + title + '\'' +
", price=" + price +
", saleCount=" + saleCount +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public Integer getSaleCount() {
return saleCount;
}
public void setSaleCount(Integer saleCount) {
this.saleCount = saleCount;
}
}
package cn.tedu.boot41.mapper;
import cn.tedu.boot41.entity.Product;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface ProductMapper {
/** 添加商品的功能 */
@Insert("insert into product values(null,#{title},#{price},#{saleCount})")
void insert(Product product);
}
(2)查—商品管理商品列表步骤:
1.在index.html页面中添加商品列表超链接 请求地址为/list.html页面
2.创建list.html页面, 在页面中添加表格,并且通过Vue进行管理,在Vue的created方法中
发出异步的get请求,把请求回来的数据交给一个数组变量, 然后让页面中表格的内容和数据
变量进行绑定, 当数组有值时页面会自动显示数据
3.在ProductController中添加select方法 处理/select请求,
方法中调用mapper的select方法 把得到的List集合直接返回给客户端
4.实现 mapper里面的select方法
2、查询数据库商品功能:
改动页面index 新建页面list
改动的类ProductController
改动的接口ProductMapper
(5)①index添加一个超链接:【<a href="/list.html">商品列表</a>】
②创建页面商品列表list,
在表格属性里写v-for进行遍历数组,
在从下面写插值:让此处的文本内容和变量进行绑定,不依赖某个标签
从Vue对象里写【Vue对象创建时执行的方法:created:function(){xxx}】
再从上面这个方法里写【发出异步请求获取数据】:
axios.get("/select").then(function (response){
v.arr = response.data;
})
(6)测试列表是否有假数据(自己在arr里自定义的):http://localhost:8080
点击商品列表超链接:
发现跳转到:
罗列的数组中的商品列表页面
(7)①list列表写一个方法:create:function ()
②回到ProductController中,写一个List<Product>类型的select方法
此时select()会爆红,按住alt+回车,会自动在ProductMapper中生成该方法
(8)①测试1:http://localhost:8080
点击商品列表:
跳转到商品列表页面:
发现已经变为list页面中arr自定义的商品信息!
成功!
②测试2:http://localhost:8080/select
可以跳转到一个用list集合显示数据库里的product的信息的页面!
(9)在ProductController中:
①把select()方法中的返回语句:【return mapper.select();】
换为:
【List<Product> list = mapper.select();
return list;
】时,
②测试:http://localhost:8080
点击商品列表:
跳转到商品列表页面:
发现已经变为数据库里的真正商品信息!!!
成功!
index添加一个超链接:【<a href="/list.html">商品列表</a>】
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>商品列表</h1>
<table border="">
<tr>
<th>商品</th>
<th>商品标题</th>
<th>商品价格</th>
<th>商品销量</th>
</tr>
<tr v-for="p in arr">
<td>{{p.id}}</td>
<td>{{p.title}}</td>
<td>{{p.price}}</td>
<td>{{p.saleCount}}</td>
</tr>
</table>
<script src="js/vue.js"></script>
<script src="js/axios.min.js"></script>
<script>
let v = new Vue({
el:"table",
data:{
arr:[
{id:1,title:"小米手机",price:3000,saleCount:688},
{id:2,title:"华为手机",price:2000,saleCount:68},
{id:3,title:"辣椒手机",price:4000,saleCount:988},
]
},
created:function () {//此方法是Vue对象创建时执行的方法
// 一般会把加载完页面请求数据的代码 写在此方法中
//发出异步请求获取数据
axios.get("/select").then(function (response) {
//服务器返回的数据直接给到arr数组 由于页面和数组进行了绑定
//数组值发生改变时页面会自动发生改变
//服务器传递过来的是二进制的数据Axios框架会先将二进制的数据转回JSON格式的字符串
//转弯字符串之后会再将JSON格式的字符串转成数组或对象
v.arr = response.data;
})
}
})
</script>
</body>
</html>
package cn.tedu.boot41.controller;
import cn.tedu.boot41.mapper.ProductMapper;
import cn.tedu.boot41.entity.Product;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
//此注解相当于在每一个方法上面添加ResponseBody注解
@RestController
public class ProductController {
/*
此注解的作用是:
Spring框架结合Mybatis框架会自动将HeroMapper生成一个实现类和实现里面的方法,
而且会自动实例化对象,required = false告诉idea编译器此对象是非必要的,
因此"mapper"若报错的话需要这样写: @Autowired(required = false)
*/
@Autowired(required = false)
ProductMapper mapper;
@RequestMapping("/insert")
public void insert(@RequestBody Product product){
System.out.println("product = " + product);
mapper.insert(product);
}
@RequestMapping("/select")
public List<Product> select(){
//SpringMvc框架当发现返回值类型为集合或自定义的对象类型时,
//会将集合或对象转成JSON格式的字符串,然后再将JSON格式字符串转成二进制数据进行网络传输
/*
此时测试: http://localhost:8080/select
可以跳转到一个用list集合显示数据库里的product的信息的页面!
[{"id":5,"title":"狮子","price":520.0,"saleCount":22},
{"id":6,"title":"老虎狮子","price":2000.0,"saleCount":2155},
{"id":7,"title":null,"price":null,"saleCount":null}]
*/
//此时想看数据库真实商品信息时,需要把这句代码注释后,改为下边两句
// return mapper.select();
List<Product> list = mapper.select();
return list;
}
}
package cn.tedu.boot41.mapper;
import cn.tedu.boot41.entity.Product;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface ProductMapper {
/** 添加商品的功能 */
@Insert("insert into product values(null,#{title},#{price},#{saleCount})")
void insert(Product product);
/** 查询商品的功能 */
@Select("select * from product")
@Result(property = "saleCount",column = "sale_count")//@Result:指定该属性(saleCount:商品销量)
List<Product> select();
}
(3)删—商品管理删除商品步骤:
1.在商品列表页面中添加删除的超链接 ,废掉超链接的跳转功能添加点击事件,调用del方法,
在方法中向/delete发出异步get请求并且把商品的id传递过去
2.在ProductController中 添加delete方法处理/delete请求在方法中调用mapper的
deleteById方法把接收到的id传递进去
3.实现mapper里面的deleteById方法
3、删—商品管理删除商品步骤:
改动的页面list 改动的类ProductController
改动的接口ProductMapper
1.
(1)list页面中把arr数组里的内容清空
(2)①在商品列表页面中表格最后添加一列【操作】用来删除商品
②表格中最后添加删除的超链接:【<td><a href="/delete">删除</a></td>】,
③废掉超链接的跳转功能添加点击事件,调用del方法,在方法中向/delete发出异步get请求并且把商品的id传递过去
把②红代码改为:【<td><a href="/javascript:void(0)" @click="del(p.id)">删除</a></td>】
④在list最下边添加一个methods里的【del(id)】方法
2.在ProductController中 添加delete方法处理/delete请求在方法中调用mapper的
deleteById方法把接收到的id传递进去
3.在接口ProductMapper中:实现mapper里面的deleteById方法
4.测试:http://localhost:8080
点击超链接查询商品,
跳转到商品列表页面,此时显示的是真正的数据库商品信息!
测试发现右侧有一列删除的按钮,
删除一个商品的信息,
弹出框显示:删除完成,此时该商品已经被删除
再测试添加商品功能,也能添加成功,并且在商品列表显示出来!
成功!!!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>商品列表</h1>
<table border="">
<tr>
<th>商品</th>
<th>商品标题</th>
<th>商品价格</th>
<th>商品销量</th>
<th>操作</th>
</tr>
<tr v-for="p in arr">
<td>{{p.id}}</td>
<td>{{p.title}}</td>
<td>{{p.price}}</td>
<td>{{p.saleCount}}</td>
<td>
<!--废掉超链接自身的跳转功能-->
<a href="/javascript:void(0)" @click="del(p.id)">删除</a>
</td>
</tr>
</table>
<script src="js/vue.js"></script>
<script src="js/axios.min.js"></script>
<script>
let v = new Vue({
el:"table",
data:{
arr:[],
},
created:function () {//此方法是Vue(自带的方法不用写到methods里)对象创建时执行的方法
// 一般会把加载完页面请求数据的代码 写在此方法中
//发出异步请求获取数据
axios.get("/select").then(function (response) {
//服务器返回的数据直接给到arr数组 由于页面和数组进行了绑定
//数组值发生改变时页面会自动发生改变
//服务器传递过来的是二进制的数据Axios框架会先将二进制的数据转回JSON格式的字符串
//转弯字符串之后会再将JSON格式的字符串转成数组或对象
v.arr = response.data;
})
},
methods:{
del(id){
//发出删除的异步请求
axios.get("/delete?id="+id).then(function (response) {
alert("删除完成");
location.reload();//刷新页面
})
}
}
})
</script>
</body>
</html>
package cn.tedu.boot41.controller;
import cn.tedu.boot41.mapper.ProductMapper;
import cn.tedu.boot41.entity.Product;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
//此注解相当于在每一个方法上面添加ResponseBody注解
@RestController
public class ProductController {
/*
此注解的作用是:
Spring框架结合Mybatis框架会自动将HeroMapper生成一个实现类和实现里面的方法,
而且会自动实例化对象,required = false告诉idea编译器此对象是非必要的,
因此"mapper"若报错的话需要这样写: @Autowired(required = false)
*/
@Autowired(required = false)
ProductMapper mapper;
@RequestMapping("/insert")
public void insert(@RequestBody Product product){
System.out.println("product = " + product);
mapper.insert(product);
}
@RequestMapping("/select")
public List<Product> select(){
//SpringMvc框架当发现返回值类型为集合或自定义的对象类型时,
//会将集合或对象转成JSON格式的字符串,然后再将JSON格式字符串转成二进制数据进行网络传输
/*
此时测试: http://localhost:8080/select
可以跳转到一个用list集合显示数据库里的product的信息的页面!
[{"id":5,"title":"狮子","price":520.0,"saleCount":22},
{"id":6,"title":"老虎狮子","price":2000.0,"saleCount":2155},
{"id":7,"title":null,"price":null,"saleCount":null}]
*/
//此时想看数据库真实商品信息时,需要把这句代码注释后,改为下边两句
// return mapper.select();
List<Product> list = mapper.select();
return list;
}
@RequestMapping("/delete")
public void delete(int id){
System.out.println("id = "+id);
mapper.deleteById(id);
}
}
package cn.tedu.boot41.mapper;
import cn.tedu.boot41.entity.Product;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
public interface ProductMapper {
/** 添加商品的功能 */
@Insert("insert into product values(null,#{title},#{price},#{saleCount})")
void insert(Product product);
/** 查询商品的功能 */
@Select("select * from product")
@Result(property = "saleCount",column = "sale_count")//@Result:指定该属性(saleCount:商品销量)
List<Product> select();
/** 删除商品的功能 */
@Delete("delete from product where id=#{id}")
void deleteById(int id);
}
(4)改—商品管理修改商品步骤:
4、改—商品管理修改商品步骤:
改动页面list 新建页面update
改动的类ProductController
改动的接口ProductMapper
1.给列表页面添加:【修改超链接】 往/update.html页面跳转 并且传递过去商品id
在list中:【table中的废掉超链接自身的跳转功能下再天啊及一个修改的超链接】:
<a :href="'/update.html?id='+p.id">修改</a>
2.创建update.html页面 在Vue的created方法中得到传递过来的id 然后向/selectById
发出异步的get请求 把id传递过去 把服务返回的数据用一个product对象接收, 并且页面
内容和此对象进行绑定 这样对象有值页面就能跟着显示
3.在ProductController里面添加selectById方法 处理/selectById请求,
在方法中调用Mapper的selectById方法
4.实现mapper里面的selectById方法
5.给update.html页面中的修改按钮添加点击事件, 点击时向/update地址发出异步的
post请求把商品的信息一起提交
6.在ProductController里面添加update方法处理/update请求,在方法中掉用mapper
的update方法
7.实现mapper里面的update方法 .
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>商品列表</h1>
<table border="1">
<tr>
<th>商品id</th><th>商品标题</th>
<th>商品价格</th><th>商品销量</th><th>操作</th>
</tr>
<tr v-for="p in arr">
<td>{{p.id}}</td><td>{{p.title}}</td>
<td>{{p.price}}</td><td>{{p.saleCount}}</td>
<!--废掉超链接自身的跳转功能-->
<td><a href="javascript:void(0)" @click="del(p.id)">删除</a>
<!--点击修改需要跳转页面 所以不能像删除一样废掉跳转功能-->
<!--元素的属性里面出现变量 需要进行属性绑定-->
<a :href="'/update.html?id='+p.id">修改</a>
</td>
</tr>
</table>
<script src="js/vue.js"></script>
<script src="js/axios.min.js"></script>
<script>
let v = new Vue({
el:"table",
data:{
arr:[]
},
created:function () {//此方法是Vue对象创建时执行的方法
// 一般会把加载完页面请求数据的代码 写在此方法中
//发出异步请求获取数据
axios.get("/select").then(function (response) {
//服务器返回的数据直接给到arr数组 由于页面和数组进行了绑定
//数组值发生改变时页面会自动发生改变
//服务器传递过来的是二进制的数据先将二进制的数据转回JSON格式的字符串
//转完字符串之后Axios框架会再将JSON格式的字符串转成数组或对象
v.arr = response.data;
})
},
methods:{
del(id){
//发出删除的异步请求
axios.get("/delete?id="+id).then(function (response) {
alert("删除完成!");
location.reload();//刷新页面
})
}
}
})
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>修改商品页面</h1>
<div>
<!--readonly设置为只读-->
<input type="text" v-model="product.id" placeholder="id" readonly>
<input type="text" v-model="product.title" placeholder="标题">
<input type="text" v-model="product.price" placeholder="价格">
<input type="text" v-model="product.saleCount" placeholder="销量">
<input type="button" value="修改" @click="update()">
</div>
<script src="js/vue.js"></script>
<script src="js/axios.min.js"></script>
<script>
let v = new Vue({
el:"div",
data:{
product:{}
},
created:function () {
//页面加载完之后 得到商品的id 通过id查询商品信息 并显示到页面中
//从地址栏中得到id参数
let id = location.search.split("=")[1];
//发出异步请求通过id查询对应的商品信息
axios.get("/selectById?id="+id).then(function (response) {
//把服务器查询回来的商品信息赋值给Vue里面的product变量
//让页面和product对象进行绑定 当product得到值页面就会显示出来
v.product = response.data;
})
},
methods:{
update(){
//发出异步post请求
axios.post("/update",v.product).then(function (response) {
alert("修改完成!");
location.href="/list.html";
})
}
}
})
</script>
</body>
</html>
package cn.tedu.boot41.controller;
import cn.tedu.boot41.mapper.ProductMapper;
import cn.tedu.boot41.entity.Product;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
//此注解相当于在每一个方法上面添加ResponseBody注解
@RestController
public class ProductController {
/*
此注解的作用是:
Spring框架结合Mybatis框架会自动将HeroMapper生成一个实现类和实现里面的方法,
而且会自动实例化对象,required = false告诉idea编译器此对象是非必要的,
因此"mapper"若报错的话需要这样写: @Autowired(required = false)
*/
@Autowired
ProductMapper mapper;
//@RequestBody注解作用, 当客户端发出post请求并且提交的是自定义对象时,
// 服务器端接收参数必须使用此注解 否则得不到传递过来的参数.
@RequestMapping("/insert")
public void insert(@RequestBody Product product){
System.out.println("product = " + product);
mapper.insert(product);
}
@RequestMapping("/select")
public List<Product> select(){
// SpringMVC框架当发现返回值类型为集合或自定义的对象类型时,
//会将集合或对象转成JSON格式的字符串,然后再将JSON格式字符串转成二进制数据进行网络传输
/*
[{"id":1,"title":"阿迪袜子","price":10.0,"saleCount":1000},
{"id":3,"title":"裤子","price":50.0,"saleCount":400},
{"id":4,"title":"袜子","price":5.0,"saleCount":100}]
*/
//此时想看数据库真实商品信息时,需要把这句代码注释后,改为下边两句
// return mapper.select();
List<Product> list = mapper.select();
return list;
}
@RequestMapping("/delete")
public void delete(int id){
System.out.println("id = " + id);
mapper.deleteById(id);
}
@RequestMapping("/selectById")
public Product selectById(int id){
System.out.println("id = " + id);
//当SpringMVC框架发现返回的是一个自定义对象会自动转成JSON字符串再转成二进制进行网络传输
return mapper.selectById(id);
}
@RequestMapping("/update")
public void update(@RequestBody Product product){
mapper.update(product);
}
}
package cn.tedu.boot41.mapper;
import cn.tedu.boot41.entity.Product;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
public interface ProductMapper {
/** 1、添加商品的功能 */
@Insert("insert into product values(null,#{title},#{price},#{saleCount})")
void insert(Product product);
/** 2、查询商品的功能 */
@Select("select * from product")
@Result(property = "saleCount", column = "sale_count")
List<Product> select();
/** 3、删除商品的功能 */
@Delete("delete from product where id=#{id}")
void deleteById(int id);
/** 4、(1)查询商品id的功能 */
@Select("select * from product where id=#{id}")
@Result(property = "saleCount", column = "sale_count")
Product selectById(int id);
/** 4、(1)添加商品的功能 */
@Update("update product set title=#{title},price=#{price}" +
",sale_count=#{saleCount} where id=#{id}")
void update(Product product);
}
02.文件上传:boot4-2
0、前提:
(1)创建工程boot4-2 打钩3个
(2)从之前工程中复制application.properties里面的连接数据库的信息,
复制完之后立即启动工程,测试是否创建成功
如果工程启动不起来:检查报错是不是因为端口被占用, 如果不是,刷新maven 再次启动,
如果还是启动不了,删除工程重新创建新工程,直至启动成功。
一、文件上传
新建页面index、upload 新建类UploadController
1.
(1)创建文件上传index页面
(2)复制css和js文件夹——>到src/main/resources/static下
(3)把Web工程里day07下的【start.html】复制到上边的static目录下
并改名为【upload】
(4)在elementUI网站:https://element.eleme.cn
①找到组件中的Upload上传
②找到下面的照片墙:复制标签<script>上面部分:粘贴到upload的【<div id="app">】中
<el-upload
action="https://jsonplaceholder.typicode.com/posts/"
list-type="picture-card"
:on-preview="handlePictureCardPreview"
:on-remove="handleRemove">
<i class="el-icon-plus"></i>
</el-upload>
<el-dialog :visible.sync="dialogVisible">
<img width="100%" :src="dialogImageUrl" alt="">
</el-dialog>
③把下面data中的两个变量粘贴到upload中对应的部分:
dialogImageUrl: '',
dialogVisible: false
④把整个的methods(下边代码:)复制到upload的data下
methods: {
handleRemove(file, fileList) {
console.log(file, fileList);
},
handlePictureCardPreview(file) {
this.dialogImageUrl = file.url;
this.dialogVisible = true;
}
}
(5)build一下,启动工程,测试:http://localhost:8080
点击超链接文件上传页面,
会跳转到一个可以点击页面“+”号成功上传图片到该网页的页面!
点击加号上传图片,上传成功后可能会消失,很正常,
2.改动现有代码:
(1)将upload中:
把<el-upload>中上传的地址改成自己的:[action="/upload"]
(2)①src/main/java/cn/tedu/boot42 下:创建包controller 下创建类:UploadController
②写一个upload方法,标上注解
(3)upload中:<el-upload>中的action下加一个name:【name="pidFile"】
注:name代表上传文件时 文件的参数名
(4)UploadController:方法后加参数 打桩输出
测试:http://localhost:8080/
点击超链接文件上传页面,跳转到文件上传页面,点击加号选择一张图片,
点击上传后控制台会输出该图片路径信息:
【picFile = org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile@6b789718】
(5)设置文件上传大小的限制:
在resources下的application.properties中:添加:
[spring.servlet.multipart.max-file-size=10MB]
此时10MB以内的文件都可以了!
(6)UploadController中:在数据库得到文件,把文件保存起来:
upload方法中:
①得到原始文件名:[String fileName = picFile.getOriginalFilename();]
②得到后缀名: 从最后一个【,】出现的位置截取到最后
[String suffix = fileName.substring(fileName.lastIndexOf("."));]
③得到唯一文件名:[fileName = UUID.randomUUID()+suffix;]并打桩输出
测试:http://localhost:8080/
点击超链接文件上传页面,跳转到文件上传页面,点击加号选择一张图片,
此时点击上传后控制台会多出来一个文件名的输出:
【picFile = org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile@6b789718】
【文件名:d7dc68f2-2a06-4993-9ae2-4f6253e1ef77.jpg】
(7)在UploadController:
①准备保存的图片的文件夹路径:[.......]
②若该文件夹不存在,则创建:[........]
③得到文件的完整路径:[..........]
④把文件保存的此路径 有异常抛出:[.....]
测试:http://localhost:8080/
点击超链接文件上传页面,跳转到文件上传页面,点击加号选择一张图片,
此时点击上传后控制台会多出来一个【文件保存完成】的输出:
【picFile = org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile@6b789718】
【文件名:d7dc68f2-2a06-4993-9ae2-4f6253e1ef77.jpg】
【文件保存完成!请去此路径检查文件是否存在:D:/Softa/danei/ideaBoot4-2testPath/files/e13b87fa-0ca0-401e-8ccc-9d0e0432cdd8.jpg】
3.此时页面中点击上传图片成功后,鼠标放到图片上点击删除突变后,
页面中图片不存在了,但图片保存到的路径中仍然有该图片,如何解决?
(1)①在上面导入本地的axios.min.js文件
②在upload页面中:在Vue对象里的methods中handleRemove方法中添加:
【当点击删除图片时方法会执行:
axios.get("/remove").then(function (response) {
console.log("服务器图片已经删除")
}) 】
③UploadController中:
将upload方法由【默认void改为String类型】的【并返回一个fileName字符串】
④在upload页面中:【当点击删除图片时方法会执行:】的代码改为:
//当点击删除图片时方法会执行:
//file代表要删除的文件
//file.response代表文件上传成功时 服务器响应的数据(文件名)
console.log("文件名="+file.response);
axios.get("/remove?name="+file.response).then(function (response) {
console.log("服务器图片已经删除")
})
测试:http://localhost:8080/
点击超链接文件上传页面,跳转到文件上传页面,点击加号选择一张图片,
根据控制台的路径查看该图片,末尾为:【c4fc.jpg】
在页面中右键检查,发现Get请求是404,因为还没有写remove这个请求
4.在UploadController中:
添加一个remove方法区处理这个请求:
/** 删除的请求 */
@RequestMapping("/remove")
public void remove(String name){
String dirPath = "D:/files";
String filePath = dirPath+"/"+name;
new File(filePath).delete();//删除文件
}
5.测试:http://localhost:8080/
点击超链接文件上传页面,
跳转到文件上传页面,
点击加号选择一张图片,保存图片的文件夹中就多一种张图片
再添加一张,文件夹又多一张,
点击图片上的删除按钮,则文件夹中的也会删除!
成功!!!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>文件上传测试</h1>
<a href="/upload.html">文件上传页面</a>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<!-- import CSS -->
<link rel="stylesheet" href="css/eui.css">
</head>
<body>
<div id="app">
<!--name代表上传文件时 文件的参数名-->
<el-upload
action="/upload"
name="picFile"
list-type="picture-card"
:on-preview="handlePictureCardPreview"
:on-remove="handleRemove">
<i class="el-icon-plus"></i>
</el-upload>
<el-dialog :visible.sync="dialogVisible">
<img width="100%" :src="dialogImageUrl" alt="">
</el-dialog>
</div>
</body>
<!-- import Vue before Element -->
<script src="js/vue.js"></script>
<!-- import JavaScript -->
<script src="js/eui.js"></script>
<script src="js/axios.min.js"></script>
<script>
let v = new Vue({
el: '#app',
data: function() {
return {
dialogImageUrl: '',
dialogVisible: false
}
},
methods: {
handleRemove(file, fileList) {
console.log(file, fileList);
//当点击删除图片时方法会执行
//file代表要删除的文件
//file.response代表文件上传成功时 服务器响应的数据(文件名)
console.log("文件名="+file.response);
axios.get("/remove?name="+file.response).then(function (response) {
console.log("服务器图片已经删除")
})
},
handlePictureCardPreview(file) {
this.dialogImageUrl = file.url;
this.dialogVisible = true;
}
}
})
</script>
</html>
package cn.tedu.boot42.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
//此注解相当于在每一个方法上面添加ResponseBody注解
@RestController
public class UploadController {
@RequestMapping("/upload")
public String upload(MultipartFile picFile) throws IOException {
System.out.println("picFile = " + picFile);
//得到文件原始文件名 a.jpg
String fileName = picFile.getOriginalFilename();
//得到后缀名 从最后一个.出现的位置截取到最后:得到.jpg
String suffix = fileName.substring(fileName.lastIndexOf("."));
//得到唯一文件名 UUID.randomUUID()得到一个唯一标识符
fileName = UUID.randomUUID()+suffix;
System.out.println("文件名:"+fileName);
//准备保存图片的文件夹路径
String dirPath = "D:/Softa/danei/ideaBoot4-2testPath/files";
File dirFile = new File(dirPath);
//如果该文件夹不存在 则创建此文件夹
if (!dirFile.exists()){
dirFile.mkdirs();//创建文件夹
}
//得到文件的完整路径
String filePath = dirPath+"/"+fileName;
//把文件保存到此路径 异常抛出
picFile.transferTo(new File(filePath));
System.out.println("文件保存完成! 请去此路径检查文件是否存在 "+filePath);
return fileName;
}
/** 处理删除的请求 */
@RequestMapping("/remove")
public void remove(String name){
String dirPath = "D:/Softa/danei/ideaBoot4-2testPath/files";
String filePath = dirPath+"/"+name;
new File(filePath).delete();//删除文件
}
}
03.微博服务:boot5-1
(1)微博的—注册
一、微博
新建页面index、reg 新建类UserController、User
新建接口UserMapper
0.前提:
(1)创建工程boot5-1 打钩3个
(2)①从之前工程boot4-1中复制application.properties里面的连接数据库的信息,
复制完之后立即启动工程,测试是否创建成功,
如果工程启动不起来:检查报错是不是因为端口被占用, 如果不是,刷新maven 再次启动,
如果还是启动不了,删除工程重新创建新工程,直至启动成功。
②application.properties里:将spring.datasource.url的链接empdb改为【weibo】
③添加设置文件大小:
代码:
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.url=jdbc:mysql://localhost:3306/weibo?characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false
#设置文件上传大小的限制
spring.servlet.multipart.max-file-size=10MB
1.创建数据库和表user:
在数据库客户端进行操作:
create database weibo charset=utf8;
use weibo;
create table user(id int primary key auto_increment,username varchar(50),password varchar(50),nick varchar(50))charset=utf8;
show tables;
2.新建index首页页面 和 reg注册页面
3.在工程中添加本地需要导入的文件:在boot4-2中把js和css文件粘贴到static下
重新编译工程:点击Rebuild Project
4.注册功能:
(1)在reg:
①导入本地vue.js和axios.min.js文件
②下面引入Vue对象,上面div标签里的input标签和下面Vue里的data中进行双向绑定:【v-model】
③按钮后添加点击事件
④下面在methods里写一个点击事件的reg方法:【发出post注册请求】:【xxxx】
(2)src/main/java/cn/tedu/boot51下创建包:controller 下创建类:UserController
(3)src/main/java/cn/tedu/boot51下创建包:entity 下创建类:User
(4)测试:http://localhost:8080/
点击超链接注册
输入信息(admin/root/admin)——>点击注册,弹出框提示:注册成功
5.验证重复用户功能:
(1)①src/main/java/cn/tedu/boot51下创建包:mapper 下创建接口:UserMapper
②在UserController:添加注解【@Autowired(required = false) UserMapper mapper;】
继续在reg方法里进行页面中输入重复用户名的注册判断
③测试:http://localhost:8080/
点击超链接注册
输入信息(admin2/root/admin2)——>点击注册,弹出框提示:注册成功
输入信息(admin/root/admin)——>点击注册,弹出框提示:用户名已存在
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>微博首页</h1>
<div>
<a href="/reg.html">注册</a>
<a href="/login.html">登录</a>
</div>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>注册页面</h1>
<div>
<input type="text" v-model="user.username" placeholder="用户名">
<input type="text" v-model="user.password" placeholder="密码">
<input type="text" v-model="user.nick" placeholder="昵称">
<input type="button" value="注册" @click="reg()">
</div>
<script src="js/vue.js"></script>
<script src="js/axios.min.js"></script>
<script>
let v = new Vue({
el:"div",
data:{
user:{
username:"",
password:"",
nick:""
}
},
methods:{
reg(){
//发出post注册请求
axios.post("/reg",v.user).then(function (response) {
if (response.data==1){
alert("注册成功!");
location.href="/";
}else{
alert("用户名已存在!");
}
})
}
}
})
</script>
</body>
</html>
package cn.tedu.boot51.controller;
import cn.tedu.boot51.entity.User;
import cn.tedu.boot51.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
//此注解相当于在每一个方法上面添加ResponseBody注解
@RestController
public class UserController {
/*
此注解的作用是:
Spring框架结合Mybatis框架会自动将HeroMapper生成一个实现类和实现里面的方法,
而且会自动实例化对象,required = false告诉idea编译器此对象是非必要的,
因此"mapper"若报错的话需要这样写: @Autowired(required = false)
*/
@Autowired(required = false)
UserMapper mapper;
//@RequestBody注解作用, 当客户端发出post请求并且提交的是自定义对象时,
// 服务器端接收参数必须使用此注解 否则得不到传递过来的参数.
@RequestMapping("/reg")
public int reg(@RequestBody User user){
System.out.println("user = " + user);
User u = mapper.selectByUsername(user.getUsername());
if (u!=null){
return 2;//代表用户名已存在
}
mapper.insert(user);//注册
return 1;
}
}
package cn.tedu.boot51.entity;
public class User {
private Integer id;
private String username;
private String password;
private String nick;
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", nick='" + nick + '\'' +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getNick() {
return nick;
}
public void setNick(String nick) {
this.nick = nick;
}
}
package cn.tedu.boot51.mapper;
import cn.tedu.boot51.entity.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
@Mapper
public interface UserMapper {
/** 查询user用户的功能 */
/*
#{xxxx}:
会从下面方法的参数列表找到同名的变量,
若找不到同名变量则进入到对象里面查找同名的get方法
Mybatis框架会根据此方法声明生成具体的实现类实现此方法,方法内部就是jbdc代码
*/
@Select("select * from user where username=#{username}")
User selectByUsername(String username);
/** 插入user的功能 */
@Insert("insert into user values(null,#{username},#{password},#{nick})")
void insert(User user);
}
(2)微博的—登录
2、登录功能
新建页面login 改动的类UserController
(1)创建login页面
(2)在UserController:添加login方法控制的条件
(3)测试:http://localhost:8080/
点击超链接登录
输入信息(admin/root)——>点击登录,弹出框提示:登陆成功
输入信息(admin/123)——>点击登录,弹出框提示:密码错误
输入信息(admin3/123)——>点击登录,弹出框提示:用户名不存在
成功!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>登录页面</h1>
<div>
<input type="text" v-model="user.username" placeholder="用户名">
<input type="password" v-model="user.password" placeholder="密码">
<input type="button" value="登录" @click="login()">
</div>
<script src="js/vue.js"></script>
<script src="js/axios.min.js"></script>
<script>
let v = new Vue({
el:"div",
data:{
user:{username:"",password:""}
},
methods:{
login(){
axios.post("/login",v.user).then(function (response) {
if (response.data==1){
alert("登录成功!");
location.href="/";
}else if(response.data==2){
alert("用户名不存在!");
}else{
alert("密码错误!");
}
})
}
}
})
</script>
</body>
</html>
package cn.tedu.boot51.controller;
import cn.tedu.boot51.entity.User;
import cn.tedu.boot51.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
//此注解相当于在每一个方法上面添加ResponseBody注解
@RestController
public class UserController {
/*
此注解的作用是:
Spring框架结合Mybatis框架会自动将HeroMapper生成一个实现类和实现里面的方法,
而且会自动实例化对象,required = false告诉idea编译器此对象是非必要的,
因此"mapper"若报错的话需要这样写: @Autowired(required = false)
*/
@Autowired(required = false)
UserMapper mapper;
//@RequestBody注解作用, 当客户端发出post请求并且提交的是自定义对象时,
// 服务器端接收参数必须使用此注解 否则得不到传递过来的参数.
@RequestMapping("/reg")
public int reg(@RequestBody User user){
System.out.println("user = " + user);
User u = mapper.selectByUsername(user.getUsername());
if (u!=null){
return 2;//代表用户名已存在
}
mapper.insert(user);//注册
return 1;
}
@RequestMapping("/login")
public int login(@RequestBody User user){
System.out.println("user = " + user);
User u = mapper.selectByUsername(user.getUsername());
if (u!=null){
if (user.getPassword().equals(u.getPassword())){
return 1;//登录成功
}
return 3; //密码错误
}
return 2;//用户名不存在
}
}
(3)如何通过Session对象记住登录状态?
- ——如何通过登录后的页面不再显示注册登录,而是显示欢迎xx回来
1.在登录成功时把当前客户端登录的user用户对象保存到当前客户端所对应的Session对象里面,
2.每个客户端进入到首页index.html时会立即发请求获取当前客户端登录的用户对象,
服务器接收到请求后会从当前客户端所对应的Session对象里面获取曾经保存过的用户对象(前
提是登陆过),如果没有登录直接获取得到的是null返回给客户端,
此时客户端得到的是""空字符串, 客户端判断是否是空字符来表示是否登录过,
通过给isLogin赋值true或false来控制页面显示的内容
3、如何通过登录后的页面不再显示注册登录,而是显示欢迎xx回来
改动的页面index 改动的类UserController
(1)index里:导入本地文件,引入Vue对象后在data里写一个【是否登陆过:】
测试:http://localhost:8080/
正常会显示:【微博首页】的页面,显示【注册 登录两个超链接】
页面右键检查,
控制台输入:【v.islogin=true】回车——>回复true
控制台输入:【v.islogin=false】回车—>回复false
成功!
(2)在index里的【是否登陆过:】写一个假数据:【user:{nick:"刘德华"}】
(3)UserController:①login方法的参数列表添加session,
②在下面if里添加:【把当前登录用户的对象保存到会话对象中】
(4)在index:data下添加一个【created:function (){xxx}】方法
用来【发请求获取当前登录的用户对象】
(5)在UserController:写一个currentUser当前用户的方法
(6)测试:http://localhost:8080/
点击超链接 登录
输入信息(admin/root),
点击登录,弹出框提示:登录成功! 再点击弹出框的 确定 按钮,
此时页面刷新跳转到:
【微博首页 欢迎admin回来 两个超链接发布微博和登出】的页面!
————>成功!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>微博首页</h1>
<div>
<div v-if="isLogin"><!--如果登录了 显示里面内容-->
<p>欢迎{{user.nick}}回来!</p>
<a href="/insert.html">发布微博</a>
<a href="">登出</a>
</div>
<div v-else><!--如果没有登录 显示里面内容-->
<a href="/reg.html">注册</a>
<a href="/login.html">登录</a>
</div>
</div>
<script src="js/vue.js"></script>
<script src="js/axios.min.js"></script>
<script>
let v = new Vue({
el:"body>div",
data:{
isLogin:false,
user:{nick:"刘德华"}
},
created:function (){
//发请求获取当前登录的用户对象 currentUser当前的用户
axios.get("/currentUser").then(function (response){
v.user = response.data;
//如果当前客户端没有登录的话得到的是空字符串
// if(v.user==""){
// v.isLogin = false;
// }else{
// v.isLogin = true;
// }
//上边代码可以简化为下边这句三目的样式:
//如果是空字符串代表还没有登录 isLogin为false 如果不是空字符串代表登录过
v.isLogin =v.user==""?false:true;
})
}
})
</script>
</body>
</html>
package cn.tedu.boot51.controller;
import cn.tedu.boot51.entity.User;
import cn.tedu.boot51.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpSession;
//此注解相当于在每一个方法上面添加ResponseBody注解
@RestController
public class UserController {
/*
此注解的作用是:
Spring框架结合Mybatis框架会自动将UserMapper生成一个实现类和实现里面的方法,
而且会自动实例化对象,required = false告诉idea编译器此对象是非必要的,
因此"mapper"若报错的话需要这样写: @Autowired(required = false)
*/
@Autowired(required = false)
UserMapper mapper;
//@RequestBody注解作用, 当客户端发出post请求并且提交的是自定义对象时,
// 服务器端接收参数必须使用此注解 否则得不到传递过来的参数.
@RequestMapping("/reg")
public int reg(@RequestBody User user){
System.out.println("user = " + user);
User u = mapper.selectByUsername(user.getUsername());
if (u!=null){
return 2;//代表用户名已存在
}
mapper.insert(user);//注册
return 1;
}
/*
在参数列表里面声明session 即可得到当前客户端所对应的Session(Http会话)
注:什么是Session? 服务器为了保存用户状态而创建的一个特殊的对象。
当浏览器第一次访问服务器时,服务器创建一个session对象(该对象有一个唯一的id,
一般称之为sessionId),服务器会将sessionId以cookie的方式发送给浏览器。
当浏览器再次访问服务器时,会将sessionId发送过来,服务器依据sessionId就可以找
到对应的session对象。
*/
@RequestMapping("/login")
public int login(@RequestBody User user, HttpSession session){
System.out.println("user = " + user);
User u = mapper.selectByUsername(user.getUsername());
if (u!=null){
if (user.getPassword().equals(u.getPassword())){
//把当前登录用户的对象保存到会话对象中:
/*
user:代表用户输入的信息 包括:用户名和密码
u:代表从数据库查询到的信息 包括:id 用户名和密码,昵称
setAttribute():设置属性
*/
session.setAttribute("user",u);
return 1;//登录成功
}
return 3; //密码错误
}
return 2;//用户名不存在
}
@RequestMapping("/currentUser")
public User currentUser(HttpSession session){//currentUser:当前用户
//从会话对象中得到登录成功是保存的用户对象
User u = (User) session.getAttribute("user");
return u;
}
}
(重要+)会话管理:Cookie 和 Session
客户端和服务器之间进行数据传输遵循的是HTTP协议,
此协议属于无状态协议(一次请求对应一次响应,响应完之后链接就会断开)
服务器是无法跟踪客户端的请求
通过Cookie服务器可以给客户端添加一个标识,当客户端再次发出请求时会带着这个Cookie
这样服务器就能识别此客户端了, 但是由于Cookie是保存在客户端的存在被篡改的风险,
Session的出现解决了此问题
Cookie: 打孔式会员卡, 数据是保存在【浏览器内存中】
(1)只能保存字符串数据
(2)生命周期:默认数据当会话结束(浏览器关闭)数据会删除,
也可以设置自定义的保存时长,设置完之后数据会保存在磁盘中时间到了之后清除.
(3)应用场景: 需要长时间保存的数据,比如:记住用户名和密码
Session:相当于银行卡, 数据保存在【服务器内存中】(工程重新启动会清空所有Session)
(1)可以保存任意对象类型的数据,
(2)生命周期:默认数据只能保存半小时左右。
而且不建议修改保存时长,因为数据是保存在服务器的内存中的,
服务器只有一个所以不能占用太多的内存.
(3)应用场景: 保存登录状态,涉及敏感数据,因为数据保存在服务器会更安全
(4)微博的—登出
4、登出
改动的页面index 改动的类UserController
(1)在index中:
①将div标签里废掉登出的功能:将【<a href="">登出</a>】改为:
【<a href="javascript:void(0)" @click="logout()">登出</a>】
②在下边Vue对象里,data下写一个methods,包含louout退出登录的方法
(2)在UserController:
①写一个登出logout的方法,参数类型HttpSession session
②里面:【把登录成功时保存的user对象删除】
(3)测试:http://localhost:8080/
点击超链接 登录
输入信息(admin/root),
点击登录,弹出框提示:登录成功! 再点击弹出框的 确定 按钮,
此时页面刷新跳转到:【微博首页 欢迎admin回来 两个超链接发布微博和登出】的页面!
点击超链接 登出,跳转到登录之前的页面:【微博首页 两个超链接注册和登录】
成功!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>微博首页</h1>
<div>
<div v-if="isLogin"><!--如果登录了 显示里面内容-->
<p>欢迎{{user.nick}}回来!</p>
<a href="/insert.html">发布微博</a>
<a href="javascript:void(0)" @click="logout()">登出</a>
</div>
<div v-else><!--如果没有登录 显示里面内容-->
<a href="/reg.html">注册</a>
<a href="/login.html">登录</a>
</div>
</div>
<script src="js/vue.js"></script>
<script src="js/axios.min.js"></script>
<script>
let v = new Vue({
el:"body>div",
data:{
isLogin:false,
user:{nick:"刘德华"}
},
created:function (){
//发请求获取当前登录的用户对象 currentUser当前的用户
axios.get("/currentUser").then(function (response){
v.user = response.data;
//如果当前客户端没有登录的话得到的是空字符串
// if(v.user==""){
// v.isLogin = false;
// }else{
// v.isLogin = true;
// }
//上边代码可以简化为下边这句三目的样式:
//如果是空字符串代表还没有登录 isLogin为false 如果不是空字符串代表登录过
v.isLogin =v.user==""?false:true;
})
},
methods:{
logout(){
//发出退出登录的请求
axios.get("/logout").then(function (response){
location.reload();//刷新页面
})
}
}
})
</script>
</body>
</html>
package cn.tedu.boot51.controller;
import cn.tedu.boot51.entity.User;
import cn.tedu.boot51.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpSession;
//此注解相当于在每一个方法上面添加ResponseBody注解
@RestController
public class UserController {
/*
此注解的作用是:
Spring框架结合Mybatis框架会自动将HeroMapper生成一个实现类和实现里面的方法,
而且会自动实例化对象,required = false告诉idea编译器此对象是非必要的,
因此"mapper"若报错的话需要这样写: @Autowired(required = false)
*/
@Autowired(required = false)
UserMapper mapper;
//@RequestBody注解作用, 当客户端发出post请求并且提交的是自定义对象时,
// 服务器端接收参数必须使用此注解 否则得不到传递过来的参数.
@RequestMapping("/reg")
public int reg(@RequestBody User user){
System.out.println("user = " + user);
User u = mapper.selectByUsername(user.getUsername());
if (u!=null){
return 2;//代表用户名已存在
}
mapper.insert(user);//注册
return 1;
}
/*
在参数列表里面声明session 即可得到当前客户端所对应的Session(Http会话)
注:什么是Session? 服务器为了保存用户状态而创建的一个特殊的对象。
当浏览器第一次访问服务器时,服务器创建一个session对象(该对象有一个唯一的id,
一般称之为sessionId),服务器会将sessionId以cookie的方式发送给浏览器。
当浏览器再次访问服务器时,会将sessionId发送过来,服务器依据sessionId就可以找
到对应的session对象。
*/
@RequestMapping("/login")
public int login(@RequestBody User user, HttpSession session){
System.out.println("user = " + user);
User u = mapper.selectByUsername(user.getUsername());
if (u!=null){
if (user.getPassword().equals(u.getPassword())){
//把当前登录用户的对象保存到会话对象中:
/*
user:代表用户输入的信息 包括:用户名和密码
u:代表从数据库查询到的信息 包括:id 用户名和密码,昵称
setAttribute():设置属性
*/
session.setAttribute("user",u);
return 1;//登录成功
}
return 3; //密码错误
}
return 2;//用户名不存在
}
@RequestMapping("/currentUser")
public User currentUser(HttpSession session){//currentUser:当前用户
//从会话对象中得到登录成功是保存的用户对象
User u = (User) session.getAttribute("user");
return u;
}
@RequestMapping("/logout")
public void logout(HttpSession session){
//把登录成功时保存的user对象删除
session.removeAttribute("user");
}
}
(5)微博的—发布
5、发布
新建页面insert 新建类UploadController、WeiboController、Weibo
新建接口WeiboMapper
(1)新建insert发布微博页面:
①写一个div标签
②把boot4-2的upload页面复制到该insert页面中,
添加代码:
【<h1>发布微博页面</h1>】
div标签里添加两句:
【<input type="text" placeholder="说点什么...">】
【<input type="button" value="发布微博" @click="insert()">】
methods里添加一个:【insert()】方法
③测试:http://localhost:8080/
点击超链接 登录
输入信息(admin/root),
点击登录,弹出框提示:登录成功! 再点击弹出框的 确定 按钮,
此时页面刷新跳转到:【微博首页 欢迎admin回来 两个超链接发布微博和登出】的页面!
点击超链接 发布微博 后,
会跳转到:该页面:【发布微博页面 一个文本框 一个中间可以点击的加号 发布微博按钮】
成功!!!
(2)将boot4-2的UploadController复制到该工程的controller下,
重新启动工程,
测试:http://localhost:8080/
点击超链接 登录
输入信息(admin/root),
点击登录,弹出框提示:登录成功! 再点击弹出框的 确定 按钮,
此时页面刷新跳转到:【微博首页 欢迎admin回来 两个超链接发布微博和登出】的页面!
点击超链接 发布微博 后,
会跳转到:该页面:【发布微博页面 一个文本框 一个中间可以点击的加号 发布微博按钮】
点击加号上传一张图片,上传成功后,会在控制台看到该图片保存路径,
删除该浏览器页面中的图片,发现路径中的也被删除,
成功!
(3)页面源代码中的控制台拿到图片名:
①限制只能上传一张图片:
在insert页面:将【list-type】上添加【:limit="1"】即可!
②在insert中:Vue对象中:data中:添加:【weibo:{xxx}】
③将②中的weibo进行双向绑定:在上边input标签里添加:【v-model="weibo.content"】
④在上边:<el-upload>标签里添加:【:on-success="handleSuccess"】
⑤在下边methods里添加【处理成功:handleSuccess】方法
⑥测试:http://localhost:8080/
点击超链接 登录
输入信息(admin/root),
点击登录,弹出框提示:登录成功! 再点击弹出框的 确定 按钮,
此时页面刷新跳转到:【微博首页 欢迎admin回来 两个超链接发布微博和登出】的页面!
点击超链接 发布微博 后,
会跳转到:该页面:【发布微博页面 一个文本框 一个中间可以点击的加号 发布微博按钮】
点击加号上传一张图片,上传成功后,
页面右键检查,会发现html源代码的控制台会出现:
【文件上传完成,图片名=8a18344c-339b-4265-b546-d7811d2316d4.jpg】
成功!!!
(4)在insert中:
①insert方法中添加:【得到用户输入的微博文本内容和图片名 一起提交给服务器:】
测试:http://localhost:8080/
点击超链接 登录
输入信息(admin/root),
点击登录,弹出框提示:登录成功! 再点击弹出框的 确定 按钮,
此时页面刷新跳转到:【微博首页 欢迎admin回来 两个超链接发布微博和登出】的页面!
点击超链接 发布微博 后,
会跳转到:该页面:【发布微博页面 一个文本框 一个中间可以点击的加号 发布微博按钮】
点击加号上传一张图片,上传成功后,
若不在文本框输入文字,点击超链接发布微博,弹出框显示:【微博内容或图片不能为空!】
若文本输入文字后,点击超链接发布微博,弹出框显示:【添加完成!】
成功!!!
(5)创建WeiboController:添加insert方法
(6)创建Weibo实体类:添加6个成员变量,并生成get set toString方法
(7)在WeiboController:insert方法里添加具体步骤
(8)在insert中:
【axios.post("/insert",v.weibo).then(function (response) {xxx}】:中进行改动
(9)测试:http://localhost:8080/
点击超链接 登录
输入信息(admin/root),
点击登录,弹出框提示:登录成功! 再点击弹出框的 确定 按钮,
此时页面刷新跳转到:【微博首页 欢迎admin回来 两个超链接发布微博和登出】的页面!
点击超链接 发布微博 后,
会跳转到:该页面:【发布微博页面 一个文本框 一个中间可以点击的加号 发布微博按钮】
点击加号上传一张图片,上传成功后,
若文本输入文字后,点击超链接发布微博,弹出框显示:【添加完成!】
此时idea控制台显示:
【weibo = Weibo{id=null, content='你好呀',
url='edd4260f-86ab-4559-a3d5-3552a9f1ed65.jpg',
nick='admin', created=Mon May 16 17:37:59 GMT+08:00 2022,
userId=2}】
成功!!!
(10)发布微博与数据库建立连接
创建微博表:
数据库客户端输入:
use weibo;
create table weibo(id int primary key auto_increment,content varchar(100),url varchar(255),nick varchar(50),created timestamp,user_id int)charset=utf8;
show tables;
①创建WeiboMapper:添加insert方法:【void insert(Weibo weibo);】
②WeiboController:
最上边添加:【@Autowired WeiboMapper mapper;】
下边添加:【mapper.insert(weibo);】
测试:http://localhost:8080/
登录账号后,点击发布微博,
进入数据库客户端,输入:select * from weibo;
可以看到发布的微博!:
MariaDB [weibo]> select * from weibo;
+----+---------+----------------+-------+---------------------+---------+
| id | content | url | nick | created | user_id |
+----+---------+----------------+-------+---------------------+---------+
| 1 | 第一条 | ac0-xxx-535.jpg | admin | 2022-05-16 17:50:59 | 2 |
成功!!!
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<!-- import CSS -->
<link rel="stylesheet" href="css/eui.css">
</head>
<body>
<h1>发布微博页面</h1>
<div id="app">
<input type="text" v-model="weibo.content" placeholder="说点儿什么....">
<!--name代表上传文件时 文件的参数名
limit="1" 设置只能选择一张图片
-->
<el-upload
action="/upload"
name="picFile"
:limit="1"
list-type="picture-card"
:on-preview="handlePictureCardPreview"
:on-success="handleSuccess"
:on-remove="handleRemove">
<i class="el-icon-plus"></i>
</el-upload>
<el-dialog :visible.sync="dialogVisible">
<img width="100%" :src="dialogImageUrl" alt="">
</el-dialog>
<input type="button" value="发布微博" @click="insert()">
</div>
</body>
<!-- import Vue before Element -->
<script src="js/vue.js"></script>
<!-- import JavaScript -->
<script src="js/eui.js"></script>
<script src="js/axios.min.js"></script>
<script>
let v = new Vue({
el: '#app',
data: function() {
return {
dialogImageUrl: '',
dialogVisible: false,
weibo:{
content:"",
url:""
}
}
},
methods: {
handleRemove(file, fileList) {
console.log(file, fileList);
//当点击删除图片时方法会执行
//file代表要删除的文件
//file.response代表文件上传成功时 服务器响应的数据(文件名)
console.log("文件名="+file.response);
//http://localhost:8080/remove?name=xxx.jpg
axios.get("/remove?name="+file.response).then(function (response) {
console.log("服务器图片已经删除")
})
},
handlePictureCardPreview(file) {
this.dialogImageUrl = file.url;
this.dialogVisible = true;
},
insert(){
//得到用户输入的微博文本内容和图片名 一起提交给服务器
if (v.weibo.content.trim()==""||v.weibo.url==""){
alert("微博内容或图片不能为空!")
return;
}
axios.post("/insert",v.weibo).then(function (response) {
if (response.data==1){
alert("添加完成!");
location.href="/"; //回到首页
}else{
alert("请先登录!");
location.href="/login.html";//显示登录页面
}
})
},
handleSuccess(response,file,fileList){
//response=file.response
console.log("文件上传完成, 图片名="+response);
v.weibo.url = response;
}
}
})
</script>
</html>
package cn.tedu.boot51.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
@RestController
public class UploadController {
@RequestMapping("/upload")
public String upload(MultipartFile picFile) throws IOException {
System.out.println("picFile = " + picFile);
//得到文件原始文件名 a.jpg
String fileName = picFile.getOriginalFilename();
//得到后缀名 从最后一个.出现的位置截取到最后
String suffix = fileName.substring(fileName.lastIndexOf("."));
//得到唯一文件名 UUID.randomUUID()得到一个唯一标识符
fileName = UUID.randomUUID()+suffix;
System.out.println("文件名:"+fileName);
//准备保存图片的文件夹路径
String dirPath = "D:/Softa/danei/ideaBoot4-2testPath/files";
File dirFile = new File(dirPath);
//如果该文件夹不存在 则创建此文件夹
if (!dirFile.exists()){
dirFile.mkdirs();//创建文件夹
}
//得到文件的完整路径
String filePath = dirPath+"/"+fileName;
//把文件保存到此路径 异常抛出
picFile.transferTo(new File(filePath));
System.out.println("文件保存完成! 请去此路径检查文件是否存在 "+filePath);
return fileName;
}
/** 处理删除的请求 */
@RequestMapping("/remove")
public void remove(String name){
String dirPath = "D:/Softa/danei/ideaBoot4-2testPath/files";
String filePath = dirPath+"/"+name;
new File(filePath).delete();//删除文件
}
}
package cn.tedu.boot51.controller;
import cn.tedu.boot51.entity.User;
import cn.tedu.boot51.entity.Weibo;
import cn.tedu.boot51.mapper.WeiboMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpSession;
import java.util.Date;
@RestController
public class WeiboController {
@Autowired
WeiboMapper mapper;
@RequestMapping("/insert")
public int insert(@RequestBody Weibo weibo, HttpSession session){
//得到当前登录的用户对象
User u = (User) session.getAttribute("user");
if (u==null){ return 2;//代表未登录
}
//new Date()得到当前的系统时间
weibo.setCreated(new Date());
//把当前登录的用户信息 添加到weibo对象中
weibo.setUserId(u.getId());
weibo.setNick(u.getNick());
System.out.println("weibo = " + weibo);
mapper.insert(weibo);
return 1;//代表发布微博成功!
}
}
package cn.tedu.boot51.entity;
import java.util.Date;
public class Weibo {
private Integer id;
private String content;
private String url;
private String nick;//发布微博的作者名字
private Date created; //发布微博的日期
private Integer userId; //发布微博作者的用户id
@Override
public String toString() {
return "Weibo{" +
"id=" + id +
", content='" + content + '\'' +
", url='" + url + '\'' +
", nick='" + nick + '\'' +
", created=" + created +
", userId=" + userId +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getNick() {
return nick;
}
public void setNick(String nick) {
this.nick = nick;
}
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
}
package cn.tedu.boot51.mapper;
import cn.tedu.boot51.entity.Weibo;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface WeiboMapper {
@Insert("insert into weibo values" +
"(null,#{content},#{url},#{nick},#{created},#{userId})")
void insert(Weibo weibo);
}
(6)查询所有的微博
6、在登录网址首页显示发布的微博的文本内容
改动的页面index 改动的类WeiboController
改动的接口WeiboMapper
(1)在index:
①created:function方法里下面添加:【发请求获取所有的微博消息】
②在Vue对象里的【data:】里添加:【arr:[]】
③在上面【div标签:如果没有登录 显示里面内容】的下边加一个:
【<!--显示所有微博数据-->:div标签:遍历微博的数组信息:】
④在WeiboController:加一个【select方法,并使用mapper调用该方法,此时select会爆红】
⑤在WeiboMapper:【查询所有微博数据并且降序排序】:
⑥测试:http://localhost:8080/
页面直接就会显示:
【微博首页】的页面、
两个超链接【注册 登录】、
发送微博的【文本内容:第一条 第二条 ...】
成功!!!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>微博首页</h1>
<div>
<div v-if="isLogin"><!--如果登录了 显示里面内容-->
<p>欢迎{{user.nick}}回来!</p>
<a href="/insert.html">发布微博</a>
<a href="javascript:void(0)" @click="logout()">登出</a>
</div>
<div v-else><!--如果没有登录 显示里面内容-->
<a href="/reg.html">注册</a>
<a href="/login.html">登录</a>
</div>
<!--显示所有微博数据-->
<hr>
<div v-for="weibo in arr"><!-- 遍历微博的数组信息 -->
<h3>{{weibo.content}}</h3>
</div>
</div>
<script src="js/vue.js"></script>
<script src="js/axios.min.js"></script>
<script>
let v = new Vue({
el:"body>div",
data:{
isLogin:false,
user:{nick:"刘德华"},
arr:[]
},
created:function (){
//发请求获取当前登录的用户对象 currentUser当前的用户
axios.get("/currentUser").then(function (response){
v.user = response.data;
//如果当前客户端没有登录的话得到的是空字符串
// if(v.user==""){
// v.isLogin = false;
// }else{
// v.isLogin = true;
// }
//上边代码可以简化为下边这句三目的样式:
//如果是空字符串代表还没有登录 isLogin为false 如果不是空字符串代表登录过
v.isLogin =v.user==""?false:true;
})
//发请求获取所有的微博消息
axios.get("/select").then(function (response) {
v.arr = response.data;
})
},
methods:{
logout(){
//发出退出登录的请求
axios.get("/logout").then(function (response){
location.reload();//刷新页面
})
}
}
})
</script>
</body>
</html>
package cn.tedu.boot51.controller;
import cn.tedu.boot51.entity.User;
import cn.tedu.boot51.entity.Weibo;
import cn.tedu.boot51.mapper.WeiboMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpSession;
import java.util.Date;
import java.util.List;
//此注解相当于在每一个方法上面添加ResponseBody注解
@RestController
public class WeiboController {
/*
此注解的作用是:
Spring框架结合Mybatis框架会自动将HeroMapper生成一个实现类和实现里面的方法,
而且会自动实例化对象,required = false告诉idea编译器此对象是非必要的,
因此"mapper"若报错的话需要这样写: @Autowired(required = false)
*/
@Autowired
WeiboMapper mapper;
@RequestMapping("/insert")
public int insert(@RequestBody Weibo weibo, HttpSession session){
//得到当前登录的用户对象
User u = (User) session.getAttribute("user");
if (u==null){ return 2;//代表未登录
}
//new Date()得到当前的系统时间
weibo.setCreated(new Date());
//把当前登录的用户信息 添加到weibo对象中
weibo.setUserId(u.getId());
weibo.setNick(u.getNick());
System.out.println("weibo = " + weibo);
mapper.insert(weibo);
return 1;//代表发布微博成功!
}
@RequestMapping("/select")
public List<Weibo> select(){
return mapper.select();
}
}
package cn.tedu.boot51.mapper;
import cn.tedu.boot51.entity.Weibo;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface WeiboMapper {
/** 插入微博的信息 */
@Insert("insert into weibo values" +
"(null,#{content},#{url},#{nick},#{created},#{userId})")
void insert(Weibo weibo);
/** 查询所有微博数据并且降序排序:*/
@Select("select id,content,url,nick from weibo order by created desc")
List<Weibo> select();
}
(7)页面直接显示发布微博的文本和图片:
7、8080链接直接显示发布微博的文本和图片:
改动:application.properties
改动的页面:index
(1)在浏览器中直接访问发送微博的图片名即可在页面正常显示:
在application.properties里面填写配置静态资源文件夹的信息
①在application.properties:【#配置静态资源文件夹 默认是static文件夹】:
【spring.web.resources.static-locations=file:D:/Softa/danei/ideaBoot4-2testPath/files,classpath:static】
②在上边指定路径中放一张图片,命名为hh.jpg图片,
然后通过浏览器访问:http://localhost:8080/hh.jpg
此时页面中可以正常显示该图片!
(2)登录8080链接可以直接显示发布微博的信息,并可以正常显示发布的图片:
在index:【div标签:显示所有微博:添加:<img :src="weibo.url" width="100">】
测试:http://localhost:8080
发布微博:上传图片,输入文本信息,后:
会在8080链接页面正常显示发布微博的文本和图片!
成功!
(3)在index:【div标签:显示所有微博数据:h3标签改为】:
【<h3>{{weibo.nick}}说:{{weibo.content}}</h3>】
测试:http://localhost:8080.
该页面会显示:
【微博首页】的页面、
【注册 登录】两个超链接、
【admin说:第一条微博 下面有一张发布的图片】
【admin说:第二条微博 下面有一张发布的图片】
成功!!!
可以在数据库中删除掉微博信息,用语句:
【delete from weibo where id=2;】:可以把id为2的第二条数据删掉!
《application.properties》:
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.url=jdbc:mysql://localhost:3306/weibo?characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false
#设置文件上传大小的限制
spring.servlet.multipart.max-file-size=10MB
#配置静态资源文件夹 默认是static文件夹
spring.web.resources.static-locations=file:D:/Softa/danei/ideaBoot4-2testPath/files,classpath:static
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>微博首页</h1>
<div>
<div v-if="isLogin"><!--如果登录了 显示里面内容-->
<p>欢迎{{user.nick}}回来!</p>
<a href="/insert.html">发布微博</a>
<a href="javascript:void(0)" @click="logout()">登出</a>
</div>
<div v-else><!--如果没有登录 显示里面内容-->
<a href="/reg.html">注册</a>
<a href="/login.html">登录</a>
</div>
<!--显示所有微博数据-->
<hr>
<div v-for="weibo in arr"><!-- 遍历微博的数组信息 -->
<h3>{{weibo.nick}}说:{{weibo.content}}</h3>
<img :src="weibo.url" width="100">
</div>
</div>
<script src="js/vue.js"></script>
<script src="js/axios.min.js"></script>
<script>
let v = new Vue({
el:"body>div",
data:{
isLogin:false,
user:{nick:"刘德华"},
arr:[]
},
created:function (){
//发请求获取当前登录的用户对象 currentUser当前的用户
axios.get("/currentUser").then(function (response){
v.user = response.data;
//如果当前客户端没有登录的话得到的是空字符串
// if(v.user==""){
// v.isLogin = false;
// }else{
// v.isLogin = true;
// }
//上边代码可以简化为下边这句三目的样式:
//如果是空字符串代表还没有登录 isLogin为false 如果不是空字符串代表登录过
v.isLogin =v.user==""?false:true;
})
//发请求获取所有的微博消息
axios.get("/select").then(function (response) {
v.arr = response.data;
})
},
methods:{
logout(){
//发出退出登录的请求
axios.get("/logout").then(function (response){
location.reload();//刷新页面
})
}
}
})
</script>
</body>
</html>
(7)①上传图片2个注意事项:
①在application.properties里面填写最大上传文件大小的配置信息
②在application.properties里面填写配置静态资源文件夹的信息
04.酷鲨商城:Coolshark工程
(1)登录功能
创建工程时需要勾选3个内容分别是:
- a.Web->Spring Web
- b.SQL-> Mybatis Framework
- c.SQL-> MySQL Driver
0、前提:————建库建表
(1)创建coolshark项目, 打3个勾
(2)从上一个工程中boot5-1复制application.propertise 里面所有内容到新工程,
然后停掉之前工程 运行新工程测试是否能够正常启动(如果不能启动 删除工程重新创)
(3)把Web工程四个前端页面:【admin\detail\index\login .html】
和3个文件夹:【css/imgs/js(axios.min.js eui.js vue.js)】
复制添加到工程中(也可以从boot5-1项目中复制),
添加完之后Rebuild工程 , 然后运行工程访问首页检查是否正常显示
(4)application.propertise:修改application.propertise里面数据库的名字为cs:
【spring.datasource.url=jdbc:mysql://localhost:3306/cs?char.........】
(5)建库(cs)建表(user):
在数据库客户端:
create database cs charset=utf8;
show databases;
use cs;
create table user(id int primary key auto_increment,username varchar(50),password varchar(50),nick varchar(50))charset=utf8;
insert into user values(null,'admin','123456','管理员');
1、登录功能:
改动页面login 新建类UserController、User
新建接口UserMapper
(1)login:修改登录页面 给按钮添加点击事件, 点击时向/login发出请求
①引入axios.min.js文件
②45行添加登录事件:【@click="login()"】
:<el-button style="position: relative;right: 35px" type="primary" @click="login()">登录</el-button>
③下边Vue对象:data下:【写一个methods:里边写login方法】
(2)创建UserController 添加login方法处理/
①src/main/java/cn/tedu/coolshark/下创建包:controller 下创建类:UserController
(3)创建User实体类
①src/main/java/cn/tedu/coolshark/下创建包:entity 下创建类:User
添加四个成员变量,并调用get、set、toString方法
(4)创建UserMapper接口 添加 查询用户名的功能方法
①src/main/java/cn/tedu/coolshark/下创建包:mapper 下创建接口:UserMapper
(5)测试:http://localhost:8080/login.html
输入用户名admin 密码123456——>则直接跳转到酷鲨商城页面
输入用户名admin 密码123—————>弹出框:密码错误
输入用户名root 密码123—————>弹出框:用户名不存在
成功!!!
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<!-- import CSS -->
<link rel="stylesheet" href="css/eui.css">
<style>
body{
margin: 0;
background-image: url("imgs/bg.jpg");
background-size: cover;/*cover是专门用来设置全屏背景的*/
text-align: center;
}
h1{
font-size: 72px;
color: rgb(0,150,215);
margin-bottom: 0;
}
img{
width: 100px;
}
h2{
font-size: 32px;
color: #0095d7;
margin:0;
}
</style>
</head>
<body>
<div id="app">
<h1>欢迎来到酷鲨商城</h1>
<img src="imgs/shark.png" alt="">
<h2>CoolSharkMall</h2>
<el-card style="width: 600px;height: 300px;
margin: 0 auto;background-color: rgba(255,255,255,0.3)">
<el-form style="width: 400px;margin: 30px auto" label-width="60px" >
<el-form-item label="用户名">
<el-input type="text" v-model="user.username" placeholder="请输入用户名"></el-input>
</el-form-item>
<el-form-item label="密码">
<el-input type="password" v-model="user.password" placeholder="请输入密码"></el-input>
</el-form-item>
<el-form-item>
<el-button style="position: relative;right: 35px" type="primary" @click="login()">登录</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</body>
<!-- import Vue before Element -->
<script src="js/vue.js"></script>
<!-- import JavaScript -->
<script src="js/eui.js"></script>
<script src="js/axios.min.js"></script>
<script>
let v = new Vue({
el: '#app',
data: function() {
return {
user:{
username:"",
password:""
}
}
},
methods:{
login(){
axios.post("/login",v.user).then(function (response) {
if (response.data==1){
location.href="/";
}else if (response.data==2){
v.$message.error("用户名不存在!");
}else{
v.$message.error("密码错误!");
}
})
}
}
})
</script>
</html>
package cn.tedu.coolshark.controller;
import cn.tedu.coolshark.entity.User;
import cn.tedu.coolshark.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
//此注解相当于在每一个方法上面添加ResponseBody注解
@RestController
public class UserController {
/*
此注解的作用是:
Spring框架结合Mybatis框架会自动将HeroMapper生成一个实现类和实现里面的方法,
而且会自动实例化对象,required = false告诉idea编译器此对象是非必要的,
因此"mapper"若报错的话需要这样写: @Autowired(required = false)
*/
@Autowired
UserMapper mapper;
/** 处理请求 */
@RequestMapping("/login")
public int login(@RequestBody User user){
User u = mapper.selectByUsername(user.getUsername());
if(u!=null){
if(u.getPassword().equals(user.getPassword())){
return 1;//登录成功
}
return 3;//密码错误
}
return 2;//用户名不存在
}
}
package cn.tedu.coolshark.entity;
public class User {
private Integer id;
private String username;
private String password;
private String nick;
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", nick='" + nick + '\'' +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getNick() {
return nick;
}
public void setNick(String nick) {
this.nick = nick;
}
}
package cn.tedu.coolshark.mapper;
import cn.tedu.coolshark.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
@Mapper
public interface UserMapper {
/** 查询用户名的功能 */
@Select("select * from user where username=#{username}")
User selectByUsername(String username);
}
(2)首页分类展示步骤
2、首页分类展示步骤
新建类Category、CategoryController
新建接口CategoryMapper
改动页面index
01.创建表和准备数据:
数据库中操作:
create table category(id int primary key auto_increment,name varchar(50))charset=utf8;
insert into category values(null,"男装"),(null,"女装"),(null,"医药"),(null,"美食"),(null,"百货"),(null,"数码")
show tables;
02.
(1)创建Category实体类
①src/main/java/cn/tedu/coolshark/下创建包:entity 下创建类:Category
②有id和name有两个成员变量属性,生成get和set和toString方法
(2)创建CategoryMapper接口,里面提供insert和select两个方法
①src/main/java/cn/tedu/coolshark/下创建包:mapper 下创建接口:CategoryMapper
②里面提供insert和select两个方法
(3)创建CategoryController控制类
①src/main/java/cn/tedu/coolshark/controller 下创建类:CategoryController
②处理select请求查询并写List<Category>类型的select方法
(4)在首页index.html页面中:
①引入axios.min.js文件
②创建created方法,
③并向/category/select发出请求获取所有分类信息(因为不止一个表,所以写成:"/category/select")
created:function (){
//发请求获取所有分类信息
axios.get("/category/select").then(function (response){
v.categoryArr = response.data;
})
}
④并在上边data里中声明 categoryArr数组:【categoryArr:[],】
⑤【第56行:active-text-color="#fff">】和【下边:<!--搜索框开始-->】中的内容删除,
并添加一句:【<el-menu-item v-for="c in categoryArr" :index="c.id">{{c.name}}</el-menu-item>】
测试:http://localhost:8080
登录此网址后,发现最上边蓝色导航条部分变为了:【男装、女装、医药、美食、百货、数码】
在数据库中进行查询: select * from category;
查询后也会出现:【男装、女装、医药、美食、百货、数码】,
发现此时和数据库的内容正确建立及连接了!
(5)在index:添加methods在控制台显示下标
①Vue对象最下边加一个methods来对应上边57行:【<el-menu-item v-for="c in categoryArr" :index="c.id">{{c.name}}</el-menu-item>】
的【:index="c.id"】,
测试:http://localhost:8080
登录此网址后,右键检查,点击控制台后,
点击页面中的蓝色到航条,会发现html源码的控制台会显示:
6 (index):180
4 (index):180
3 (index):180
2 (index):180
1 (index):180
发现控制台会出现下标! 成功!!!
②右键检查时:发现有很多提示错误,但是不会影响我们实现的功能,
改正:
57行:【c.id是数字下标,需要转换为字符串,所以c.id后加toString()】:变为:【:index="(c.id).toString()"】
71、104行:gutter前边加冒号:【:gutter】
72行:span前边加冒号:【:span】
测试:http://localhost:8080
登录此网址后,右键检查,点击控制台后,
点击页面中的蓝色导航条,会发现html源码的控制台无报错显示!
成功!
package cn.tedu.coolshark.entity;
public class Category {
private Integer id;
private String name;
@Override
public String toString() {
return "Category{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package cn.tedu.coolshark.controller;
import cn.tedu.coolshark.entity.Category;
import cn.tedu.coolshark.mapper.CategoryMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
//此注解相当于在每一个方法上面添加ResponseBody注解
@RestController
public class CategoryController {
/*
此注解的作用是:
Spring框架结合Mybatis框架会自动将HeroMapper生成一个实现类和实现里面的方法,
而且会自动实例化对象,required = false告诉idea编译器此对象是非必要的,
因此"mapper"若报错的话需要这样写: @Autowired(required = false)
*/
@Autowired
CategoryMapper mapper;
/*
因为现在数据库里除了user表还有category表,所以我们需要单独指定查询的表格
*/
@RequestMapping("/category/select")
public List<Category> select(){
return mapper.select();
}
}
package cn.tedu.coolshark.mapper;
import cn.tedu.coolshark.entity.Category;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface CategoryMapper {
/** 添加分类表格的类别name */
@Insert("insert into category values(null,#{name})")
void insert(Category category);
/** 查询数据库中category表格中的类别 */
@Select("select * from category")
List<Category> select();
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<!-- import CSS -->
<link rel="stylesheet" href="css/eui.css">
<style>
body{
font: 18px "Microsoft YaHei UI";
margin: 0;
}
header a{
text-decoration: none;
color: #6c6c6c;
}
header a:hover{
color: #0aa1ed;
}
/*去掉自带的内边距*/
.el-table .el-table__cell{
padding: 0;
}
.p_img:hover{
position: relative;
bottom: 5px;
/*元素的阴影:x偏移值 y偏移值 浓度 范围 颜色*/
box-shadow: 0 0 10px 5px #333;
}
</style>
</head>
<body>
<div id="app">
<el-container>
<el-header style="padding: 0;height: 150px">
<div style="width: 1200px;margin: 0 auto">
<img src="imgs/logo.png"
style="width: 300px;vertical-align: middle" alt="">
<span>
<a href="">首页</a><el-divider direction="vertical"></el-divider>
<a href="">热点咨询</a><el-divider direction="vertical"></el-divider>
<a href="">商家入驻</a><el-divider direction="vertical"></el-divider>
<a href="">社会招聘</a><el-divider direction="vertical"></el-divider>
<a href="">校园招聘</a><el-divider direction="vertical"></el-divider>
<a href="">帮助中心</a>
</span>
</div>
<!--蓝色导航条开始-->
<div style="width: 100%;height: 60px;background-color: #82c8ec">
<el-menu style="width: 1200px;margin: 0 auto"
default-active="1"
class="el-menu-demo"
mode="horizontal"
@select="handleSelect"
background-color="#82c8ec"
text-color="#fff"
active-text-color="#fff">
<el-menu-item v-for="c in categoryArr" :index="(c.id).toString()">{{c.name}}</el-menu-item>
<!--搜索框开始-->
<div style="float: right;margin-top: 15px">
<el-input size="mini" placeholder="请输入关键字搜索"></el-input>
<el-button style="position: absolute;
background-color: rgba(0,0,0,0);border: 0;right: 0;top: 11px"
icon="el-icon-search"></el-button>
</div>
<!--搜索框结束-->
</el-menu>
</div>
<!--蓝色导航条结束-->
</el-header>
<el-main style="width: 1200px;margin: 0 auto">
<el-row :gutter="20">
<el-col :span="18">
<!--走马灯开始-->
<el-carousel height="300px">
<el-carousel-item v-for="b in bannerArr">
<img :src="b.url" style="width: 100%;height: 100%" alt="">
</el-carousel-item>
</el-carousel>
<!--走马灯结束-->
</el-col>
<el-col :span="6">
<!--排行榜开始-->
<el-card style="height: 300px">
<h3><i style="font-weight: bold"
class="el-icon-trophy"></i> 销量最高</h3>
<el-divider></el-divider>
<el-table :data="topArr" style="width: 500px">
<el-table-column label="排名" type="index" width="50px"></el-table-column>
<el-table-column label="商品名">
<!--scope.row代表topArr数组中遍历的对象-->
<template slot-scope="scope">
<a href="" style="text-decoration: none;color: #333">{{scope.row.title}}</a>
</template>
</el-table-column>
<el-table-column label="销量" prop="saleCount"></el-table-column>
</el-table>
</el-card>
<!--排行榜结束-->
</el-col>
</el-row>
<!--商品列表开始-->
<el-row :gutter="20">
<el-col :span="6" v-for="p in productArr">
<el-card>
<div>
<img class="p_img" :src="p.url" width="100%" alt="">
</div>
<div>
<p style="font-size: 15px;margin-top: 0;height: 38px">{{p.title}}</p>
<div style="color: #6c6c6c">
<span>¥{{p.price}}</span>
<span style="font-size: 12px;
text-decoration: line-through">{{p.oldPrice}}</span>
<span style="float: right">销量:{{p.saleCount}}件</span>
</div>
</div>
</el-card>
</el-col>
</el-row>
<!--商品列表结束-->
</el-main>
<el-footer style="padding: 0">
<div style="background-image: url('imgs/wave.png');
height: 95px;margin-bottom: -30px"></div>
<div style="background-color: #3f3f3f;height: 100px;
font-size: 14px;color: #b1b1b1;
text-align: center;padding: 30px">
<p>Copyright © 北京达内金桥科技有限公司版权所有 京ICP备12003709号-3 京公网安备 11010802029572号</p>
<p>涵盖20余门课程体系,致力于打造权威的IT职业教育学习平台</p>
<p>达内在线WWW.TMOOC.CN 专注于IT职业技能培训</p>
</div>
</el-footer>
</el-container>
</div>
</body>
<!-- import Vue before Element -->
<script src="js/vue.js"></script>
<!-- import JavaScript -->
<script src="js/eui.js"></script>
<script src="js/axios.min.js"></script>
<script>
let v = new Vue({
el: '#app',
data: function() {
return {
categoryArr:[],
bannerArr:[{url:"imgs/b1.jpg"},
{url:"imgs/b2.jpg"},
{url:"imgs/b3.jpg"}],
topArr:[{title:"小米手机",saleCount:5000},
{title:"华为手表",saleCount:4000},
{title:"毛巾",saleCount:3000},
{title:"双飞燕鼠标",saleCount:2000},
{title:"耐克篮球",saleCount:1000},
{title:"阿迪袜子",saleCount:500}],
productArr:[{title:"森马牛仔裤女宽松慢跑裤运动风2022春季新款显瘦束脚长裤复古",price:233,oldPrice:598,url:"imgs/a.jpg",saleCount:2342},
{title:"茵曼马甲连衣裙两件套春季新款娃娃领色织格长袖背心裙套装",price:233,oldPrice:598,url:"imgs/b.jpg",saleCount:2342},
{title:"雪中飞墨绿色短袖t恤女夏2022新款纯棉半袖打底体恤夏季上衣潮ins",price:233,oldPrice:598,url:"imgs/c.jpg",saleCount:2342},
{title:"【佟丽娅同款】鸭鸭明星同款羽绒服2021年冬季新款时尚连帽外套冬",price:233,oldPrice:598,url:"imgs/d.jpg",saleCount:2342},
{title:"BEASTER小恶魔鬼脸明星同款夹克毛绒保暖加厚字母印花宽松外套ins",price:233,oldPrice:598,url:"imgs/e.jpg",saleCount:2342},
{title:"香影毛呢外套女中长款2021年冬季新款气质韩版娃娃领紫色呢子大衣",price:233,oldPrice:598,url:"imgs/f.jpg",saleCount:2342},
{title:"SEMIR森马商场同款打底针织毛衣纯色高领新品显瘦",price:233,oldPrice:598,url:"imgs/g.jpg",saleCount:2342},
{title:"美特斯邦威女MTEE 贺岁系列中长款风衣736598",price:233,oldPrice:598,url:"imgs/h.jpg",saleCount:2342},
{title:"imone2021秋款黑色小西装外套女韩版学生宽松学院风外套jk外套",price:233,oldPrice:598,url:"imgs/i.jpg",saleCount:2342},
{title:"BEASTER 小恶魔明星同款保暖长袖街头潮流连帽卫衣情侣上衣",price:233,oldPrice:598,url:"imgs/j.jpg",saleCount:2342},
{title:"憨厚皇后100%绵羊皮2021秋海宁真皮皮衣女长款修身绵羊皮风衣外",price:233,oldPrice:598,url:"imgs/k.jpg",saleCount:2342},
{title:"美特斯邦威高腰牛仔裤女宽松小脚新款春秋彩色潮流女士牛仔",price:233,oldPrice:598,url:"imgs/a.jpg",saleCount:2342}]
}
},
created:function (){
//发请求获取所有分类信息
axios.get("/category/select").then(function (response){
v.categoryArr = response.data;
})
},
methods:{
handleSelect(index){
console.log(index);
}
}
})
</script>
</html>
(3)创建轮播图展示步骤:
3、创建轮播图展示步骤:
新建类Banner、BannerController
新建接口BannerMapper
改动页面index
01.创建保存轮播图的表和轮播图数据:
数据库客户端:
create table banner(id int primary key auto_increment,url varchar(255))charset=utf8;
insert into banner values(null,'/imgs/b1.jpg'),(null,'/imgs/b2.jpg'),(null,'/imgs/b3.jpg'),(null,'/imgs/b4.jpg');
补充:删除id为1的数据:delete from banner where id=1;
02.
(1)创建Banner实体类
①src/main/java/cn/tedu/coolshark/entity 下创建类:Banner
②有id和name有两个成员变量属性,生成get和set和toString方法
(2)创建BannerMapper接口,里面提供select方法
①src/main/java/cn/tedu/coolshark/mapper 下创建接口:BannerMapper
②里面提供select方法
【@Select("select * from banner")
List<Banner> select(Banner banner);】
(3)创建BannerController控制类
①src/main/java/cn/tedu/coolshark/controller 下创建类:BannerController
②添加select方法处理/banner/select请求
(4)在index:
【created方法里面:向/banner/select发出请求:把得到的数据赋值给vue里面的bannerArr数组】
代码:
axios.get("/banner/select").then(function (response) {
v.bannerArr = response.data;
})
(5)测试:http://localhost:8080
进入该网址后可以看到蓝色导航条下边有四张轮播图可以正常显示!
成功!
package cn.tedu.coolshark.entity;
public class Banner {
private Integer id;
private String url;
@Override
public String toString() {
return "Banner{" +
"id=" + id +
", url='" + url + '\'' +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
package cn.tedu.coolshark.controller;
import cn.tedu.coolshark.entity.Banner;
import cn.tedu.coolshark.mapper.BannerMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
//此注解相当于在每一个方法上面添加ResponseBody注解
@RestController
public class BannerController {
/*
此注解的作用是:
Spring框架结合Mybatis框架会自动将HeroMapper生成一个实现类和实现里面的方法,
而且会自动实例化对象,required = false告诉idea编译器此对象是非必要的,
因此"mapper"若报错的话需要这样写: @Autowired(required = false)
*/
@Autowired
BannerMapper mapper;
@RequestMapping("/banner/select")
public List<Banner> select(){
return mapper.select();
}
}
package cn.tedu.coolshark.mapper;
import cn.tedu.coolshark.entity.Banner;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface BannerMapper {
@Select("select * from banner")
List<Banner> select();
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<!-- import CSS -->
<link rel="stylesheet" href="css/eui.css">
<style>
body{
font: 18px "Microsoft YaHei UI";
margin: 0;
}
header a{
text-decoration: none;
color: #6c6c6c;
}
header a:hover{
color: #0aa1ed;
}
/*去掉自带的内边距*/
.el-table .el-table__cell{
padding: 0;
}
.p_img:hover{
position: relative;
bottom: 5px;
/*元素的阴影:x偏移值 y偏移值 浓度 范围 颜色*/
box-shadow: 0 0 10px 5px #333;
}
</style>
</head>
<body>
<div id="app">
<el-container>
<el-header style="padding: 0;height: 150px">
<div style="width: 1200px;margin: 0 auto">
<img src="imgs/logo.png"
style="width: 300px;vertical-align: middle" alt="">
<span>
<a href="">首页</a><el-divider direction="vertical"></el-divider>
<a href="">热点咨询</a><el-divider direction="vertical"></el-divider>
<a href="">商家入驻</a><el-divider direction="vertical"></el-divider>
<a href="">社会招聘</a><el-divider direction="vertical"></el-divider>
<a href="">校园招聘</a><el-divider direction="vertical"></el-divider>
<a href="">帮助中心</a>
</span>
</div>
<!--蓝色导航条开始-->
<div style="width: 100%;height: 60px;background-color: #82c8ec">
<el-menu style="width: 1200px;margin: 0 auto"
default-active="1"
class="el-menu-demo"
mode="horizontal"
@select="handleSelect"
background-color="#82c8ec"
text-color="#fff"
active-text-color="#fff">
<el-menu-item v-for="c in categoryArr" :index="c.id+''">{{c.name}}</el-menu-item>
<!--搜索框开始-->
<div style="float: right;margin-top: 15px">
<el-input size="mini" placeholder="请输入关键字搜索"></el-input>
<el-button style="position: absolute;
background-color: rgba(0,0,0,0);border: 0;right: 0;top: 11px"
icon="el-icon-search"></el-button>
</div>
<!--搜索框结束-->
</el-menu>
</div>
<!--蓝色导航条结束-->
</el-header>
<el-main style="width: 1200px;margin: 0 auto">
<el-row :gutter="20">
<el-col :span="18">
<!--走马灯开始-->
<el-carousel height="300px">
<el-carousel-item v-for="b in bannerArr">
<img :src="b.url" style="width: 100%;height: 100%" alt="">
</el-carousel-item>
</el-carousel>
<!--走马灯结束-->
</el-col>
<el-col :span="6">
<!--排行榜开始-->
<el-card style="height: 300px">
<h3><i style="font-weight: bold"
class="el-icon-trophy"></i> 销量最高</h3>
<el-divider></el-divider>
<el-table :data="topArr" style="width: 500px">
<el-table-column label="排名" type="index" width="50px"></el-table-column>
<el-table-column label="商品名">
<!--scope.row代表topArr数组中遍历的对象-->
<template slot-scope="scope">
<a href="" style="text-decoration: none;color: #333">{{scope.row.title}}</a>
</template>
</el-table-column>
<el-table-column label="销量" prop="saleCount"></el-table-column>
</el-table>
</el-card>
<!--排行榜结束-->
</el-col>
</el-row>
<!--商品列表开始-->
<el-row :gutter="20">
<el-col :span="6" v-for="p in productArr">
<el-card>
<div>
<img class="p_img" :src="p.url" width="100%" alt="">
</div>
<div>
<p style="font-size: 15px;margin-top: 0;height: 38px">{{p.title}}</p>
<div style="color: #6c6c6c">
<span>¥{{p.price}}</span>
<span style="font-size: 12px;
text-decoration: line-through">{{p.oldPrice}}</span>
<span style="float: right">销量:{{p.saleCount}}件</span>
</div>
</div>
</el-card>
</el-col>
</el-row>
<!--商品列表结束-->
</el-main>
<el-footer style="padding: 0">
<div style="background-image: url('imgs/wave.png');
height: 95px;margin-bottom: -30px"></div>
<div style="background-color: #3f3f3f;height: 100px;
font-size: 14px;color: #b1b1b1;
text-align: center;padding: 30px">
<p>Copyright © 北京达内金桥科技有限公司版权所有 京ICP备12003709号-3 京公网安备 11010802029572号</p>
<p>涵盖20余门课程体系,致力于打造权威的IT职业教育学习平台</p>
<p>达内在线WWW.TMOOC.CN 专注于IT职业技能培训</p>
</div>
</el-footer>
</el-container>
</div>
</body>
<!-- import Vue before Element -->
<script src="js/vue.js"></script>
<!-- import JavaScript -->
<script src="js/eui.js"></script>
<script src="js/axios.min.js"></script>
<script>
let v = new Vue({
el: '#app',
data: function() {
return {
categoryArr:[],
bannerArr:[{url:"imgs/b1.jpg"},
{url:"imgs/b2.jpg"},
{url:"imgs/b3.jpg"}],
topArr:[{title:"小米手机",saleCount:5000},
{title:"华为手表",saleCount:4000},
{title:"毛巾",saleCount:3000},
{title:"双飞燕鼠标",saleCount:2000},
{title:"耐克篮球",saleCount:1000},
{title:"阿迪袜子",saleCount:500}],
productArr:[{title:"森马牛仔裤女宽松慢跑裤运动风2022春季新款显瘦束脚长裤复古",price:233,oldPrice:598,url:"imgs/a.jpg",saleCount:2342},
{title:"茵曼马甲连衣裙两件套春季新款娃娃领色织格长袖背心裙套装",price:233,oldPrice:598,url:"imgs/b.jpg",saleCount:2342},
{title:"雪中飞墨绿色短袖t恤女夏2022新款纯棉半袖打底体恤夏季上衣潮ins",price:233,oldPrice:598,url:"imgs/c.jpg",saleCount:2342},
{title:"【佟丽娅同款】鸭鸭明星同款羽绒服2021年冬季新款时尚连帽外套冬",price:233,oldPrice:598,url:"imgs/d.jpg",saleCount:2342},
{title:"BEASTER小恶魔鬼脸明星同款夹克毛绒保暖加厚字母印花宽松外套ins",price:233,oldPrice:598,url:"imgs/e.jpg",saleCount:2342},
{title:"香影毛呢外套女中长款2021年冬季新款气质韩版娃娃领紫色呢子大衣",price:233,oldPrice:598,url:"imgs/f.jpg",saleCount:2342},
{title:"SEMIR森马商场同款打底针织毛衣纯色高领新品显瘦",price:233,oldPrice:598,url:"imgs/g.jpg",saleCount:2342},
{title:"美特斯邦威女MTEE 贺岁系列中长款风衣736598",price:233,oldPrice:598,url:"imgs/h.jpg",saleCount:2342},
{title:"imone2021秋款黑色小西装外套女韩版学生宽松学院风外套jk外套",price:233,oldPrice:598,url:"imgs/i.jpg",saleCount:2342},
{title:"BEASTER 小恶魔明星同款保暖长袖街头潮流连帽卫衣情侣上衣",price:233,oldPrice:598,url:"imgs/j.jpg",saleCount:2342},
{title:"憨厚皇后100%绵羊皮2021秋海宁真皮皮衣女长款修身绵羊皮风衣外",price:233,oldPrice:598,url:"imgs/k.jpg",saleCount:2342},
{title:"美特斯邦威高腰牛仔裤女宽松小脚新款春秋彩色潮流女士牛仔",price:233,oldPrice:598,url:"imgs/a.jpg",saleCount:2342}]
}
},
created:function () {
//发请求获取所有分类信息
axios.get("/category/select").then(function (response) {
v.categoryArr = response.data;
})
//发出请求获取所有轮播图信息
axios.get("/banner/select").then(function (response) {
v.bannerArr = response.data;
})
},
methods:{
handleSelect(index){
console.log(index);
}
}
})
</script>
</html>
(4)后台管理页面分类管理步骤
4、后台管理页面分类管理步骤
改动页面admin 改动的类CategoryController
改动接口CategoryMapper
01.
(1)在admin.html页面中:
①先引入axios框架
②在页面中添加created方法 在里面向/category/select发请求获取所有分类数据,
把得到的数据赋值给categoryArr数组,页面会自动展示
③【70行:<!--confirm确认事件-->】下面:
删除添加点击事件调用categoryDelete方法,参考Web阶段day07的员工列表练习:
<el-popconfirm @confirm="categoryDelete(scope.$index, scope.row)"
④在methods方法中继续写一个categoryDelete,:
向/category/delete发出请求并且把分类的id传递过去
categoryDelete(index,category){
axios.get("/category/delete?id="+category.id).then(function () {
location.reload();
})
}
(2)在CategoryController中:
添加delete方法:处理/category/delete请求,方法中调用mapper里面的deleteById方法
@RequestMapping("/category/delete")
public void delete(int id ){
System.out.println("删除分类的id = " + id);
mapper.deleteById(id);
}
(3)在CategoryMapper:
实现mapper里面的deleteById方法
/** 删除数据库(cs)中category表格中的id */
@Delete("delete from category where id=#{id}")
void deleteById(int id);
(4)测试:http://localhost:8080/admin.html
发现表格中有删除按钮,点击删除,
即可删除成功!
(5)添加分类步骤:
5、点击添加分类弹出输入框步骤:
改动页面admin
改动的类CategoryController
改动的接口CategoryMapper
(1)点击添加分类弹出输入框步骤:
在admin:
①methods中的menuChange方法中:
"1-2"中的alert删除,替换为:【弹出输入框】代码:
if (index=="1-2"){
v.$prompt("请输入分类名称","提示",{
confirmButtonText:"确定",
cancelButtonText:"取消"
}).then(function (object){
console.log("分类名称:"+object.value);
})
测试:http://localhost:8080/admin.html
点击左侧分类管理,
点击添加分类,
会弹出输入框
(2)点击确定时,向/category/insert发出请求
把分类的名称拼接到请求地址的后面
在admin中:上边①中的then方法中:添加代码:
let name = object.value;
if (name==null||name.trim()==""){
v.$message.error("分类名称不能为空");
return;
}
axios.get("/category/insert?name="+name).then(function (response) {
location.reload();
})
(3)CategoryController里面创建insert方法处理/category/insert,
在参数列表中声明Category对象 用来接受传递过来的参数,
调用mapper里面的insert方法:
@RequestMapping("/category/insert")
public void insert(Category category){
mapper.insert(category);
}
(4)实现CategoryMapper:里面的insert方法
/** 添加分类表格的类别name */
@Insert("insert into category values(null,#{name})")
void insert(Category category);
(5)测试:http://localhost:8080/admin.html
点击左侧分类管理,
点击添加分类,输入【手机手机】,
点击弹出框的确定,会发现该页面表格多出了刚才自己写的【手机手机】分类!成功!
点击删除,点击弹出框的确定,会发现该页面表格刚才自己写的【手机手机】分类已被成功删除!成功!
(6)管理页面轮播图展示
6、管理页面轮播图展示
改动页面admin
(1)在admin.html页面中的created方法中:
再写一个向/banner/select发出请求获取所有轮播图数据,
把请求到的数据赋值给bannerArr数组,
页面即可显示正确数据:
axios.get("/banner/select").then(function (response) {
v.bannerArr = response.data;
})
(2)测试:http://localhost:8080/admin.html
点击左侧轮播图管理,轮播图列表,会发现有四个图!
成功!
(7)删除轮播图同时删除文件
7、删除轮播图----参考删除分类实现此功能
改动的页面admin
改动的类BannerController
改动的接口BannerMapper
01.
(1)admin:
①96行的给删除按钮添加点击事件调用bannerDelete方法:【@confirm="bannerDelete"】
②在methods方法中加一个:【发出删除请求】
bannerDelete(index,banner){
axios.get("/banner/delete?id="+banner.id).then(function () {
location.reload();
})
}
(2)在BannerController中:
添加delete方法处理/banner/delete请求 方法中调用mapper的deleteById方法:
@RequestMapping("/banner/delete")
public void delete(int id){
mapper.deleteById(id);
}
(3)在接口BannerMapper:实现mapper中的deleteById方法
@Delete("delete from banner where id=#{id}")
void deleteById(int id);
(4)测试:http://localhost:8080/admin.html
点击左侧分类管理,添加分类,输入信息【手机手机】,点击弹出框确定,此时页面多出了手机手机的分类
此处有一个bug:
点击删除,弹出框点击确定,会跳到分类管理页面,
如何解决?
:
(5)如何解决?在admin中:
将methods里的【categoryDelete 和 bannerDelete】方法中:
then中的语句改变即可:
categoryDelete(index,category){
axios.get("/category/delete?id="+category.id).then(function () {
//splice(下标,长度)方法是JavaScript中数组删除元素的方法
v.categoryArr.splice(index,1);
})
},
bannerDelete(index,banner){
axios.get("/banner/delete?id="+banner.id).then(function () {
v.bannerArr.splice(index,1);
(6)测试:http://localhost:8080/admin.html
点击左侧分类管理,添加分类,输入信息【手机手机】,点击弹出框确定,此时页面多出了手机手机的分类
点击删除,弹出框点击确定,此时就不会跳转到分类管理页面了,
直接显示轮播图列表,发现刚才删的图片已经被删除成功!!!
(7)改错:进入admin.html页面时进行点击取消操作时,源码控制台会报错:
①访问http://localhost/8080/admin.html时会,点击取消按钮时,源码控制台会报错,
:在admin页面:189行添加:【当在admin.html页面点击取消时,源码控制台会报错】
(8)浏览器中的窗口栏显示鲨鱼图标!实现:
在guobin的idea的Coolshark工程中的:static下:复制favicon.ico鲨鱼图标到自己工程
测试:http://localhost:8080/
发现窗口栏变为小鲨鱼图标!
02.删除轮播图同时删除文件
(1)①在BannerController:
delete方法中添加删除文件对象的步骤:
//先查询到轮播图的url
String url = mapper.selectUrlById(id);
//aaa.jpg D:/Softa/danei/ideaBoot4-2testPath/files/aaa.jpg
//得到文件的完整磁盘路径(发布轮播图后,图片会保存在这里)
String filePath = "D:/Softa/danei/ideaBoot4-2testPath/files/"+url;
//创建文件对象并删除
new File(filePath).delete();
(2)在BannerMapper:实现【selectUrlById】方法:
@Select("select url from banner where id=#{id}")
String selectUrlById(int id);
(8)添加轮播图
8、添加轮播图
改动页面admin、insert、insertBanner
复制的类UploadController
改动的类BannerController
改动接口BannerMapper
(1)在admin:
①192行:添加【跳转到添加轮播图页面】代码:并注释掉alert代码
//alert("添加轮播图")
//跳转到添加轮播图页面
location.href="/insertBanner.html";
(2)从boot5-1微博练习中把insert.html页面复制到新工程中
改名为insertBanner.html, 并进行改动:(也可以复制guobin的static下的insertBanner到自己工程)
①发请求的地址改成了/banner/insert
(3)把boot5-1微博练习中的UploadController复制到新工程的controller包里面
(4)在BannerController里面添加insert方法 处理/banner/insert,
方法中调用mapper的insert方法 把banner的数据保存到数据库中
@RequestMapping("/banner/insert")
public void insert(@RequestBody Banner banner){
mapper.insert(banner);
}
(5)BannerMapper:实现mapper里面的banner方法
@Insert("insert into banner values(null,#{url})")
void insert(Banner banner);
(6)测试:http://localhost:8080/insertbanner.html
跳转到添加轮播图页面,选择图片点击发布,成功后
会自动跳转到http://localhost:8080页面,
发现蓝色导航条下方可以正确显示刚才添加的轮播图了!!
(7)在insertBanner:对添加轮播图进行美化操作
①删掉h1
②第10行下添加【页头组件和分割线】:
③.....
测试:http://localhost:8080/admin.html
点击左侧轮播图管理,添加轮播图,
跳转到 添加轮播图页面,发现此页面美化了!
(8)在insertBanner:
①methods里添加一个goBack方法:返回上一页面
②上边写一个点击事件goback
测试:http://localhost:8080/admin.html
点击左侧轮播图管理,添加轮播图,
跳转到 添加轮播图页面,
点击该页面左上角:返回 按钮
可以跳转到 轮播图管理页面!
成功!
(9)添加商品
9、添加商品
新建页面insertProduct
改动页面admin
01.创建表:
use cs;
create table product(id int primary key auto_increment,title varchar(100),url varchar(255),price double(10,2),old_price double(10,2),view_count int,sale_count int,created timestamp, category_id int)charset=utf8;
02.
(1)复制insertBanner 粘贴到static下,改名为insertProduct发布商品页面 并改动
(2)admin中:点击添加商品侧边栏时,操作的代码:
①196行:
//alert("添加商品")
//跳转到添加商品页面
location.href="/insertProduct.html";
测试:http://localhost:8080/admin.html
点击左侧商品管理按钮,
点击添加商品 ,跳转到添加商品页面!
成功!
(3)在insertProduct.html页面中:
①添加created方法,在方法中向/category/select发请求 得到所有的分类信息
created:function(){
//发送请求得到所有的分类信息
axios.get().then(function (response){
v.categoryArr = response.data;
})
}
②赋值给Vue里面的data: 里的categoryArr数组
categoryArr:[]
③遍历categoryArr数组 在下拉选中添加显示的分类
④测试:http://localhost:8080/insertProduct.html
点击下拉选框 分类:发现选项已经是数据库的内容了!
(10)管理页面商品列表展示功能
10、管理页面商品列表展示功能
改动页面admin、index
改动类ProductController
改动接口ProductMapper
(1)在admin.html页面中的created方法里面 向/product/select发出请求
把查询到的数据给到productArr数组:
axios.get("/product/select").then(function (response) {
v.productArr = response.data;
})
(2)在ProductController里面添加select方法 处理/product/select请求,
在方法中调用mapper的select方法
@RequestMapping("/product/select")
public List<Product> select(){
return mapper.select();
}
(3)ProductMapper:实现里面的select方法
@Select("select id,title,price,sale_count,url from product")
@Result(property = "saleCount",column = "sale_cont")
List<Product> select();
(4)测试:http://localhost:8080/insertProduct.html
显示添加商品页面,输入商品信息,点击添加,弹出框显示:添加成功
点击确定按钮,跳转到 分类列表页面!
查看左侧点击商品管理,商品列表,此时发现已经有刚才输入的商品信息了!
此时进入数据库查看有无刚才添加商品的信息:
use cs;
show tables;
select * from product;
发现有刚才添加 商品:龙虾 的信息!! 成功!!!
(5)将添加的商品信息显示到: http://localhost:8080/index.html
在admin中,把上边"(1)"中添加的内容复制粘贴在:
index中(Vue对象的methods中的created:function中),
测试:http://localhost:8080/index.html
发现此页面有添加的商品信息!成功!!
(11)删除商品:整体流程参考删除轮播图
11、删除商品步骤: 整体流程参考删除轮播图
改动页面admin
改动类ProductController
改动接口ProductMapper
(1)①在admin.html页面中修改点击删除时调用的方法 把handleDelete改成productDelete
124行下边:
<!--confirm确认事件-->
<el-popconfirm @confirm="productDelete(scope.$index, scope.row)"
②在methods里面添加productDelete方法,
在方法中向/product/delete发出请求;同时把商品的id传递过去
productDelete(index,product){
axios.get("/product/delete?id="+product.id).then(function () {
//删除数组中的数据
v.productArr.splice(index,1);
})
}
(3)在ProductController中:
添加delete方法处理/product/delete请求,
方法中先通过id查询到商品图片的url,然后得到商品图片的完整路径 删除图片文件,
然后在调用mapper的deleteById方法
@RequestMapping("/product/delete")
public void delete(int id){
//通过id查询到商品的图片路径
String url = mapper.selectUrlById(id);
String filePath = "D:/Softa/danei/ideaBoot4-2testPath/files/"+url;
new File(filePath).delete();
//删除数据库里面的数据
mapper.deleteById(id);
}
(4)ProductMapper:实现mapper里面deleteById方法
@Select("select url from product where id=#{id}")
String selectUrlById(int id);
@Delete("delete from product where id=#{id}")
void deleteById(int id);
(5)测试:http://localhost:8080/admin.html
点击左侧商品管理,点击商品列表,
点击右侧按钮删除商品信息,弹出框显示:是否删除,点击确定,
发现商品被成功删除!
进入数据库查看是否还有该数据:
use cs;
show tables;
select * from product;
发现页面中被删除的信息已经在数据库被删除掉了!
成功!
(12)首页展示商品
12、首页展示商品步骤:
改动页面index
改动类ProductController
改动接口ProductMapper
01.
(1)在index.html页面:
的Vue对象的methods中:
created方法中向/product/select/index发出请求,
把得到的数据赋值给productArr数组
//发出请求获取所有商品信息
axios.get("/product/select/index").then(function (response) {
v.productArr = response.data;
})
(2)ProductController:
在ProductController中添加selectIndex方法处理/product/select/index请求,
在方法中调用mapper的selectIndex方法
@RequestMapping("/product/select/index")
public List<Product> selectIndex(){
return mapper.selectIndex();
}
(3)ProductMapper:
在mapper中实现selectIndex方法(首页展示什么则查询什么)
@Select("select id,title,url,price,old_price,sale_count from product")
@Result(property = "oldPrice",column = "old_price")
@Result(property = "saleCount",column = "sale_count")
List<Product> selectIndex();
(4)测试:①http://localhost:8080/admin.html
点击左侧商品管理,点击添加商品,添加商品成功后,
点击商品列表,发现有刚才添加的商品信息,成功!
②点击 http://localhost:8080/index.html
发现此页面也有刚才添加的商品信息!
但是会出现错版问题!(在下面会解决该问题!)
02.添加10几种商品信息成功后,发现在http://localhost:8080/index.html显示错版,解决:
(1)在index中:108行控制宽高:
【<img class="p_img" :src="p.url" width="230" height="230" alt="">】
测试:http://localhost:8080/index.html
此时该页面所展示的刚才添加商品信息宽高都相同了!页面不会错版了!
(13)排行榜
13、排行榜步骤,
改动页面index
改动类ProductController
改动接口ProductMapper
01.
(1)在index首页:
created方法里面 向/product/select/top发出请求
把得到的数据赋值给topArr数组
①将data里数组的假数据都删掉(假数据存储到了static下的haha.html方便以后自己写工程是时候使用)
②在下面created方法里:【发出请求获取排行榜商品信息】
//发出请求获取排行榜商品信息
axios.get("/product/select/top").then(function (response) {
v.topArr = response.data;
})
(2)在ProductController:
里面添加selectTop处理/product/select/top请求,
在处理请求的方法中调用mapper的selectTop方法
@RequestMapping("/product/select/top")
public List<Product> selectTop(){
return mapper.selectTop();
}
(3)ProductMapper:
在Mapper里面实现selectTop方法, 查询数据时按照销量排序并且只查询6条数据
@Select("select id,title,sale_count from " +
"product order by sale_count desc limit 0,6")
@Result(property = "saleCount",column = "sale_count")
List<Product> selectTop();
(4)测试:http://localhost:8080/index.html
此时观察该页面右侧,会出现添加商品的 商品排行榜:
1 烤鸭美食
推荐吃
2 小虾
3 寿司
4 蛋糕
5 牛排
6 三文鱼
发现此网页的排行榜第一名中,会折行,很不美观,我们把它变成:
在index.html页面中查询到数据的时候如果长度超过3个字符则变成:前三个字符+...
烤鸭美食推荐吃————>烤鸭美...
处理代码写在ProductController中的selectTop方法中
(5)在ProductController:selectTop方法中:
@RequestMapping("/product/select/top")
public List<Product> selectTop(){
List<Product> list = mapper.selectTop();
//遍历list集合
for (Product p: list) {
if (p.getTitle().length()>3){
String title = p.getTitle().substring(0,3)+"...";
p.setTitle(title);
}
}
return list;
}
(6)测试:http://localhost:8080/index.html
此时观察该页面右侧,会出现添加商品的 商品排行榜:
1 烤鸭美... 1000
2 小虾 600
3 寿司 100
4 蛋糕 35
5 牛排 30
6 三文鱼 20
发现此网页的排行榜第一名中,不会折行,已经变成了:
前三个字符+...
成功!!!
(14)在application.properties里面配置自定义的值
14、在application.properties里面配置自定义的值
改动文件application.properties
改动类UploadController、ProductController、BannerController
(1)在application.properties中:
# ${}————>代表路径,
spring.web.resources.static-locations=file:${dirPath},classpath:static
#配置自定义的值
dirPath=D:/Softa/danei/ideaBoot4-2testPath/files
(2)UploadController:
①最上面:
//@Value注解:这种写法是找到配置文件中的值读取出来并赋值给dirPath遍变量
@Value("${dirPath}")
private String dirPath;
②将upload方法中的:
【String dirPath = "D:/Softa/danei/ideaBoot4-2testPath/files";】
注释掉
③remove方法中:
【String filePath = "D:/Softa/danei/ideaBoot4-2testPath/files/"+name;】
注释掉,改为:
【 String filePath = dirPath+"/"+name;】
(3)ProductController:
①最上面添加:
//@Value注解:这种写法是找到配置文件中的值读取出来并赋值给dirPath遍变量
@Value("${dirPath}")
private String dirPath;
②delete方法中:
【String filePath = "D:/Softa/danei/ideaBoot4-2testPath/files/"+url;】
注释掉,改为:
【String filePath = dirPath+"/"+url;】
(4)BannerController:
①最上面添加:
//@Value注解:这种写法是找到配置文件中的值读取出来并赋值给dirPath遍变量
@Value("${dirPath}")
private String dirPath;
②delete方法中:
【String filePath = "D:/Softa/danei/ideaBoot4-2testPath/files/"+url;】
注释掉,改为:
【String filePath = dirPath+"/"+url;】
(5)测试:http://localhost:8080/insertProduct.html
测试添加商品是否正常————>可以 成功
测试http://localhost:8080/index.html页面是否正常显示——>可以 成功
成功!!
(15)点击分类查看分类下的商品
15、点击分类查看分类下的商品
改动页面index
新建页面result
01.
(1)在index:
①49行下面的【default-active="1"】删掉
②复制index页面,粘贴到static下,改名为:result
(2)在result:
①把轮播图和排行榜删掉:
就是把<el-row>全删掉!
(3)在index:需求:当点击蓝色导航条分类的时候,跳转到result.html页面并且把分类的id传过来:
①在Vue对象的methods中:handleSelect中:
//此时的index代表的是分类的id 跳转页面并且把点击的分类id传递
location.href="/result.html?cid="+index;
(4)测试:http://localhost:8080
①之前当点击蓝色导航条分类的时候,浏览器窗口地址栏不会改变:【http://localhost:8080】
②现在当点击蓝色导航条分类的时候,浏览器窗口地址栏会随着点击导航条的不同而发生改变,
进行了cid的分类管理,地址栏变为:【http://localhost:8080/result.html?cid=2】
成功!!!
02.在result.html页面的created方法中,之前请求所有商品,现在改成请求某个分类下的商品
改动页面result
改动类ProductController
改动接口ProductMapper
(1)在result:
①在Vue对象的created方法中:
//发出请求获取当前分类的所有商品信息:
//得到地址栏中的分类id:
let cid = location.search.split("=")[1];
//通过分类id发出查询请求
axios.get("/product/select/category?cid="+cid).then(function (response) {
v.productArr = response.data;
})
(2)在ProductController中创建selectByCid方法
处理/product/select/category请求,
方法中调用mapper的selectByCid方法
@RequestMapping("/product/select/category")
public List<Product> selectByCid(int cid){
return mapper.selectByCid(cid);
}
(3)在ProductMapper:
@Select("select id,title,url,price,old_price,sale_count from product where category_id=#{cid}")
@Result(property = "oldPrice",column = "old_price")
@Result(property = "saleCount",column = "sale_count")
List<Product> selectByCid(int cid);
(4)测试:http://localhost:8080
点击导航条 美食 会跳转到添加的美食页面【http://localhost:8080/result.html?cid=4】
点击上边窗口栏点击后退,会回到首页【http://localhost:8080/】
(5)result:
①Vue对象的data中:添加数组:【currentIndex:""】
②created下:【发出请求获取当前分类下的所有商品信息:】中添加:
//发出请求获取当前分类下的所有商品信息:
//得到地址栏中的分类id:
let cid = location.search.split("=")[1];
/*
在created方法中v变量还没有创建完 不能访问v变量 此时通过this代替v:
//v.currentIndex = cid;
没创建完指在实例化的过程中调用的created方法,实例化完之后才允许访问v变量
this代表当前正在实例化的Vue对象 通过this也可以访问到Vue里面的变量
*/
this.currentIndex = cid;
③需求:点击蓝色导航条时,跳转到结果页面时,导航条对应的“分类”名单下有一条线!
在上边【蓝色导航条】:
【:default-active="currentIndex"】
(6)测试:http://localhost:8080
页面中点击蓝色导航条时,
跳转到结果页面时【例:http://localhost:8080/result.html?cid=5】,
导航条对应的“分类”名单下有一条线!
成功!
(7)完成在8080页面点击导航条时点哪跳哪
在result:
①130行【发出请求获取所有轮播图信息】删除
②在methods:handleSelect方法:
//查询点击分类下的商品信息
axios.get("/product/select/category?cid="+index).then(function (response){
v.productArr = response.data;
})
③测试:http://localhost:8080/
点击美食的时候,跳转到显示美食的页面
点击数码的时候,跳转到显示数码的页面
成功!
③39行添加:【/】:[<a href="/">首页</a>........]
(16)搜索功能
1.让搜索框和某个变量进行双向绑定,给搜索按钮添加点击事件
2.点击时跳转到结果页面 并且把需要搜索的关键字传递过去
3.在结果页面的created方法中 取出wd 然后发出搜索请求
4.在ProductController中添加search方法处理/search请求, 方法中调用mapper的selectByWd方法
5.实现mapper里面的selectByWd方法(需要用到模糊查询)
6.回车搜索功能
16、搜索功能:
改动页面index、result
改动类ProductController
改动接口ProductMapper
01.可以输入信息后按回车搜索关键字跳转到相关页面:
(1)在index:进行双向绑定
①在data里,添加:【wd:""】
②57行:【<!--搜索框开始-->】下:改动:添加model和点击事件search
<el-input size="mini" v-model="wd".....
61行添加:【@click="search()"】
③methods中添加search方法:
search(){
//跳转到结果页面并且把搜索的关键字传递过去
location.href = "/result.html?wd="+v.wd;
}
④测试:http://localhost:8080
登录页面,点击右上角搜索框,
输入搜索信息:美食,点击搜索,
跳转到一个(结果页面)空页面:http://localhost:8080/result.html?wd=美食
右键检查,源码控制台输入:
成功!
(2)在结果页面的created方法中,取出wd,然后发出搜索请求
created:function () {
//发请求获取所有分类信息
axios.get("/category/select").then(function (response) {
v.categoryArr = response.data;
})
//判断地址栏中包含的是wd还是cid
if (location.search.indexOf("cid")!=-1){//包含cid
//发出请求获取当前分类下的所有的商品信息
//得到地址栏中的分类id
let cid = location.search.split("=")[1];
//在created方法中v变量还没有创建完 不能访问v变量 此时通过this代替v
//没创建完指在实例化的过程中调用的created方法,实例化完之后才允许访问v变量
//this代表当前正在实例化的Vue对象 通过this也可以访问到Vue里面的变量
this.currentIndex = cid;
//通过分类id发出查询请求
axios.get("/product/select/category?cid="+cid).then(function (response) {
//此处是子线程请求完数据回调回来的位置
//此时Vue对象早已实例化完成
//此时的this代表的已不是Vue对象而是window对象
v.productArr = response.data;
})
}else{//包含wd搜索关键字
//得到搜索的关键字wd
let wd = location.search.split("=")[1];
axios.get("/search?wd="+wd).then(function (response) {
v.productArr = response.data;
})
}
}
(3)在ProductController中:
添加search方法处理/search请求, 方法中调用mapper的selectByWd方法
@RequestMapping("/search")
public List<Product> searchById(String wd){
System.out.println("wd = " + wd);
return mapper.selectByWd(wd);
}
(4)实现mapper里面的selectByWd方法(需要用到模糊查询)
//模糊查询like: concat是SQL语句中将多个字符串进行拼接的函数
@Select("select id,title,url,price,old_price,sale_count from " +
"product where title like concat('%',#{wd},'%')")
@Result(property = "oldPrice",column = "old_price")
@Result(property = "saleCount",column = "sale_count")
List<Product> selectByWd(String wd);
(5)在8080页面输入搜索信息后可以敲回车键进行搜索:
在index:
57行(<!--搜索框开始-->)下:div标签下:
<el-input size="mini" @keydown.native.enter="search()" v-mode.....
(6)测试:http://localhost:8080/
点击右侧搜索框,
输入关键字 虾, 点击搜索或敲回车,会跳转到有带有"虾"字商品图片的页面
点击浏览器返回按钮回到8080链接;
输入关键字 美食,点击搜索或敲回车,会跳转到有"美食"字商品图片的页面
成功!
但此时若继续从结果页面再次搜索,无法输入文字,功能还未实现!
(16.2)结果页面的搜索功能
02.结果页面的搜索功能
改动页面result
(1)给结果页面的文本框双向绑定wd 给文本框添加回车键按下事件
并且给搜索按钮添加点击事件 两个事件都调用search方法在methods里面实现search方法,
在方法中向/search发出请求把搜索的关键字传递过去,
把查询到的数据赋值给productArr数组即可
①data里添加双向绑定:【wd:""】
②58行(搜索框开始):下:添加:【v-model/@keydown/@click】
<el-input size="mini" v-model="search()" @keydown.native.enter="search()".......
<el-button ......11px" @click="search()"
③methods里添加:
search(){
axios.get("/search?wd="+v.wd).then(function (response){
v.productArr = response.data;
})
}
④测试:http://localhost:8080/
点击右侧搜索框,
输入关键字 虾, 点击搜索或敲回车,会跳转到有带有"虾"字商品图片的 结果页面;
输入关键字 美食,点击搜索或敲回车,会跳转到有"美食"字商品图片的页面;
成功!
(17)商品详情页面
1.给首页和结果页的商品添加点击跳转功能,并把商品的id传递过去
2.在detail.html详情页面的created方法中得到地址栏中传递过来的商品id,
然后向/product/selectById?id=xxx 发出请求
把查询到的一个商品的信息赋值给Vue里面的product对象
3.在页面中把product对象里面的数据展示到标签里面
4.在ProductController里面添加selectById方法处理/product/selectById请求,
在方法中调用mapper的selectById方法
5.实现mapper里面的selectById方法
17、商品详情页面
新建页面detail 改动页面index、result
改动类ProductController、Product
改动接口ProductMapper
01.
(1)新建detail详情页面:
static下删掉detail详情页面,复制index粘贴在此static下并改名为detail,
改动代码,变为现在这样:
(2)点击排行榜中的商品名时跳转到该商品的详情页面
在index: 91行:改动:【<a :href="'detail.html?id='+scope.row.id"......】
测试:http://localhost:8080/
点击右侧排行榜,点击"牛排",跳转到地址栏(http://localhost:8080/detail.html?id=8)的空页面!
测试几个商品信息,看能否跳转到对应是商品页面,此时只是浏览器地址栏有改变,仍是空页面。
(3)点击首页的商品标题时也能跳转:index中:
①106行div标签下:添加超链接并把下边那句话包括进来【<a :href="'/detail.html?id='+p.id">xxxx</a>】
②112行div标签下,把p标签也包括进来:
<a style="color: #3f3f3f;text-decoration:none" :href="'/detail.html?id='+p.id">.......</a>
(4)在result中:77行和82行 也添加点击跳转超链接:【....】
测试:http://localhost:8080/
点击导航条的美食,跳转到美食饿页面:会显示很多美食
点击其中的一个美食图片,会跳转到这个美食的详情页面!
成功!
(5)在detail.html详情页面的created方法中得到地址栏中传递过来的商品id,
然后向/product/selectById?id=xxx 发出请求
把查询到的一个商品的信息赋值给Vue里面的product对象
①created方法里:
//得到地址栏中的商品id
let id = location.search.split("=")[1];
//通过id发请求获取商品数据
axios.get("/product/selectById?id="+id).then(function (response) {
v.product = response.data;
})
②data里定义一个 : 【product:{}】
③70行写:
<el-main style="width: 1200px;margin: 0 auto">
<!--商品详情开始-->
<el-row :gutter="20">
<!--将一行划分为两等份 24/2=12 -->
<el-col :span="12">
<el-card>
<img :src="product.url" width="100%" alt="">
</el-card>
</el-col>
<el-col :span="12">
<p style="font-size: 25px">{{product.title}}</p>
<el-divider></el-divider>
</el-col>
</el-row>
<!--商品详情结束-->
(6)在ProductController:
@RequestMapping("/product/selectById")
public Product selectById(int id){
return mapper.selectById(id);
}
(7)在ProductMapper:
//查询时不推荐使用“*”,因为表字段有修改的可能
@Select("select id,title,url,price," +
"old_price,sale_count,created,view_count from product where id=#{id}")
@Result(property = "oldPrice",column = "old_price")
@Result(property = "saleCount",column = "sale_count")
@Result(property = "viewCount",column = "view_count")
Product selectById(int id);
(8)测试:http://localhost:8080/
点击任一商品图片,会跳转到该商品详情的页面!
成功!
(9)添加详情页面的时间点:
Product实体类:
/*
yyyy:表示4位 年
MM : 月份
dd : 日期
HH : 小时
mm : 分钟
ss : 秒
例: 2022/05/19 14:40:30
2022年05月19日 14时40分30秒
pattern = yyyy年MM月dd日 HH时mm分ss秒
*/
@JsonFormat(pattern = "yyyy/MM/dd HH:mm:ss",timezone = "GMT+8")
private Date created;//商品发布时间 导包 java.utli
(10)测试:http://localhost:8080/
点击商品图片,跳转到该商品详情页面,
显示发布该图片的日期!
成功!
(补充)工作中查询数据时不推荐使用 星号: *
因为表字段有修改的可能,如果原表只有2个字段,某个地方只需要2个字段的数据此时写*号没有问题,
但是如果增加的新的需求需要改动原表的字段改成5个字段,
那么之前只需要查询两个字段的地方,
由于使用的是*也会变成查询5个字段的数据但是多查询的3个字段数据是没有用的这样就造成了网络资源的浪费.
(18)浏览量
ProductController:添加:
@RequestMapping("/product/selectById")
public Product selectById(int id){
//让浏览量 +1
mapper.updateViewCount(id);
return mapper.selectById(id);
}
ProductMapper:添加:
@Update("update product set view_count=view_count+1 where id=#{id}")
void updateViewCount(int id);
(18.1补充)当浏览量发生改变发布时间跟着改变的原因
当浏览量发生改变,页面的商品发布时间跟着改变的原因:
大部分MySQL数据库软件timestamp字段的默认效果是当表中任何一个字段的值发生改变时,
timestamp字段会自动变为当前的系统时间
关闭自动更新:
alter table product change created created timestamp not null default current_timestamp;
打开自动更新:
alter table product change created created timestamp not null default current_timestamp on update current_timestamp;
(19)后台管理页面显示当前登录的用户信息
①在登录成功时把用户对象保存到Session会话对象中
②在admin.html页面中的created方法中
向/currentUser地址发请求获取当前登录的用户对象并且把对象赋值给Vue里面的user,
如果没有得到当前登录的用户对象代表未登录状态跳转到登录页面
③在UserController中处理/currentUser请求 从Session对象中获取当前登录的用户对象并返回
④
测试:http://localhost:8080/login.html
输入用户信息,admin 123456,
点击登录:跳转到管理员页面(http://localhost:8080/admin.html)
右上角显示:【欢迎管理员回来】
(20)退出登录
admin页面:
给退出登录超链接添加点击事件,在事件方法中发出退出登录请求
logout(){
//退出登录
axios.get("/logout").then(function (){
location.href = "/";//回到首页
})
}
在UserController里面处理该请求
@RequestMapping("/logout")
public void logout(HttpSession session){
//把登录成功时保存的用户对象删除
session.removeAttribute("user");
}
(过滤器:Filter)
作用:
过滤器里面的代码会在执行Controller代码之前和执行完Controller代码之后执行,
这样的话可以将多个Controller中重复的操作写在过滤器里面,这样可以起到代码重用的作用
如何使用过滤器?
(1)在Coolshark商城工程:
①src/main/java/cn/tedu/coolshark包下:创建filter包,
②点击该包,new——>创建 选择Web Filter(漏斗形状)创建类;改名为MyFilter
改类创建完后里边自动有三个方法
(2)此时coolshark包下多出一个【CoolsharkApplication】类,
加上注解【@ServletComponentScan】:在当前类所在的包以及子包下面扫描过滤器
(3)在MyFilter类:
①最上边注解中添加:
@WebFilter(filterName = "MyFilter",urlPatterns = {"/insertProduct.html","/admin.html"})
②在doFilter方法中首句打桩:【System.out.println("过滤器执行了");】
③测试:http://localhost:8080/admin.html
输入信息admin/123456,登录后,发现idea控制台输出:过滤器执行了
④在doFilter方法:添加:
//当接收到请求时会执行此方法,需要在方法中进行判断 是否允许访问Controller或静态资源文件
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
//将父类类型转换为子类类型
HttpServletRequest st = (HttpServletRequest) request;
HttpServletResponse se = (HttpServletResponse) response;
//得到会话对象
HttpSession session = st.getSession();
//从会话对象中得到登录成功时保存的用户对象
User user = (User)session.getAttribute("user");
if(user!=null){//代表登录过
//放行 允许访问资源:
chain.doFilter(request,response);
}else{
//重定向 让客户端重新向指定的地址发出请求:
se.sendRedirect("/login.html");
}
System.out.println("过滤器执行了");
chain.doFilter(request, response);//方向 允许访问资源
}
⑤测试:http://localhost:8080 可以正常显示
切换以下两个地址:
①http://localhost:8080/login.html
输入用户名密码登录成功后idea有输出:过滤器执行了
②http://localhost:8080/admin.html
输入用户名密码登录成功后idea还会有输出:过滤器执行了
简单步骤:
①创建Filter类文件
package cn.tedu.coolshark.filter;
import cn.tedu.coolshark.entity.User;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebFilter(filterName = "MyFilter",urlPatterns = {"/insertProduct.html","/admin.html"})
public class MyFilter implements Filter {
//当过滤器销毁时执行的方法
public void destroy() {
}
//当接收到请求时会执行此方法,需要在此方法中进行判断 是否允许访问Controller或静态资源文件
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//将父类类型转换成子类类型
HttpServletRequest st = (HttpServletRequest) req;
HttpServletResponse se = (HttpServletResponse) resp;
//得到会话对象
HttpSession session = st.getSession();
//从会话对象中得到登录成功时保存的用户对象
User user = (User) session.getAttribute("user");
if (user!=null){//代表登录过
chain.doFilter(req, resp);//放行 允许访问资源
}else{//代表未登录
se.sendRedirect("/login.html"); //重定向 让客户端重新向指定的地址发出请求
}
System.out.println("过滤器执行了");
}
//当过滤器初始化时执行的方法
public void init(FilterConfig config) throws ServletException {
}
}
②在XXXXApplication.java文件中添加注解
(过滤器Filter——>urlPatterns的配置方式)
精确匹配 /admin.html /insertProduct.html
后缀匹配 *.jpg *.png *.html
路径匹配: /product/* /user/* 全部匹配: /* (客户端发出的所有请求都会被过滤器拦截)
(21)删除分类时删除商品
(1)在CategoryController的delete方法中调用商品Mapper里面的通过分类id删除的方法
在CategoryController:
最上边添加:【ProductMapper productMapper;】
delete方法:添加:
//删除和分类相关的商品
productMapper.deleteByCid(id);
(2)实现ProductMapper里面的通过分类id删除的方法:
ProductMapper:
/** 删除商品 */
@Delete("delete from product where category_id=#{id}")
void deleteByCid(int id);
(3)测试:http://localhost:8080/admin.html
删除一个分类:点击删除,刷新页面,发现刚才选中删除的分类被删除!
成功!