一、快速上手SpringBoot
1.SpringBoot入门程序开发步骤:
①创建新模块,选择Spring Initializr,并配置模块相关基础信息
②:选择当前模块需要使用的技术集
③:开发控制器类:controller
④:运行自动生成的Application类
2.Spring程序与SpringBoot程序对比
Spring程序缺点
- 依赖设置繁琐
- 配置繁琐
SpringBoot程序优点
- 起步依赖(简化依赖配置)
- 自动配置(简化常用工程相关配置)
- 辅助功能(内置服务器,……)
注意事项:基于idea开发SpringBoot程序需要确保联网且能够加载到程序框架结构
3.创建SpringBoot工程的四种方式
- 基于Idea创建SpringBoot工程,上述
- 基于官网创建SpringBoot工程
- 基于阿里云创建SpringBoot工程
- 手工创建Maven工程修改为SpringBoot工程(一般不用)
3.1基于官网创建SpringBoot工程
步骤:
- 打开SpringBoot官网(start.spring.io/),选择Quickst… Your Project
- 创建工程,并保存项目
- 解压项目,通过IDE导入项目
3.2基于阿里云创建SpringBoot工程
基于阿里云创建项目,地址:start.aliyun.com
注意事项:
阿里云提供的坐标版本较低,如果需要使用高版本,进入工程后手工切换SpringBoot版本 阿里云提供的工程模板与Spring官网提供的工程模板略有不同 步骤:
- 选择start来源为自定义URL
- 输入阿里云start地址
- 创建项目
4.如何隐藏指定文件/文件夹、复制模块
- Setting → File Types → Ignored Files and Folders
- 输入要隐藏的文件名,支持*号通配符
- 回车确认添加
复制模块
5.浅谈工作原理
5.1.parent(仅定义,未使用)
- 开发SpringBoot程序要继承spring-boot-starter-parent
- spring-boot-starter-parent中定义了若干个依赖管理
- 继承parent模块可以避免多个依赖使用相同技术时出现依赖版本冲突
- 继承parent的形式也可以采用引入依赖的形式实现效果
5.2.starter(解决配置问题)
starter的作用是为了简化配置,因为如果开发中使用的某项技术需要导入的jar包依赖比较多,就需要添加多个依赖配置。starter会为某种技术提供一系列常用的依赖坐标,使得我们使用这种技术的时候,只需要导入starter就可以了,而不是多个依赖,进而简化的配置。
- 开发SpringBoot程序需要导入坐标时通常导入对应的starter
- 每个不同的starter根据功能不同,通常包含多个依赖坐标
- 使用starter可以实现快速配置的效果,达到简化配置的目的
5.3.引导类
- SpringBoot的引导类是Boot工程的执行入口,运行main方法就可以启动项目 (未启动Web服务器)
- SpringBoot工程运行后初始化Spring容器,扫描引导类所在包加载bean
5.4.辅助功能(内嵌tomcat)
- 内嵌Tomcat服务器是SpringBoot辅助功能之一
- 内嵌Tomcat工作原理是将Tomcat服务器作为对象运行,并将该对象交给Spring容器管理
- 变更内嵌服务器思想是去除现有服务器,添加全新的服务器
<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>
6.REST风格
-
REST(Representational State Transfer)表现形式状态转换
-
传统风格资源描述形式
- http://localhost/user/getById?id=1 (得到id为1的用户)
- http://localhost/user/saveUser (保存用户)
-
REST风格描述形式- http://localhost/user/1 (得到id为1的用户)
- http://localhost/user (保存用户)
-
优点:
- 隐藏资源的访问行为, 无法通过地址得知对资源是何种操作
- 书写简化
-
按照REST风格访问资源时使用行为动作区分对资源进行了何种操作
GET用来获取资源,POST用来新建资源,PUT用来更新资源,DELETE用来删除资源- http://localhost/users 查询全部用户信息
GET(查询) - http://localhost/users/1 查询指定用户信息
GET(查询) - http://localhost/users 添加用户信息
POST(新增/保存) - http://localhost/users 修改用户信息
PUT(修改/更新) - http://localhost/users/1 删除用户信息
DELETE(删除)
-
注意:
- 上述行为是约定方式,约定不是规范,可以打破,所以称
REST风格,而不是REST规范 - 描述模块的名称通常使用复数,也就是加s的格式描述,表示此类资源,而非单个资源,例如:
users、 books、 accounts…
- 上述行为是约定方式,约定不是规范,可以打破,所以称
-
@RequestMapping
- @PathVariable
- @RequestBody @RequestParam @PathVariable
6.1 RESTful快速开发
使用 @RestController 注解开发 RESTful 风格
@RequestMapping("/books") //请求路径前缀
使用 @GetMapping @PostMapping @PutMapping @DeleteMapping 简化 @RequestMapping 注解开发
二、基础配置
SpringBoot默认配置文件application.properties,通过键值对配置对应属性
# 服务器端口配制 server.port=80
# 日志 logging.level.root=info
SpringBoot提供了多种属性配置方式,yml是主流格式。
-
SpringBoot配置文件加载顺序
application.properties>application.yml>application.yaml
1.yaml
基本语法
- key: value -> value 前面一定要有空格
- 大小写敏感
- 属性层级关系使用多行描述,每行结尾使用冒号结束
- 使用缩进表示层级关系,同层级左侧对齐,只允许使用空格(不允许使用Tab键)
- 属性值前面添加空格(属性名与属性值之间使用冒号+空格作为分隔)
1.1数据读取
1)读取单个数据
使用@Value读取单个数据,属性名引用方式: ${一级属性名.二级属性名……}
- 使用@Value配合SpEL读取单个数据
- 如果数据存在多层级,依次书写层级名称即可
使用属性名引用方式引用属性读取数据
- 在配置文件中可以使用${属性名}方式引用属性值
- 如果属性中出现特殊字符,可以使用双引号包裹起来作为字符解析
2)读取yaml全部属性数据,封装全部数据到Environment对象
- 使用Environment对象封装全部配置信息
- 使用@Autowired自动装配数据到Environment对象中
3)读取yaml引用类型属性数据
- 自定义对象封装指定数据
@component表示将MyDataSaource定义为spring管控的bean- 使用自动装配封装指定数据
@Autowired
private MyDataSource myDataSource;
System.out.println(myDataSource);
三、Springboot整合第三方技术
1.SpringBoot整合JUnit
- 添加Junit的起步依赖 Spring Initializr 创建时自带
xml
复制代码
<!--测试的起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
- SpringBoot整合JUnit
typescript
复制代码
@SpringBootTest //设置JUnit加载的SpringBoot启动类
class Springboot07JunitApplicationTests {
@Autowired //使用自动装配的形式添加要测试的对象
private BookService bookService;
@Test //需要自动测试的函数
public void testSave(){
bookService.save();
}
}
整合JUnit——classes属性
- 测试类如果不存在于引导类所在的包或子包中,需要通过
classes属性指定引导类。
java
复制代码
@SpringBootTest(classes = Springboot04JunitApplication.class)
//@ContextConfiguration(classes = Springboot04JunitApplication.class)
class Springboot04JunitApplicationTests {
@Autowired //1.注入你要测试的对象
private BookDao bookDao;
@Test
void contextLoads() {
//2.执行要测试的对象对应的方法
bookDao.save();
System.out.println("two...");
}
}
2. SpringBoot整合MyBatis
- 创建新模块,选择当前模块需要使用的技术集(
MyBatis、MySQL) - 设置数据源参数
yaml
复制代码
#DB Configuration:
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot_db
username: root
password: 123456
-
在 springboot_db 数据库中创建 user 表
-
创建实体Bean
arduino
复制代码
public class User {
// 主键
private Long id;
// 用户名
private String username;
// 密码
private String password;
// 姓名
private String name;
//此处省略getter,setter,toString方法 .. ..
}
- 定义数据层接口与映射配置
kotlin
复制代码
@Mapper // 如果在接口类上添加了@Mapper,在编译之后会生成相应的接口实现类。
public interface UserDao {
@Select("select * from user")
public List<User> getAll();
}
- 测试类中注入dao接口,测试功能
ruby
复制代码
@SpringBootTest
class Springboot05MybatisApplicationTests {
@Autowired
private UserDao userDao;
@Test
void contextLoads() {
List<User> userList = userDao.getAll();
System.out.println(userList);
// [User{id=1, username='zhangsan', password='123', name='张三'}, User{id=2, username='lisi', password='123', name='李四'}]
}
}
总结:
- 勾选MyBatis技术,也就是导入MyBatis对应的starter
- 数据库连接相关信息转换成配置
- 数据库SQL映射需要添加@Mapper被容器识别到
SpringBoot整合MyBatis常见问题处理
SpringBoot版本低于2.4.3(不含),Mysql驱动版本大于8.0时,需要在url连接串中配置时区
bash
复制代码
jdbc:mysql://localhost:3306/springboot_db?serverTimezone=UTC
-
或在
MySQL数据库端配置时区解决此问题-
MySQL 8.X驱动强制要求设置时区
- 修改url,添加
serverTimezone设定 - 修改
MySQL数据库配置(略)
- 修改url,添加
-
驱动类过时,提醒更换为
com.mysql.cj.jdbc.Drive
-
3.Springboot整合MP
- 手动添加
SpringBoot整合MyBatis-Plus的坐标,可以通过mvnrepository获取。由于SpringBoot中未收录MyBatis-Plus的坐标版本,需要指定对应的Version
xml
复制代码
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3</version>
</dependency>
- 数据层接口使用BaseMapper简化开发,定义数据层接口与映射配置,继承BaseMapper
java
复制代码
@Mapper
public interface UserDao extends BaseMapper<User> {
// 构建时,会自动生成对应user的增删查改函数
}
- 其他同SpringBoot整合MyBatis
- 测试类中注入dao接口,测试功能
ruby
复制代码
@SpringBootTest
class Springboot06MybatisPlusApplicationTests {
@Autowired
private UserDao userDao;
@Test
void contextLoads() {
List<User> users = userDao.selectList(null);
System.out.println(users);
// [User{id=1, username='zhangsan', password='123', name='张三'}, User{id=2, username='lisi', password='123', name='李四'}]
}
}
- 注意: 如果你的数据库表有前缀要在 application.yml 添加如下配制
yaml
复制代码
#设置Mp相关的配置
mybatis-plus:
global-config:
db-config:
table-prefix: tbl_
4.SpringBoot整合Druid
- 导入Druid对应的starter
xml
复制代码
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.6</version>
</dependency>
- 指定数据源类型 (这种方式只需导入一个 Druid 的坐标)
yaml
复制代码
#DB Configuration:
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot_db?serverTimezone=UTC
username: root
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
- 或者 变更Druid的配置方式(推荐) 这种方式需要导入 Druid对应的starter
yaml
复制代码
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot_db?serverTimezone=UTC
username: root
password: 123456
5.SSMP整合案例制作分析
- 案例实现方案分析与流程解析
markdown
复制代码
1. 案例实现方案分析
实体类开发————使用Lombok快速制作实体类
Dao开发————整合MyBatisPlus,制作数据层测试类
Service开发————基于MyBatisPlus进行增量开发,制作业务层测试类
Controller开发————基于Restful开发,使用PostMan测试接口功能
Controller开发————前后端开发协议制作
页面开发————基于VUE+ElementUI制作,前后端联调,页面数据处理,页面消息处理,列表、新增、修改、删除、分页、查询
项目异常处理
按条件查询————页面功能调整、Controller修正功能、Service修正功能
2. SSMP案例制作流程解析
先开发基础CRUD功能,做一层测一层
调通页面,确认异步提交成功后,制作所有功能
添加分页功能与查询功能
5.1模块创建
- 创建模块和数据库
- 勾选SpringMVC与MySQL坐标
- pom.xml文件增加mybatis-plus和druid库
javascript
复制代码
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
// mybatis-plus
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3</version>
</dependency>
// druid
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.6</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
5.2实体类开发
Lombok,一个Java类库,提供了一组注解,简化POJO实体类开发。- 为当前实体类在编译期设置对应的
get/set方法,toString方法,hashCode方法,equals方法等。
xml
复制代码
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
lombok版本由SpringBoot提供,无需指定版本,@Data注解。
5.3 数据层标准开发(基础CRUD)
- 导入MyBatisPlus与Druid对应的starter
xml
复制代码
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.6</version>
</dependency>
- 配置数据源与MyBatisPlus对应的基础配置(id生成策略使用数据库自增策略)
yaml
复制代码
# druid 数据源配制
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot_db?serverTimezone=UTC
username: root
password: 123456
# mybatis-plus
mybatis-plus:
global-config:
db-config:
table-prefix: tbl_
id-type: auto # 主键策略
- 继承BaseMapper并指定泛型
java
复制代码
@Mapper
public interface BookDao extends BaseMapper<Book> {
/**
* 查询一个
* 这是 Mybatis 开发
* @param id
* @return
*/
@Select("select * from tbl_book where id = #{id}")
Book getById(Integer id);
}
- 制作测试类测试结果
typescript
复制代码
@SpringBootTest
public class BookDaoTestCase {
@Autowired
private BookDao bookDao;
@Test
void testGetById() {
System.out.println(bookDao.getById(1));
System.out.println(bookDao.selectById(1));
}
@Test
void testSave() {
Book book = new Book();
book.setType("测试数据123");
book.setName("测试数据123");
book.setDescription("测试数据123");
bookDao.insert(book);
}
@Test
void testUpdate() {
Book book = new Book();
book.setId(13);
book.setType("测试数据asfd");
book.setName("测试数据123");
book.setDescription("测试数据123");
bookDao.updateById(book);
}
@Test
void testDelete() {
bookDao.deleteById(13);
}
@Test
void testGetAll() {
System.out.println(bookDao.selectList(null));
}
}
开启MP运行日志
- 为方便调试可以开启MyBatisPlus的日志
yaml
复制代码
# mybatis-plus
mybatis-plus:
global-config:
db-config:
table-prefix: tbl_
id-type: auto # 主键策略
configuration:
# 开启MyBatisPlus的日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
- 设置日志输出方式为标准输出
5.4分页
- 分页操作需要设定分页对象IPage
java
复制代码
@Test
void testGetPage() {
IPage page = new Page(1, 5);
bookDao.selectPage(page, null);
}
-
IPage对象中封装了分页操作中的所有数据
- 数据
- 当前页码值
- 每页数据总量
- 最大页码值
- 数据总量
-
分页操作是在
MyBatisPlus的常规操作基础上增强得到,内部是动态的拼写SQL语句,因此需要增强对应的功能,使用MyBatisPlus拦截器实现。
java
复制代码
@Configuration //配置类,会自动扫描加载
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
//1. 定义 Mp 拦截器
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//2. 添加具体的拦截器 分页拦截器
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
}
5.5数据层标准开发(条件查询)
- 使用
QueryWrapper对象封装查询条件,推荐使用LambdaQueryWrapper对象,所有查询操作封装成方法调用
typescript
复制代码
@Test
void testGetBy2() {
LambdaQueryWrapper<Book> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.like(Book::getName, "Spring");
bookDao.selectList(lambdaQueryWrapper);
}
@Test
void testGetBy() {
QueryWrapper<Book> queryWrapper = new QueryWrapper<>();
queryWrapper.like("name", "Spring"); // name like %spring%
bookDao.selectList(queryWrapper);
}
- 支持动态拼写查询条件
ini
复制代码
@Test
void testGetBy2() {
String name = "1";
LambdaQueryWrapper<Book> lambdaQueryWrapper = new LambdaQueryWrapper<>();
//if (name != null) lambdaQueryWrapper.like(Book::getName,name);
// 条件
lambdaQueryWrapper.like(Strings.isNotEmpty(name), Book::getName, name);
bookDao.selectList(lambdaQueryWrapper);
}
5.6-业务层标准开发(基础CRUD)
-
Service层接口定义与数据层接口定义具有较大区别,不要混用selectByUserNameAndPassword(String username,String password);数据层接口login(String username,String password);Service层接口
-
接口定义,
Service接口名称定义成业务名称,并与Dao接口名称进行区分
scss
复制代码
public interface BookService {
Boolean save(Book book);
Boolean update(Book book);
Boolean delete(Integer id);
Book getById(Integer id);
List<Book> getAll();
IPage<Book> getPage(int currentPage,int pageSize);
}
- 实现类定义
typescript
复制代码
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
@Override
public Boolean save(Book book) {
return bookDao.insert(book) > 0;
}
@Override
public Boolean update(Book book) {
return bookDao.updateById(book) > 0;
}
@Override
public Boolean delete(Integer id) {
return bookDao.deleteById(id) > 0;
}
@Override
public Book getById(Integer id) {
return bookDao.selectById(id);
}
@Override
public List<Book> getAll() {
return bookDao.selectList(null);
}
@Override
public IPage<Book> getPage(int currentPage, int pageSize) {
IPage page = new Page(currentPage, pageSize);
bookDao.selectPage(page, null);
return page;
}
}
- 测试类定义
scss
复制代码
@SpringBootTest
public class BookServiceTestCase {
@Autowired
private BookService bookService;
@Test
void testGetById() {
System.out.println(bookService.getById(4));
}
@Test
void testSave() {
Book book = new Book();
book.setType("测试数据123");
book.setName("测试数据123");
book.setDescription("测试数据123");
bookService.save(book);
}
@Test
void testUpdate() {
Book book = new Book();
book.setId(14);
book.setType("测试数据asfd");
book.setName("测试数据123");
book.setDescription("测试数据123");
bookService.update(book);
}
@Test
void testDelete() {
bookService.delete(14);
}
@Test
void testGetAll() {
System.out.println(bookService.getAll());
}
@Test
void testGetPage() {
IPage<Book> page = bookService.getPage(2, 5);
System.out.println(page.getCurrent());
System.out.println(page.getSize());
System.out.println(page.getPages());
System.out.println(page.getTotal());
System.out.println(page.getRecords());
}
}
5.7(基于MyBatisPlus构建)
-
快速开发方案
- 使用
MyBatisPlus提供有业务层通用接口(ISerivce)与业务层通用实现类(ServiceImpl<M,T>) - 在通用类基础上做功能重载或功能追加
- 注意重载时不要覆盖原始操作,避免原始提供的功能丢失
- 使用
-
接口定义 与 接口追加功能
- 使用通用接口(ISerivce)快速开发Service
- 使用通用接口(ISerivce)快速开发Service
-
实现类定义,使用通用实现类(ServiceImpl<M,T>)快速开发ServiceImpl
scala
复制代码
@Service
public class BookServiceImpl extends ServiceImpl<BookDao, Book> implements IBookService { }
- 实现类追加功能,可以在通用接口基础上做功能重载或功能追加,注意重载时不要覆盖原始操作,避免原始提供的功能丢失
typescript
复制代码
@Service
public class BookServiceImpl 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);
}
}
- 测试类定义
scss
复制代码
@SpringBootTest
public class BookServiceTest {
@Autowired
private IBookService bookService;
@Test
void testGetById() {
System.out.println(bookService.getById(4));
}
@Test
void testSave() {
Book book = new Book();
book.setType("测试数据123");
book.setName("测试数据123");
book.setDescription("测试数据123");
bookService.save(book);
}
@Test
void testUpdate() {
Book book = new Book();
book.setId(14);
book.setType("===========");
book.setName("测试数据123");
book.setDescription("测试数据123");
bookService.updateById(book);
}
@Test
void testDelete() {
bookService.removeById(14);
}
@Test
void testGetAll() {
System.out.println(bookService.list());
}
@Test
void testGetPage() {
IPage<Book> page = new Page<>(2, 5);
bookService.page(page);
System.out.println(page.getCurrent());
System.out.println(page.getSize());
System.out.println(page.getPages());
System.out.println(page.getTotal());
System.out.println(page.getRecords());
}
}
5.8表现层标准开发
- 基于Restful进行表现层接口开发
- 使用Postman测试表现层接口功能
- 表现层开发
less
复制代码
@RestController
@RequestMapping("/books")
public class BookController {
@Autowired
private IBookService bookService;
@GetMapping
public List<Book> getAll() {
return bookService.list();
}
@PostMapping
public Boolean save(@RequestBody Book book) {
return bookService.save(book);
}
@PutMapping
public Boolean update(@RequestBody Book book) {
return bookService.modify(book);
}
@DeleteMapping("{id}")
public Boolean delete(@PathVariable Integer id) {
return bookService.delete(id);
}
@GetMapping("{id}")
public Book getById(@PathVariable Integer id) {
return bookService.getById(id);
}
@GetMapping("{currentPage}/{pageSize}")
public IPage<Book> getPage(@PathVariable Integer currentPage, @PathVariable int pageSize) {
return bookService.getPage(currentPage, pageSize);
}
}
- 添加分页的业务层方法
- IBookService
arduino
复制代码
IPage<Book> getPage(int currentPage,int pageSize);
- BookServiceImpl
java
复制代码
@Override
public IPage<Book> getPage(int currentPage, int pageSize) {
IPage page = new Page(currentPage, pageSize);
bookDao.selectPage(page, null);
return page;
}
- 功能测试
39-表现层数据一致性处理(R对象)
-
之前的格式,当数据为 null 可能出现的问题
- 查询id不存在的数据,返回 null
- 查询过程中抛出异常,catch 中返回 null
-
增加一个 data 属性,把数据全部封装到 data 里
-
增加 一个状态属性
-
设计表现层返回结果的模型类,用于后端与前端进行数据格式统一,也称为前后端数据协议
kotlin
复制代码
@Data
public class R {
private Boolean flag;
private Object data;
public R() {
}
/**
* 不返回数据的构造方法
*
* @param flag
*/
public R(Boolean flag) {
this.flag = flag;
}
/**
* 返回数据的构造方法
*
* @param flag
* @param data
*/
public R(Boolean flag, Object data) {
this.flag = flag;
this.data = data;
}
}
- 表现层接口统一返回值类型结果
less
复制代码
@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) {
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 Integer currentPage, @PathVariable int pageSize) {
return new R(true, bookService.getPage(currentPage, pageSize));
}
}
40-前后端调用(axios发送异步请求)
-
使用VUE的方法时提示报错:
-
表示:该方法定义的缺陷是不支持当前的JavaScript版本,虽然可以程序可以正常运行,但是这个方法会出现红色的波浪线,很不爽。
-
解决办法:
打开 File -> Settings -> Languages & Frameworks -> Javascript
把JavaScript版本为ECMAScript 6就可以了- 前后端分离结构设计中页面归属前端服务器
- 单体工程中页面放置在resources目录下的static目录中(建议执行clean)
- 前端发送异步请求,调用后端接口
javascript
复制代码
//钩子函数,VUE对象初始化完成后自动执行
created() {
//调用查询全部数据的操作
this.getAll();
},
//列表
getAll() {
//发送异步请求
axios.get("/books").then((res)=>{
console.log(res.data);
})
},
-
小结:
- 单体项目中页面放置在resources/static目录下
- created钩子函数用于初始化页面时发起调用
- 页面使用axios发送异步请求获取数据后确认前后端是否联通
41-列表功能
- 列表页
javascript
复制代码
//列表
getAll() {
//发送异步请求
axios.get("/books").then((res) => {
//console.log(res.data);
this.dataList = res.data.data;
})
},
-
小结
- 将查询数据返回到页面,利用前端数据双向绑定进行数据展示
42-添加功能
- 弹出添加窗口
javascript
复制代码
// 弹出添加窗口
handleCreate() {
this.dialogFormVisible = true;
},
- 清除数据
javascript
复制代码
//重置表单
resetForm() {
this.formData = {};
},
- 在弹出添加窗口时 清除数据
javascript
复制代码
//弹出添加窗口
handleCreate() {
this.dialogFormVisible = true;
this.resetForm();
},
- 发送添加请求
kotlin
复制代码
//添加
handleAdd() {
axios.post("/books", this.formData).then((res) => {
//判断当前操作是否成功
if (res.data.flag) {
//1.关闭弹层
this.dialogFormVisible = false;
this.$message.success("添加成功");
} else {
this.$message.error("添加失败");
}
}).finally(() => {
//2.重新加载数据
this.getAll();
})
},
- 取消添加
javascript
复制代码
//取消
cancel() {
//1.关闭弹层
this.dialogFormVisible = false;
//2.提示用户
this.$message.info("当前操作取消");
},
-
小结:
- 请求方式使用POST调用后台对应操作
- 添加操作结束后动态刷新页面加载数据
- 根据操作结果不同,显示对应的提示信息
- 弹出添加Div时清除表单数据
43-删除功能
- 删除
javascript
复制代码
// 删除
handleDelete(row) {
axios.delete("/books/" + row.id).then((res) => {
if (res.data.flag) {
this.$message.success("删除成功");
} else {
this.$message.error("删除失败");
}
}).finally(() => {
this.getAll();
});
}
- 加入确认删除对话框
kotlin
复制代码
// 删除
handleDelete(row) {
//1. 弹出提示框
this.$confirm("些操作永久删除当前信息,是否继续?", "提示", {type: "info"}).then(() => {
//2. 做删除业务
axios.delete("/books/" + row.id).then((res) => {
//判断当前操作是否成功
if (res.data.flag) {
this.$message.success("删除成功");
} else {
this.$message.error("删除失败");
}
}).finally(() => {
//2.重新加载数据
this.getAll();
})
}).catch(() => {
//3. 取消删除
this.$message.info("取消操作");
});
},
-
小结:
- 请求方式使用Delete调用后台对应操作
- 删除操作需要传递当前行数据对应的id值到后台
- 删除操作结束后动态刷新页面加载数据
- 根据操作结果不同,显示对应的提示信息
- 删除操作前弹出提示框避免误操作
44-修改功能(加载数据)
- 弹出修改窗口
kotlin
复制代码
//弹出编辑窗口
handleUpdate(row) {
axios.get("/books/" + row.id).then((res) => {
if (res.data.flag && res.data.data != null) {
// 展示弹层,加载数据
this.dialogFormVisible4Edit = true;
this.formData = res.data.data;
} else {
this.$message.error("数据同步失败,自动刷新");
}
}).finally(() => {
//重新加载数据
this.getAll();
});
},
- 删除消息维护
kotlin
复制代码
// 删除
handleDelete(row) {
//1. 弹出提示框
this.$confirm("些操作永久删除当前信息,是否继续?", "提示", {type: "info"}).then(() => {
//2. 做删除业务
axios.delete("/books/" + row.id).then((res) => {
//判断当前操作是否成功
if (res.data.flag) {
this.$message.success("删除成功");
} else {
this.$message.error("数据同步失败,自动刷新");
}
}).finally(() => {
//2.重新加载数据
this.getAll();
});
}).catch(() => {
//3. 取消删除
this.$message.info("取消操作");
});
},
-
小结:
- 加载要修改数据通过传递当前行数据对应的id值到后台查询数据
- 利用前端数据双向绑定将查询到的数据进行回显
45-修改功能
- 修改
kotlin
复制代码
//修改
handleEdit() {
axios.put("/books", this.formData).then((res) => {
//判断当前操作是否成功
if (res.data.flag) {
//1.关闭弹层
this.dialogFormVisible4Edit = false;
this.$message.success("修改成功");
} else {
this.$message.error("修改失败");
}
}).finally(() => {
//2.重新加载数据
this.getAll();
});
},
- 取消添加和修改
kotlin
复制代码
//取消
cancel() {
//1.关闭弹层
this.dialogFormVisible = false;
this.dialogFormVisible4Edit = false;
//2.提示用户
this.$message.info("当前操作取消");
},
-
小结:
- 请求方式使用PUT调用后台对应操作
- 修改操作结束后动态刷新页面加载数据(同新增)
- 根据操作结果不同,显示对应的提示信息(同新增)
46-异常消息处理
- 业务操作成功或失败返回数据格式
json
复制代码
{ "flag": true, "data": null }
{ "flag": false, "data": null }
- 后台代码BUG导致数据格式不统一性
json
复制代码
{ "timestamp": "2021-11-07T12:44:29.343+00:00", "status": 500, "error": "Internal Server Error", "path": "/books" }
- 对异常进行统一处理,出现异常后,返回指定信息
java
复制代码
@RestControllerAdvice
public class ProjectExceptionAdvice {
//拦截所有的异常信息
@ExceptionHandler(Exception.class)
public R doException(Exception ex) {
// 记录日志
// 发送消息给运维
// 发送邮件给开发人员 ,ex 对象发送给开发人员
ex.printStackTrace();
return new R(false, null, "系统错误,请稍后再试!");
}
}
-
修改表现层返回结果的模型类,封装出现异常后对应的信息
- flag:false
- Data: null
- 消息(msg): 要显示信息
kotlin
复制代码
@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;
}
}
- 页面消息处理,没有传递消息加载默认消息,传递消息后加载指定消息
kotlin
复制代码
//添加
handleAdd() {
axios.post("/books", this.formData).then((res) => {
//判断当前操作是否成功
if (res.data.flag) {
//1.关闭弹层
this.dialogFormVisible = false;
this.$message.success("添加成功");
} else {
this.$message.error(res.data.msg);
}
}).finally(() => {
//2.重新加载数据
this.getAll();
})
},
- 可以在表现层Controller中进行消息统一处理
less
复制代码
@PostMapping
public R save(@RequestBody Book book) throws IOException {
//if (book.getName().equals("123")) throw new IOException();
boolean flag = bookService.save(book);
return new R(flag, flag ? "添加成功^_^" : "添加失败-_-!");
}
- 页面消息处理
kotlin
复制代码
//添加
handleAdd() {
axios.post("/books", this.formData).then((res) => {
//判断当前操作是否成功
if (res.data.flag) {
//1.关闭弹层
this.dialogFormVisible = false;
this.$message.success(res.data.msg);
} else {
this.$message.error(res.data.msg);
}
}).finally(() => {
//2.重新加载数据
this.getAll();
})
},
-
小结:
- 使用注解@RestControllerAdvice定义SpringMVC异常处理器用来处理异常的
- 异常处理器必须被扫描加载,否则无法生效
- 表现层返回结果的模型类中添加消息属性用来传递消息到页面
47-分页
- 页面使用 el 分页组件添加分页功能
ini
复制代码
<!--分页组件-->
<div class="pagination-container">
<el-pagination
class="pagiantion"
@current-change="handleCurrentChange"
:current-page="pagination.currentPage"
:page-size="pagination.pageSize"
layout="total, prev, pager, next, jumper"
:total="pagination.total">
</el-pagination>
</div>
- 定义分页组件需要使用的数据并将数据绑定到分页组件
kotlin
复制代码
data: {
pagination: { // 分页相关模型数据
currentPage: 1, // 当前页码
pageSize: 10, // 每页显示的记录数
total: 0, // 总记录数
}
},
- 替换查询全部功能为分页功能
javascript
复制代码
getAll() {
axios.get("/books/" + this.pagination.currentPage + "/" + this.pagination.pageSize).then((res) => {});
},
-
分页查询
- 使用路径参数传递分页数据或封装对象传递数据
less
复制代码
@GetMapping("{currentPage}/{pageSize}")
public R getPage(@PathVariable Integer currentPage, @PathVariable int pageSize) {
return new R(true, bookService.getPage(currentPage, pageSize));
}
- 加载分页数据
kotlin
复制代码
//分页查询
getAll() {
//发送异步请求
axios.get("/books/" + this.pagination.currentPage + "/" + this.pagination.pageSize).then((res) => {
//console.log(res.data);
this.pagination.currentPage = res.data.data.current;
this.pagination.pageSize = res.data.data.size;
this.pagination.total = res.data.data.total;
this.dataList = res.data.data.records;
})
},
- 分页页码值切换
javascript
复制代码
//切换页码
handleCurrentChange(currentPage) {
//修改页码值为当前选中的页码值
this.pagination.currentPage = currentPage;
//执行查询
this.getAll();
},
-
小结:
- 使用el分页组件
- 定义分页组件绑定的数据模型
- 异步调用获取分页数据
- 分页数据页面回显
48-分页功能维护(删除BUG)
- 对查询结果进行校验,如果当前页码值大于最大页码值,使用最大页码值作为当前页码值重新查询
less
复制代码
@GetMapping("{currentPage}/{pageSize}")
public R getPage(@PathVariable Integer 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);
}
49-条件查询
-
查询条件数据封装
- 单独封装
- 与分页操作混合封装
go
复制代码
pagination: {//分页相关模型数据
currentPage: 1,//当前页码
pageSize: 10,//每页显示的记录数
total: 0,//总记录数
type: "",
name: "",
description: ""
}
- 页面数据模型绑定
ini
复制代码
<div class="filter-container">
<el-input placeholder="图书类别" v-model="pagination.type" class="filter-item" />
<el-input placeholder="图书名称" v-model="pagination.name" class="filter-item" />
<el-input placeholder="图书描述" v-model="pagination.description" class="filter-item" />
<el-button @click="getAll()" class="dalfBut">查询</el-button>
<el-button type="primary" class="butT" @click="handleCreate()">新建</el-button>
</div>
- 组织数据成为get请求发送的数据
kotlin
复制代码
//分页查询
getAll() {
console.log(this.pagination.type);
// /books/1/10?type=???&name=???&decription=?? ;
//1. 获取查询条件 , 拼接查询条件
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) => {
//console.log(res.data);
this.pagination.currentPage = res.data.data.current;
this.pagination.pageSize = res.data.data.size;
this.pagination.total = res.data.data.total;
this.dataList = res.data.data.records;
})
},
- 条件参数组织可以通过条件判定书写的更简洁
- Controller接收参数
less
复制代码
@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);
}
- 业务层接口功能开发
java
复制代码
/**
* 分页的条件查询
*
* @param currentPage
* @param pageSize
* @param book
* @return
*/
IPage<Book> getPage(Integer currentPage, int pageSize, Book book);
- 业务层接口实现类功能开发
less
复制代码
@Override
public IPage<Book> getPage(Integer currentPage, int pageSize, Book book) {
LambdaQueryWrapper<Book> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.like(Strings.isNotEmpty(book.getType()), Book::getType, book.getType());
lambdaQueryWrapper.like(Strings.isNotEmpty(book.getName()), Book::getName, book.getName());
lambdaQueryWrapper.like(Strings.isNotEmpty(book.getDescription()), Book::getDescription, book.getDescription());
IPage page = new Page(currentPage, pageSize);
bookDao.selectPage(page, lambdaQueryWrapper);
return page;
}
- Controller调用业务层分页条件查询接口
less
复制代码
@GetMapping("{currentPage}/{pageSize}")
public R getPage(@PathVariable Integer currentPage, @PathVariable int pageSize, Book book) {
// System.out.println("book=>" + book);
IPage<Book> page = bookService.getPage(currentPage, pageSize, book);
// 如果当前页码值大于了总页码值,那么重新执行查询操作,使用最大页码值作为当前页码值
if (currentPage > page.getPages()) {
page = bookService.getPage((int) page.getPages(), pageSize, book);
}
return new R(true, page);
}
- 页面回显数据
kotlin
复制代码
//分页查询
getAll() {
console.log(this.pagination.type);
// /books/1/10?type=???&name=???&decription=?? ;
//1. 获取查询条件 , 拼接查询条件
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) => {
//console.log(res.data);
this.pagination.currentPage = res.data.data.current;
this.pagination.pageSize = res.data.data.size;
this.pagination.total = res.data.data.total;
this.dataList = res.data.data.records;
})
},
-
小结:
- 定义查询条件数据模型(当前封装到分页数据模型中)
- 异步调用分页功能并通过请求参数传递数据到后台
50-基础篇完结
-
基于SpringBoot的SSMP整合案例
-
pom.xml 配置起步依赖
-
application.yml 设置数据源、端口、框架技术相关配置等
-
dao 继承BaseMapper、设置@Mapper
-
dao测试类
-
service 调用数据层接口或MyBatis-Plus提供的接口快速开发
-
service测试类
-
controller 基于Restful开发,使用Postman测试跑通功能
-
页面 放置在resources目录下的static目录中
-