小技巧(隐藏指定文件/文件夹)
SpringBoot每次创建时都会携带很多没啥用的文件,可以在设置中进行隐藏,不需要每次都删除
在设置中,可以进行配置
SpringBoot简介
- SpringBoot是由Pivotal团队提供的全新框架,其设计目的是用来简化Spring应用的初始搭建以及开发过程
- Spring的缺点
- SpringBoot的优点
- 起步依赖(简化依赖配置)
- 自动配置(简化常用工程相关配置)
- 辅助功能(内置服务器,…)
parent
有时候做项目,可能会导入两个相同的坐标,此时可以将它合并到一个文件中去,直接调用一个文件中的内容即可,但依然不太方便,springboot给出了优化的解决
它在pom.xml文件中继承了一个父类
1 2 3 4 5 6
| <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.15</version> <relativePath/> </parent>
|
而这里面又继承了一个父类
1 2 3 4 5
| <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.7.15</version> </parent>
|
点进去查看后,发现里面有很多的依赖,这些依赖也有设置了对应的版本,当我们使用某一个依赖时,它就会自动通过场景启动器里的spring-boot-starter-parent
中的spring-boot-dependencies
中写入的对应依赖,来自动设置最合适的版本,这样就不会因为版本不同而出现问题
总结:
- 开发Springboot程序要继承spring-boot-starter-parent
- spring-boot-starter-parent中定义了若干个依赖管理
- 继承parent模块可以避免多个依赖使用相同技术时出现依赖版本冲突
- 继承parent的形式也可以采用引入依赖的形式实现效果
starter
- SpringBoot中常见项目名称,定义了当前项目使用的所有依赖坐标,以达到
减少依赖配置
的目的
实际开发
- 使用任意坐标时,仅书写GAV中的G和A,V(version)由SpringBoot提供,除非Springboot未提供对应版本的V
- 如发生坐标错误,再指定Version(要小心版本冲突)
- 开发SpringBoot程序需要导入坐标时,通常导入对应的starter
- 每个不同的starter根据功能不同,通常包含多个依赖坐标
- 使用starter可以实现快速配置的效果,达到
简化配置
的目的
引导类
启动方式
1 2 3 4 5 6 7 8
| @SpringBootApplication public class BootDemoApplication {
public static void main(String[] args) { SpringApplication.run(BootDemoApplication.class, args); }
}
|
- SpringBoot的引导类是Boot工程的执行入口,运行main方法就可以启动项目
- SpringBoot工程运行后初始化Spring容器,扫描引导类所在包加载bean
内嵌Tomcat
Tomcat其实是依靠一个依赖引入进来的
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
|
点入该依赖中
这里内嵌了一个
1 2 3 4 5 6
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <version>2.7.15</version> <scope>compile</scope> </dependency>
|
tomcat场景启动器的依赖,里面就有相关的tomcat配置
1 2 3
| <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-core</artifactId> <version>9.0.79</version>
|
tomcat-embed-core
这个是tomcat的核心嵌入,里面依赖了它,才会有tomcat服务器,而如果,你不想用tomcat,你可以这样做
1 2 3 4 5 6 7 8 9 10 11
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId>
<exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency>
|
内置服务器
- tomcat(默认):apache出品,应用面广,负载了若干较重的组件
- jetty:更轻量级,负载性能远不及tomcat
- undertow:负载性能勉强能跑赢tomcat
总结:
- 内嵌Tomcat服务器是SpringBoot辅助功能之一
- 内嵌Tomcat工作原理是将Tomcat服务器作为对象运行,并将该对象交给Spring容器管理
- 变更内嵌服务器思想是去除现有服务器,添加全新的服务器
REST风格开发
REST简介
REST(Representtational State Transfer),表现形式状态转换
- 传统风格资源描述形式
http://localhost/user/getById?id=1
http://localhost/user/saverUser
- REST风格描述形式
http://localhost/user/1
http://localhost/user
- 优点:
- 隐藏资源的访问行为,无法通过地址得知对资源是何种操作
- 书写简化
按照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
注意事项:
上述行为是约定方式,约定不是规范,可以打破,所以称为REST风格,而不是REST规范
描述模块的名称通常使用复数,也就是加s的格式描述,表示此类资源,而非单个资源,比如:users、books、accounts………
参数接收的注解介绍
@RequestBody
:用于接收json数据
@PathVariable
:用于接收url地址传参或表单传参
@RequestParam
:用于接收路径参数,使用{参数名称}描述路径参数
应用:
- 后期开发中,发送请求参数超过1个时,以json为主,@RequestBody应用较广
- 如果发送非json格式数据,选用@RequestParam接收请求参数
- 采用RESTful进行开发,当参数数量较少时,例如1个,可以采用@PathVariable接收请求路径变量,通常用于传递id值
RSET快速开发
编写测试代码
引入依赖
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*;
@RestController @Slf4j public class HelloController {
@GetMapping("/hellos") public void getAll(){ System.out.println("getAll"); }
@GetMapping("/hellos/{id}") public void getById(@PathVariable Integer id){ System.out.println("getById"+id); }
@PutMapping("/hellos") public void update(){ System.out.println("更新"); }
@PostMapping("/hellos") public void save(){ System.out.println("保存"); }
@DeleteMapping("/hellos/{id}") public void delete(@PathVariable Integer id){ System.out.println("删除"); }
}
|
优化了之前冗余的代码,并且有了一个相当好的风格
基础配置
一般的文件配置可以在application.properties中修改
修改服务器端口
1 2 3 4 5 6 7 8 9 10
| server.port= 80
spring.main.banner-mode=off
logging.level.root = info
|
- SpringBoot中导入对应的starter后,提供对应配置属性
- 书写SpringBoot配置采用关键字+提示形式书写
三种配置文件类型
SpringBoot提供了多种属性配置方式
application.properties
application.yml
application.yaml
配置文件之间的加载优先级
- properties(最高)
- yml
- yaml(最低)
不同配置文件中相同配置按照加载优先级相互覆盖,不同配置文件中的不同配置全部保留
yml或yaml属性消失提示
按照步骤操作即可
yaml数据样式
YAML(YAML Ain’t Markup Language),一种数据序列化格式
优点:
- 容易阅读
- 容易与脚本语言交互
- 以数据为核心,重数据轻格式
YAML文件拓展名
yaml语法规则
- 大小写敏感
- 属性层级关系使用多行描述,每行结尾使用冒号结束
- 使用缩进表示层级关系,同层级左侧对齐,只允许使用空格(不允许使用Tab键)
- 属性值前添加空格(属性名与属性值之间使用冒号+空格作为分隔)
核心规则:数据前面要加空格与冒号隔开
字面值的表示方式
数组表示方式:在属性名书写位置的下方使用减号作为数据开始符号,每行书写一个数据,减号与数据空格分隔
读取yaml单一属性数据
application.yml
1 2 3 4 5 6 7 8 9
| server: port: 8080
count: "100"
likes: - game - games - games2
|
HelloController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.*;
@RestController @Slf4j public class HelloController {
@Value("${server.port}") private String port;
@Value("${count}") private String count;
@Value("${likes[1]}") private String game;
@GetMapping("/test") public String getPort(){ System.out.println("端口号为+"+port); System.out.println("统计+"+count); System.out.println("game"+game); return "测试完成"; }
}
|
使用@Value读取单个数据,属性名引用方式:${一级属性名.二级属性名……}
yaml文件中的变量引用
application.yml
1 2 3 4 5 6 7 8 9 10 11 12 13
| baseDir: /var/lib
tempDir: ${baseDir}/1
TestDir: - test1: /var/lib/1 - test2: /var/lib/2 - test3: /var/lib/3 UseDir: ${TestDir[0].test1}/test
|
HelloController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.*;
@RestController @Slf4j public class HelloController {
@Value("${tempDir}") private String tempDir;
@Value("${UseDir}") private String useDir;
@GetMapping("/test") public String getPort(){ System.out.println("temp"+tempDir); System.out.println("useDir = " + useDir); return "测试完成"; }
}
|
运行测试
此时,这里也获取到了对应的数据
如果需要对代码转义,可以在上面加上字符串,下方的\t就会变为对应的转义字符
1
| UseDir: "${TestDir[0].test1}\test"
|
结果如下
总结:
- 在配置文件中可以使用属性名引用方式引用属性
- 属性值如果出现转义字符,需要使用双引号包裹
读取yaml全部属性数据
创建一个Environment
并将全部属性自动装配进去,通过Environment.getProperty
来得到环境中对应的属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.*;
@RestController @Slf4j public class HelloController {
@Value("${tempDir}") private String tempDir;
@Value("${UseDir}") private String useDir;
@Autowired private Environment environment;
@GetMapping("/test") public String getPort(){ System.out.println("temp"+tempDir); System.out.println("useDir = " + useDir); System.out.println("------------------"); System.out.println(environment.getProperty("tempDir")); System.out.println(environment.getProperty("UseDir")); return "测试完成"; }
}
|
读取yaml引用数据类型数据
application.yaml
1 2 3 4 5 6 7 8 9
|
datasource: driver: com.mysql.jdbc.Driver url: jdbc:mysql//localhost:db username: root password: root
|
模型类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component;
@Data @Component
@ConfigurationProperties(prefix = "datasource") public class MyDataSource { private String driver; private String url;
private String username;
private String password; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import fun.eastwind.studyboot.MyDataSource; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*;
@RestController @Slf4j public class HelloController {
@Autowired private MyDataSource myDataSource;
@GetMapping("/test") public String getPort(){ System.out.println(myDataSource); return "测试完成"; }
}
|
测试得出结果
整合第三方技术
整合Junit
步骤:
1、导入测试对应的starter
1 2 3 4 5
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
|
2、测试类使用@SpringBootTest修饰(一般都是默认自带的)
1 2 3 4 5 6 7 8 9 10 11
| import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest class BootDemo2ApplicationTests {
@Test void contextLoads() { }
}
|
3、使用自动装配(@Autowired)的形式添加要测试的对象
如果将测试类切换到其他的包下,不在初始的包下,会报错,原因是与引导类不在同一个包下
此时你需要在@SpringBootTest上添加
1
| @SpringBootTest(classes = BootDemo2Application.class)
|
指定对应的引导类,这样测试类就可以找到对应的引导类了
或者还有一种方法,与上面的方法是一样的效果
1
| @ContextConfiguration(classes = BootDemo2Application.class)
|
注解学习
名称:@SpringBootTest
类型:测试类注解
位置:测试类定义上方
作用:设置Junit加载的SpringBoot启动类
相关属性:
注意:如果测试类在SpringBoot启动类的包或子包中,可以省略启动类的设置,也就是省略classes的设定
整合MyBatis
- 核心配置:数据库连接相关信息(连什么?连谁?什么权限)
- 映射配置:SQL映射(XML/注解)
创建一个Spring Initializr项目
并勾选需要的MyBatis相关依赖
在application.yml中编写对应配置
1 2 3 4 5 6
| spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver username: root password: 123456 url: jdbc:mysql://localhost:3306/study
|
这里的配置改成自己的
创建实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| public class User { private Integer id; private String name; private String sex;
@Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", sex='" + sex + '\'' + '}'; }
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getSex() { return sex; }
public void setSex(String sex) { this.sex = sex; } }
|
编写Mapper
1 2 3 4 5 6 7 8 9 10
| import fun.eastwind.domain.User; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select;
@Mapper public interface UserMapper {
@Select("select * from studytable where id = #{id}") public User getUser(Integer id); }
|
在测试类中进行测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import fun.eastwind.domain.User; import fun.eastwind.mapper.UserMapper; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest class DemoApplicationTests {
@Autowired private UserMapper userMapper;
@Test void contextLoads() { User user = userMapper.getUser(1); System.out.println("user = " + user); }
}
|
测试后,发现没有问题,说明整合完成
整合MyBatis的小问题
- MySQL8.x驱动强制要求设置时区
- 修改url,添加serverTimezone设定
- 修改MYSQL数据库配置(略)
- 驱动类过时,提醒更换为com.mysql.cj.jdbc.Driver
整合MyBatisPlus
MyBtais-Plus与MyBatis的区别
由于MyBatisPlus未被Spring收录,这里只引入一个mysql的坐标
mybatisplus需要自己引入对应的坐标
1 2 3 4 5 6
| <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.2</version> </dependency>
|
导入后,你可以将spring-boot-starter的依赖删除,因为mybatis-plus-boot-starter是包含着它的
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency>
|
application.yml
1 2 3 4 5 6
| spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver username: root password: 123456 url: jdbc:mysql://localhost:3306/study
|
实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| public class User { private Integer id; private String name; private String sex;
@Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", sex='" + sex + '\'' + '}'; }
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getSex() { return sex; }
public void setSex(String sex) { this.sex = sex; } }
|
UserMapper
1 2 3 4 5 6 7 8 9
| import com.baomidou.mybatisplus.core.mapper.BaseMapper; import fun.eastwind.demo1.domain.User; import org.apache.ibatis.annotations.Mapper;
@Mapper public interface UserMapper extends BaseMapper<User> {
}
|
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import fun.eastwind.demo1.domain.User; import fun.eastwind.demo1.mapper.UserMapper; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest class Demo1ApplicationTests {
@Autowired private UserMapper userMapper;
@Test void contextLoads() { User user = userMapper.selectById(1); System.out.println("user = " + user); }
}
|
运行后,会出现一个错误Cause: java.sql.SQLSyntaxErrorException: Table 'study.user' doesn't exist
说是表不存在,为什么呢,因为MP(mybatisplus)会直接将实体类的名称小写后,作为表名
解决方法:为实体类直接添加表名
在实体类上方添加注解@TableName("studytable")
,这样就可以指定表名了
此时,就查询得到结果了
user = User{id=1, name='张三', sex='男'}
整合Druid
druid的依赖包需要自己导入,在新建项目时勾选mybatis和mysql,因为druid是给数据库使用的
1 2 3 4 5 6
| <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.8</version> </dependency>
|
配置与实体类,同整合mybatis与mybatisplus时相同
这里我使用的mybatisplus,所以引入一下依赖
1 2 3 4 5 6
| <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.2</version> </dependency>
|
配置方式:
1、直接在application.yml中编写
1 2 3 4 5 6 7
| spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver username: root password: 123456 url: jdbc:mysql://localhost:3306/study type: com.alibaba.druid.pool.DruidDataSource
|
2、推荐使用第二种,虽然两种都行
1 2 3 4 5 6 7
| spring: datasource: druid: driver-class-name: com.mysql.cj.jdbc.Driver username: root password: 123456 url: jdbc:mysql://localhost:3306/study
|
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import fun.eastwind.demo2.mapper.UserMapper; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest class Demo2ApplicationTests {
@Autowired private UserMapper userMapper;
@Test void contextLoads() { System.out.println("userMapper.selectById(1) = " + userMapper.selectById(1)); }
}
|
查看打印结果
这里显示初始化数据源
SSMP整合
SSMP(spring、springmvc、mybatisplus)
整合分析
- 实体类开发:使用Lombok快速制作实体类
- Dao开发:整合MyBatisplus,制作数据层测试类
- Service开发:基于MyBatisplus进行增量开发,制作业务层测试类
- Controller开发:基于Restful开发,使用PostMan测试接口功能,前后端开发协议制作
- 页面开发:基于VUE+ElementUI制作,前后端联调,页面数据处理,页面消息处理
- 项目异常处理
- 按条件查询:页面功能调整、Controller修正功能、Service修正功能
模块创建
导入依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| <dependencies> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.8</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
<dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.2</version> </dependency> </dependencies>
|
修改配置
application.yml
实体类开发
1 2 3 4 5 6 7 8 9 10 11
| import com.baomidou.mybatisplus.annotation.TableName; import lombok.AllArgsConstructor; import lombok.Data;
@TableName("studytable") @Data @AllArgsConstructor public class User { private String name; private String sex; }
|
数据层标准开发
修改配置
1 2 3 4 5 6 7 8 9 10
| server: port: 80
spring: datasource: druid: password: 123456 username: root url: jdbc:mysql://localhost:3306/study driver-class-name: com.mysql.cj.jdbc.Driver
|
编写Mapper接口
1 2 3 4 5 6 7 8
| import com.baomidou.mybatisplus.core.mapper.BaseMapper; import fun.eastwind.module_practice.domain.User; import org.apache.ibatis.annotations.Mapper;
@Mapper public interface UserMapper extends BaseMapper<User> { }
|
编写测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import fun.eastwind.module_practice.domain.User; import fun.eastwind.module_practice.mapper.UserMapper; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest class ModulePracticeApplicationTests {
@Autowired private UserMapper userMapper;
@Test void contextLoads() { userMapper.insert(new User("test","女")); }
}
|
测试一下,没啥问题
为MP添加一下id自增策略,不然后面可能会有问题,因为表中的id采用的是自增策略
1 2 3 4
| mybatis-plus: global-config: db-config: id-type: auto
|
开启MP的运行日志
1 2 3 4 5 6 7
| mybatis-plus: global-config: db-config: id-type: auto configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
再次运行测试类,就可以在控制台看到打印的日志信息了
分页
创建拦截器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import org.springframework.context.annotation.Configuration;
@Configuration public class MPConfig {
@Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){ MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); return interceptor; }
}
|
编写测试类
1 2 3 4 5 6 7
| @Test void test2() { IPage page = new Page(1,5); userMapper.selectPage(page, null); }
|
分页的方法
条件查询
使用QueryWrapper对象封装查询条件,推荐使用LambdaQueryWrapper对象,所有查询操作封装成方法调用
编写代码
1 2 3 4 5 6 7 8
| @Test void test3() { QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.like("name","张"); List<User> userList = userMapper.selectList(queryWrapper); }
|
这里可以很明显的看到下面的语句是模糊查询
也可以使用另一种
1 2 3 4 5 6 7
| @Test void test3() { LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.like(User::getName,"张"); List<User> userList = userMapper.selectList(queryWrapper); }
|
一般情况下,数据是由外界传递的,万一出现状况,数据传递出来一个Null,此时,就会出现问题,像这样的like匹配,就会将null传递过来,并由null进行模糊匹配(%null%)
queryWrapper这里可以添加一个condition(条件),在这个条件中就可以对null值进行判断了
1 2 3 4 5 6 7 8 9
| @Test void test3() { LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>(); String name = "张"; queryWrapper.like(name != null,User::getName,name); List<User> userList = userMapper.selectList(queryWrapper); }
|
业务层开发
- Service层接口定义与数据层接口定义具有较大区别,不要混用
- selectByUserNameAndPassword(String username,String password)
- login(String username,String password)
UserService
1 2 3 4 5 6 7 8 9 10 11
| import fun.eastwind.module_practice.domain.User;
import java.util.List;
public interface UserService { Boolean save(User user); Boolean update(User user); Boolean delete(Integer id); User selectById(Integer id); List<User> selectAll(); }
|
进行数据库增删改操作时,会返回一个数值,如果数值为整数,说明操作成功,否则失败
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| import fun.eastwind.module_practice.domain.User; import fun.eastwind.module_practice.mapper.UserMapper; import fun.eastwind.module_practice.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service;
import java.util.List;
@Service public class UserServiceImpl implements UserService {
@Autowired private UserMapper userMapper;
@Override public Boolean save(User user) { return userMapper.insert(user) > 0; }
@Override public Boolean update(User user) { return userMapper.updateById(user) > 0; }
@Override public Boolean delete(Integer id) { return userMapper.deleteById(id) > 0; }
@Override public User selectById(Integer id) { return userMapper.selectById(id); }
@Override public List<User> selectAll() { return userMapper.selectList(null); } }
|
测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import fun.eastwind.module_practice.domain.User; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest public class UserServiceTest {
@Autowired private UserService userService;
@Test public void test1(){ userService.save(new User("zhangsan","男")); }
}
|
没啥问题
刚刚少写了一个分页,再到service中进行编写
UserService
1
| IPage<User> page(int current, int pageSize);
|
UserServiceImpl
1 2 3 4 5
| @Override public IPage<User> page(int current, int pageSize) { IPage iPage = new Page<User>(current,pageSize); return userMapper.selectPage(iPage,null); }
|
测试一下
1 2 3 4
| @Test public void test2(){ userService.page(1,5); }
|
这里对UserService的方法说明一下
1 2 3 4
| @Override public Boolean save(User user) { return userMapper.insert(user) > 0; }
|
为什么是判断是否大于0呢,因为这里操作成功后都是返回正值,而失败都是负值,测试一下,来看看效果
测试service可能不太明确,直接拿mapper来进行一个测试
1 2 3 4 5 6 7 8 9
| @Autowired private UserMapper userMapper;
@Test public void test3(){ System.out.println("--------------------------------"); System.out.println(userMapper.insert(new User("zhangsan", "男"))); System.out.println("--------------------------------"); }
|
这里发现成功后,会返回正值1,插入失败,通常会返回-1
业务层快速开发
快速开发方案
- 使用MyBatisPlus提供有业务层通用接口(IService)与业务层通用实现类(ServiceImpl<M,T>)
- 使用通用类基础上做功能重载或功能追加
- 注意重载时不要覆盖原始操作,避免原始提供的功能丢失
IUserService
1 2 3 4 5
| import com.baomidou.mybatisplus.extension.service.IService; import fun.eastwind.module_practice.domain.User;
public interface IUserService extends IService<User> { }
|
IUserServiceImpl
1 2 3 4 5 6 7 8 9
| import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import fun.eastwind.module_practice.domain.User; import fun.eastwind.module_practice.mapper.UserMapper; import fun.eastwind.module_practice.service.IUserService; import org.springframework.stereotype.Service;
@Service public class IUserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService { }
|
测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import fun.eastwind.module_practice.domain.User; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest public class IUserServiceTest {
@Autowired private IUserService userService;
@Test public void test1(){ List<User> list = userService.list(); for (User user : list) { System.out.println(user); } }
}
|
没啥问题
表现层标准开发
- 基于Restful进行表现层接口开发
- 使用Postman测试表现层接口功能
UserController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import fun.eastwind.module_practice.domain.User; import fun.eastwind.module_practice.service.IUserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController @RequestMapping("/users") public class UserController {
@Autowired private IUserService iUserService;
@GetMapping public List<User> getUsers() { return iUserService.list(); }
}
|
在postman中测试一下,得到了查询的数据
接着补全一下其他的代码,方法就不测试了,效果都差不多
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| import fun.eastwind.module_practice.domain.User; import fun.eastwind.module_practice.service.IUserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController @RequestMapping("/users") public class UserController {
@Autowired private IUserService iUserService;
@GetMapping public List<User> getUsers() { return iUserService.list(); }
@PostMapping public Boolean save(@RequestBody User user) { return iUserService.save(user); }
@PutMapping public Boolean update(@RequestBody User user){ return iUserService.updateById(user); }
@DeleteMapping("{id}") public Boolean delete(@PathVariable Integer id){ return iUserService.removeById(id); }
@GetMapping("{id}") public User getUser(@PathVariable Integer id){ return iUserService.getById(id); }
}
|
总结:
1、基于Restful制作表现层接口
- 新增:POST
- 删除:DELETE
- 修改:PUT
- 查询:GET
2、接收参数
- 实体数据:@RequestBody
- 路径变量:@PathVariable
表现层消息的一致性处理
设置表现层返回结果的模型类,用于后端与前端进行数据格式统一,也称为前后端数据协议
Result(模型类)
模型类中的flag表示是否成功,data接收数据
1 2 3 4 5 6 7 8
| import lombok.Data;
@Data public class Result { private Boolean flag; private Object data;
}
|
但是这样写,每次都需要set里面的flag和data
所以,进行一下优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import lombok.Data;
@Data public class Result { private Boolean flag; private Object data;
public Result() { }
public Result(Boolean flag, Object data) { this.flag = flag; this.data = data; }
public Result(Boolean flag) { this.flag = flag; } }
|
编写控制类统一结果返回
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| import fun.eastwind.module_practice.domain.User; import fun.eastwind.module_practice.service.IUserService; import fun.eastwind.module_practice.utils.Result; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController @RequestMapping("/users") public class UserController {
@Autowired private IUserService iUserService;
@GetMapping public Result getUsers() { return new Result(true,iUserService.list()); }
@PostMapping public Result save(@RequestBody User user) { return new Result(iUserService.save(user)); }
@PutMapping public Result update(@RequestBody User user){ return new Result(iUserService.updateById(user)); }
@DeleteMapping("{id}") public Result delete(@PathVariable Integer id){ return new Result(iUserService.removeById(id)); }
@GetMapping("{id}") public Result getUser(@PathVariable Integer id){ return new Result(true,iUserService.getById(id)); }
}
|
统一异常处理
创建一个异常处理器
1 2 3 4 5 6 7 8 9 10 11 12
| import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice public class AllException {
@ExceptionHandler public Result doException(Exception ex){ ex.printStackTrace(); return new Result("出现异常,请联系程序猿小哥为您服务"); }
}
|
修改之前的Result对象(一致性处理的对象),出现异常被异常处理器拦截后,肯定会有提示信息,所以需要新增一个msg(消息)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| import lombok.Data;
@Data public class Result { private Boolean flag; private Object data;
private String msg;
public Result(String msg) { this.flag = false; this.msg = msg; }
public Result() { }
public Result(Boolean flag, Object data) { this.flag = flag; this.data = data; }
public Result(Boolean flag) { this.flag = flag; } }
|
报个异常测试一下
如果想要指定某些异常,可以在@ExceptionHandler
后添加对应的异常类
1 2 3 4 5
| @ExceptionHandler(Exception.class) public Result doException(Exception ex){ ex.printStackTrace(); return new Result("出现异常,请联系程序猿小哥为您服务"); }
|
总结:
- 使用注解@RestControllerAdvice定义SpringMVC异常处理器来处理异常
- 异常处理器必须被扫描加载,否则无法生效
- 表现层返回结果的模型类中添加消息属性用来传递消息到页面中
基础篇完结
- pom.xml:配置起步依赖
- application.yml:设置数据源、端口、框架技术相关配置等
- mapper:继承BaseMapper、设置@Mapper注解
- mapper测试类
- service:调用数据层接口或MyBatis-Plus提供的接口快速开发
- service测试类
- controller:基于Restful开发,使用Postman测试跑通功能
- 页面:放置在resources目录下的static目录中