【SpringBoot2 基础篇】学习笔记

前言

SpringBoot 简介

  • SpringBoot是由Pivotal团队提供的全新框架,其设计目的是用来简化Spring应用的初始搭建以及开发过程
  • Spring程序缺点
    • 依赖设置繁琐
    • 配置繁琐
  • SpringBoot程序优点
    • 起步依赖(简化依赖配置)
    • 自动配置(简化常用工程相关配置)
    • 辅助功能(内置服务器,……)

1. 快速上手SpringBoot

创建SpringBoot工程的四种方式

  • 基于Idea创建SpringBoot工程
  • 基于官网创建SpringBoot工程
  • 基于阿里云创建SpringBoot工程
  • 手工创建Maven工程修改为SpringBoot工程`

1.1 SpringBoot入门程序开发

1.1.1 IDE创建工程

  • 创建新模块,选择Spring Initializr,并配置模块相关基础信息
    在这里插入图片描述
  • 选择当前模块需要使用的技术集
    在这里插入图片描述
    创建好之后,检查maven以及JDK。
  • 开发控制器类
//Rest模式
@RestController
@RequestMapping("/books")
public class BookController {
	@GetMapping
	public String getById(){
		System.out.println("springboot is running...");
		return "springboot is running...";
	}
}
  • 运行自动生成的Application类
    在这里插入图片描述
    最简单的SpringBoot入门案例:
    在这里插入图片描述
    在这里插入图片描述
    最简SpringBoot程序所包含的基础文件:
    • pom.xml文件
    • Application类

Spring程序与SpringBoot程序对比:

在这里插入图片描述

1.1.2 官网创建工程

如果IDEA无法联网,如何创建SpringBoot工程呢?

  1. 打开SpringBoot官网,选择Quickstart Your Project
  2. 创建工程,并保存项目
  3. 解压项目,通过IDE导入项目

基于SpringBoot官网创建项目,地址:SpringBoot官网
在这里插入图片描述

1.1.3 基于阿里云创建项目

  1. 选择start来源为自定义URL
  2. 输入阿里云start地址
  3. 创建项目

地址:https://start.aliyun.com
在这里插入图片描述
在这里插入图片描述

1.1.4 手工创建项目

  1. 创建普通Maven工程
  2. 继承spring-boot-starter-parent
  3. 添加依赖spring-boot-starter-web
  4. 制作引导类Application
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.5.4</version>
	</parent>
		<groupId>com.itheima</groupId>
		<artifactId>springboot_01_03_quickstart</artifactId>
		<version>1.0-SNAPSHOT</version>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
	</dependencies>
</project>

制作引导类Application

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

在这里插入图片描述

1.2 浅谈入门程序工作原理

1.2.1 parent

在这里插入图片描述
小结:

  1. 开发SpringBoot程序要继承spring-boot-starter-parent
  2. spring-boot-starter-parent中定义了若干个依赖管理
  3. 继承parent模块可以避免多个依赖使用相同技术时出现依赖版本冲突
  4. 继承parent的形式也可以采用引入依赖的形式实现效果

1.2.2 starter

在这里插入图片描述

  • starter
    • SpringBoot中常见项目名称,定义了当前项目使用的所有依赖坐标,以达到减少依赖配置的目的
  • parent
    • 所有SpringBoot项目要继承的项目,定义了若干个坐标版本号(依赖管理,而非依赖),以达到减少依赖冲突的目的
    • spring-boot-starter-parent各版本间存在着诸多坐标版本不同
  • 实际开发
    • 使用任意坐标时,仅书写GAV中的G和A,V由SpringBoot提供,除非SpringBoot未提供对应版本V
    • 如发生坐标错误,再指定Version(要小心版本冲突)

小结:

  1. 开发SpringBoot程序需要导入坐标时通常导入对应的starte
  2. 每个不同的starter根据功能不同,通常包含多个依赖坐标
  3. 使用starter可以实现快速配置的效果,达到简化配置的目的

1.2.3 引导类

  • 启动方式
    @SpringBootApplication
    public class Springboot01QuickstartApplication {
    	public static void main(String[] args) {
    		SpringApplication.run(Springboot01QuickstartApplication.class, args);
    	}
    }
    
  • SpringBoot的引导类是Boot工程的执行入口,运行main方法就可以启动项目
  • SpringBoot工程运行后初始化Spring容器,扫描引导类所在包加载bean

小结:

  • SpringBoot工程提供引导类用来启动程序
  • SpringBoot工程启动后创建并初始化Spring容器

1.2.4 内嵌tomcat

在这里插入图片描述
使用maven依赖管理变更起步依赖项

<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
		<!--web起步依赖环境中,排除Tomcat起步依赖-->
		<exclusions>
			<exclusion>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-starter-tomcat</artifactId>
			</exclusion>
		</exclusions>
	</dependency>
<!--添加Jetty起步依赖,版本由SpringBoot的starter控制-->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-jetty</artifactId>
	</dependency>
</dependencies>

Jetty比Tomcat更轻量级,可扩展性更强(相较于Tomcat),谷歌应用引擎(GAE)已经全面切换为Jetty

内置服务器:

  • tomcat(默认): apache出品,粉丝多,应用面广,负载了若干较重的组件
  • jetty:更轻量级,负载性能远不及tomcat
  • undertow:负载性能勉强跑赢tomcat

小结:

  1. 内嵌Tomcat服务器是SpringBoot辅助功能之一
  2. 内嵌Tomcat工作原理是将Tomcat服务器作为对象运行,并将该对象交给Spring容器管理
  3. 变更内嵌服务器思想是去除现有服务器,添加全新的服务器

总结:

  1. 入门案例(4种方式创建SpringBoot工程)
  2. SpringBoot概述
    • parent
    • starter
    • 引导类
    • 辅助功能(内嵌tomcat)

2. SpringBoot基础配置

2.1 属性配置

  • 修改服务器端口
    在这里插入图片描述

  • SpringBoot默认配置文件application.properties,通过键值对配置对应属性

  • 修改配置

    • 修改服务器端口
    • 关闭运行日志图标(banner)
    • 设置日志相关
#服务器端口配置
server.port=80
#关闭运行日志图标(banner)
spring.main.banner-mode=off
#设置日志相关
logging.level.root=debug

2.2 配置文件分类

在这里插入图片描述

SpringBoot提供了3种配置文件的格式:

  • properties(传统格式/默认格式)
  • yml(主流格式)
  • yaml

如果三种配置文件共存,则优先properties

  • SpringBoot配置文件加载顺序
    • application.properties > application.yml > application.yaml
  • 常用配置文件种类
    • application.yml

小结:

  1. 配置文件间的加载优先级
    • properties(最高)
    • yml
    • yaml(最低)
  2. 不同配置文件中相同配置按照加载优先级相互覆盖,不同配置文件中不同配置全部保留
    在这里插入图片描述
    在这里插入图片描述

2.3 yaml文件

在这里插入图片描述
yaml语法规则:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
小结:

  1. yaml语法规则
    • 大小写敏感
    • 属性层级关系使用多行描述,每行结尾使用冒号结束
    • 使用缩进表示层级关系,同层级左侧对齐,只允许使用空格(不允许使用Tab键)
    • 属性值前面添加空格(属性名与属性值之间使用冒号+空格作为分隔)
    • #表示注释
  2. 注意属性名冒号后面与数据之间有一个空格
  3. 字面值、对象数据格式、数组数据格式(略)

2.4 yaml数据读取

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如果数据量过大,可以封装全部数据到Environment对象

在这里插入图片描述
读取yaml引用类型属性数据:

  1. 定义数据模型封装yaml文件中对应的数据
  2. 定义为spring管控的bean(@Component)
  3. 指定加载的数据(@ConfigurationProperties)

在这里插入图片描述

@Component
@ConfigurationProperties(prefix = "datasource")
public class MyDataSource {
    private String drive;
    private String url;
    private String username;
    private String password;

    public MyDataSource() {
    }

    public MyDataSource(String drive, String url, String username, String password) {
        this.drive = drive;
        this.url = url;
        this.username = username;
        this.password = password;
    }

    public String getDrive() {
        return drive;
    }

    public void setDrive(String drive) {
        this.drive = drive;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    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;
    }

    @Override
    public String toString() {
        return "MyDataSource{" + "drive='" + drive + '\'' + ", url='" + url + '\'' + ", username='" + username + '\'' + ", password='" + password + '\'' + '}';
    }
}

小结:

  1. 使用@Value配合SpEL读取单个数据
  2. 如果数据存在多层级,依次书写层级名称即可
  3. 在配置文件中可以使用 ${属性名} 方式引用属性值
  4. 如果属性中出现特殊字符,可以使用双引号包裹起来作为字符解析
  5. 使用Environment对象封装全部配置信息
  6. 使用@Autowired自动装配数据到Environment对象中
  7. 使用@ConfigurationProperties注解绑定配置信息到封装类中
  8. 封装类需要定义为Spring管理的bean,否则无法进行属性注入

3. Rest

3.1 REST简介

  • REST (Representational State Transfer),表现形式状态转换
    • 传统风格资源描述形式
      • http://localhost/user/getById?id=1
      • http://localhost/user/saveUser
    • REST风格描述形式
      • http: / / localhost/user/1
      • http: / / localhost/user
  • 优点:
    • 隐藏资源的访问行为,无法通过地址得知对资源是何种操作
    • 书写简化

3.2 REST风格简介

  • 按照REST风格访问资源时使用行为动作区分对资源进行了何种操作

    • http: / /localhost/users
      查询全部用户信息 GET(查询)
    • http: // localhost/users/1
      查询指定用户信息 GET(查询)
    • http: / / localhost/users
      添加用户信息 POST(新增/保存)
    • http: / / localhost/users
      修改用户信息 PUT(修改/更新)
    • http: / / localhost/users/1
      删除用户信息 DELETE(删除)
  • 根据REST风格对资源进行访问称为RESTful
    在这里插入图片描述
    小结:

    1. REST
    2. 动作4个
    3. RESTful

3.3 REST入门案例

在这里插入图片描述
在这里插入图片描述

名称:@RequestMapping

类型:方法注解

位置:SpringMVC控制器方法定义上方

作用:设置当前控制器方法请求访问路径

范例:

@RequestMapping(value = "/users", method = RequestMethod.POST)
@ResponseBody
public String save(@RequestBody User user){
	system.out.println( "user save. . ." + user);
	return "{ 'module' : 'user save ' }";
}
  • 属性
    • value(默认)︰请求访问路径
    • method: http请求动作,标准动作 (GET/POST/PUT/DELETE)

小结:

  • @RequestBody
  • @RequestParam
  • @PathVariable
  1. 区别

    @RequestParam用于接收url地址传参或表单传参

    @RequestBody用于接收json数据

    @PathVariable用于接收路径参数,使用{参数名称}描述路径参数

  2. 应用

    后期开发中,发送请求参数超过1个时,以json格式为主,@RequestBody应用较广

    如果发送非json格式数据,选用@RequestParam接收请求参数

    采用RESTful进行开发,当参数数量较少时,例如1个,可以采用@PathVariable接收请求路径变量,通常用于传递id值

3.4 REST快速开发

  • 名称:@RestController

  • 类型:类注解

  • 位置:基于SpringMVC的RESTful开发控制器类定义上方

  • 作用:设置当前控制器类为RESTful风格,等同于@Controller与@ResponseBody两个注解组合功能

  • 范例:

    @RestController
    public class BookController {}
    
  • 名称:@GetMapping @PostMapping @PutMapping @DeleteMapping

  • 类型:方法注解

  • 位置:基于SpringMVC的RESTful开发控制器方法定义上方
    作用:设置当前控制器方法请求访问路径与请求动作,每种对应一个请求动作,例如@GetMapping对应GET请求

  • 范例:

    @GetMapping("/{id}")
    public String getById(@PathVariable Integer id){
    	system.out.println( "book getById.. . "+id);
    	return "{'module' : 'book getById'}";
    }
    
  • 属性

    • value(默认)︰请求访问路径

小结:

  1. RESTful快速开发(标准开发)
  2. @RestController
  3. 标准请求动作映射(4种)

4. 基于SpringBoot实现SSM整合

4.1 整合JUnit

SpringBoot整合JUnit:

  • 名称:@SpringBootTest
  • 类型:测试类注解
  • 位置:测试类定义上方
  • 作用:设置JUnit加载的SpringBoot启动类
  • 相关属性
    • classes:设置SpringBoot启动类
@SpringBootTest
class Springboot04JunitApplicationTests {
    //1.注入你要测试的对象
    @Autowired
    private BookDao bookDao;

    @Test
    void contextLoads() {
        //2.执行要测试的对象对应的方法
        bookDao.save();
    }
}

小结:

  1. 导入测试对应的starter
  2. 测试类使用@SpringBootTest修饰
  3. 使用自动装配的形式添加要测试的对象

若是将测试类移到其他包,再运行,会出现以下错误:

在这里插入图片描述
原 因:当前的测试类并在引导类所在包或则子包下(不在同级目录),无法被扫描,测试类无法找到引导类。

@SpringBootTest(classes = Springboot04JunitApplication.class)
class Springboot04JunitApplicationTests {
}

注意事项:

如果测试类在SpringBoot启动类的包或子包中,可以省略启动类的设置,也就是省略classes的设定

小结:

  1. 测试类如果存在于引导类所在包或子包中无需指定引导类
  2. 测试类如果不存在于引导类所在的包或子包中需要通过classes属性指定引导类

4.2 整合MyBatis

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
小结:

  1. 勾选MyBatis技术,也就是导入MyBatis对应的starter
  2. 数据库连接相关信息转换成配置
  3. 数据库SQL映射需要添加@Mapper被容器识别到

4.3 整合MyBatis-Plus

MyBatis-Plus与MyBatis区别:

  • 导入坐标不同
  • 数据层实现简化
    在这里插入图片描述
    在这里插入图片描述其余和整合MyBatis相同。

小结:

  1. 手工添加MyBatis-Plus对应的starter
  2. 数据层接口使用BaseMapper简化开发
  3. 需要使用的第三方技术无法通过勾选确定时,需要手工添加坐标

4.4 整合Druid

  • 指定数据源类型(非整合)
spring:
  datasource:
	driver-class-name: com.mysql.cj.jdbc.Driver
	url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC
	username: root
	password: root
	type: com.alibaba.druid.pool.DruidDataSource
  • 导入Druid对应的starter
<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>druid-spring-boot-starter</artifactId>
	<version>1.2.6</version>
</dependency>
  • 变更Druid的配置方式
spring:
  datasource:
	druid:
	  driver-class-name: com.mysql.cj.jdbc.Driver
	  url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC
	  username: root
	  password: root

小结:

  1. 整合Druid需要导入Druid对应的starter
  2. 根据Druid提供的配置方式进行配置
  3. 整合第三方技术通用方式( 导入对应的starter,根据提供的配置格式,配置非默认值对应的配置项)

4.5 SSMP整合案例

  • 案例实现方案分析
    • 实体类开发——使用Lombok快速制作实体类
    • Dao开发——整合MyBatisPlus,制作数据层测试类
    • Service开发——基于MyBatisPlus进行增量开发,制作业务层测试类
    • Controller开发——基于Restful开发,使用PostMan测试接口功能
    • Controller开发——前后端开发协议制作
    • 页面开发——基于VUE+ElementUI制作,前后端联调,页面数据处理,页面消息处理
      • 列表、新增、修改、删除、分页、查询
    • 项目异常处理
    • 按条件查询——页面功能调整、Controller修正功能、Service修正功能

4.5.1 模块创建

  • 勾选SpringMVC与MySQL坐标

  • 导入MyBatis-Plus和Druid坐标

    <dependency>
          <groupId>com.alibaba</groupId>
          <artifactId>druid-spring-boot-starter</artifactId>
          <version>1.1.23</version>
      </dependency>
    
      <dependency>
          <groupId>com.baomidou</groupId>
          <artifactId>mybatis-plus-boot-starter</artifactId>
          <version>3.4.2</version>
      </dependency>
    
  • 修改配置文件为yml格式

  • 设置端口为80方便访问

    server:
      port: 80
    

4.5.2 实体类开发

Lombok,一个Java类库,提供了一组注解,简化POJO实体类开发

<dependency>
	<groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
</dependency>

常用注解:@Data

为当前实体类在编译期设置对应的get/set方法,toString方法,hashCode方法,equals方法等

@Data
public class Book {
	private Integer id;
	private String type;
	private String name;
	private String description;
}

4.5.3 数据层开发

  • 导入MyBatisPlus与Druid对应的starter(模块创建时已导入)
  • 继承BaseMapper并指定泛型
    @Mapper
    public interface BookDao extends BaseMapper<Book> {
    }
    
  • 制作测试类测试结果
    @SpringBootTest
    public class BookDaoTest {
    	@Autowired
    	private BookDao bookDao;
    	@Test
    	void testSave() {
    		Book book = new Book();
    		book.setName("测试数据");
    		book.setType("测试类型");
    		book.setDescription("测试描述数据");
    		bookDao.insert(book);
    	}
    	@Test
    	void testGetById() {
    		System.out.println(bookDao.selectById(2));
    	}
    	...
    }
    

注意:如果这里直接运行testSave方法,会出现以下错误:

在这里插入图片描述

原因: 雪花算法丢失精度

解决方法:配置数据源与MyBatisPlus对应的基础配置(id生成策略使用数据库自增策略

mybatis-plus:
  global-config:
    db-config:
      table-prefix: tb1_
      id-type: auto

为方便调试可以开启MyBatisPlus的日志

mybatis-plus:
  configuration:
	log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

分页

分页操作需要设定分页对象IPage

    @Test
    void testGetPage() {
        IPage page = new Page(1,2);
        bookDao.selectPage(page, null);
    }
  • IPage对象中封装了分页操作中的所有数据
    • 数据
    • 当前页码值
    • 每页数据总量
    • 最大页码值
    • 数据总量

如果直接在测试类这么写,会出现以下问题:

在这里插入图片描述

由于我的数据比较少,如果数据比较多,会全部显示出来,而不是出现分页的效果,可以看到sql语句尾部缺少了limit,因为无法做到分页。想要分页,必须使用拦截器(分页操作是在MyBatisPlus的常规操作基础上增强得到,内部是动态的拼写SQL语句,因此需要增强对应的功能,使用MyBatisPlus拦截器实现

@Configuration
public class MPConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
   		//定义Mp拦截器
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //添加具体的拦截器
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return interceptor;
    }
}

结果如下:

在这里插入图片描述

小结:

  1. 使用IPage封装分页数据
  2. 分页操作依赖MyBatisPlus分页拦截器实现功能
  3. 借助MyBatisPlus日志查阅执行SQL语句

条件查询

使用QueryWrapper对象封装查询条件,推荐使用LambdaQueryWrapper对象,所有查询操作封装成方法调用

@Test
void testGetByCondition(){
	IPage page = new Page(1,2);
	LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<Book>();
	lqw.like(Book::getName,"Spring");
	bookDao.selectPage(page,lqw);
}
@Test
void testGetByCondition(){
	QueryWrapper<Book> qw = new QueryWrapper<Book>();
	qw.like("name","Spring");
	bookDao.selectList(qw);
}

4.5.4 业务层开发

  • Service层接口定义与数据层接口定义具有较大区别,不要混用

    • selectByUserNameAndPassword(String username,String password); //数据层
    • login(String username,String password); //业务层
  • 接口定义

    public interface BookService {
    	boolean save(Book book);
    	boolean delete(Integer id);
    	boolean update(Book book);
    	Book getById(Integer id);
    	List<Book> getAll();
    	IPage<Book> getByPage(int currentPage,int pageSize);
    }
    
  • 实现类定义

    @Service
    public class BookServiceImpl implements BookService {
    	@Autowired
    	private BookDao bookDao;
    	
    	//大于0,是因为测出来结果是修改的行数
    	public Boolean save(Book book) {
    	return bookDao.insert(book) > 0;
    	}
    	
    	public Boolean delete(Integer id) {
    	return bookDao.deleteById(id) > 0;
    	}
    	
    	public Boolean update(Book book) {
    	return bookDao.updateById(book) > 0;
    	}
    }
    
  • 测试类定义

    @SpringBootTest
    public class BookServiceTest {
    	@Autowired
    	private BookService bookService;
    	
    	@Test
    	void testGetById(){
    	bookService.getById(9);
    	}
    	
    	@Test
    	void testGetAll(){
    	bookService.getAll();
    	}
    	
    	@Test
    	void testGetByPage(){
    	bookService.getByPage(1,5);
    	}
    }
    

快速开发

  • 快速开发方案
    • 使用MyBatisPlus提供有业务层通用接口(ISerivce)与业务层通用实现(ServiceImpl<M,T>)
    • 在通用类基础上做功能重载或功能追加
    • 注意重载时不要覆盖原始操作,避免原始提供的功能丢失
      在这里插入图片描述
  • 接口定义
    在这里插入图片描述
  • 实现类定义
@Service
public class BookServiceImpl2 extends ServiceImpl<BookDao,Book> implements IBookService {
}
  • 实现类追加功能
@Service
public class BookServiceImpl2 extends ServiceImpl<BookDao,Book> implements IBookService {
	@Autowired
	private BookDao bookDao;
	
	public Boolean insert(Book book) {
		return bookDao.insert(book) > 0;
	}
	
	public Boolean modify(Book book) {
		return bookDao.updateById(book) > 0;
	}
	
	public Boolean delete(Integer id) {
		return bookDao.deleteById(id) > 0;
	}
	
	public Book get(Integer id) {
		return bookDao.selectById(id);
	}
}

总结小结:

  1. 使用通用接口(ISerivce)快速开发Service
  2. 使用通用实现类(ServiceImpl<M,T>)快速开发ServiceImpl
  3. 可以在通用接口基础上做功能重载或功能追加
  4. 注意重载时不要覆盖原始操作,避免原始提供的功能丢失

业务层消息一致性处理

在这里插入图片描述

  • 对异常进行统一处理,出现异常后,返回指定信息
    //作为springmvc的异常处理器
    @RestControllerAdvice
    public class ProjectExceptionAdvice {
    	//拦截所有的异常信息
    	@ExceptionHandler(Exception.class)
    	public R doOtherException(Exception ex){
    		//记录日志
    		//发送消息给运维
    		//发送邮件给开发人员,ex对象发送给开发人员
    		ex.printStackTrace();
    		return new R(false,null,"系统错误,请稍后再试!");
    	}
    }
    
  • 修改表现层返回结果的模型类,封装出现异常后对应的信息
    • flag:false
    • Data: null
    • 消息(msg): 要显示信息
    @Data
    public class R{
    	private Boolean flag;
    	private Object data;
    	private String msg;
    	public R(Boolean flag,Object data,String msg){
    		this.flag = flag;
    		this.data = data;
    		this.msg = msg;
    	}
    }
    
  • 页面消息处理,没有传递消息加载默认消息,传递消息后加载指定消息
    //添加
    handleAdd () {
    	//发送ajax请求
    	axios.post("/books",this.formData).then((res)=>{
    	//如果操作成功,关闭弹层,显示数据
    		if(res.data.flag){
    			this.dialogFormVisible = false;
    			this.$message.success("添加成功");
    		}else {
    			this.$message.error(res.data.msg);
    		}
    	}).finally(()=>{
    		this.getAll();
    	});
    },
    
  • 可以在表现层Controller中进行消息统一处理
@PostMapping
public R save(@RequestBody Book book) throws IOException {
	Boolean flag = bookService.insert(book);
	return new R(flag , flag ? "添加成功^_^" : "添加失败-_-!");
}
  • 页面消息处理
//添加
handleAdd () {
	//发送ajax请求
	axios.post("/books",this.formData).then((res)=>{
		if(res.data.flag){
			this.dialogFormVisible = false;
			this.$message.success(res.data.msg);
		}else {
			this.$message.error(res.data.msg);
		}
	}).finally(()=>{
		this.getAll();
	});
}

小结:

  1. 使用注解@RestControllerAdvice定义SpringMVC异常处理器用来处理异常
  2. 异常处理器必须被扫描加载,否则无法生效
  3. 表现层返回结果的模型类中添加消息属性用来传递消息到页面

4.5.5 表现层

表现层开发

  • 基于Restful进行表现层接口开发
  • 使用Postman测试表现层接口功能

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

小结:

  1. 基于Restful制作表现层接口
    • 新增:POST
    • 删除:DELETE
    • 修改:PUT
    • 查询:GET
  2. 接收参数
    • 实体数据:@RequestBody
    • 路径变量:@PathVariable

表现层消息一致性处理

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 设计表现层返回结果的模型类,用于后端与前端进行数据格式统一,也称为前后端数据协议
    @Data
    public class R{
    	private Boolean flag;
    	private Object data;
    }
    
    public R(){}
    
    public R(Boolean flag){
    	this.flag = flag;
    }
    
    public R(Boolean flag,Object data){
    	this.flag = flag;
    	this.data = data;
    	}
    }
    
  • 表现层接口统一返回值类型结果
    @RestController
    @RequestMapping("/books")
    public class BookController {
    	 @Autowired
        private IBookService bookService;
    
         @GetMapping
    	public R getAll() {
        	return new R(true, bookService.list());
    	}
    
        @PostMapping
        public R save(@RequestBody Book book) {
    //        R r = new R();
    //        boolean flag = bookService.save(book);
    //        r.setFlag(flag);
    //		  return r;
    
            return new R(bookService.save(book));
        }
    
        @PutMapping
        public R update(@RequestBody Book book) {
            return new R(bookService.modify(book));
        }
    
        @DeleteMapping("{id}")
        public R delete(@PathVariable Integer id) {
            return new R(bookService.delete(id));
        }
    
        @GetMapping("{id}")
        public R getById(@PathVariable Integer id) {
            return new R(true, bookService.getById(id));
        }
    
        @GetMapping("{currentPage}/{PageSize}")
        public R getPage(@PathVariable int currentPage, int pageSize) {
            return new R(true, bookService.getPage(currentPage, pageSize));
        }
    }
    

小结:

  1. 设计统一的返回值结果类型便于前端开发读取数据
  2. 返回值结果类型可以根据需求自行设定,没有固定格式
  3. 返回值结果模型类用于后端与前端进行数据格式统一,也称为前后端数据协议

4.5.6 前后端协议联调

  • 前后端分离结构设计中页面归属前端服务器
  • 单体工程中页面放置在resources目录下的static目录中(建议执行clean)

前端发送异步请求,调用后端接口

//列表
getAll() {
	axios.get("/books").then((res)=>{
		console.log(res.data);
	});
},

在这里插入图片描述

小结:

  1. 单体项目中页面放置在resources/static目录下
  2. created钩子函数用于初始化页面时发起调用
  3. 页面使用axios发送异步请求获取数据后确认前后端是否联

列表页

将查询数据返回到页面,利用前端数据双向绑定进行数据展示

methods: {
    //列表
    getAll() {
        //发送异步请求
        axios.get("/books").then((res)=>{
            // console.log(res.data);
            this.dataList =res.data.data;
        });
    },

在这里插入图片描述

4.5.7 功能开发

添加功能

  • 弹出添加窗口
    //弹出添加窗口
    handleCreate() {
    	this.dialogFormVisible = true;
    },
    
  • 添加功能
     //添加
    handleAdd() {
         //发送异步请求
         axios.post("/books",this.formData).then((res)=>{
             //如果操作成功,关闭弹层,显示数据
             if(res.data.flag){
                 this.dialogFormVisible = false;
                 this.$message.success("添加成功");
             }else {
                 this.$message.error("添加失败");
             }
         }).finally(()=>{
             //重新加载数据
             this.getAll();
         });
     },
    

添加完数据之后,点击新建,发现之前创建的数据仍在表单里,如下:

在这里插入图片描述

解决这种情况,需要设置“重置表单,清除数据”

  • 清除数据
    //重置表单
    resetForm() {
    this.formData = {};
    },
    
  • 重置表单应该在每次点开新建时操作完成
    //弹出添加窗口
    handleCreate() {
    this.dialogFormVisible = true;
    this.resetForm();
    },
    
  • 误点开创建功能,如何取消,设置取消添加功能
    //取消
    cancel(){
    	this.dialogFormVisible = false;
    	this.$message.info("操作取消");
    },
    

小结:

  1. 请求方式使用POST调用后台对应操作
  2. 添加操作结束后动态刷新页面加载数据
  3. 根据操作结果不同,显示对应的提示信息
  4. 弹出添加Div时清除表单数据

删除功能

// 删除
handleDelete(row) {
    // console.log(row);
    axios.delete("/books/" + row.id).then((res) => {
        if (res.data.flag) {
            this.$message.success("删除成功");
        } else {
            this.$message.error("删除失败");
        }
    }).finally(() => {
        this.getAll();
    });
},

通过上面的方法可以实现删除的功能,但是很容易误删,需要设置弹窗提醒,是否删除

// 删除
handleDelete(row) {
	//1.弹出提示框
	this.$confirm("此操作永久删除当前数据,是否继续?","提示",{
		type:'info'
	}).then(()=>{
		//2.做删除业务
		axios.delete("/books/"+row.id).then((res)=>{
		}).finally(()=>{
		this.getAll();
		});
	}).catch(()=>{
		//3.取消删除
		this.$message.info("取消删除操作");
	});
}

在这里插入图片描述
小结:

  1. 请求方式使用Delete调用后台对应操作
  2. 删除操作需要传递当前行数据对应的id值到后台
  3. 删除操作结束后动态刷新页面加载数据
  4. 根据操作结果不同,显示对应的提示信息
  5. 删除操作前弹出提示框避免误操作

修改功能

  • 弹出修改窗口
    //弹出编辑窗口
    handleUpdate(row) {
         axios.get("/books/" + row.id).then((res) => {
             if (res.data.flag && res.data.data != null) {
                 //展示弹层,加载数据
                 this.formData = res.data.data;
                 this.dialogFormVisible4Edit = true;
             } else {
                 this.$message.error("数据同步失败,自动刷新");
             }
         }).finally(()=>{
    		  this.getAll();
    	});
     },
    
  • 删除信息维护
    // 删除
    handleDelete(row) {
        // console.log(row);
        axios.delete("/books/" + row.id).then((res) => {
            if (res.data.flag) {
                this.$message.success("删除成功");
            } else {
                this.$message.error("数据同步失败,自动刷新");
            }
        }).finally(() => {
            this.getAll();
        });
    },
    
  • 修改
     //修改
    handleEdit() {
    	axios.put("/books",this.formData).then((res)=>{
    		//如果操作成功,关闭弹层并刷新页面
    		if(res.data.flag){
    			this.dialogFormVisible4Edit = false;
    			this.$message.success("修改成功");
    		}else {
    			this.$message.error("修改失败,请重试");
    		}
    	}).finally(()=>{
    		  this.getAll();
    	});
    },
    
  • 取消添加和修改
cancel(){
	this.dialogFormVisible = false;
	this.dialogFormVisible4Edit = false;
	this.$message.info("操作取消");
},

小结:

  1. 加载要修改数据通过传递当前行数据对应的id值到后台查询数据
  2. 利用前端数据双向绑定将查询到的数据进行回显
  3. 请求方式使用PUT调用后台对应操作
  4. 修改操作结束后动态刷新页面加载数据(同新增)
  5. 根据操作结果不同,显示对应的提示信息(同新增)

分页功能

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
小结:

  1. 使用el分页组件
  2. 定义分页组件绑定的数据模型
  3. 异步调用获取分页数据
  4. 分页数据页面回显

这样操作有一个BUG,最后一页,若只有一个数据,删了以后,最后一页的页面还在

处理方法:

对查询结果进行校验,如果当前页码值大于最大页码值,使用最大页码值作为当前页码值重新查询

@GetMapping("{currentPage}/{pageSize}")
public R getPage(@PathVariable int currentPage,@PathVariable int pageSize){
	IPage<Book> page = bookService.getPage(currentPage, pageSize);
	//如果当前页码值大于了总页码值,那么重新执行查询操作,使用最大页码值作为当前页码值
	if( currentPage > page.getPages()){
		page = bookService.getPage((int)page.getPages(), pageSize);
	}
	return new R(true, page);
}

条件查询

  • 查询条件数据封装
    • 单独封装
    • 与分页操作混合封装

由于这三个属性(name、type、description)都是跟着分页走的,所以直接放到pagination中

pagination: { 			//分页相关模型数据
	currentPage: 1, 	//当前页码
	pageSize:10, 		//每页显示的记录数
	total:0,			//总记录数
	name: "",
	type: "",
	description: ""
}
  • 页面数据模型绑定
     <div class="filter-container">
         <el-input placeholder="图书类别" v-model="pagination.type" style="width: 200px;"
                class="filter-item"></el-input>
         <el-input placeholder="图书名称" v-model="pagination.name" style="width: 200px;"
                class="filter-item"></el-input>
         <el-input placeholder="图书描述" v-model="pagination.description" style="width: 200px;"
                class="filter-item"></el-input>
         <el-button @click="getAll()" class="dalfBut">查询</el-button>
         <el-button type="primary" class="butT" @click="handleCreate()">新建</el-button>
    </div>
    
  • 组织数据成为get请求发送的数据并页面回显数据
    //分页查询
    getAll() {
        //获取查询条件,拼接查询条件
        // url:/books/1/10?type=???name=???&description=???
         param = "?name=" + this.pagination.name;
         param += "&type=" + this.pagination.type;
         param += "&description=" + this.pagination.description;
         console.log("-----------------" + param);
         
         //发送异步请求
         axios.get("/books/" + this.pagination.currentPage + "/" + this.pagination.pageSize + param).then((res) => {
                this.pagination.total = res.data.data.total;
                this.pagination.currentPage = res.data.data.current;
                this.pagination.pageSize = res.data.data.size;
                this.dataList = res.data.data.records;
            });
    },
    
  • Controller接收参数
    @GetMapping("{currentPage}/{pageSize}")
    public R getAll(@PathVariable int currentPage,@PathVariable int pageSize,Book book) {
    	System.out.println("参数=====>"+book);
    	IPage<Book> pageBook = bookService.getPage(currentPage,pageSize);
    	return new R(null != pageBook ,pageBook);
    }
    
  • 业务层接口功能开发
    public interface IBookService extends IService<Book> {
    	IPage<Book> getPage(Integer currentPage,Integer pageSize,Book book);
    }
    
    @Service
    public class IBookServiceImpl extends ServiceImpl<BookDao,Book> implements IBookService {
    	public IPage<Book> getPage(Integer currentPage,Integer pageSize,Book book){
     		IPage page = new Page(currentPage, pageSize);
     		
        	LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<>();
            lqw.like(Strings.isNotEmpty(book.getName()), Book::getName, book.getName());
            lqw.like(Strings.isNotEmpty(book.getType()), Book::getType, book.getType());
            lqw.like(Strings.isNotEmpty(book.getDescription()), Book::getDescription, book.getDescription());
            
       	 	return bookDao.selectPage(page, lqw);
    	}
    }
    

4.5.8 SSMP整合总结

  1. pom.xml
    配置起步依赖
  2. application.yml
    设置数据源、端口、框架技术相关配置等
  3. dao
    继承BaseMapper、设置@Mapper
  4. dao测试类
  5. service
    调用数据层接口或MyBatis-Plus提供的接口快速开发
  6. service测试类
  7. controller
    基于Restful开发,使用Postman测试跑通功能
  8. 页面
    放置在resources目录下的static目录中
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值