第三阶段:Web前端:03(2)Mybatis框架 | 后端MVC设计模式 | 酷鲨商城广告页引流平台

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   打钩 32.在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.添加英雄功能并在数据库可以查到
  新建类UserHeroHeroController  
  新建接口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调用HeroMapperList中的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调用ProductMapperList中的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
新建类UserControllerUserUserMapper

注:若没有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  新建类ProductControllerProduct
   新建接口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  新建类UserControllerUser
   新建接口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赋值truefalse来控制页面显示的内容
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  新建类UploadControllerWeiboControllerWeibo
   新建接口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-2UploadController复制到该工程的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)页面直接显示发布微博的文本和图片:

78080链接直接显示发布微博的文本和图片:
   改动: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  新建类UserControllerUser
  新建接口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、首页分类展示步骤
   新建类CategoryCategoryController
   新建接口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()"71104行: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、创建轮播图展示步骤:
   新建类BannerBannerController
   新建接口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
    改动类UploadControllerProductControllerBannerController
(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
    改动类ProductControllerProduct
    改动接口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
		删除一个分类:点击删除,刷新页面,发现刚才选中删除的分类被删除!
		成功!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值