前言:
通过idea正常换皮,start.spring.io创建springboot工程
通过阿里云start.aliyun.com创建springboot工程
注解详解:
@Qualifier :作用就是为了给Bean打上一个标记,用来查找bean,
@Autowired:默认按着byType自动注入
@Resource:默认按着byName自动注入,也可以按着byType两者区别
@Controller: 处理http请求
@RestController: 等于@Controller和@Responsebody之和
@RequestMapping: 配置url映射三者的简单介绍
@GetMapping,@PostMapping,@PutMapping,@DeleteMapping: 对应着restful风格的请求;
@PathVariable,和@RequestBody: 前者是代表从url中获取的参数,以简单对象(也有用String来)接收前端传过来的json数据:
@Configuration: 注册拦截器等要用到的,好像将bean装入容器的意思;
@Bean: 产生一个Bean对象,保证单例
@Mapper: Bean后注入到ServiceImpl中。不知道为啥,我这个必须要把@Mapper@Repository两个注解加上才会正确运行数据库查询;
@Repository: 需要在Spring中配置扫描包地址,然后生成dao层的bean,之后被注入到ServiceImpl中
@Select(“”),@Update(“”): dao层对数据库的方法的注解;
@SpringBootApplication: 引导类
@SpringBootTest: 测试类
@RestControllerAdvice: 拦截异常类
@ExceptionHandler(Exception.class): 对异常抛出方法的注解,可以指定要拦截的异常;
idea快速生成方法注释(含有参数及返回值)
使用时,是/**+enter键
**
* @Description: $description$
* @Author: ladidol
* @Date: $Date$ $Time$
* @Param: $param$
* @Return: $return$
*/
效果图:
idea自带文件比较功能
引导类
引导类:SpringApplication.run返回的就是spring容器
boot工程的运行入口:
@SpringBootApplication
public class SpringbootbyaliyunApplication {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(SpringbootbyaliyunApplication.class, args);
BookController bean = ctx.getBean(BookController.class);
bean.getById();
System.out.println(bean);
//springboot is running now!
//com.feng.controller.BookController@49298ce7
}
}
使用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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
[补]知识加油站-01-REST风格简介(强行记忆一下)
什么是 rest :
- REST(Representational State Transfer)表现形式状态转换
通过访问资源时使用的行为动作来区分操作:
-
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风格对资源进行访问称为RESTful
- 小结:
- REST
- 动作4个get,post,put,delete
- RESTful
[补]知识加油站-02-RESTful入门案例(restful原始开发)
@Controller
public class UserController {
@RequestMapping(value = "/users",method = RequestMethod.POST)
//注解的method属性声明请求的方法
@ResponseBody
//将返回对象转换为json
public String save(@RequestBody User user) {
//requsetBody注解 获取请求体内容。直接使用得到是 key=value&key=value…结构的数据。
System.out.println("user save...");
return "{'module':'user save'}";
}
@RequestMapping(value = "/users/{id}",method = RequestMethod.DELETE)
@ResponseBody
public String save(@PathVariable Integer id) {
//@PathVariable用于绑定 url 中的占位符。例如:请求 url 中 /delete/{id},
System.out.println("user delete...");
return "{'module':'user delete'}";
}
}
[补]知识加油站-03-RESTful(快速开发)
使用 @RestController
注解开发 RESTful 风格
@RestController
@RequestMapping("/cars")
public class CarController {
@PostMapping
public String save(@RequestBody Car car) {
System.out.println("car save..."+car.getAge()+", "+car.getName());
return "{'module':'car save'}";
}
@GetMapping("/{id}")
public String getById(@PathVariable Integer id){
System.out.println("book getById..."+id);
return "{'module': book getById}";
}
}
12-教你一招:复制模块
- 原则
保留工程基础结构
抹掉原始工程痕迹
小结:
- 在工作空间中复制对应工程,并修改工程名称
- 删除与Idea相关配置文件,仅保留src目录与pom.xml文件
- 修改pom.xml文件中的artifactId与新工程/模块名相同
- 删除name标签(可选)
- 保留备份工程供后期使用
好像我没有做成功,先放一边,好像是因为,我用的基础module是aliyun来建立,改名字,没有完全消除以前工程的依赖
13-属性配置方式
配置文件 (44条消息) Spring Boot 2 基础篇学习笔记_陈建兵-CSDN博客
看上面的链接就行了
字面表示方式:
# 字面值表示方式
boolean: TRUE #TRUE,true,True,FALSE,false , False 均可
float: 3.14 #6.8523015e+5 # 支持科学计数法
int: 123 #0b1010_0111_0100_1010_1110 # 支持二进制、八进制、十六进制
# null: ~ # 使用 ~ 表示 null
string: HelloWorld # 字符串可以直接书写
string2: "Hello World" # 可以使用双引号包裹特殊字符
date: 2018-02-17 # 日期必须使用 yyyy-MM-dd 格式
datetime: 2018-02-17T15:02:31+08:00 # 时间和日期之间使用 T 连接,最后使用 + 代表时区
数组表示方式:在属性名书写位置的下方使用减号作为数据开始符号,每行书写一个数据,减号与数据间空格分隔
subject:
- Java
- 前端
- 大数据
enterprise:
name: zhangsan
age: 16
subject2:
- Java
- 前端
- 大数据
likes: [王者荣耀,刺激战场] # 数组书写缩略格式
users: # 对象数组格式
- name: Tom
age: 4
- name: Jerry
age: 5
users2: # 对象数组格式二
-
name: Tom
age: 4
-
name: Jerry
age: 5
# 对象数组缩略格式
users3: [ { name:Tom , age:4 } , { name:Jerry , age:5 } ]
19-读取yaml单一属性数据
第一种是直接在Controller中读取:
使用@Value读取单个数据,属性名引用方式:${一级属性名.二级属性名……}
car:
name: itcast
age: 34
lesson: SpringBoot
server:
port: 8888
enterprise:
name: itcast
age: 16
tel: 4006184000
subject:
- Java
- 前端
- 大数据
@RestController
@RequestMapping("/cars")
public class CarController {
//读取yaml数据中单一的数据
@Value("${lesson}")
private String lesson;
@Value("${server.port}")
private int port;
@Value("${enterprise.subject[1]}")
private String subject_01;
@GetMapping
public String getById() {
System.out.println("springboot is running2...");
System.out.println("lesson got==> " + lesson);
System.out.println("port got==> " + port);
System.out.println("subject_01 got==> " + subject_01);
return "springboot is running2...";
/*springboot is running2...
lesson got==> SpringBoot
port got==> 8080
subject_01 got==> 前端
*/
}
}
- 封装全部数据到Environment对象
- 注意 要导这个 包
- import org.springframework.core.env.Environment;
后面还有一种在配置类中读取:
//配置类,要加@Component注解,才能使用,他是实现bean的注入,
@Component
@Data
@NoArgsConstructor
@AllArgsConstructor
@ConfigurationProperties(prefix = "datasource")
public class MyDataSource {
private String driver;
private String url;
private String username;
private String password;
}
# 创建类,用于封装下面的数据
# 由spring帮我们去加载数据到对象中,一定要告诉spring加载这组信息
# 使用时候从spring中直接获取信息使用
datasource:
driver: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost/springboot_db
username: root
password: nihao123
23-SpringBoot整合JUnit
- 添加Junit的起步依赖 Spring Initializr 创建时自带
<!--测试的起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
- 先在约定路径上建Dao类和Dao的实现类:
public interface BookDao {
public void save();
}
@Repository
//注意这里要受springboot的管理的话,就必须有Repository注解
public class BookDaoImpl implements BookDao {
@Override
public void save() {
System.out.println("BookDao's save is running!");
}
}
- SpringBoot整合JUnit
@SpringBootTest
class SpringbootjunitApplicationTests {
//先注入
@Autowired
private BookDao bookDao;
@Test
void contextLoads() {
bookDao.save();
}
}
@SpringBootTest
名称:@SpringBootTest
类型:测试类注解
位置:测试类定义上方
作用:设置JUnit加载的SpringBoot启动类
范例
@SpringBootTest
class Springboot05JUnitApplicationTests {}
25-SpringBoot整合MyBatis
①:创建新模块,选择Spring初始化,并配置模块相关基础信息
#DB Configuration:
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot_db?serverTimezone=UTC
username: root
password: nihao123
测试类中注入dao接口,测试功能:
@SpringBootTest
//@MapperScan,可以不用配置了
class SpringbootmybatisApplicationTests {
@Autowired
private UserDao userDao;
@Test
void contextLoads() {
List<User> userList = userDao.getAll();
System.out.println(userList);
}
}
定义数据层接口与映射配置:
//@Repository,可以不用配置了
@Mapper
public interface UserDao {
@Select("select * from user")
public List<User> getAll();
}
总结:
- 勾选MyBatis技术,也就是导入MyBatis对应的starter
- 数据库连接相关信息转换成配置
- 数据库SQL映射需要添加@Mapper被容器识别到
27-SpringBoot整合MyBatisPlus
手动添加SpringBoot整合MyBatis-Plus的坐标,可以通过mvnrepository获取
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3</version>
</dependency>
注意事项: 由于SpringBoot中未收录MyBatis-Plus的坐标版本,需要指定对应的Version
约定:定义数据层接口与映射配置,继承BaseMapper:
@Mapper
@Repository//这里要加这个了,好像这里是和mybatis不一样的地方:
public interface UserDao extends BaseMapper<User> {
}
其他同SpringBoot整合MyBatis
测试类中注入dao接口,测试功能
@SpringBootTest
class Springboot06MybatisPlusApplicationTests {
@Autowired
private UserDao userDao;
@Test
void contextLoads() {
List<User> users = userDao.selectList(null);
System.out.println(users);
}
}
28-SpringBoot整合Druid
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.6</version>
</dependency>
小结:
1.整合Druid需要导入Druid对应的starter
2.根据Druid提供的配置方式进行配置
3.整合第三方技术通用方式
导入对应的starter
根据提供的配置格式,配置非默认值对应的配置项
29-SSMP整合案例制作分析
案例实现方案分析与流程解析
1. 案例实现方案分析
实体类开发————使用Lombok快速制作实体类
Dao开发————整合MyBatisPlus,制作数据层测试类
Service开发————基于MyBatisPlus进行增量开发,制作业务层测试类
Controller开发————基于Restful开发,使用PostMan测试接口功能
Controller开发————前后端开发协议制作
页面开发————基于VUE+ElementUI制作,前后端联调,页面数据处理,页面消息处理
列表、新增、修改、删除、分页、查询
项目异常处理
按条件查询————页面功能调整、Controller修正功能、Service修正功能
2. SSMP案例制作流程解析
先开发基础CRUD功能,做一层测一层
调通页面,确认异步提交成功后,制作所有功能
添加分页功能与查询功能
表示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JDSGgO3R-1643037502814)(…/全部图片/image-20220118222545295.png)]
国产牛逼
========================================================================
这里待做笔记!!!
========================================================================
30-模块创建
pom.xml
<dependencies>
<!--web starter-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--mysql 驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--mp starter-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3</version>
</dependency>
<!--druid starter-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.6</version>
</dependency>
<!--自带有的test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
tbl_book.sql
DROP TABLE IF EXISTS `tbl_book`;
CREATE TABLE `tbl_book` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`type` varchar(20) DEFAULT NULL,
`name` varchar(50) DEFAULT NULL,
`description` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of tbl_book
-- ----------------------------
INSERT INTO `tbl_book` VALUES ('1', '计算机理论', 'Spring实战第5版', 'Spring入门经典教程,深入理解Spring原理技术内幕');
INSERT INTO `tbl_book` VALUES ('2', '计算机理论', 'Spring 5核心原理与30个类手写实战', '十年沉淀之作,写Spring精华思想');
INSERT INTO `tbl_book` VALUES ('3', '计算机理论', 'Spring 5设计模式', '深入Spring源码剖析Spring源码中蕴含的10大设计模式');
INSERT INTO `tbl_book` VALUES ('4', '计算机理论', 'Spring MVC+ MyBatis开发从入门到项目实战', '全方位解析面向Web应用的轻量级框架,带你成为Spring MVC开发高手');
INSERT INTO `tbl_book` VALUES ('5', '计算机理论', '轻量级Java Web企业应用实战', '源码级剖析Spring框架,适合已掌握Java基础的读者');
INSERT INTO `tbl_book` VALUES ('6', '计算机理论', 'Java核心技术卷|基础知识(原书第11版)', 'Core Java第11版,Jolt大奖获奖作品,针对Java SE9、10、 11全面更新');
INSERT INTO `tbl_book` VALUES ('7', '计算机理论', '深入理解Java虚拟机', '5个维度全面剖析JVM,面试知识点全覆盖');
INSERT INTO `tbl_book` VALUES ('8', '计算机理论', 'Java编程思想(第4版)', 'Java学习必读经典殿堂级著作!赢得了全球程序员的广泛赞誉');
INSERT INTO `tbl_book` VALUES ('9', '计算机理论', '零基础学Java (全彩版)', '零基础自学编程的入门]图书,由浅入深,详解Java语言的编程思想和核心技术');
INSERT INTO `tbl_book` VALUES ('10', '市场营销', '直播就该这么做:主播高效沟通实战指南', '李子柒、李佳琦、薇娅成长为网红的秘密都在书中');
INSERT INTO `tbl_book` VALUES ('11', '市场营销', '直播销讲实战一本通', '和秋叶一起学系列网络营销书籍');
INSERT INTO `tbl_book` VALUES ('12', '市场营销', '直播带货:淘宝、天猫直播从新手到高手', '一本教你如何玩转直播的书, 10堂课轻松实现带货月入3W+');
小结:
- 勾选SpringMVC与MySQL坐标
- 修改配置文件为yml格式
- 设置端口为80方便访问
application.yaml
server:
port: 80
# druid 数据源配制
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot_db?serverTimezone=UTC
username: root
password: nihao123
# mybatis-plus
mybatis-plus:
global-config:
db-config:
table-prefix: tbl_
id-type: auto # 主键策略
configuration:
# 开启MyBatisPlus的日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
domain.Book.java
@Data
public class Book {
private Integer id;
private String type;
private String name;
private String description;
}
32-数据层标准开发(基础CRUD)
使用mp时要导入相关依赖;
- 导入MyBatisPlus与Druid对应的starter
<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>
- 继承BaseMapper并指定泛型
@Mapper
@Repository
public interface BookDao extends BaseMapper<Book> {
@Select("select * from tbl_book where id = #{id}")
Book getById(Integer id);
//需要什么没提供的方法时,自己可以创;
// @Insert("insert into tbl_book values(#{id},#{type},#{name},#{description})")
// int insert(Book book);
//
//
// @Update("update tbl_book set type = #{type}, name = #{name}, description = #{description} where id = #{id}")
// int updateById(Book book);
//
// @Delete("delete from tbl_book where id = #{id}")
// int deleteById(Integer id);
}
- 制作测试类测试结果
@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));
}
@Test
void testGetPage() {
//分页方法,待定;
}
@Test
void testGetBy() {
}
}
34-分页
- 分页操作需要设定分页对象IPage
@Test
void testGetPage() {
IPage page = new Page(1, 5);
bookDao.selectPage(page, null);
}
- IPage对象中封装了分页操作中的所有数据
数据
当前页码值
每页数据总量
最大页码值
数据总量 - 分页操作是在MyBatisPlus的常规操作基础上增强得到,内部是动态的拼写SQL语句,因此需要增强对应的功能,
使用MyBatisPlus拦截器实现
拦截器如下:
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
//1. 定义mp拦截器
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//2. 添加具体的拦截器,分页拦截器
//用来做分页相关的功能的话,就是在里面new一个
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
//你想要其他拦截功能自己加
// interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
// interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
}
test中的分页方法:
@Test
void testGetPage() {
IPage page = new Page(1, 5);
bookDao.selectPage(page, null);
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());
}
35-数据层标准开发(条件查询)
- 使用QueryWrapper对象封装查询条件,推荐使用LambdaQueryWrapper对象,所有查询操作封装成方法调用
@Test
void testGetBy() {
//使用QueryWrapper对象封装查询条件,需要什么条件,可以直接对同一个query进行加入
QueryWrapper<Book> queryWrapper = new QueryWrapper<>();
/* queryWrapper.like("name","spring");
bookDao.selectList(queryWrapper);*/
queryWrapper.likeLeft("name","spring");
bookDao.selectList(queryWrapper);
}
优化版本:
@Test
void testGetBy2(){
//Parameters: %null%(String)
//可能会出现这个情况;
String name = "直播";
LambdaQueryWrapper<Book> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// lambdaQueryWrapper.like(Book::getName,"直播");
//真就传参
lambdaQueryWrapper.like(name != null ,Book::getName,name);
bookDao.selectList(lambdaQueryWrapper);
}
36-业务层标准开发(基础CRUD)
- Service层接口定义与数据层接口定义具有较大区别,不要混用
selectByUserNameAndPassword(String username,String password); 数据层接口
login(String username,String password); Service层接口 - 接口定义
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);
List<Book> getList(@Param(Constants.WRAPPER) Wrapper<Book> queryWrapper);
}
- 实现类定义
@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;
}
@Override
public List<Book> getList(Wrapper<Book> queryWrapper) {
return bookDao.selectList(queryWrapper);
}
}
- 测试类定义
@SpringBootTest
public class BookServiceTestCase {
@Autowired
private BookService bookService;
@Test
void testGetById(){
System.out.println(bookService.getById(3));
}
@Test
void testUpdate() {
Book book = new Book();
book.setId(14);
book.setName("fengfeng之书");
book.setDescription("this is fengfeng's book!");
book.setType("wuwuwu");
bookService.update(book);
//等等一堆方法:
}
@Test
void testGetBy2(){
//Parameters: %null%(String)
//可能会出现这个情况;
String name = "直播";
LambdaQueryWrapper<Book> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// lambdaQueryWrapper.like(Book::getName,"直播");
//真就传参
lambdaQueryWrapper.like(name != null ,Book::getName,name);
bookService.getList(lambdaQueryWrapper);
}
}
37-业务层快速开发(基于MyBatisPlus构建)
快速开发方案
使用MyBatisPlus提供有业务层通用接口(ISerivce)与业务层通用实现类(ServiceImpl<M,T>)
在通用类基础上做功能重载或功能追加
注意重载时不要覆盖原始操作,避免原始提供的功能丢失
- 接口定义
public interface IBookService extends IService<Book> {
}
- 接口追加功能:
/*service接口是不需要@Service*/
public interface IBookService extends IService<Book> {
//基本业务方法这里都有,不够的话自己加;
// 追加的操作与原始操作通过名称区分,功能类似
Boolean delete(Integer id);
Boolean saveBook(Book book);
Boolean insert(Book book);
Boolean modify(Book book);
Book get(Integer id);
IPage<Book> getPage(int current, int pageSize);
//这里的两个分页方法是后面要用到的;
IPage<Book> getPage(int currentPage, int pageSize, Book book);
}
- 实现类的定义:
@Service
public class IBookServiceImpl extends ServiceImpl<BookDao, Book> implements IBookService {
}
- 实现类追加功能
@Service
public class IBookServiceImpl extends ServiceImpl<BookDao, Book> implements IBookService{
//这里要继承两个类;
/*如果怕有警告的话就,@Resource*/
@Autowired
private BookDao bookDao;
@Override
public Boolean insert(Book book){
return bookDao.insert(book) > 0;
}
@Override
public Boolean modify(Book book) {
return bookDao.updateById(book) > 0;
}
@Override
public Boolean delete(Integer id) {
return bookDao.deleteById(id) > 0;
}
@Override
public Boolean saveBook(Book book) {
return bookDao.insert(book)>0;
}
@Override
public Book get(Integer id) {
return bookDao.selectById(id);
}
@Override
public IPage<Book> getPage(int currentPage, int pageSize) {
IPage page = new Page(currentPage,pageSize);
bookDao.selectPage(page,null);
return page;
}
@Override
//注意重载的时候,方法1(int sth)和方法2(Integer sth)是不同的呜呜呜
public IPage<Book> getPage(int 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;
}
}
- 测试类定义:
@SpringBootTest
public class IBookServiceTest {
//注意方法名字都变了
@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());
}
}
小结:
- 使用通用接口(ISerivce)快速开发Service
- 使用通用实现类(ServiceImpl<M,T>)快速开发ServiceImpl
- 可以在通用接口基础上做功能重载或功能追加
- 注意重载时不要覆盖原始操作,避免原始提供的功能丢失
38-表现层标准开发
- 基于Restful进行表现层接口开发
- 使用Postman测试表现层接口功能
controller层的开发:
@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);
}
}
小结:
- 基于Restful制作表现层接口
新增:POST
删除:DELETE
修改:PUT
查询:GET - 接收参数
实体数据:@RequestBody
路径变量:@PathVariable
39-表现层数据一致性处理(R对象)
之前的格式:
增加一个data属性,把数据全部封装到data里面
统一格式:方便前后端对接;
- 设计表现层返回结果的模型类,用于后端与前端进行数据格式统一,也称为前后端数据协议
@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;
}
}
- 表现层接口统一返回值类型结果
@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的方法时提示报错(这里主要是前端的知识):
Method definition shorthands are not supported by current JavaScript version
表示:该方法定义的缺陷是不支持当前的JavaScript版本,虽然可以程序可以正常运行,但是这个方法会出现红色的波浪线,很不爽
解决:
打开 File -> Settings -> Languages & Frameworks -> Javascript
把JavaScript版本为ECMAScript 6就可以了
- 前后端分离结构设计中页面归属前端服务器
- 单体工程中页面放置在resources目录下的static目录中(建议执行clean)
- 前端发送异步请求,调用后端接口
在book.html代码页面中:
//钩子函数,VUE对象初始化完成后自动执行
created() {
//调用查询全部数据的操作
this.getAll();
},
//列表
getAll() {
//发送异步请求
axios.get("/books").then((res)=>{
console.log(res.data);
})
},
小结:
- 单体项目中页面放置在resources/static目录下
- created钩子函数用于初始化页面时发起调用
- 页面使用axios发送异步请求获取数据后确认前后端是否联通
41-列表功能
- 列表页
//列表
getAll() {
//发送异步请求
axios.get("/books").then((res) => {
//console.log(res.data);
this.dataList = res.data.data;
})
},
小结:
- 将查询数据返回到页面,利用前端数据双向绑定进行数据展示
42-添加功能
- 弹出添加窗口
// 弹出添加窗口
handleCreate() {
this.dialogFormVisible = true;
},
- 清楚数据
//重置表单
resetForm() {
this.formData = {};
},
- 在弹出添加窗口时 清除数据
//弹出添加窗口
handleCreate() {
this.dialogFormVisible = true;
this.resetForm();
},
- 发送添加请求
//添加
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();
})
},
- 取消添加
//取消
cancel() {
//1.关闭弹层
this.dialogFormVisible = false;
//2.提示用户
this.$message.info("当前操作取消");
},
小结:
- 请求方式使用POST调用后台对应操作
- 添加操作结束后动态刷新页面加载数据
- 根据操作结果不同,显示对应的提示信息
- 弹出添加Div时清除表单数据
43-删除功能
- 删除
// 删除
handleDelete(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) => {
//判断当前操作是否成功
if (res.data.flag) {
this.$message.success("删除成功");
} else {
this.$message.error("删除失败");
}
}).finally(() => {
//2.重新加载数据
this.getAll();
})
}).catch(() => {
//3. 取消删除
this.$message.info("取消操作");
});
},
小结:
- 请求方式使用Delete调用后台对应操作
- 删除操作需要传递当前行数据对应的id值到后台
- 删除操作结束后动态刷新页面加载数据
- 根据操作结果不同,显示对应的提示信息
- 删除操作前弹出提示框避免误操作
44修改功能(加载数据)
- 弹出修改窗口
//弹出编辑窗口
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();
});
},
- 删除消息维护
// 删除
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-修改功能
- 修改
//修改
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();
});
},
- 取消添加和修改
//取消
cancel() {
//1.关闭弹层
this.dialogFormVisible = false;
this.dialogFormVisible4Edit = false;
//2.提示用户
this.$message.info("当前操作取消");
},
小结:
- 请求方式使用PUT调用后台对应操作
- 修改操作结束后动态刷新页面加载数据(同新增)
- 根据操作结果不同,显示对应的提示信息(同新增)
46-异常消息处理(这里是需要掌握的)
- 业务操作成功或失败返回数据格式
{
"flag": true,
"data": null
}
{
"flag": false,
"data": null
}
- 后台代码BUG导致数据格式不统一性
{
"timestamp": "2021-11-07T12:44:29.343+00:00",
"status": 500,
"error": "Internal Server Error",
"path": "/books"
}
- 对异常进行统一处理,出现异常后,返回指定信息
- 注意@RestControllerAdvice
- @ExceptionHandler(Exception.class)
@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): 要显示信息
@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() {
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中进行消息统一处理
@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 ? "添加成功^_^" : "添加失败-_-!");
}
- 页面消息处理
//添加
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 分页组件添加分页功能
<!--分页组件-->
<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>
- 定义分页组件需要使用的数据并将数据绑定到分页组件
data: {
pagination: { // 分页相关模型数据
currentPage: 1, // 当前页码
pageSize: 10, // 每页显示的记录数
total: 0, // 总记录数
}
},
- 替换查询全部功能为分页功能
getAll() {
axios.get("/books/" + this.pagination.currentPage + "/" + this.pagination.pageSize).then((res) => {});
},
- 分页查询
使用路径参数传递分页数据或封装对象传递数据
@GetMapping("{currentPage}/{pageSize}")
public R getPage(@PathVariable Integer currentPage, @PathVariable int pageSize) {
return new R(true, bookService.getPage(currentPage, pageSize));
}
- 加载分页数据
//分页查询
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;
})
},
- 分页页码值切换
//切换页码
handleCurrentChange(currentPage) {
//修改页码值为当前选中的页码值
this.pagination.currentPage = currentPage;
//执行查询
this.getAll();
},
小结:
- 使用el分页组件
- 定义分页组件绑定的数据模型
- 异步调用获取分页数据
- 分页数据页面回显
48-分页功能维护(删除BUG)
- 对查询结果进行校验,如果当前页码值大于最大页码值,使用最大页码值作为当前页码值重新查询
@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-条件查询
- 查询条件数据封装
单独封装
与分页操作混合封装
pagination: {//分页相关模型数据
currentPage: 1,//当前页码
pageSize: 10,//每页显示的记录数
total: 0,//总记录数
type: "",
name: "",
description: ""
}
- 页面数据模型绑定
<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请求发送的数据
//分页查询
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接收参数
@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);
}
- 业务层接口功能开发
/**
* 分页的条件查询
*
* @param currentPage
* @param pageSize
* @param book
* @return
*/
IPage<Book> getPage(Integer currentPage, int pageSize, Book book);
- 业务层接口实现类功能开发
@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调用业务层分页条件查询接口
@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);
}
- 页面回显数据
//分页查询
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;
})
},
123456789101112131415161718192021
小结:
- 定义查询条件数据模型(当前封装到分页数据模型中)
- 异步调用分页功能并通过请求参数传递数据到后台
50-基础篇完结
基于SpringBoot的SSMP整合案例
- pom.xml
配置起步依赖 - application.yml
设置数据源、端口、框架技术相关配置等 - dao
继承BaseMapper、设置@Mapper - dao测试类
- service
调用数据层接口或MyBatis-Plus提供的接口快速开发 - service测试类
- controller
基于Restful开发,使用Postman测试跑通功能 - 页面
放置在resources目录下的static目录中
总结:
- 整合JUint
- 整合MyBatis
- 整合MyBatis-Plus
- 整合Druid
- 基于SpringBoot的SSMP整合案例
后续学习
-
基础篇
- 能够创建SpringBoot工程
- 基于SpringBoot实现ssm/ssmp整合
-
实用篇
-
运维实用篇
Spring Boot 2 运维实用篇学习笔记
- 能够掌握SpringBoot程序多环境开发
- 能够基于Linux系统发布SpringBoot工程
- 能够解决线上灵活配置SpringBoot工程的需求
-
开发实用篇
- 能够基于SpringBoot整合任意第三方技术
-
-
原理篇
可能笔记要用到的图片:
添加一个新对象浏览器中network中可以看到的东西
一个小坑:
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
Registered plugin: 'com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor@4f281aaf'
Property 'mapperLocations' was not specified.
可能是因为你的类写错了,比如本来就该是一个好端端的impl,你偏偏给加上一个abstract;
新报错:
Caused by: java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'bookController' method
com.feng.controller.BookController#getById(Integer)
to {GET /books}: There is already 'bookController' bean method
这是因为你的getById方法注释没有加{id},从而导致,bean方法重复