前言
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工程呢?
- 打开SpringBoot官网,选择Quickstart Your Project
- 创建工程,并保存项目
- 解压项目,通过IDE导入项目
基于SpringBoot官网创建项目,地址:SpringBoot官网
1.1.3 基于阿里云创建项目
- 选择start来源为自定义URL
- 输入阿里云start地址
- 创建项目
地址:https://start.aliyun.com
1.1.4 手工创建项目
- 创建普通Maven工程
- 继承spring-boot-starter-parent
- 添加依赖spring-boot-starter-web
- 制作引导类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
小结:
- 开发SpringBoot程序要继承spring-boot-starter-parent
- spring-boot-starter-parent中定义了若干个依赖管理
- 继承parent模块可以避免多个依赖使用相同技术时出现依赖版本冲突
- 继承parent的形式也可以采用引入依赖的形式实现效果
1.2.2 starter
- starter
- SpringBoot中常见项目名称,定义了当前项目使用的所有依赖坐标,以达到减少依赖配置的目的
- parent
- 所有SpringBoot项目要继承的项目,定义了若干个坐标版本号(依赖管理,而非依赖),以达到减少依赖冲突的目的
- spring-boot-starter-parent各版本间存在着诸多坐标版本不同
- 实际开发
- 使用任意坐标时,仅书写GAV中的G和A,V由SpringBoot提供,除非SpringBoot未提供对应版本V
- 如发生坐标错误,再指定Version(要小心版本冲突)
小结:
- 开发SpringBoot程序需要导入坐标时通常导入对应的starte
- 每个不同的starter根据功能不同,通常包含多个依赖坐标
- 使用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
小结:
- 内嵌Tomcat服务器是SpringBoot辅助功能之一
- 内嵌Tomcat工作原理是将Tomcat服务器作为对象运行,并将该对象交给Spring容器管理
- 变更内嵌服务器思想是去除现有服务器,添加全新的服务器
总结:
- 入门案例(4种方式创建SpringBoot工程)
- 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
小结:
- 配置文件间的加载优先级
- properties(最高)
- yml
- yaml(最低)
- 不同配置文件中相同配置按照加载优先级相互覆盖,不同配置文件中不同配置全部保留
2.3 yaml文件
yaml语法规则:
小结:
- yaml语法规则
- 大小写敏感
- 属性层级关系使用多行描述,每行结尾使用冒号结束
- 使用缩进表示层级关系,同层级左侧对齐,只允许使用空格(不允许使用Tab键)
- 属性值前面添加空格(属性名与属性值之间使用冒号+空格作为分隔)
- #表示注释
- 注意属性名冒号后面与数据之间有一个
空格
- 字面值、对象数据格式、数组数据格式(略)
2.4 yaml数据读取
如果数据量过大,可以封装全部数据到Environment对象
读取yaml引用类型属性数据:
- 定义数据模型封装yaml文件中对应的数据
- 定义为spring管控的bean(@Component)
- 指定加载的数据(@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 + '\'' + '}';
}
}
小结:
- 使用
@Value
配合SpEL读取单个数据 - 如果数据存在多层级,依次书写层级名称即可
- 在配置文件中可以使用
${属性名}
方式引用属性值 - 如果属性中出现特殊字符,可以使用
双引号
包裹起来作为字符解析 - 使用Environment对象封装全部配置信息
- 使用
@Autowired
自动装配数据到Environment对象中 - 使用@ConfigurationProperties注解绑定配置信息到封装类中
- 封装类需要定义为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(删除)
- http: / /localhost/users
-
根据REST风格对资源进行访问称为
RESTful
小结:- REST
- 动作4个
- 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
-
区别
@RequestParam用于接收url地址传参或表单传参
@RequestBody用于接收json数据
@PathVariable用于接收路径参数,使用{参数名称}描述路径参数
-
应用
后期开发中,发送请求参数超过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(默认)︰请求访问路径
小结:
- RESTful快速开发(标准开发)
- @RestController
- 标准请求动作映射(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();
}
}
小结:
- 导入测试对应的starter
- 测试类使用@SpringBootTest修饰
- 使用
自动装配
的形式添加要测试的对象
若是将测试类移到其他包,再运行,会出现以下错误:
原 因:当前的测试类并在引导类所在包或则子包下(不在同级目录),无法被扫描,测试类无法找到引导类。
@SpringBootTest(classes = Springboot04JunitApplication.class)
class Springboot04JunitApplicationTests {
}
注意事项:
如果测试类在SpringBoot启动类的包或子包中,可以省略启动类的设置,也就是省略classes的设定
小结:
- 测试类如果存在于引导类所在包或子包中无需指定引导类
- 测试类如果不存在于引导类所在的包或子包中需要通过classes属性指定引导类
4.2 整合MyBatis
小结:
- 勾选MyBatis技术,也就是导入MyBatis对应的starter
- 数据库连接相关信息转换成配置
- 数据库SQL映射需要添加@Mapper被容器识别到
4.3 整合MyBatis-Plus
MyBatis-Plus与MyBatis区别:
- 导入坐标不同
- 数据层实现简化
其余和整合MyBatis相同。
小结:
- 手工添加MyBatis-Plus对应的starter
- 数据层接口使用BaseMapper简化开发
- 需要使用的第三方技术无法通过勾选确定时,需要手工添加坐标
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
小结:
- 整合Druid需要导入Druid对应的starter
- 根据Druid提供的配置方式进行配置
- 整合第三方技术通用方式( 导入对应的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;
}
}
结果如下:
小结:
- 使用IPage封装分页数据
- 分页操作依赖MyBatisPlus分页拦截器实现功能
- 借助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);
}
}
总结小结:
- 使用通用接口(ISerivce)快速开发Service
- 使用通用实现类(ServiceImpl<M,T>)快速开发ServiceImpl
- 可以在通用接口基础上做功能重载或功能追加
- 注意重载时不要覆盖原始操作,避免原始提供的功能丢失
业务层消息一致性处理
- 对异常进行统一处理,出现异常后,返回指定信息
//作为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();
});
}
小结:
- 使用注解@RestControllerAdvice定义SpringMVC异常处理器用来处理异常
- 异常处理器必须被扫描加载,否则无法生效
- 表现层返回结果的模型类中添加消息属性用来传递消息到页面
4.5.5 表现层
表现层开发
- 基于Restful进行表现层接口开发
- 使用Postman测试表现层接口功能
小结:
- 基于Restful制作表现层接口
- 新增:POST
- 删除:DELETE
- 修改:PUT
- 查询:GET
- 接收参数
- 实体数据:@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)); } }
小结:
- 设计统一的返回值结果类型便于前端开发读取数据
- 返回值结果类型可以根据需求自行设定,没有固定格式
- 返回值结果模型类用于后端与前端进行数据格式统一,也称为前后端数据协议
4.5.6 前后端协议联调
- 前后端分离结构设计中页面归属前端服务器
- 单体工程中页面放置在resources目录下的static目录中(建议执行clean)
前端发送异步请求,调用后端接口
//列表
getAll() {
axios.get("/books").then((res)=>{
console.log(res.data);
});
},
小结:
- 单体项目中页面放置在resources/static目录下
- created钩子函数用于初始化页面时发起调用
- 页面使用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("操作取消"); },
小结:
- 请求方式使用POST调用后台对应操作
- 添加操作结束后动态刷新页面加载数据
- 根据操作结果不同,显示对应的提示信息
- 弹出添加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("取消删除操作");
});
}
小结:
- 请求方式使用Delete调用后台对应操作
- 删除操作需要传递当前行数据对应的id值到后台
- 删除操作结束后动态刷新页面加载数据
- 根据操作结果不同,显示对应的提示信息
- 删除操作前弹出提示框避免误操作
修改功能
- 弹出修改窗口
//弹出编辑窗口 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("操作取消");
},
小结:
- 加载要修改数据通过传递当前行数据对应的id值到后台查询数据
- 利用前端数据双向绑定将查询到的数据进行回显
- 请求方式使用PUT调用后台对应操作
- 修改操作结束后动态刷新页面加载数据(同新增)
- 根据操作结果不同,显示对应的提示信息(同新增)
分页功能
小结:
- 使用el分页组件
- 定义分页组件绑定的数据模型
- 异步调用获取分页数据
- 分页数据页面回显
这样操作有一个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整合总结
- pom.xml
配置起步依赖 - application.yml
设置数据源、端口、框架技术相关配置等 - dao
继承BaseMapper、设置@Mapper - dao测试类
- service
调用数据层接口或MyBatis-Plus提供的接口快速开发 - service测试类
- controller
基于Restful开发,使用Postman测试跑通功能 - 页面
放置在resources目录下的static目录中